web3 6.20.3__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.3.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.3.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.3.dist-info/METADATA +0 -104
- web3-6.20.3.dist-info/RECORD +0 -283
- web3-6.20.3.dist-info/entry_points.txt +0 -2
- /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
- {web3-6.20.3.dist-info → web3-7.0.0.dist-info}/WHEEL +0 -0
|
@@ -15,12 +15,20 @@ from typing import (
|
|
|
15
15
|
Union,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
from eth_utils.toolz import (
|
|
19
|
+
compose,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from web3._utils.batching import (
|
|
23
|
+
BATCH_REQUEST_ID,
|
|
24
|
+
)
|
|
18
25
|
from web3._utils.caching import (
|
|
19
26
|
RequestInformation,
|
|
20
27
|
generate_cache_key,
|
|
21
28
|
)
|
|
22
29
|
from web3.exceptions import (
|
|
23
30
|
TaskNotRunning,
|
|
31
|
+
Web3ValueError,
|
|
24
32
|
)
|
|
25
33
|
from web3.types import (
|
|
26
34
|
RPCEndpoint,
|
|
@@ -71,17 +79,14 @@ class RequestProcessor:
|
|
|
71
79
|
self,
|
|
72
80
|
provider: "PersistentConnectionProvider",
|
|
73
81
|
subscription_response_queue_size: int = 500,
|
|
74
|
-
request_information_cache_size: int = 500,
|
|
75
82
|
) -> None:
|
|
76
83
|
self._provider = provider
|
|
77
84
|
|
|
85
|
+
self._request_information_cache: SimpleCache = SimpleCache(500)
|
|
78
86
|
self._request_response_cache: SimpleCache = SimpleCache(500)
|
|
79
87
|
self._subscription_response_queue: TaskReliantQueue[
|
|
80
88
|
Union[RPCResponse, TaskNotRunning]
|
|
81
89
|
] = TaskReliantQueue(maxsize=subscription_response_queue_size)
|
|
82
|
-
self._request_information_cache: SimpleCache = SimpleCache(
|
|
83
|
-
request_information_cache_size
|
|
84
|
-
)
|
|
85
90
|
|
|
86
91
|
@property
|
|
87
92
|
def active_subscriptions(self) -> Dict[str, Any]:
|
|
@@ -97,14 +102,35 @@ class RequestProcessor:
|
|
|
97
102
|
self,
|
|
98
103
|
method: RPCEndpoint,
|
|
99
104
|
params: Any,
|
|
100
|
-
response_formatters: Tuple[
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
response_formatters: Tuple[
|
|
106
|
+
Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
|
|
107
|
+
Callable[..., Any],
|
|
108
|
+
Callable[..., Any],
|
|
109
|
+
],
|
|
110
|
+
) -> Optional[str]:
|
|
111
|
+
cached_requests_key = generate_cache_key((method, params))
|
|
112
|
+
if cached_requests_key in self._provider._request_cache._data:
|
|
113
|
+
cached_response = self._provider._request_cache._data[cached_requests_key]
|
|
114
|
+
cached_response_id = cached_response.get("id")
|
|
115
|
+
cache_key = generate_cache_key(cached_response_id)
|
|
116
|
+
if cache_key in self._request_information_cache:
|
|
117
|
+
self._provider.logger.debug(
|
|
118
|
+
"This is a cached request, not caching request info because it is "
|
|
119
|
+
f"not unique:\n method={method},\n params={params}"
|
|
120
|
+
)
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
if self._provider._is_batching:
|
|
124
|
+
# the _batch_request_counter is set when entering the context manager
|
|
125
|
+
current_request_id = self._provider._batch_request_counter
|
|
126
|
+
self._provider._batch_request_counter += 1
|
|
127
|
+
else:
|
|
128
|
+
# copy the request counter and find the next request id without incrementing
|
|
129
|
+
# since this is done when / if the request is successfully sent
|
|
130
|
+
current_request_id = next(copy(self._provider.request_counter))
|
|
131
|
+
cache_key = generate_cache_key(current_request_id)
|
|
106
132
|
|
|
107
|
-
self._bump_cache_if_key_present(cache_key,
|
|
133
|
+
self._bump_cache_if_key_present(cache_key, current_request_id)
|
|
108
134
|
|
|
109
135
|
request_info = RequestInformation(
|
|
110
136
|
method,
|
|
@@ -112,19 +138,13 @@ class RequestProcessor:
|
|
|
112
138
|
response_formatters,
|
|
113
139
|
)
|
|
114
140
|
self._provider.logger.debug(
|
|
115
|
-
f"Caching request info:\n request_id={
|
|
141
|
+
f"Caching request info:\n request_id={current_request_id},\n"
|
|
116
142
|
f" cache_key={cache_key},\n request_info={request_info.__dict__}"
|
|
117
143
|
)
|
|
118
144
|
self._request_information_cache.cache(
|
|
119
145
|
cache_key,
|
|
120
146
|
request_info,
|
|
121
147
|
)
|
|
122
|
-
if self._request_information_cache.is_full():
|
|
123
|
-
self._provider.logger.warning(
|
|
124
|
-
"Request information cache is full. This may result in unexpected "
|
|
125
|
-
"behavior. Consider increasing the ``request_information_cache_size`` "
|
|
126
|
-
"on the provider."
|
|
127
|
-
)
|
|
128
148
|
return cache_key
|
|
129
149
|
|
|
130
150
|
def _bump_cache_if_key_present(self, cache_key: str, request_id: int) -> None:
|
|
@@ -168,9 +188,9 @@ class RequestProcessor:
|
|
|
168
188
|
) -> RequestInformation:
|
|
169
189
|
if "method" in response and response["method"] == "eth_subscription":
|
|
170
190
|
if "params" not in response:
|
|
171
|
-
raise
|
|
191
|
+
raise Web3ValueError("Subscription response must have params field")
|
|
172
192
|
if "subscription" not in response["params"]:
|
|
173
|
-
raise
|
|
193
|
+
raise Web3ValueError(
|
|
174
194
|
"Subscription response params must have subscription field"
|
|
175
195
|
)
|
|
176
196
|
|
|
@@ -182,15 +202,23 @@ class RequestProcessor:
|
|
|
182
202
|
# i.e. subscription request information remains in the cache
|
|
183
203
|
self._request_information_cache.get_cache_entry(cache_key)
|
|
184
204
|
)
|
|
185
|
-
|
|
186
205
|
else:
|
|
187
|
-
# retrieve the request info from the cache using the
|
|
206
|
+
# retrieve the request info from the cache using the response id
|
|
188
207
|
cache_key = generate_cache_key(response["id"])
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
if response in self._provider._request_cache._data.values():
|
|
209
|
+
request_info = (
|
|
210
|
+
# don't pop the request info from the cache, since we need to keep
|
|
211
|
+
# it to process future responses
|
|
212
|
+
# i.e. request information remains in the cache
|
|
213
|
+
self._request_information_cache.get_cache_entry(cache_key)
|
|
214
|
+
)
|
|
215
|
+
else:
|
|
216
|
+
request_info = (
|
|
217
|
+
# pop the request info from the cache since we don't need to keep it
|
|
218
|
+
# this keeps the cache size bounded
|
|
219
|
+
self.pop_cached_request_information(cache_key)
|
|
220
|
+
)
|
|
221
|
+
|
|
194
222
|
if (
|
|
195
223
|
request_info is not None
|
|
196
224
|
and request_info.method == "eth_unsubscribe"
|
|
@@ -204,6 +232,33 @@ class RequestProcessor:
|
|
|
204
232
|
|
|
205
233
|
return request_info
|
|
206
234
|
|
|
235
|
+
def append_result_formatter_for_request(
|
|
236
|
+
self, request_id: int, result_formatter: Callable[..., Any]
|
|
237
|
+
) -> None:
|
|
238
|
+
cache_key = generate_cache_key(request_id)
|
|
239
|
+
cached_request_info_for_id: RequestInformation = (
|
|
240
|
+
self._request_information_cache.get_cache_entry(cache_key)
|
|
241
|
+
)
|
|
242
|
+
if cached_request_info_for_id is not None:
|
|
243
|
+
(
|
|
244
|
+
current_result_formatters,
|
|
245
|
+
error_formatters,
|
|
246
|
+
null_result_formatters,
|
|
247
|
+
) = cached_request_info_for_id.response_formatters
|
|
248
|
+
cached_request_info_for_id.response_formatters = (
|
|
249
|
+
compose(
|
|
250
|
+
result_formatter,
|
|
251
|
+
current_result_formatters,
|
|
252
|
+
),
|
|
253
|
+
error_formatters,
|
|
254
|
+
null_result_formatters,
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
self._provider.logger.debug(
|
|
258
|
+
f"No cached request info for response id `{request_id}`. Cannot "
|
|
259
|
+
f"append response formatter for response."
|
|
260
|
+
)
|
|
261
|
+
|
|
207
262
|
def append_middleware_response_processor(
|
|
208
263
|
self,
|
|
209
264
|
response: RPCResponse,
|
|
@@ -242,12 +297,22 @@ class RequestProcessor:
|
|
|
242
297
|
"Subscription queue is full. Waiting for provider to consume "
|
|
243
298
|
"messages before caching."
|
|
244
299
|
)
|
|
300
|
+
self._provider._listen_event.clear()
|
|
245
301
|
await self._provider._listen_event.wait()
|
|
246
302
|
|
|
247
303
|
self._provider.logger.debug(
|
|
248
304
|
f"Caching subscription response:\n response={raw_response}"
|
|
249
305
|
)
|
|
250
306
|
await self._subscription_response_queue.put(raw_response)
|
|
307
|
+
elif isinstance(raw_response, list):
|
|
308
|
+
# Since only one batch should be in the cache at all times, we use a
|
|
309
|
+
# constant cache key for the batch response.
|
|
310
|
+
cache_key = generate_cache_key(BATCH_REQUEST_ID)
|
|
311
|
+
self._provider.logger.debug(
|
|
312
|
+
f"Caching batch response:\n cache_key={cache_key},\n"
|
|
313
|
+
f" response={raw_response}"
|
|
314
|
+
)
|
|
315
|
+
self._request_response_cache.cache(cache_key, raw_response)
|
|
251
316
|
else:
|
|
252
317
|
response_id = raw_response.get("id")
|
|
253
318
|
cache_key = generate_cache_key(response_id)
|
|
@@ -288,7 +353,7 @@ class RequestProcessor:
|
|
|
288
353
|
)
|
|
289
354
|
else:
|
|
290
355
|
if not cache_key:
|
|
291
|
-
raise
|
|
356
|
+
raise Web3ValueError(
|
|
292
357
|
"Must provide cache key when popping a non-subscription response."
|
|
293
358
|
)
|
|
294
359
|
|
|
@@ -305,10 +370,7 @@ class RequestProcessor:
|
|
|
305
370
|
# request processor class methods
|
|
306
371
|
|
|
307
372
|
def clear_caches(self) -> None:
|
|
308
|
-
"""
|
|
309
|
-
Clear the request processor caches.
|
|
310
|
-
"""
|
|
311
|
-
|
|
373
|
+
"""Clear the request processor caches."""
|
|
312
374
|
self._request_information_cache.clear()
|
|
313
375
|
self._request_response_cache.clear()
|
|
314
376
|
self._subscription_response_queue = TaskReliantQueue(
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from web3.exceptions import (
|
|
9
|
+
Web3ValidationError,
|
|
10
|
+
)
|
|
11
|
+
from web3.providers import (
|
|
12
|
+
PersistentConnectionProvider,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from web3.main import ( # noqa: F401
|
|
17
|
+
AsyncWeb3,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def persistent_connection_provider_method(message: str = None) -> Callable[..., Any]:
|
|
22
|
+
"""
|
|
23
|
+
Decorator that raises an exception if the provider is not an instance of
|
|
24
|
+
``PersistentConnectionProvider``.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
28
|
+
@functools.wraps(func)
|
|
29
|
+
def inner(self: "AsyncWeb3", *args: Any, **kwargs: Any) -> Any:
|
|
30
|
+
nonlocal message
|
|
31
|
+
if message is None:
|
|
32
|
+
message = (
|
|
33
|
+
f"``{func.__name__}`` can only be called on a "
|
|
34
|
+
"``PersistentConnectionProvider`` instance."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not isinstance(self.provider, PersistentConnectionProvider):
|
|
38
|
+
raise Web3ValidationError(message)
|
|
39
|
+
return func(self, *args, **kwargs)
|
|
40
|
+
|
|
41
|
+
return inner
|
|
42
|
+
|
|
43
|
+
return decorator
|
|
@@ -15,14 +15,19 @@ from eth_typing import (
|
|
|
15
15
|
from toolz import (
|
|
16
16
|
merge,
|
|
17
17
|
)
|
|
18
|
+
from websockets import (
|
|
19
|
+
WebSocketClientProtocol,
|
|
20
|
+
)
|
|
18
21
|
from websockets.client import (
|
|
19
22
|
connect,
|
|
20
23
|
)
|
|
21
24
|
from websockets.exceptions import (
|
|
25
|
+
ConnectionClosedOK,
|
|
22
26
|
WebSocketException,
|
|
23
27
|
)
|
|
24
28
|
|
|
25
29
|
from web3.exceptions import (
|
|
30
|
+
PersistentConnectionClosedOK,
|
|
26
31
|
ProviderConnectionError,
|
|
27
32
|
Web3ValidationError,
|
|
28
33
|
)
|
|
@@ -30,7 +35,6 @@ from web3.providers.persistent import (
|
|
|
30
35
|
PersistentConnectionProvider,
|
|
31
36
|
)
|
|
32
37
|
from web3.types import (
|
|
33
|
-
RPCEndpoint,
|
|
34
38
|
RPCResponse,
|
|
35
39
|
)
|
|
36
40
|
|
|
@@ -51,15 +55,16 @@ def get_default_endpoint() -> URI:
|
|
|
51
55
|
return URI(os.environ.get("WEB3_WS_PROVIDER_URI", "ws://127.0.0.1:8546"))
|
|
52
56
|
|
|
53
57
|
|
|
54
|
-
class
|
|
55
|
-
logger = logging.getLogger("web3.providers.
|
|
58
|
+
class WebSocketProvider(PersistentConnectionProvider):
|
|
59
|
+
logger = logging.getLogger("web3.providers.WebSocketProvider")
|
|
56
60
|
is_async: bool = True
|
|
57
61
|
|
|
62
|
+
_ws: Optional[WebSocketClientProtocol] = None
|
|
63
|
+
|
|
58
64
|
def __init__(
|
|
59
65
|
self,
|
|
60
66
|
endpoint_uri: Optional[Union[URI, str]] = None,
|
|
61
67
|
websocket_kwargs: Optional[Dict[str, Any]] = None,
|
|
62
|
-
silence_listener_task_exceptions: bool = False,
|
|
63
68
|
# `PersistentConnectionProvider` kwargs can be passed through
|
|
64
69
|
**kwargs: Any,
|
|
65
70
|
) -> None:
|
|
@@ -88,9 +93,7 @@ class WebsocketProviderV2(PersistentConnectionProvider):
|
|
|
88
93
|
|
|
89
94
|
self.websocket_kwargs = merge(DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {})
|
|
90
95
|
|
|
91
|
-
super().__init__(
|
|
92
|
-
silence_listener_task_exceptions=silence_listener_task_exceptions, **kwargs
|
|
93
|
-
)
|
|
96
|
+
super().__init__(**kwargs)
|
|
94
97
|
|
|
95
98
|
def __str__(self) -> str:
|
|
96
99
|
return f"WebSocket connection: {self.endpoint_uri}"
|
|
@@ -110,17 +113,7 @@ class WebsocketProviderV2(PersistentConnectionProvider):
|
|
|
110
113
|
) from e
|
|
111
114
|
return False
|
|
112
115
|
|
|
113
|
-
async def
|
|
114
|
-
self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
|
|
115
|
-
|
|
116
|
-
async def _provider_specific_disconnect(self) -> None:
|
|
117
|
-
if self._ws is not None and not self._ws.closed:
|
|
118
|
-
await self._ws.close()
|
|
119
|
-
self._ws = None
|
|
120
|
-
|
|
121
|
-
async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
122
|
-
request_data = self.encode_rpc_request(method, params)
|
|
123
|
-
|
|
116
|
+
async def socket_send(self, request_data: bytes) -> None:
|
|
124
117
|
if self._ws is None:
|
|
125
118
|
raise ProviderConnectionError(
|
|
126
119
|
"Connection to websocket has not been initiated for the provider."
|
|
@@ -130,16 +123,24 @@ class WebsocketProviderV2(PersistentConnectionProvider):
|
|
|
130
123
|
self._ws.send(request_data), timeout=self.request_timeout
|
|
131
124
|
)
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
async def socket_recv(self) -> RPCResponse:
|
|
127
|
+
raw_response = await self._ws.recv()
|
|
128
|
+
return json.loads(raw_response)
|
|
129
|
+
|
|
130
|
+
# -- private methods -- #
|
|
131
|
+
|
|
132
|
+
async def _provider_specific_connect(self) -> None:
|
|
133
|
+
self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
|
|
135
134
|
|
|
136
|
-
|
|
135
|
+
async def _provider_specific_disconnect(self) -> None:
|
|
136
|
+
if self._ws is not None and not self._ws.closed:
|
|
137
|
+
await self._ws.close()
|
|
138
|
+
self._ws = None
|
|
137
139
|
|
|
138
|
-
async def
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
response, subscription=subscription
|
|
140
|
+
async def _provider_specific_socket_reader(self) -> RPCResponse:
|
|
141
|
+
try:
|
|
142
|
+
return await self.socket_recv()
|
|
143
|
+
except ConnectionClosedOK:
|
|
144
|
+
raise PersistentConnectionClosedOK(
|
|
145
|
+
user_message="WebSocket connection received `ConnectionClosedOK`."
|
|
145
146
|
)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Dict,
|
|
6
|
+
Iterable,
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Tuple,
|
|
10
|
+
Union,
|
|
11
|
+
cast,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from aiohttp import (
|
|
15
|
+
ClientError,
|
|
16
|
+
ClientSession,
|
|
17
|
+
)
|
|
18
|
+
from eth_typing import (
|
|
19
|
+
URI,
|
|
20
|
+
)
|
|
21
|
+
from eth_utils import (
|
|
22
|
+
to_dict,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from web3._utils.empty import (
|
|
26
|
+
Empty,
|
|
27
|
+
empty,
|
|
28
|
+
)
|
|
29
|
+
from web3._utils.http import (
|
|
30
|
+
construct_user_agent,
|
|
31
|
+
)
|
|
32
|
+
from web3.types import (
|
|
33
|
+
RPCEndpoint,
|
|
34
|
+
RPCResponse,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from ..._utils.batching import (
|
|
38
|
+
sort_batch_response_by_response_ids,
|
|
39
|
+
)
|
|
40
|
+
from ..._utils.caching import (
|
|
41
|
+
async_handle_request_caching,
|
|
42
|
+
)
|
|
43
|
+
from ..._utils.http_session_manager import (
|
|
44
|
+
HTTPSessionManager,
|
|
45
|
+
)
|
|
46
|
+
from ..async_base import (
|
|
47
|
+
AsyncJSONBaseProvider,
|
|
48
|
+
)
|
|
49
|
+
from .utils import (
|
|
50
|
+
ExceptionRetryConfiguration,
|
|
51
|
+
check_if_retry_on_failure,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AsyncHTTPProvider(AsyncJSONBaseProvider):
|
|
56
|
+
logger = logging.getLogger("web3.providers.AsyncHTTPProvider")
|
|
57
|
+
endpoint_uri = None
|
|
58
|
+
_request_kwargs = None
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
endpoint_uri: Optional[Union[URI, str]] = None,
|
|
63
|
+
request_kwargs: Optional[Any] = None,
|
|
64
|
+
exception_retry_configuration: Optional[
|
|
65
|
+
Union[ExceptionRetryConfiguration, Empty]
|
|
66
|
+
] = empty,
|
|
67
|
+
**kwargs: Any,
|
|
68
|
+
) -> None:
|
|
69
|
+
self._request_session_manager = HTTPSessionManager()
|
|
70
|
+
|
|
71
|
+
if endpoint_uri is None:
|
|
72
|
+
self.endpoint_uri = (
|
|
73
|
+
self._request_session_manager.get_default_http_endpoint()
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
self.endpoint_uri = URI(endpoint_uri)
|
|
77
|
+
|
|
78
|
+
self._request_kwargs = request_kwargs or {}
|
|
79
|
+
self._exception_retry_configuration = exception_retry_configuration
|
|
80
|
+
|
|
81
|
+
super().__init__(**kwargs)
|
|
82
|
+
|
|
83
|
+
async def cache_async_session(self, session: ClientSession) -> ClientSession:
|
|
84
|
+
return await self._request_session_manager.async_cache_and_return_session(
|
|
85
|
+
self.endpoint_uri, session
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def __str__(self) -> str:
|
|
89
|
+
return f"RPC connection {self.endpoint_uri}"
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def exception_retry_configuration(self) -> ExceptionRetryConfiguration:
|
|
93
|
+
if isinstance(self._exception_retry_configuration, Empty):
|
|
94
|
+
self._exception_retry_configuration = ExceptionRetryConfiguration(
|
|
95
|
+
errors=(ClientError, TimeoutError)
|
|
96
|
+
)
|
|
97
|
+
return self._exception_retry_configuration
|
|
98
|
+
|
|
99
|
+
@exception_retry_configuration.setter
|
|
100
|
+
def exception_retry_configuration(
|
|
101
|
+
self, value: Union[ExceptionRetryConfiguration, Empty]
|
|
102
|
+
) -> None:
|
|
103
|
+
self._exception_retry_configuration = value
|
|
104
|
+
|
|
105
|
+
@to_dict
|
|
106
|
+
def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]:
|
|
107
|
+
if "headers" not in self._request_kwargs:
|
|
108
|
+
yield "headers", self.get_request_headers()
|
|
109
|
+
yield from self._request_kwargs.items()
|
|
110
|
+
|
|
111
|
+
def get_request_headers(self) -> Dict[str, str]:
|
|
112
|
+
return {
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
"User-Agent": construct_user_agent(type(self)),
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async def _make_request(self, method: RPCEndpoint, request_data: bytes) -> bytes:
|
|
118
|
+
"""
|
|
119
|
+
If exception_retry_configuration is set, retry on failure; otherwise, make
|
|
120
|
+
the request without retrying.
|
|
121
|
+
"""
|
|
122
|
+
if (
|
|
123
|
+
self.exception_retry_configuration is not None
|
|
124
|
+
and check_if_retry_on_failure(
|
|
125
|
+
method, self.exception_retry_configuration.method_allowlist
|
|
126
|
+
)
|
|
127
|
+
):
|
|
128
|
+
for i in range(self.exception_retry_configuration.retries):
|
|
129
|
+
try:
|
|
130
|
+
return await self._request_session_manager.async_make_post_request(
|
|
131
|
+
self.endpoint_uri, request_data, **self.get_request_kwargs()
|
|
132
|
+
)
|
|
133
|
+
except tuple(self.exception_retry_configuration.errors):
|
|
134
|
+
if i < self.exception_retry_configuration.retries - 1:
|
|
135
|
+
await asyncio.sleep(
|
|
136
|
+
self.exception_retry_configuration.backoff_factor * 2**i
|
|
137
|
+
)
|
|
138
|
+
continue
|
|
139
|
+
else:
|
|
140
|
+
raise
|
|
141
|
+
return None
|
|
142
|
+
else:
|
|
143
|
+
return await self._request_session_manager.async_make_post_request(
|
|
144
|
+
self.endpoint_uri, request_data, **self.get_request_kwargs()
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@async_handle_request_caching
|
|
148
|
+
async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
149
|
+
self.logger.debug(
|
|
150
|
+
f"Making request HTTP. URI: {self.endpoint_uri}, Method: {method}"
|
|
151
|
+
)
|
|
152
|
+
request_data = self.encode_rpc_request(method, params)
|
|
153
|
+
raw_response = await self._make_request(method, request_data)
|
|
154
|
+
response = self.decode_rpc_response(raw_response)
|
|
155
|
+
self.logger.debug(
|
|
156
|
+
f"Getting response HTTP. URI: {self.endpoint_uri}, "
|
|
157
|
+
f"Method: {method}, Response: {response}"
|
|
158
|
+
)
|
|
159
|
+
return response
|
|
160
|
+
|
|
161
|
+
async def make_batch_request(
|
|
162
|
+
self, batch_requests: List[Tuple[RPCEndpoint, Any]]
|
|
163
|
+
) -> List[RPCResponse]:
|
|
164
|
+
self.logger.debug(f"Making batch request HTTP - uri: `{self.endpoint_uri}`")
|
|
165
|
+
request_data = self.encode_batch_rpc_request(batch_requests)
|
|
166
|
+
raw_response = await self._request_session_manager.async_make_post_request(
|
|
167
|
+
self.endpoint_uri, request_data, **self.get_request_kwargs()
|
|
168
|
+
)
|
|
169
|
+
self.logger.debug("Received batch response HTTP.")
|
|
170
|
+
responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
|
|
171
|
+
return sort_batch_response_by_response_ids(responses_list)
|