web3 7.6.1__py3-none-any.whl → 7.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ens/async_ens.py +1 -1
- ens/ens.py +1 -1
- web3/_utils/caching/caching_utils.py +64 -0
- web3/_utils/caching/request_caching_validation.py +1 -0
- web3/_utils/events.py +1 -1
- web3/_utils/http_session_manager.py +32 -3
- web3/_utils/module_testing/eth_module.py +5 -18
- web3/_utils/module_testing/module_testing_utils.py +1 -43
- web3/_utils/module_testing/persistent_connection_provider.py +696 -207
- web3/_utils/module_testing/utils.py +99 -33
- web3/beacon/api_endpoints.py +10 -0
- web3/beacon/async_beacon.py +47 -0
- web3/beacon/beacon.py +45 -0
- web3/contract/async_contract.py +2 -206
- web3/contract/base_contract.py +217 -13
- web3/contract/contract.py +2 -205
- web3/datastructures.py +15 -16
- web3/eth/async_eth.py +23 -5
- web3/exceptions.py +7 -0
- web3/main.py +24 -3
- web3/manager.py +140 -48
- web3/method.py +1 -1
- web3/middleware/attrdict.py +12 -22
- web3/middleware/base.py +14 -6
- web3/module.py +17 -21
- web3/providers/async_base.py +23 -14
- web3/providers/base.py +6 -8
- web3/providers/ipc.py +7 -6
- web3/providers/legacy_websocket.py +1 -1
- web3/providers/persistent/async_ipc.py +5 -3
- web3/providers/persistent/persistent.py +121 -17
- web3/providers/persistent/persistent_connection.py +11 -4
- web3/providers/persistent/request_processor.py +49 -41
- web3/providers/persistent/subscription_container.py +56 -0
- web3/providers/persistent/subscription_manager.py +298 -0
- web3/providers/persistent/websocket.py +4 -4
- web3/providers/rpc/async_rpc.py +16 -3
- web3/providers/rpc/rpc.py +9 -5
- web3/types.py +28 -14
- web3/utils/__init__.py +4 -0
- web3/utils/subscriptions.py +289 -0
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/LICENSE +1 -1
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/METADATA +68 -56
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/RECORD +46 -43
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/WHEEL +1 -1
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/top_level.txt +0 -0
ens/async_ens.py
CHANGED
|
@@ -152,12 +152,12 @@ class AsyncENS(BaseENS):
|
|
|
152
152
|
:param int coin_type: if provided, look up the address for this coin type
|
|
153
153
|
:raises InvalidName: if `name` has invalid syntax
|
|
154
154
|
"""
|
|
155
|
-
r = await self.resolver(name)
|
|
156
155
|
if coin_type is None:
|
|
157
156
|
# don't validate `addr(bytes32)` interface id since extended resolvers
|
|
158
157
|
# can implement a "resolve" function as of ENSIP-10
|
|
159
158
|
return cast(ChecksumAddress, await self._resolve(name, "addr"))
|
|
160
159
|
else:
|
|
160
|
+
r = await self.resolver(name)
|
|
161
161
|
await _async_validate_resolver_and_interface_id(
|
|
162
162
|
name, r, ENS_MULTICHAIN_ADDRESS_INTERFACE_ID, "addr(bytes32,uint256)"
|
|
163
163
|
)
|
ens/ens.py
CHANGED
|
@@ -154,12 +154,12 @@ class ENS(BaseENS):
|
|
|
154
154
|
:raises UnsupportedFunction: if the resolver does not support the ``addr()``
|
|
155
155
|
function
|
|
156
156
|
"""
|
|
157
|
-
r = self.resolver(name)
|
|
158
157
|
if coin_type is None:
|
|
159
158
|
# don't validate `addr(bytes32)` interface id since extended resolvers
|
|
160
159
|
# can implement a "resolve" function as of ENSIP-10
|
|
161
160
|
return cast(ChecksumAddress, self._resolve(name, "addr"))
|
|
162
161
|
else:
|
|
162
|
+
r = self.resolver(name)
|
|
163
163
|
_validate_resolver_and_interface_id(
|
|
164
164
|
name, r, ENS_MULTICHAIN_ADDRESS_INTERFACE_ID, "addr(bytes32,uint256)"
|
|
165
165
|
)
|
|
@@ -64,10 +64,12 @@ if TYPE_CHECKING:
|
|
|
64
64
|
from web3.providers import ( # noqa: F401
|
|
65
65
|
AsyncBaseProvider,
|
|
66
66
|
BaseProvider,
|
|
67
|
+
PersistentConnectionProvider,
|
|
67
68
|
)
|
|
68
69
|
from web3.types import ( # noqa: F401
|
|
69
70
|
AsyncMakeRequestFn,
|
|
70
71
|
MakeRequestFn,
|
|
72
|
+
RPCRequest,
|
|
71
73
|
RPCResponse,
|
|
72
74
|
)
|
|
73
75
|
|
|
@@ -367,3 +369,65 @@ def async_handle_request_caching(
|
|
|
367
369
|
# save a reference to the decorator on the wrapped function
|
|
368
370
|
wrapper._decorator = async_handle_request_caching # type: ignore
|
|
369
371
|
return wrapper
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def async_handle_send_caching(
|
|
375
|
+
func: Callable[
|
|
376
|
+
[ASYNC_PROVIDER_TYPE, RPCEndpoint, Any],
|
|
377
|
+
Coroutine[Any, Any, "RPCRequest"],
|
|
378
|
+
],
|
|
379
|
+
) -> Callable[..., Coroutine[Any, Any, "RPCRequest"]]:
|
|
380
|
+
async def wrapper(
|
|
381
|
+
provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
382
|
+
) -> "RPCRequest":
|
|
383
|
+
if is_cacheable_request(provider, method, params):
|
|
384
|
+
request_cache = provider._request_cache
|
|
385
|
+
cache_key = generate_cache_key(
|
|
386
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
387
|
+
)
|
|
388
|
+
cached_response = request_cache.get_cache_entry(cache_key)
|
|
389
|
+
if cached_response is not None:
|
|
390
|
+
# The request data isn't used, this just prevents a cached request from
|
|
391
|
+
# being sent - return an empty request object
|
|
392
|
+
return {"id": -1, "method": RPCEndpoint(""), "params": []}
|
|
393
|
+
return await func(provider, method, params)
|
|
394
|
+
|
|
395
|
+
# save a reference to the decorator on the wrapped function
|
|
396
|
+
wrapper._decorator = async_handle_send_caching # type: ignore
|
|
397
|
+
return wrapper
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def async_handle_recv_caching(
|
|
401
|
+
func: Callable[
|
|
402
|
+
["PersistentConnectionProvider", "RPCRequest"],
|
|
403
|
+
Coroutine[Any, Any, "RPCResponse"],
|
|
404
|
+
]
|
|
405
|
+
) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
|
|
406
|
+
async def wrapper(
|
|
407
|
+
provider: "PersistentConnectionProvider",
|
|
408
|
+
rpc_request: "RPCRequest",
|
|
409
|
+
) -> "RPCResponse":
|
|
410
|
+
method = rpc_request["method"]
|
|
411
|
+
params = rpc_request["params"]
|
|
412
|
+
if is_cacheable_request(provider, method, params):
|
|
413
|
+
request_cache = provider._request_cache
|
|
414
|
+
cache_key = generate_cache_key(
|
|
415
|
+
f"{threading.get_ident()}:{(method, params)}"
|
|
416
|
+
)
|
|
417
|
+
cache_result = request_cache.get_cache_entry(cache_key)
|
|
418
|
+
if cache_result is not None:
|
|
419
|
+
return cache_result
|
|
420
|
+
else:
|
|
421
|
+
response = await func(provider, rpc_request)
|
|
422
|
+
if await _async_should_cache_response(
|
|
423
|
+
provider, method, params, response
|
|
424
|
+
):
|
|
425
|
+
async with provider._request_cache_lock:
|
|
426
|
+
request_cache.cache(cache_key, response)
|
|
427
|
+
return response
|
|
428
|
+
else:
|
|
429
|
+
return await func(provider, rpc_request)
|
|
430
|
+
|
|
431
|
+
# save a reference to the decorator on the wrapped function
|
|
432
|
+
wrapper._decorator = async_handle_recv_caching # type: ignore
|
|
433
|
+
return wrapper
|
web3/_utils/events.py
CHANGED
|
@@ -18,6 +18,7 @@ from aiohttp import (
|
|
|
18
18
|
ClientResponse,
|
|
19
19
|
ClientSession,
|
|
20
20
|
ClientTimeout,
|
|
21
|
+
TCPConnector,
|
|
21
22
|
)
|
|
22
23
|
from eth_typing import (
|
|
23
24
|
URI,
|
|
@@ -119,11 +120,19 @@ class HTTPSessionManager:
|
|
|
119
120
|
def get_response_from_post_request(
|
|
120
121
|
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
121
122
|
) -> requests.Response:
|
|
123
|
+
kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
|
|
122
124
|
session = self.cache_and_return_session(
|
|
123
125
|
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
124
126
|
)
|
|
125
127
|
return session.post(endpoint_uri, *args, **kwargs)
|
|
126
128
|
|
|
129
|
+
def json_make_post_request(
|
|
130
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
131
|
+
) -> Dict[str, Any]:
|
|
132
|
+
response = self.get_response_from_post_request(endpoint_uri, *args, **kwargs)
|
|
133
|
+
response.raise_for_status()
|
|
134
|
+
return response.json()
|
|
135
|
+
|
|
127
136
|
def make_post_request(
|
|
128
137
|
self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
|
|
129
138
|
) -> bytes:
|
|
@@ -142,8 +151,9 @@ class HTTPSessionManager:
|
|
|
142
151
|
else:
|
|
143
152
|
return response.content
|
|
144
153
|
|
|
154
|
+
@staticmethod
|
|
145
155
|
def _handle_streaming_response(
|
|
146
|
-
|
|
156
|
+
response: requests.Response, start: float, timeout: float
|
|
147
157
|
) -> bytes:
|
|
148
158
|
response_body = b""
|
|
149
159
|
for data in response.iter_content():
|
|
@@ -174,7 +184,12 @@ class HTTPSessionManager:
|
|
|
174
184
|
async with async_lock(self.session_pool, self._lock):
|
|
175
185
|
if cache_key not in self.session_cache:
|
|
176
186
|
if session is None:
|
|
177
|
-
session = ClientSession(
|
|
187
|
+
session = ClientSession(
|
|
188
|
+
raise_for_status=True,
|
|
189
|
+
connector=TCPConnector(
|
|
190
|
+
force_close=True, enable_cleanup_closed=True
|
|
191
|
+
),
|
|
192
|
+
)
|
|
178
193
|
|
|
179
194
|
cached_session, evicted_items = self.session_cache.cache(
|
|
180
195
|
cache_key, session
|
|
@@ -213,7 +228,12 @@ class HTTPSessionManager:
|
|
|
213
228
|
)
|
|
214
229
|
|
|
215
230
|
# replace stale session with a new session at the cache key
|
|
216
|
-
_session = ClientSession(
|
|
231
|
+
_session = ClientSession(
|
|
232
|
+
raise_for_status=True,
|
|
233
|
+
connector=TCPConnector(
|
|
234
|
+
force_close=True, enable_cleanup_closed=True
|
|
235
|
+
),
|
|
236
|
+
)
|
|
217
237
|
cached_session, evicted_items = self.session_cache.cache(
|
|
218
238
|
cache_key, _session
|
|
219
239
|
)
|
|
@@ -278,6 +298,15 @@ class HTTPSessionManager:
|
|
|
278
298
|
response = await session.post(endpoint_uri, *args, **kwargs)
|
|
279
299
|
return response
|
|
280
300
|
|
|
301
|
+
async def async_json_make_post_request(
|
|
302
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
303
|
+
) -> Dict[str, Any]:
|
|
304
|
+
response = await self.async_get_response_from_post_request(
|
|
305
|
+
endpoint_uri, *args, **kwargs
|
|
306
|
+
)
|
|
307
|
+
response.raise_for_status()
|
|
308
|
+
return await response.json()
|
|
309
|
+
|
|
281
310
|
async def async_make_post_request(
|
|
282
311
|
self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
|
|
283
312
|
) -> bytes:
|
|
@@ -58,7 +58,6 @@ from web3._utils.module_testing.module_testing_utils import (
|
|
|
58
58
|
assert_contains_log,
|
|
59
59
|
async_mock_offchain_lookup_request_response,
|
|
60
60
|
flaky_geth_dev_mining,
|
|
61
|
-
flaky_with_xfail_on_exception,
|
|
62
61
|
mock_offchain_lookup_request_response,
|
|
63
62
|
)
|
|
64
63
|
from web3._utils.module_testing.utils import (
|
|
@@ -77,7 +76,6 @@ from web3.exceptions import (
|
|
|
77
76
|
MultipleFailedRequests,
|
|
78
77
|
NameNotFound,
|
|
79
78
|
OffchainLookup,
|
|
80
|
-
RequestTimedOut,
|
|
81
79
|
TimeExhausted,
|
|
82
80
|
TooManyRequests,
|
|
83
81
|
TransactionNotFound,
|
|
@@ -1991,9 +1989,8 @@ class AsyncEthModuleTest:
|
|
|
1991
1989
|
# Test with None overflowing
|
|
1992
1990
|
filter_params: FilterParams = {
|
|
1993
1991
|
"fromBlock": BlockNumber(0),
|
|
1994
|
-
"topics": [None, None, None],
|
|
1992
|
+
"topics": [None, None, None, None],
|
|
1995
1993
|
}
|
|
1996
|
-
|
|
1997
1994
|
result = await async_w3.eth.get_logs(filter_params)
|
|
1998
1995
|
assert len(result) == 0
|
|
1999
1996
|
|
|
@@ -2391,10 +2388,7 @@ class AsyncEthModuleTest:
|
|
|
2391
2388
|
with pytest.raises(Web3ValueError):
|
|
2392
2389
|
await async_w3.eth.replace_transaction(txn_hash, txn_params)
|
|
2393
2390
|
|
|
2394
|
-
@
|
|
2395
|
-
reason="Very flaky on CI runs, hard to reproduce locally.",
|
|
2396
|
-
exception=RequestTimedOut,
|
|
2397
|
-
)
|
|
2391
|
+
@flaky_geth_dev_mining
|
|
2398
2392
|
@pytest.mark.asyncio
|
|
2399
2393
|
async def test_async_eth_replace_transaction_gas_price_defaulting_minimum(
|
|
2400
2394
|
self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
|
|
@@ -2418,10 +2412,7 @@ class AsyncEthModuleTest:
|
|
|
2418
2412
|
gas_price * 1.125
|
|
2419
2413
|
) # minimum gas price
|
|
2420
2414
|
|
|
2421
|
-
@
|
|
2422
|
-
reason="Very flaky on CI runs, hard to reproduce locally.",
|
|
2423
|
-
exception=RequestTimedOut,
|
|
2424
|
-
)
|
|
2415
|
+
@flaky_geth_dev_mining
|
|
2425
2416
|
@pytest.mark.asyncio
|
|
2426
2417
|
async def test_async_eth_replace_transaction_gas_price_defaulting_strategy_higher(
|
|
2427
2418
|
self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
|
|
@@ -2437,7 +2428,7 @@ class AsyncEthModuleTest:
|
|
|
2437
2428
|
|
|
2438
2429
|
two_gwei_in_wei = async_w3.to_wei(2, "gwei")
|
|
2439
2430
|
|
|
2440
|
-
def higher_gas_price_strategy(
|
|
2431
|
+
def higher_gas_price_strategy(_async_w3: "AsyncWeb3", _txn: TxParams) -> Wei:
|
|
2441
2432
|
return two_gwei_in_wei
|
|
2442
2433
|
|
|
2443
2434
|
async_w3.eth.set_gas_price_strategy(higher_gas_price_strategy)
|
|
@@ -2450,16 +2441,12 @@ class AsyncEthModuleTest:
|
|
|
2450
2441
|
) # Strategy provides higher gas price
|
|
2451
2442
|
async_w3.eth.set_gas_price_strategy(None) # reset strategy
|
|
2452
2443
|
|
|
2453
|
-
@
|
|
2454
|
-
reason="Very flaky on CI runs, hard to reproduce locally.",
|
|
2455
|
-
exception=RequestTimedOut,
|
|
2456
|
-
)
|
|
2444
|
+
@flaky_geth_dev_mining
|
|
2457
2445
|
@pytest.mark.asyncio
|
|
2458
2446
|
async def test_async_eth_replace_transaction_gas_price_defaulting_strategy_lower(
|
|
2459
2447
|
self, async_w3: "AsyncWeb3", async_keyfile_account_address: ChecksumAddress
|
|
2460
2448
|
) -> None:
|
|
2461
2449
|
gas_price = async_w3.to_wei(2, "gwei")
|
|
2462
|
-
|
|
2463
2450
|
txn_params: TxParams = {
|
|
2464
2451
|
"from": async_keyfile_account_address,
|
|
2465
2452
|
"to": async_keyfile_account_address,
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import functools
|
|
3
|
-
import pytest
|
|
4
2
|
from typing import (
|
|
5
3
|
TYPE_CHECKING,
|
|
6
4
|
Any,
|
|
7
|
-
Callable,
|
|
8
5
|
Collection,
|
|
9
6
|
Dict,
|
|
10
7
|
Generator,
|
|
11
8
|
Literal,
|
|
12
9
|
Sequence,
|
|
13
|
-
Tuple,
|
|
14
|
-
Type,
|
|
15
10
|
Union,
|
|
16
11
|
)
|
|
17
12
|
|
|
@@ -63,50 +58,13 @@ due to timing of the test running as a block is mined.
|
|
|
63
58
|
flaky_geth_dev_mining = flaky(max_runs=3, min_passes=1)
|
|
64
59
|
|
|
65
60
|
|
|
66
|
-
def flaky_with_xfail_on_exception(
|
|
67
|
-
reason: str,
|
|
68
|
-
exception: Union[Type[Exception], Tuple[Type[Exception], ...]],
|
|
69
|
-
max_runs: int = 3,
|
|
70
|
-
min_passes: int = 1,
|
|
71
|
-
) -> Callable[[Any], Any]:
|
|
72
|
-
"""
|
|
73
|
-
Some tests inconsistently fail hard with a particular exception and retrying
|
|
74
|
-
these tests often times does not get them "unstuck". If we've exhausted all flaky
|
|
75
|
-
retries and this expected exception is raised, `xfail` the test with the given
|
|
76
|
-
reason.
|
|
77
|
-
"""
|
|
78
|
-
runs = max_runs
|
|
79
|
-
|
|
80
|
-
def decorator(func: Any) -> Any:
|
|
81
|
-
@flaky(max_runs=max_runs, min_passes=min_passes)
|
|
82
|
-
@functools.wraps(func)
|
|
83
|
-
async def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
|
|
84
|
-
nonlocal runs
|
|
85
|
-
try:
|
|
86
|
-
return await func(self, *args, **kwargs)
|
|
87
|
-
except exception:
|
|
88
|
-
# xfail the test only if the exception is raised and we have exhausted
|
|
89
|
-
# all flaky retries
|
|
90
|
-
if runs == 1:
|
|
91
|
-
pytest.xfail(reason)
|
|
92
|
-
runs -= 1
|
|
93
|
-
pytest.fail(f"xfailed but {runs} run(s) remaining with flaky...")
|
|
94
|
-
except Exception as e:
|
|
95
|
-
# let flaky handle it
|
|
96
|
-
raise e
|
|
97
|
-
|
|
98
|
-
return wrapper
|
|
99
|
-
|
|
100
|
-
return decorator
|
|
101
|
-
|
|
102
|
-
|
|
103
61
|
def assert_contains_log(
|
|
104
62
|
result: Sequence[LogReceipt],
|
|
105
63
|
block_with_txn_with_log: BlockData,
|
|
106
64
|
emitter_contract_address: ChecksumAddress,
|
|
107
65
|
txn_hash_with_log: HexStr,
|
|
108
66
|
) -> None:
|
|
109
|
-
assert len(result)
|
|
67
|
+
assert len(result) > 0
|
|
110
68
|
log_entry = result[0]
|
|
111
69
|
assert log_entry["blockNumber"] == block_with_txn_with_log["number"]
|
|
112
70
|
assert log_entry["blockHash"] == block_with_txn_with_log["hash"]
|