Skip to content

Commit 63dd970

Browse files
committed
Async HTTP Provider
1 parent cfd97bd commit 63dd970

File tree

14 files changed

+259
-12
lines changed

14 files changed

+259
-12
lines changed

newsfragments/1978.feature.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add new AsyncHTTPProvider. No middleware or session caching support yet.
2+
3+
Also adds async ``w3.eth.gas_price``, and async ``w3.isConnected()`` methods.

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
url='https://github.com/ethereum/web3.py',
7373
include_package_data=True,
7474
install_requires=[
75+
"aiohttp>=3.7.4.post0,<4",
7576
"eth-abi>=2.0.0b6,<3.0.0",
7677
"eth-account>=0.5.3,<0.6.0",
7778
"eth-hash[pycryptodome]>=0.2.0,<1.0.0",

tests/integration/go_ethereum/common.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
from web3._utils.module_testing import ( # noqa: F401
7+
AsyncEthModuleTest,
78
EthModuleTest,
89
GoEthereumAdminModuleTest,
910
GoEthereumPersonalModuleTest,
@@ -80,3 +81,7 @@ class GoEthereumAdminModuleTest(GoEthereumAdminModuleTest):
8081

8182
class GoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest):
8283
pass
84+
85+
86+
class GoEthereumAsyncEthModuleTest(AsyncEthModuleTest):
87+
pass

tests/integration/go_ethereum/test_goethereum_http.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,24 @@
44
get_open_port,
55
)
66
from web3 import Web3
7+
from web3.eth import (
8+
AsyncEth,
9+
)
10+
from web3.providers.async_rpc import (
11+
AsyncHTTPProvider,
12+
)
713

814
from .common import (
915
GoEthereumAdminModuleTest,
16+
GoEthereumAsyncEthModuleTest,
1017
GoEthereumEthModuleTest,
1118
GoEthereumNetModuleTest,
1219
GoEthereumPersonalModuleTest,
1320
GoEthereumTest,
1421
GoEthereumVersionModuleTest,
1522
)
1623
from .utils import (
24+
wait_for_aiohttp,
1725
wait_for_http,
1826
)
1927

@@ -63,6 +71,18 @@ def web3(geth_process, endpoint_uri):
6371
return _web3
6472

6573

74+
@pytest.fixture(scope="module")
75+
async def async_w3_http(geth_process, endpoint_uri):
76+
await wait_for_aiohttp(endpoint_uri)
77+
_web3 = Web3(
78+
AsyncHTTPProvider(endpoint_uri),
79+
middlewares=[],
80+
modules={
81+
'async_eth': (AsyncEth,),
82+
})
83+
return _web3
84+
85+
6686
class TestGoEthereumTest(GoEthereumTest):
6787
pass
6888

@@ -97,3 +117,7 @@ class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest):
97117

98118
class TestGoEthereumPersonalModuleTest(GoEthereumPersonalModuleTest):
99119
pass
120+
121+
122+
class TestGoEthereumAsyncEthModuleTest(GoEthereumAsyncEthModuleTest):
123+
pass

tests/integration/go_ethereum/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import socket
33
import time
44

5+
import aiohttp
56
import requests
67

78

@@ -29,6 +30,18 @@ def wait_for_http(endpoint_uri, timeout=60):
2930
break
3031

3132

33+
async def wait_for_aiohttp(endpoint_uri, timeout=60):
34+
start = time.time()
35+
while time.time() < start + timeout:
36+
try:
37+
async with aiohttp.ClientSession() as session:
38+
await session.get(endpoint_uri)
39+
except aiohttp.client_exceptions.ClientConnectorError:
40+
time.sleep(0.01)
41+
else:
42+
break
43+
44+
3245
def wait_for_popen(proc, timeout):
3346
start = time.time()
3447
while time.time() < start + timeout:

