web3 6.20.2__py3-none-any.whl → 7.0.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/__init__.py +13 -2
- ens/_normalization.py +2 -17
- ens/async_ens.py +33 -21
- ens/base_ens.py +3 -1
- ens/ens.py +16 -11
- ens/exceptions.py +16 -29
- ens/specs/nf.json +1 -1
- ens/specs/normalization_spec.json +1 -1
- ens/utils.py +52 -63
- web3/__init__.py +20 -24
- web3/_utils/abi.py +115 -271
- web3/_utils/async_transactions.py +7 -4
- web3/_utils/batching.py +217 -0
- web3/_utils/blocks.py +6 -2
- web3/_utils/caching.py +128 -5
- web3/_utils/compat/__init__.py +2 -3
- web3/_utils/contract_sources/compile_contracts.py +1 -1
- web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
- web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
- web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
- web3/_utils/contracts.py +130 -236
- web3/_utils/datatypes.py +5 -1
- web3/_utils/decorators.py +13 -23
- web3/_utils/empty.py +1 -1
- web3/_utils/encoding.py +16 -12
- web3/_utils/ens.py +2 -1
- web3/_utils/error_formatters_utils.py +5 -3
- web3/_utils/events.py +66 -69
- web3/_utils/fee_utils.py +1 -3
- web3/_utils/filters.py +24 -22
- web3/_utils/formatters.py +2 -2
- web3/_utils/http.py +5 -3
- web3/_utils/http_session_manager.py +303 -0
- web3/_utils/math.py +14 -15
- web3/_utils/method_formatters.py +34 -36
- web3/_utils/module.py +2 -1
- web3/_utils/module_testing/__init__.py +0 -3
- web3/_utils/module_testing/eth_module.py +695 -643
- web3/_utils/module_testing/module_testing_utils.py +61 -34
- web3/_utils/module_testing/persistent_connection_provider.py +56 -25
- web3/_utils/module_testing/utils.py +258 -0
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/_utils/normalizers.py +13 -11
- web3/_utils/rpc_abi.py +5 -32
- web3/_utils/threads.py +8 -7
- web3/_utils/transactions.py +14 -12
- web3/_utils/type_conversion.py +5 -1
- web3/_utils/validation.py +17 -17
- web3/auto/gethdev.py +7 -2
- web3/beacon/__init__.py +6 -1
- web3/beacon/async_beacon.py +9 -5
- web3/beacon/{main.py → beacon.py} +7 -5
- web3/contract/__init__.py +7 -0
- web3/contract/async_contract.py +47 -46
- web3/contract/base_contract.py +183 -158
- web3/contract/contract.py +49 -43
- web3/contract/utils.py +203 -59
- web3/datastructures.py +79 -31
- web3/eth/__init__.py +7 -0
- web3/eth/async_eth.py +39 -51
- web3/eth/base_eth.py +17 -10
- web3/eth/eth.py +30 -68
- web3/exceptions.py +108 -82
- web3/gas_strategies/time_based.py +8 -6
- web3/geth.py +1 -254
- web3/main.py +75 -122
- web3/manager.py +316 -146
- web3/method.py +38 -31
- web3/middleware/__init__.py +67 -89
- web3/middleware/attrdict.py +36 -49
- web3/middleware/base.py +174 -0
- web3/middleware/buffered_gas_estimate.py +20 -21
- web3/middleware/filter.py +157 -117
- web3/middleware/formatting.py +124 -108
- web3/middleware/gas_price_strategy.py +20 -32
- web3/middleware/names.py +29 -26
- web3/middleware/proof_of_authority.py +68 -0
- web3/middleware/pythonic.py +2 -2
- web3/middleware/signing.py +74 -89
- web3/middleware/stalecheck.py +52 -79
- web3/middleware/validation.py +5 -13
- web3/module.py +54 -10
- web3/providers/__init__.py +10 -6
- web3/providers/async_base.py +117 -39
- web3/providers/auto.py +3 -3
- web3/providers/base.py +89 -33
- web3/providers/eth_tester/__init__.py +5 -0
- web3/providers/eth_tester/defaults.py +1 -64
- web3/providers/eth_tester/main.py +99 -31
- web3/providers/eth_tester/middleware.py +45 -73
- web3/providers/ipc.py +42 -46
- web3/providers/{websocket/websocket.py → legacy_websocket.py} +32 -7
- web3/providers/persistent/__init__.py +22 -0
- web3/providers/persistent/async_ipc.py +153 -0
- web3/providers/{persistent.py → persistent/persistent.py} +106 -25
- web3/providers/persistent/persistent_connection.py +84 -0
- web3/providers/{websocket → persistent}/request_processor.py +94 -32
- web3/providers/persistent/utils.py +43 -0
- web3/providers/{websocket/websocket_v2.py → persistent/websocket.py} +29 -28
- web3/providers/rpc/__init__.py +11 -0
- web3/providers/rpc/async_rpc.py +171 -0
- web3/providers/rpc/rpc.py +179 -0
- web3/providers/rpc/utils.py +92 -0
- web3/testing.py +4 -4
- web3/tools/benchmark/main.py +22 -22
- web3/tools/benchmark/node.py +2 -8
- web3/tools/benchmark/reporting.py +2 -2
- web3/tools/benchmark/utils.py +1 -1
- web3/tracing.py +9 -5
- web3/types.py +30 -107
- web3/utils/__init__.py +58 -5
- web3/utils/abi.py +575 -10
- web3/utils/async_exception_handling.py +19 -7
- web3/utils/caching.py +32 -13
- web3/utils/exception_handling.py +7 -5
- {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/LICENSE +1 -1
- web3-7.0.0.dist-info/METADATA +112 -0
- web3-7.0.0.dist-info/RECORD +167 -0
- {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/WHEEL +1 -1
- {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/top_level.txt +0 -1
- ethpm/__init__.py +0 -20
- ethpm/_utils/__init__.py +0 -0
- ethpm/_utils/backend.py +0 -93
- ethpm/_utils/cache.py +0 -44
- ethpm/_utils/chains.py +0 -119
- ethpm/_utils/contract.py +0 -35
- ethpm/_utils/deployments.py +0 -145
- ethpm/_utils/ipfs.py +0 -116
- ethpm/_utils/protobuf/__init__.py +0 -0
- ethpm/_utils/protobuf/ipfs_file_pb2.py +0 -33
- ethpm/_utils/registry.py +0 -29
- ethpm/assets/__init__.py +0 -0
- ethpm/assets/ens/v3.json +0 -1
- ethpm/assets/escrow/with_bytecode_v3.json +0 -1
- ethpm/assets/ipfs_file.proto +0 -32
- ethpm/assets/owned/output_v3.json +0 -1
- ethpm/assets/owned/with_contract_type_v3.json +0 -1
- ethpm/assets/registry/contracts/Authority.sol +0 -156
- ethpm/assets/registry/contracts/IndexedOrderedSetLib.sol +0 -106
- ethpm/assets/registry/contracts/PackageDB.sol +0 -225
- ethpm/assets/registry/contracts/PackageRegistry.sol +0 -361
- ethpm/assets/registry/contracts/PackageRegistryInterface.sol +0 -97
- ethpm/assets/registry/contracts/ReleaseDB.sol +0 -309
- ethpm/assets/registry/contracts/ReleaseValidator.sol +0 -152
- ethpm/assets/registry/solc_input.json +0 -1
- ethpm/assets/registry/solc_output.json +0 -1
- ethpm/assets/registry/v3.json +0 -1
- ethpm/assets/safe-math-lib/v3-strict-no-deployments.json +0 -1
- ethpm/assets/simple-registry/contracts/Ownable.sol +0 -63
- ethpm/assets/simple-registry/contracts/PackageRegistry.sol +0 -373
- ethpm/assets/simple-registry/contracts/PackageRegistryInterface.sol +0 -96
- ethpm/assets/simple-registry/solc_input.json +0 -33
- ethpm/assets/simple-registry/solc_output.json +0 -1
- ethpm/assets/simple-registry/v3.json +0 -1
- ethpm/assets/standard-token/output_v3.json +0 -1
- ethpm/assets/standard-token/with_bytecode_v3.json +0 -1
- ethpm/assets/vyper_registry/0.1.0.json +0 -1
- ethpm/assets/vyper_registry/registry.vy +0 -216
- ethpm/assets/vyper_registry/registry_with_delete.vy +0 -244
- ethpm/backends/__init__.py +0 -0
- ethpm/backends/base.py +0 -43
- ethpm/backends/http.py +0 -108
- ethpm/backends/ipfs.py +0 -219
- ethpm/backends/registry.py +0 -154
- ethpm/constants.py +0 -17
- ethpm/contract.py +0 -187
- ethpm/dependencies.py +0 -58
- ethpm/deployments.py +0 -80
- ethpm/ethpm-spec/examples/escrow/1.0.0-pretty.json +0 -146
- ethpm/ethpm-spec/examples/escrow/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/escrow/contracts/Escrow.sol +0 -32
- ethpm/ethpm-spec/examples/escrow/contracts/SafeSendLib.sol +0 -20
- ethpm/ethpm-spec/examples/escrow/v3-pretty.json +0 -171
- ethpm/ethpm-spec/examples/escrow/v3.json +0 -1
- ethpm/ethpm-spec/examples/owned/1.0.0-pretty.json +0 -21
- ethpm/ethpm-spec/examples/owned/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/owned/contracts/Owned.sol +0 -12
- ethpm/ethpm-spec/examples/owned/v3-pretty.json +0 -27
- ethpm/ethpm-spec/examples/owned/v3.json +0 -1
- ethpm/ethpm-spec/examples/piper-coin/1.0.0-pretty.json +0 -31
- ethpm/ethpm-spec/examples/piper-coin/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/piper-coin/v3-pretty.json +0 -21
- ethpm/ethpm-spec/examples/piper-coin/v3.json +0 -1
- ethpm/ethpm-spec/examples/safe-math-lib/1.0.0-pretty.json +0 -85
- ethpm/ethpm-spec/examples/safe-math-lib/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/safe-math-lib/contracts/SafeMathLib.sol +0 -24
- ethpm/ethpm-spec/examples/safe-math-lib/v3-pretty.json +0 -117
- ethpm/ethpm-spec/examples/safe-math-lib/v3.json +0 -1
- ethpm/ethpm-spec/examples/standard-token/1.0.0-pretty.json +0 -55
- ethpm/ethpm-spec/examples/standard-token/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/standard-token/contracts/AbstractToken.sol +0 -20
- ethpm/ethpm-spec/examples/standard-token/contracts/StandardToken.sol +0 -84
- ethpm/ethpm-spec/examples/standard-token/v3-pretty.json +0 -460
- ethpm/ethpm-spec/examples/standard-token/v3.json +0 -1
- ethpm/ethpm-spec/examples/transferable/1.0.0-pretty.json +0 -21
- ethpm/ethpm-spec/examples/transferable/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/transferable/contracts/Transferable.sol +0 -14
- ethpm/ethpm-spec/examples/transferable/v3-pretty.json +0 -27
- ethpm/ethpm-spec/examples/transferable/v3.json +0 -1
- ethpm/ethpm-spec/examples/wallet/1.0.0-pretty.json +0 -120
- ethpm/ethpm-spec/examples/wallet/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/wallet/contracts/Wallet.sol +0 -41
- ethpm/ethpm-spec/examples/wallet/v3-pretty.json +0 -181
- ethpm/ethpm-spec/examples/wallet/v3.json +0 -1
- ethpm/ethpm-spec/examples/wallet-with-send/1.0.0-pretty.json +0 -135
- ethpm/ethpm-spec/examples/wallet-with-send/1.0.0.json +0 -1
- ethpm/ethpm-spec/examples/wallet-with-send/contracts/WalletWithSend.sol +0 -18
- ethpm/ethpm-spec/examples/wallet-with-send/v3-pretty.json +0 -207
- ethpm/ethpm-spec/examples/wallet-with-send/v3.json +0 -1
- ethpm/ethpm-spec/spec/package.spec.json +0 -379
- ethpm/ethpm-spec/spec/v3.spec.json +0 -483
- ethpm/exceptions.py +0 -68
- ethpm/package.py +0 -438
- ethpm/tools/__init__.py +0 -4
- ethpm/tools/builder.py +0 -930
- ethpm/tools/checker.py +0 -312
- ethpm/tools/get_manifest.py +0 -19
- ethpm/uri.py +0 -141
- ethpm/validation/__init__.py +0 -0
- ethpm/validation/manifest.py +0 -146
- ethpm/validation/misc.py +0 -39
- ethpm/validation/package.py +0 -80
- ethpm/validation/uri.py +0 -163
- web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
- web3/_utils/miner.py +0 -88
- web3/_utils/module_testing/go_ethereum_personal_module.py +0 -323
- web3/_utils/request.py +0 -265
- web3/middleware/abi.py +0 -11
- web3/middleware/async_cache.py +0 -99
- web3/middleware/cache.py +0 -374
- web3/middleware/exception_handling.py +0 -49
- web3/middleware/exception_retry_request.py +0 -188
- web3/middleware/fixture.py +0 -190
- web3/middleware/geth_poa.py +0 -81
- web3/middleware/normalize_request_parameters.py +0 -11
- web3/middleware/simulate_unmined_transaction.py +0 -43
- web3/pm.py +0 -602
- web3/providers/async_rpc.py +0 -99
- web3/providers/rpc.py +0 -98
- web3/providers/websocket/__init__.py +0 -11
- web3/providers/websocket/websocket_connection.py +0 -42
- web3/tools/__init__.py +0 -4
- web3/tools/pytest_ethereum/__init__.py +0 -0
- web3/tools/pytest_ethereum/_utils.py +0 -145
- web3/tools/pytest_ethereum/deployer.py +0 -48
- web3/tools/pytest_ethereum/exceptions.py +0 -22
- web3/tools/pytest_ethereum/linker.py +0 -128
- web3/tools/pytest_ethereum/plugins.py +0 -33
- web3-6.20.2.dist-info/METADATA +0 -103
- web3-6.20.2.dist-info/RECORD +0 -283
- web3-6.20.2.dist-info/entry_points.txt +0 -2
- /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import functools
|
|
1
3
|
import pytest
|
|
2
|
-
import time
|
|
3
4
|
from typing import (
|
|
4
5
|
TYPE_CHECKING,
|
|
5
6
|
Any,
|
|
7
|
+
Callable,
|
|
6
8
|
Collection,
|
|
7
9
|
Dict,
|
|
8
10
|
Generator,
|
|
9
|
-
|
|
11
|
+
Literal,
|
|
10
12
|
Sequence,
|
|
13
|
+
Tuple,
|
|
14
|
+
Type,
|
|
11
15
|
Union,
|
|
12
16
|
)
|
|
13
17
|
|
|
14
18
|
from aiohttp import (
|
|
19
|
+
ClientSession,
|
|
15
20
|
ClientTimeout,
|
|
16
21
|
)
|
|
17
22
|
from eth_typing import (
|
|
@@ -27,14 +32,10 @@ from flaky import (
|
|
|
27
32
|
from hexbytes import (
|
|
28
33
|
HexBytes,
|
|
29
34
|
)
|
|
35
|
+
import requests
|
|
30
36
|
|
|
31
|
-
from web3._utils.
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
from web3._utils.request import (
|
|
35
|
-
async_cache_and_return_session,
|
|
36
|
-
asyncio,
|
|
37
|
-
cache_and_return_session,
|
|
37
|
+
from web3._utils.http import (
|
|
38
|
+
DEFAULT_HTTP_TIMEOUT,
|
|
38
39
|
)
|
|
39
40
|
from web3.types import (
|
|
40
41
|
BlockData,
|
|
@@ -43,7 +44,9 @@ from web3.types import (
|
|
|
43
44
|
|
|
44
45
|
if TYPE_CHECKING:
|
|
45
46
|
from _pytest.monkeypatch import MonkeyPatch # noqa: F401
|
|
46
|
-
from aiohttp import
|
|
47
|
+
from aiohttp import ( # noqa: F401
|
|
48
|
+
ClientResponse,
|
|
49
|
+
)
|
|
47
50
|
from requests import Response # noqa: F401
|
|
48
51
|
|
|
49
52
|
from web3 import Web3 # noqa: F401
|
|
@@ -57,19 +60,44 @@ flaky_geth_dev_mining decorator for tests requiring a pending block
|
|
|
57
60
|
for the duration of the test. This behavior can be flaky
|
|
58
61
|
due to timing of the test running as a block is mined.
|
|
59
62
|
"""
|
|
60
|
-
flaky_geth_dev_mining = flaky(max_runs=3)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
flaky_geth_dev_mining = flaky(max_runs=3, min_passes=1)
|
|
64
|
+
|
|
65
|
+
|
|
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
|
|
73
101
|
|
|
74
102
|
|
|
75
103
|
def assert_contains_log(
|
|
@@ -117,13 +145,13 @@ def mock_offchain_lookup_request_response(
|
|
|
117
145
|
|
|
118
146
|
# mock response only to specified url while validating appropriate fields
|
|
119
147
|
if url_from_args == mocked_request_url:
|
|
120
|
-
assert kwargs["timeout"] ==
|
|
148
|
+
assert kwargs["timeout"] == DEFAULT_HTTP_TIMEOUT
|
|
121
149
|
if http_method.upper() == "POST":
|
|
122
150
|
assert kwargs["data"] == {"data": calldata, "sender": sender}
|
|
123
151
|
return MockedResponse()
|
|
124
152
|
|
|
125
153
|
# else, make a normal request (no mocking)
|
|
126
|
-
session =
|
|
154
|
+
session = requests.Session()
|
|
127
155
|
return session.request(method=http_method.upper(), url=url_from_args, **kwargs)
|
|
128
156
|
|
|
129
157
|
monkeypatch.setattr(
|
|
@@ -167,29 +195,29 @@ def async_mock_offchain_lookup_request_response(
|
|
|
167
195
|
|
|
168
196
|
# mock response only to specified url while validating appropriate fields
|
|
169
197
|
if url_from_args == mocked_request_url:
|
|
170
|
-
assert kwargs["timeout"] == ClientTimeout(
|
|
198
|
+
assert kwargs["timeout"] == ClientTimeout(DEFAULT_HTTP_TIMEOUT)
|
|
171
199
|
if http_method.upper() == "post":
|
|
172
200
|
assert kwargs["data"] == {"data": calldata, "sender": sender}
|
|
173
201
|
return AsyncMockedResponse()
|
|
174
202
|
|
|
175
203
|
# else, make a normal request (no mocking)
|
|
176
|
-
session =
|
|
177
|
-
|
|
204
|
+
session = ClientSession()
|
|
205
|
+
response = await session.request(
|
|
178
206
|
method=http_method.upper(), url=url_from_args, **kwargs
|
|
179
207
|
)
|
|
208
|
+
await session.close()
|
|
209
|
+
return response
|
|
180
210
|
|
|
181
211
|
monkeypatch.setattr(
|
|
182
212
|
f"aiohttp.ClientSession.{http_method.lower()}", _mock_specific_request
|
|
183
213
|
)
|
|
184
214
|
|
|
185
215
|
|
|
186
|
-
class
|
|
216
|
+
class WebSocketMessageStreamMock:
|
|
187
217
|
closed: bool = False
|
|
188
218
|
|
|
189
219
|
def __init__(
|
|
190
|
-
self,
|
|
191
|
-
messages: Optional[Collection[bytes]] = None,
|
|
192
|
-
raise_exception: Optional[Exception] = None,
|
|
220
|
+
self, messages: Collection[bytes] = None, raise_exception: Exception = None
|
|
193
221
|
) -> None:
|
|
194
222
|
self.queue = asyncio.Queue() # type: ignore # py38 issue
|
|
195
223
|
for msg in messages or []:
|
|
@@ -206,12 +234,11 @@ class WebsocketMessageStreamMock:
|
|
|
206
234
|
return self
|
|
207
235
|
|
|
208
236
|
async def __anext__(self) -> bytes:
|
|
209
|
-
return await self.
|
|
237
|
+
return await self.queue.get()
|
|
210
238
|
|
|
211
239
|
async def recv(self) -> bytes:
|
|
212
240
|
if self.raise_exception:
|
|
213
241
|
raise self.raise_exception
|
|
214
|
-
|
|
215
242
|
return await self.queue.get()
|
|
216
243
|
|
|
217
244
|
@staticmethod
|
|
@@ -19,18 +19,35 @@ from web3.datastructures import (
|
|
|
19
19
|
AttributeDict,
|
|
20
20
|
)
|
|
21
21
|
from web3.middleware import (
|
|
22
|
-
|
|
22
|
+
ExtraDataToPOAMiddleware,
|
|
23
23
|
)
|
|
24
24
|
from web3.types import (
|
|
25
25
|
FormattedEthSubscriptionResponse,
|
|
26
|
+
RPCEndpoint,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
if TYPE_CHECKING:
|
|
29
30
|
from web3.main import (
|
|
30
|
-
|
|
31
|
+
AsyncWeb3,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
SOME_BLOCK_KEYS = [
|
|
36
|
+
"number",
|
|
37
|
+
"hash",
|
|
38
|
+
"parentHash",
|
|
39
|
+
"transactionsRoot",
|
|
40
|
+
"stateRoot",
|
|
41
|
+
"receiptsRoot",
|
|
42
|
+
"size",
|
|
43
|
+
"gasLimit",
|
|
44
|
+
"gasUsed",
|
|
45
|
+
"timestamp",
|
|
46
|
+
"transactions",
|
|
47
|
+
"baseFeePerGas",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
34
51
|
class PersistentConnectionProviderTest:
|
|
35
52
|
@pytest.mark.asyncio
|
|
36
53
|
@pytest.mark.parametrize(
|
|
@@ -278,7 +295,7 @@ class PersistentConnectionProviderTest:
|
|
|
278
295
|
)
|
|
279
296
|
async def test_async_eth_subscribe_mocked(
|
|
280
297
|
self,
|
|
281
|
-
async_w3: "
|
|
298
|
+
async_w3: "AsyncWeb3",
|
|
282
299
|
subscription_params: Tuple[Any, ...],
|
|
283
300
|
ws_subscription_response: Dict[str, Any],
|
|
284
301
|
expected_formatted_result: Any,
|
|
@@ -295,21 +312,22 @@ class PersistentConnectionProviderTest:
|
|
|
295
312
|
ws_subscription_response, subscription=True
|
|
296
313
|
)
|
|
297
314
|
|
|
298
|
-
async for msg in async_w3.
|
|
315
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
299
316
|
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
300
317
|
assert response["subscription"] == sub_id
|
|
301
318
|
assert response["result"] == expected_formatted_result
|
|
302
319
|
|
|
303
320
|
# only testing one message, so break here
|
|
321
|
+
await async_w3.eth.unsubscribe(sub_id)
|
|
304
322
|
break
|
|
305
323
|
|
|
306
324
|
@pytest.mark.asyncio
|
|
307
|
-
async def
|
|
325
|
+
async def test_async_extradata_poa_middleware_on_eth_subscription(
|
|
308
326
|
self,
|
|
309
|
-
async_w3: "
|
|
327
|
+
async_w3: "AsyncWeb3",
|
|
310
328
|
) -> None:
|
|
311
329
|
async_w3.middleware_onion.inject(
|
|
312
|
-
|
|
330
|
+
ExtraDataToPOAMiddleware, "poa_middleware", layer=0
|
|
313
331
|
)
|
|
314
332
|
|
|
315
333
|
sub_id = await async_w3.eth.subscribe("newHeads")
|
|
@@ -331,7 +349,7 @@ class PersistentConnectionProviderTest:
|
|
|
331
349
|
subscription=True,
|
|
332
350
|
)
|
|
333
351
|
|
|
334
|
-
async for msg in async_w3.
|
|
352
|
+
async for msg in async_w3.socket.process_subscriptions():
|
|
335
353
|
response = cast(FormattedEthSubscriptionResponse, msg)
|
|
336
354
|
assert response.keys() == {"subscription", "result"}
|
|
337
355
|
assert response["subscription"] == sub_id
|
|
@@ -348,7 +366,7 @@ class PersistentConnectionProviderTest:
|
|
|
348
366
|
@pytest.mark.asyncio
|
|
349
367
|
async def test_asyncio_gather_for_multiple_requests_matches_the_responses(
|
|
350
368
|
self,
|
|
351
|
-
async_w3: "
|
|
369
|
+
async_w3: "AsyncWeb3",
|
|
352
370
|
) -> None:
|
|
353
371
|
(
|
|
354
372
|
latest,
|
|
@@ -371,22 +389,10 @@ class PersistentConnectionProviderTest:
|
|
|
371
389
|
assert isinstance(pending, AttributeDict)
|
|
372
390
|
|
|
373
391
|
# assert block values
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
"transactionsRoot",
|
|
379
|
-
"stateRoot",
|
|
380
|
-
"receiptsRoot",
|
|
381
|
-
"size",
|
|
382
|
-
"gasLimit",
|
|
383
|
-
"gasUsed",
|
|
384
|
-
"timestamp",
|
|
385
|
-
"transactions",
|
|
386
|
-
"baseFeePerGas",
|
|
387
|
-
]
|
|
388
|
-
assert all(k in latest.keys() for k in some_block_keys)
|
|
389
|
-
assert all(k in pending.keys() for k in some_block_keys)
|
|
392
|
+
assert latest is not None
|
|
393
|
+
assert all(k in latest.keys() for k in SOME_BLOCK_KEYS)
|
|
394
|
+
assert pending is not None
|
|
395
|
+
assert all(k in pending.keys() for k in SOME_BLOCK_KEYS)
|
|
390
396
|
|
|
391
397
|
assert isinstance(block_num, int)
|
|
392
398
|
assert latest["number"] == block_num
|
|
@@ -394,3 +400,28 @@ class PersistentConnectionProviderTest:
|
|
|
394
400
|
assert isinstance(chain_id, int)
|
|
395
401
|
assert isinstance(chain_id2, int)
|
|
396
402
|
assert isinstance(chain_id3, int)
|
|
403
|
+
|
|
404
|
+
@pytest.mark.asyncio
|
|
405
|
+
async def test_public_socket_api(self, async_w3: "AsyncWeb3") -> None:
|
|
406
|
+
# send a request over the socket
|
|
407
|
+
await async_w3.socket.send(
|
|
408
|
+
RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
# recv and validate the unprocessed response
|
|
412
|
+
response = await async_w3.socket.recv()
|
|
413
|
+
assert "id" in response, "Expected 'id' key in response."
|
|
414
|
+
assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
|
|
415
|
+
assert "result" in response, "Expected 'result' key in response."
|
|
416
|
+
assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
|
|
417
|
+
assert not isinstance(response["result"]["number"], int) # assert not processed
|
|
418
|
+
|
|
419
|
+
# make a request over the socket
|
|
420
|
+
response = await async_w3.socket.make_request(
|
|
421
|
+
RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
|
|
422
|
+
)
|
|
423
|
+
assert "id" in response, "Expected 'id' key in response."
|
|
424
|
+
assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
|
|
425
|
+
assert "result" in response, "Expected 'result' key in response."
|
|
426
|
+
assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
|
|
427
|
+
assert not isinstance(response["result"]["number"], int) # assert not processed
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from asyncio import (
|
|
2
|
+
iscoroutinefunction,
|
|
3
|
+
)
|
|
4
|
+
import copy
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
Any,
|
|
8
|
+
Dict,
|
|
9
|
+
Union,
|
|
10
|
+
cast,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from toolz import (
|
|
14
|
+
merge,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from web3 import ( # noqa: F401
|
|
19
|
+
AsyncWeb3,
|
|
20
|
+
Web3,
|
|
21
|
+
)
|
|
22
|
+
from web3._utils.compat import ( # noqa: F401
|
|
23
|
+
Self,
|
|
24
|
+
)
|
|
25
|
+
from web3.types import ( # noqa: F401
|
|
26
|
+
AsyncMakeRequestFn,
|
|
27
|
+
MakeRequestFn,
|
|
28
|
+
RPCEndpoint,
|
|
29
|
+
RPCResponse,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RequestMocker:
|
|
34
|
+
"""
|
|
35
|
+
Context manager to mock requests made by a web3 instance. This is meant to be used
|
|
36
|
+
via a ``request_mocker`` fixture defined within the appropriate context.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
-------
|
|
40
|
+
|
|
41
|
+
def test_my_w3(w3, request_mocker):
|
|
42
|
+
assert w3.eth.block_number == 0
|
|
43
|
+
|
|
44
|
+
with request_mocker(w3, mock_results={"eth_blockNumber": "0x1"}):
|
|
45
|
+
assert w3.eth.block_number == 1
|
|
46
|
+
|
|
47
|
+
assert w3.eth.block_number == 0
|
|
48
|
+
|
|
49
|
+
Example with async and a mocked response object:
|
|
50
|
+
-----------------------------------------------
|
|
51
|
+
|
|
52
|
+
async def test_my_w3(async_w3, request_mocker):
|
|
53
|
+
def _iter_responses():
|
|
54
|
+
while True:
|
|
55
|
+
yield {"error": {"message": "transaction indexing in progress"}}
|
|
56
|
+
yield {"error": {"message": "transaction indexing in progress"}}
|
|
57
|
+
yield {"result": {"status": "0x1"}}
|
|
58
|
+
|
|
59
|
+
iter_responses = _iter_responses()
|
|
60
|
+
|
|
61
|
+
async with request_mocker(
|
|
62
|
+
async_w3,
|
|
63
|
+
mock_responses={
|
|
64
|
+
"eth_getTransactionReceipt": lambda *_: next(iter_responses)
|
|
65
|
+
},
|
|
66
|
+
):
|
|
67
|
+
# assert that the first two error responses are handled and the result
|
|
68
|
+
# is eventually returned when present
|
|
69
|
+
assert await w3.eth.get_transaction_receipt("0x1") == "0x1"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
- ``mock_results`` is a dict mapping method names to the desired "result" object of
|
|
73
|
+
the RPC response.
|
|
74
|
+
- ``mock_errors`` is a dict mapping method names to the desired
|
|
75
|
+
"error" object of the RPC response.
|
|
76
|
+
-``mock_responses`` is a dict mapping method names to the entire RPC response
|
|
77
|
+
object. This can be useful if you wish to return an iterator which returns
|
|
78
|
+
different responses on each call to the method.
|
|
79
|
+
|
|
80
|
+
If a method name is not present in any of the dicts above, the request is made as
|
|
81
|
+
usual.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
w3: Union["AsyncWeb3", "Web3"],
|
|
88
|
+
mock_results: Dict[Union["RPCEndpoint", str], Any] = None,
|
|
89
|
+
mock_errors: Dict[Union["RPCEndpoint", str], Any] = None,
|
|
90
|
+
mock_responses: Dict[Union["RPCEndpoint", str], Any] = None,
|
|
91
|
+
):
|
|
92
|
+
self.w3 = w3
|
|
93
|
+
self.mock_results = mock_results or {}
|
|
94
|
+
self.mock_errors = mock_errors or {}
|
|
95
|
+
self.mock_responses = mock_responses or {}
|
|
96
|
+
self._make_request: Union[
|
|
97
|
+
"AsyncMakeRequestFn", "MakeRequestFn"
|
|
98
|
+
] = w3.provider.make_request
|
|
99
|
+
|
|
100
|
+
def __enter__(self) -> "Self":
|
|
101
|
+
# mypy error: Cannot assign to a method
|
|
102
|
+
self.w3.provider.make_request = self._mock_request_handler # type: ignore[method-assign] # noqa: E501
|
|
103
|
+
# reset request func cache to re-build request_func with mocked make_request
|
|
104
|
+
self.w3.provider._request_func_cache = (None, None)
|
|
105
|
+
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
# define __exit__ with typing information
|
|
109
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
|
110
|
+
# mypy error: Cannot assign to a method
|
|
111
|
+
self.w3.provider.make_request = self._make_request # type: ignore[method-assign] # noqa: E501
|
|
112
|
+
# reset request func cache to re-build request_func with original make_request
|
|
113
|
+
self.w3.provider._request_func_cache = (None, None)
|
|
114
|
+
|
|
115
|
+
def _mock_request_handler(
|
|
116
|
+
self, method: "RPCEndpoint", params: Any
|
|
117
|
+
) -> "RPCResponse":
|
|
118
|
+
self.w3 = cast("Web3", self.w3)
|
|
119
|
+
self._make_request = cast("MakeRequestFn", self._make_request)
|
|
120
|
+
|
|
121
|
+
if all(
|
|
122
|
+
method not in mock_dict
|
|
123
|
+
for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
|
|
124
|
+
):
|
|
125
|
+
return self._make_request(method, params)
|
|
126
|
+
|
|
127
|
+
request_id = (
|
|
128
|
+
next(copy.deepcopy(self.w3.provider.request_counter))
|
|
129
|
+
if hasattr(self.w3.provider, "request_counter")
|
|
130
|
+
else 1
|
|
131
|
+
)
|
|
132
|
+
response_dict = {"jsonrpc": "2.0", "id": request_id}
|
|
133
|
+
|
|
134
|
+
if method in self.mock_responses:
|
|
135
|
+
mock_return = self.mock_responses[method]
|
|
136
|
+
if callable(mock_return):
|
|
137
|
+
mock_return = mock_return(method, params)
|
|
138
|
+
|
|
139
|
+
if "result" in mock_return:
|
|
140
|
+
mock_return = {"result": mock_return["result"]}
|
|
141
|
+
elif "error" in mock_return:
|
|
142
|
+
mock_return = self._create_error_object(mock_return["error"])
|
|
143
|
+
|
|
144
|
+
mocked_response = merge(response_dict, mock_return)
|
|
145
|
+
elif method in self.mock_results:
|
|
146
|
+
mock_return = self.mock_results[method]
|
|
147
|
+
if callable(mock_return):
|
|
148
|
+
mock_return = mock_return(method, params)
|
|
149
|
+
mocked_response = merge(response_dict, {"result": mock_return})
|
|
150
|
+
elif method in self.mock_errors:
|
|
151
|
+
error = self.mock_errors[method]
|
|
152
|
+
if callable(error):
|
|
153
|
+
error = error(method, params)
|
|
154
|
+
mocked_response = merge(response_dict, self._create_error_object(error))
|
|
155
|
+
else:
|
|
156
|
+
raise Exception("Invariant: unreachable code path")
|
|
157
|
+
|
|
158
|
+
decorator = getattr(self._make_request, "_decorator", None)
|
|
159
|
+
if decorator is not None:
|
|
160
|
+
# If the original make_request was decorated, we need to re-apply
|
|
161
|
+
# the decorator to the mocked make_request. This is necessary for
|
|
162
|
+
# the request caching decorator to work properly.
|
|
163
|
+
return decorator(lambda *_: mocked_response)(
|
|
164
|
+
self.w3.provider, method, params
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
return mocked_response
|
|
168
|
+
|
|
169
|
+
# -- async -- #
|
|
170
|
+
async def __aenter__(self) -> "Self":
|
|
171
|
+
# mypy error: Cannot assign to a method
|
|
172
|
+
self.w3.provider.make_request = self._async_mock_request_handler # type: ignore[method-assign] # noqa: E501
|
|
173
|
+
# reset request func cache to re-build request_func with mocked make_request
|
|
174
|
+
self.w3.provider._request_func_cache = (None, None)
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
|
178
|
+
# mypy error: Cannot assign to a method
|
|
179
|
+
self.w3.provider.make_request = self._make_request # type: ignore[method-assign] # noqa: E501
|
|
180
|
+
# reset request func cache to re-build request_func with original make_request
|
|
181
|
+
self.w3.provider._request_func_cache = (None, None)
|
|
182
|
+
|
|
183
|
+
async def _async_mock_request_handler(
|
|
184
|
+
self, method: "RPCEndpoint", params: Any
|
|
185
|
+
) -> "RPCResponse":
|
|
186
|
+
self.w3 = cast("AsyncWeb3", self.w3)
|
|
187
|
+
self._make_request = cast("AsyncMakeRequestFn", self._make_request)
|
|
188
|
+
|
|
189
|
+
if all(
|
|
190
|
+
method not in mock_dict
|
|
191
|
+
for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
|
|
192
|
+
):
|
|
193
|
+
return await self._make_request(method, params)
|
|
194
|
+
|
|
195
|
+
request_id = (
|
|
196
|
+
next(copy.deepcopy(self.w3.provider.request_counter))
|
|
197
|
+
if hasattr(self.w3.provider, "request_counter")
|
|
198
|
+
else 1
|
|
199
|
+
)
|
|
200
|
+
response_dict = {"jsonrpc": "2.0", "id": request_id}
|
|
201
|
+
|
|
202
|
+
if method in self.mock_responses:
|
|
203
|
+
mock_return = self.mock_responses[method]
|
|
204
|
+
|
|
205
|
+
if callable(mock_return):
|
|
206
|
+
mock_return = mock_return(method, params)
|
|
207
|
+
elif iscoroutinefunction(mock_return):
|
|
208
|
+
# this is the "correct" way to mock the async make_request
|
|
209
|
+
mock_return = await mock_return(method, params)
|
|
210
|
+
|
|
211
|
+
if "result" in mock_return:
|
|
212
|
+
mock_return = {"result": mock_return["result"]}
|
|
213
|
+
elif "error" in mock_return:
|
|
214
|
+
mock_return = self._create_error_object(mock_return["error"])
|
|
215
|
+
|
|
216
|
+
mocked_result = merge(response_dict, mock_return)
|
|
217
|
+
elif method in self.mock_results:
|
|
218
|
+
mock_return = self.mock_results[method]
|
|
219
|
+
if callable(mock_return):
|
|
220
|
+
# handle callable to make things easier since we're mocking
|
|
221
|
+
mock_return = mock_return(method, params)
|
|
222
|
+
elif iscoroutinefunction(mock_return):
|
|
223
|
+
# this is the "correct" way to mock the async make_request
|
|
224
|
+
mock_return = await mock_return(method, params)
|
|
225
|
+
|
|
226
|
+
mocked_result = merge(response_dict, {"result": mock_return})
|
|
227
|
+
|
|
228
|
+
elif method in self.mock_errors:
|
|
229
|
+
error = self.mock_errors[method]
|
|
230
|
+
if callable(error):
|
|
231
|
+
error = error(method, params)
|
|
232
|
+
elif iscoroutinefunction(error):
|
|
233
|
+
error = await error(method, params)
|
|
234
|
+
mocked_result = merge(response_dict, self._create_error_object(error))
|
|
235
|
+
|
|
236
|
+
else:
|
|
237
|
+
raise Exception("Invariant: unreachable code path")
|
|
238
|
+
|
|
239
|
+
decorator = getattr(self._make_request, "_decorator", None)
|
|
240
|
+
if decorator is not None:
|
|
241
|
+
# If the original make_request was decorated, we need to re-apply
|
|
242
|
+
# the decorator to the mocked make_request. This is necessary for
|
|
243
|
+
# the request caching decorator to work properly.
|
|
244
|
+
|
|
245
|
+
async def _coro(
|
|
246
|
+
_provider: Any, _method: "RPCEndpoint", _params: Any
|
|
247
|
+
) -> "RPCResponse":
|
|
248
|
+
return mocked_result
|
|
249
|
+
|
|
250
|
+
return await decorator(_coro)(self.w3.provider, method, params)
|
|
251
|
+
else:
|
|
252
|
+
return mocked_result
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def _create_error_object(error: Dict[str, Any]) -> Dict[str, Any]:
|
|
256
|
+
code = error.get("code", -32000)
|
|
257
|
+
message = error.get("message", "Mocked error")
|
|
258
|
+
return {"error": merge({"code": code, "message": message}, error)}
|