web3/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
from web3.providers.rpc import ( # noqa: E402
1919
HTTPProvider,
2020
)
21+
from web3.providers.async_rpc import ( # noqa: E402
22+
AsyncHTTPProvider,
23+
)
2124
from web3.providers.websocket import ( # noqa: E402
2225
WebsocketProvider,
2326
)
@@ -45,4 +48,5 @@
4548
"TestRPCProvider",
4649
"EthereumTesterProvider",
4750
"Account",
51+
"AsyncHTTPProvider",
4852
]

web3/_utils/module_testing/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .eth_module import ( # noqa: F401
2+
AsyncEthModuleTest,
23
EthModuleTest,
34
)
45
from .go_ethereum_admin_module import ( # noqa: F401

web3/_utils/module_testing/eth_module.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@
5858
from web3.contract import Contract # noqa: F401
5959

6060

61+
class AsyncEthModuleTest:
62+
@pytest.mark.asyncio
63+
async def test_eth_gas_price(self, async_w3_http: "Web3") -> None:
64+
gas_price = await async_w3_http.async_eth.gas_price
65+
assert gas_price > 0
66+
67+
@pytest.mark.asyncio
68+
async def test_isConnected(self, async_w3_http: "Web3") -> None:
69+
is_connected = await async_w3_http.isConnected() # type: ignore
70+
assert is_connected is True
71+
72+
6173
class EthModuleTest:
6274
def test_eth_protocol_version(self, web3: "Web3") -> None:
6375
with pytest.warns(DeprecationWarning,

web3/_utils/request.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import os
12
from typing import (
23
Any,
34
)
45

6+
from aiohttp import (
7+
ClientSession,
8+
ClientTimeout,
9+
)
510
from eth_typing import (
611
URI,
712
)
@@ -13,6 +18,10 @@
1318
)
1419

1520

21+
def get_default_http_endpoint() -> URI:
22+
return URI(os.environ.get('WEB3_HTTP_PROVIDER_URI', 'http://localhost:8545'))
23+
24+
1625
def cache_session(endpoint_uri: URI, session: requests.Session) -> None:
1726
cache_key = generate_cache_key(endpoint_uri)
1827
_session_cache[cache_key] = session
@@ -40,3 +49,15 @@ def make_post_request(endpoint_uri: URI, data: bytes, *args: Any, **kwargs: Any)
4049
response.raise_for_status()
4150

4251
return response.content
52+
53+
54+
async def async_make_post_request(
55+
endpoint_uri: URI, data: bytes, *args: Any, **kwargs: Any
56+
) -> bytes:
57+
kwargs.setdefault('timeout', ClientTimeout(10))
58+
async with ClientSession(raise_for_status=True) as session:
59+
async with session.post(endpoint_uri,
60+
data=data,
61+
*args,
62+
**kwargs) as response:
63+
return await response.read()

web3/eth.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,23 @@
104104
)
105105

106106

107-
class Eth(Module):
107+
class BaseEth(Module):
108+
_gas_price: Method[Callable[[], Wei]] = Method(
109+
RPC.eth_gasPrice,
110+
mungers=None,
111+
)
112+
113+
114+
class AsyncEth(BaseEth):
115+
is_async = True
116+
117+
@property
118+
async def gas_price(self) -> Wei:
119+
# types ignored b/c mypy conflict with BlockingEth properties
120+
return await self._gas_price() # type: ignore
121+
122+
123+
class Eth(BaseEth, Module):
108124
account = Account()
109125
_default_account: Union[ChecksumAddress, Empty] = empty
110126
_default_block: BlockIdentifier = "latest"
@@ -175,11 +191,6 @@ def mining(self) -> bool:
175191
def hashrate(self) -> int:
176192
return self.get_hashrate()
177193

178-
_gas_price: Method[Callable[[], Wei]] = Method(
179-
RPC.eth_gasPrice,
180-
mungers=None,
181-
)
182-
183194
@property
184195
def gas_price(self) -> Wei:
185196
return self._gas_price()

0 commit comments

Comments
 (0)