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
web3/_utils/filters.py
CHANGED
|
@@ -19,6 +19,7 @@ from eth_abi.grammar import (
|
|
|
19
19
|
parse as parse_type_string,
|
|
20
20
|
)
|
|
21
21
|
from eth_typing import (
|
|
22
|
+
ABIEvent,
|
|
22
23
|
ChecksumAddress,
|
|
23
24
|
HexStr,
|
|
24
25
|
TypeStr,
|
|
@@ -50,10 +51,11 @@ from web3._utils.validation import (
|
|
|
50
51
|
validate_address,
|
|
51
52
|
)
|
|
52
53
|
from web3.exceptions import (
|
|
54
|
+
Web3TypeError,
|
|
53
55
|
Web3ValidationError,
|
|
56
|
+
Web3ValueError,
|
|
54
57
|
)
|
|
55
58
|
from web3.types import (
|
|
56
|
-
ABIEvent,
|
|
57
59
|
BlockIdentifier,
|
|
58
60
|
FilterParams,
|
|
59
61
|
LogReceipt,
|
|
@@ -71,8 +73,8 @@ def construct_event_filter_params(
|
|
|
71
73
|
contract_address: Optional[ChecksumAddress] = None,
|
|
72
74
|
argument_filters: Optional[Dict[str, Any]] = None,
|
|
73
75
|
topics: Optional[Sequence[HexStr]] = None,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
from_block: Optional[BlockIdentifier] = None,
|
|
77
|
+
to_block: Optional[BlockIdentifier] = None,
|
|
76
78
|
address: Optional[ChecksumAddress] = None,
|
|
77
79
|
) -> Tuple[List[List[Optional[HexStr]]], FilterParams]:
|
|
78
80
|
filter_params: FilterParams = {}
|
|
@@ -82,17 +84,13 @@ def construct_event_filter_params(
|
|
|
82
84
|
|
|
83
85
|
if topics is not None:
|
|
84
86
|
if len(topic_set) > 1:
|
|
85
|
-
raise
|
|
87
|
+
raise Web3TypeError(
|
|
86
88
|
"Merging the topics argument with topics generated "
|
|
87
89
|
"from argument_filters is not supported."
|
|
88
90
|
)
|
|
89
91
|
topic_set = topics
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
# type ignored b/c list-like check on line 88
|
|
93
|
-
filter_params["topics"] = topic_set[0] # type: ignore
|
|
94
|
-
else:
|
|
95
|
-
filter_params["topics"] = topic_set
|
|
93
|
+
filter_params["topics"] = topic_set
|
|
96
94
|
|
|
97
95
|
if address and contract_address:
|
|
98
96
|
if is_list_like(address):
|
|
@@ -104,7 +102,7 @@ def construct_event_filter_params(
|
|
|
104
102
|
else [address]
|
|
105
103
|
)
|
|
106
104
|
else:
|
|
107
|
-
raise
|
|
105
|
+
raise Web3ValueError(
|
|
108
106
|
f"Unsupported type for `address` parameter: {type(address)}"
|
|
109
107
|
)
|
|
110
108
|
elif address:
|
|
@@ -120,11 +118,11 @@ def construct_event_filter_params(
|
|
|
120
118
|
else:
|
|
121
119
|
validate_address(filter_params["address"])
|
|
122
120
|
|
|
123
|
-
if
|
|
124
|
-
filter_params["fromBlock"] =
|
|
121
|
+
if from_block is not None:
|
|
122
|
+
filter_params["fromBlock"] = from_block
|
|
125
123
|
|
|
126
|
-
if
|
|
127
|
-
filter_params["toBlock"] =
|
|
124
|
+
if to_block is not None:
|
|
125
|
+
filter_params["toBlock"] = to_block
|
|
128
126
|
|
|
129
127
|
data_filters_set = construct_event_data_set(event_abi, abi_codec, argument_filters)
|
|
130
128
|
|
|
@@ -178,7 +176,7 @@ class BaseFilter:
|
|
|
178
176
|
class Filter(BaseFilter):
|
|
179
177
|
def __init__(self, filter_id: HexStr, eth_module: "Eth") -> None:
|
|
180
178
|
self.eth_module = eth_module
|
|
181
|
-
super(
|
|
179
|
+
super().__init__(filter_id)
|
|
182
180
|
|
|
183
181
|
def get_new_entries(self) -> List[LogReceipt]:
|
|
184
182
|
log_entries = self._filter_valid_entries(
|
|
@@ -196,7 +194,7 @@ class Filter(BaseFilter):
|
|
|
196
194
|
class AsyncFilter(BaseFilter):
|
|
197
195
|
def __init__(self, filter_id: HexStr, eth_module: "AsyncEth") -> None:
|
|
198
196
|
self.eth_module = eth_module
|
|
199
|
-
super(
|
|
197
|
+
super().__init__(filter_id)
|
|
200
198
|
|
|
201
199
|
async def get_new_entries(self) -> List[LogReceipt]:
|
|
202
200
|
filter_changes = await self.eth_module.get_filter_changes(self.filter_id)
|
|
@@ -250,7 +248,8 @@ class LogFilter(Filter):
|
|
|
250
248
|
def set_data_filters(
|
|
251
249
|
self, data_filter_set: Collection[Tuple[TypeStr, Any]]
|
|
252
250
|
) -> None:
|
|
253
|
-
"""
|
|
251
|
+
"""
|
|
252
|
+
Sets the data filters (non indexed argument filters)
|
|
254
253
|
|
|
255
254
|
Expects a set of tuples with the type and value, e.g.:
|
|
256
255
|
(('uint256', [12345, 54321]), ('string', ('a-single-string',)))
|
|
@@ -292,7 +291,8 @@ class AsyncLogFilter(AsyncFilter):
|
|
|
292
291
|
def set_data_filters(
|
|
293
292
|
self, data_filter_set: Collection[Tuple[TypeStr, Any]]
|
|
294
293
|
) -> None:
|
|
295
|
-
"""
|
|
294
|
+
"""
|
|
295
|
+
Sets the data filters (non indexed argument filters)
|
|
296
296
|
|
|
297
297
|
Expects a set of tuples with the type and value, e.g.:
|
|
298
298
|
(('uint256', [12345, 54321]), ('string', ('a-single-string',)))
|
|
@@ -318,7 +318,8 @@ normalize_to_text = apply_formatter_if(not_text, decode_utf8_bytes)
|
|
|
318
318
|
|
|
319
319
|
|
|
320
320
|
def normalize_data_values(type_string: TypeStr, data_value: Any) -> Any:
|
|
321
|
-
"""
|
|
321
|
+
"""
|
|
322
|
+
Decodes utf-8 bytes to strings for abi string values.
|
|
322
323
|
|
|
323
324
|
eth-abi v1 returns utf-8 bytes for string values.
|
|
324
325
|
This can be removed once eth-abi v2 is required.
|
|
@@ -326,7 +327,7 @@ def normalize_data_values(type_string: TypeStr, data_value: Any) -> Any:
|
|
|
326
327
|
_type = parse_type_string(type_string)
|
|
327
328
|
if _type.base == "string":
|
|
328
329
|
if _type.arrlist is not None:
|
|
329
|
-
return tuple(
|
|
330
|
+
return tuple(normalize_to_text(value) for value in data_value)
|
|
330
331
|
else:
|
|
331
332
|
return normalize_to_text(data_value)
|
|
332
333
|
return data_value
|
|
@@ -336,7 +337,8 @@ def normalize_data_values(type_string: TypeStr, data_value: Any) -> Any:
|
|
|
336
337
|
def match_fn(
|
|
337
338
|
codec: ABICodec, match_values_and_abi: Collection[Tuple[str, Any]], data: Any
|
|
338
339
|
) -> bool:
|
|
339
|
-
"""
|
|
340
|
+
"""
|
|
341
|
+
Match function used for filtering non-indexed event arguments.
|
|
340
342
|
|
|
341
343
|
Values provided through the match_values_and_abi parameter are
|
|
342
344
|
compared to the abi decoded log data.
|
|
@@ -352,7 +354,7 @@ def match_fn(
|
|
|
352
354
|
normalized_data = normalize_data_values(abi_type, data_value)
|
|
353
355
|
for value in match_values:
|
|
354
356
|
if not codec.is_encodable(abi_type, value):
|
|
355
|
-
raise
|
|
357
|
+
raise Web3ValueError(
|
|
356
358
|
f"Value {value} is of the wrong abi type. "
|
|
357
359
|
f"Expected {abi_type} typed value."
|
|
358
360
|
)
|
web3/_utils/formatters.py
CHANGED
|
@@ -114,13 +114,13 @@ def apply_key_map(
|
|
|
114
114
|
def is_array_of_strings(value: Any) -> bool:
|
|
115
115
|
if not is_list_like(value):
|
|
116
116
|
return False
|
|
117
|
-
return all(
|
|
117
|
+
return all(is_string(item) for item in value)
|
|
118
118
|
|
|
119
119
|
|
|
120
120
|
def is_array_of_dicts(value: Any) -> bool:
|
|
121
121
|
if not is_list_like(value):
|
|
122
122
|
return False
|
|
123
|
-
return all(
|
|
123
|
+
return all(is_dict(item) for item in value)
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
@curry
|
web3/_utils/http.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
DEFAULT_HTTP_TIMEOUT = 30.0
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def construct_user_agent(class_type: type) -> str:
|
|
2
5
|
from web3 import (
|
|
3
6
|
__version__ as web3_version,
|
|
4
7
|
)
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
return user_agent
|
|
9
|
+
return f"web3.py/{web3_version}/{class_type.__module__}.{class_type.__qualname__}"
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from concurrent.futures import (
|
|
3
|
+
ThreadPoolExecutor,
|
|
4
|
+
)
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Dict,
|
|
12
|
+
List,
|
|
13
|
+
Optional,
|
|
14
|
+
Union,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from aiohttp import (
|
|
18
|
+
ClientResponse,
|
|
19
|
+
ClientSession,
|
|
20
|
+
ClientTimeout,
|
|
21
|
+
)
|
|
22
|
+
from eth_typing import (
|
|
23
|
+
URI,
|
|
24
|
+
)
|
|
25
|
+
import requests
|
|
26
|
+
|
|
27
|
+
from web3._utils.async_caching import (
|
|
28
|
+
async_lock,
|
|
29
|
+
)
|
|
30
|
+
from web3._utils.caching import (
|
|
31
|
+
generate_cache_key,
|
|
32
|
+
)
|
|
33
|
+
from web3._utils.http import (
|
|
34
|
+
DEFAULT_HTTP_TIMEOUT,
|
|
35
|
+
)
|
|
36
|
+
from web3.exceptions import (
|
|
37
|
+
TimeExhausted,
|
|
38
|
+
)
|
|
39
|
+
from web3.utils.caching import (
|
|
40
|
+
SimpleCache,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HTTPSessionManager:
|
|
45
|
+
logger = logging.getLogger("web3._utils.http_session_manager.HTTPSessionManager")
|
|
46
|
+
_lock: threading.Lock = threading.Lock()
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
cache_size: int = 100,
|
|
51
|
+
session_pool_max_workers: int = 5,
|
|
52
|
+
) -> None:
|
|
53
|
+
self.session_cache = SimpleCache(cache_size)
|
|
54
|
+
self.session_pool = ThreadPoolExecutor(max_workers=session_pool_max_workers)
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def get_default_http_endpoint() -> URI:
|
|
58
|
+
return URI(os.environ.get("WEB3_HTTP_PROVIDER_URI", "http://localhost:8545"))
|
|
59
|
+
|
|
60
|
+
def cache_and_return_session(
|
|
61
|
+
self,
|
|
62
|
+
endpoint_uri: URI,
|
|
63
|
+
session: requests.Session = None,
|
|
64
|
+
request_timeout: Optional[float] = None,
|
|
65
|
+
) -> requests.Session:
|
|
66
|
+
# cache key should have a unique thread identifier
|
|
67
|
+
cache_key = generate_cache_key(f"{threading.get_ident()}:{endpoint_uri}")
|
|
68
|
+
|
|
69
|
+
cached_session = self.session_cache.get_cache_entry(cache_key)
|
|
70
|
+
if cached_session is not None:
|
|
71
|
+
# If read from cache yields a session, no need to lock; return the session.
|
|
72
|
+
# Sync is a bit simpler in this way since a `requests.Session` doesn't
|
|
73
|
+
# really "close" in the same way that an async `ClientSession` does.
|
|
74
|
+
# When "closed", it still uses http / https adapters successfully if a
|
|
75
|
+
# request is made.
|
|
76
|
+
return cached_session
|
|
77
|
+
|
|
78
|
+
if session is None:
|
|
79
|
+
session = requests.Session()
|
|
80
|
+
|
|
81
|
+
with self._lock:
|
|
82
|
+
cached_session, evicted_items = self.session_cache.cache(cache_key, session)
|
|
83
|
+
self.logger.debug(f"Session cached: {endpoint_uri}, {cached_session}")
|
|
84
|
+
|
|
85
|
+
if evicted_items is not None:
|
|
86
|
+
evicted_sessions = evicted_items.values()
|
|
87
|
+
for evicted_session in evicted_sessions:
|
|
88
|
+
self.logger.debug(
|
|
89
|
+
"Session cache full. Session evicted from cache: "
|
|
90
|
+
f"{evicted_session}",
|
|
91
|
+
)
|
|
92
|
+
threading.Timer(
|
|
93
|
+
# If `request_timeout` is `None`, don't wait forever for the closing
|
|
94
|
+
# session to finish the request. Instead, wait over the default timeout.
|
|
95
|
+
request_timeout or DEFAULT_HTTP_TIMEOUT + 0.1,
|
|
96
|
+
self._close_evicted_sessions,
|
|
97
|
+
args=[evicted_sessions],
|
|
98
|
+
).start()
|
|
99
|
+
|
|
100
|
+
return cached_session
|
|
101
|
+
|
|
102
|
+
def get_response_from_get_request(
|
|
103
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
104
|
+
) -> requests.Response:
|
|
105
|
+
kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
|
|
106
|
+
session = self.cache_and_return_session(
|
|
107
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
108
|
+
)
|
|
109
|
+
response = session.get(endpoint_uri, *args, **kwargs)
|
|
110
|
+
return response
|
|
111
|
+
|
|
112
|
+
def json_make_get_request(
|
|
113
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
response = self.get_response_from_get_request(endpoint_uri, *args, **kwargs)
|
|
116
|
+
response.raise_for_status()
|
|
117
|
+
return response.json()
|
|
118
|
+
|
|
119
|
+
def get_response_from_post_request(
|
|
120
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
121
|
+
) -> requests.Response:
|
|
122
|
+
session = self.cache_and_return_session(
|
|
123
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
124
|
+
)
|
|
125
|
+
return session.post(endpoint_uri, *args, **kwargs)
|
|
126
|
+
|
|
127
|
+
def make_post_request(
|
|
128
|
+
self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
|
|
129
|
+
) -> bytes:
|
|
130
|
+
kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
|
|
131
|
+
kwargs.setdefault("stream", False)
|
|
132
|
+
|
|
133
|
+
start = time.time()
|
|
134
|
+
timeout = kwargs["timeout"]
|
|
135
|
+
|
|
136
|
+
with self.get_response_from_post_request(
|
|
137
|
+
endpoint_uri, data=data, **kwargs
|
|
138
|
+
) as response:
|
|
139
|
+
response.raise_for_status()
|
|
140
|
+
if kwargs.get("stream"):
|
|
141
|
+
return self._handle_streaming_response(response, start, timeout)
|
|
142
|
+
else:
|
|
143
|
+
return response.content
|
|
144
|
+
|
|
145
|
+
def _handle_streaming_response(
|
|
146
|
+
self, response: requests.Response, start: float, timeout: float
|
|
147
|
+
) -> bytes:
|
|
148
|
+
response_body = b""
|
|
149
|
+
for data in response.iter_content():
|
|
150
|
+
response_body += data
|
|
151
|
+
# Manually manage timeout so streaming responses time out
|
|
152
|
+
# rather than resetting the timeout each time a response comes back
|
|
153
|
+
if (time.time() - start) > timeout:
|
|
154
|
+
raise TimeExhausted
|
|
155
|
+
return response_body
|
|
156
|
+
|
|
157
|
+
def _close_evicted_sessions(self, evicted_sessions: List[requests.Session]) -> None:
|
|
158
|
+
for evicted_session in evicted_sessions:
|
|
159
|
+
evicted_session.close()
|
|
160
|
+
self.logger.debug(f"Closed evicted session: {evicted_session}")
|
|
161
|
+
|
|
162
|
+
# -- async -- #
|
|
163
|
+
|
|
164
|
+
async def async_cache_and_return_session(
|
|
165
|
+
self,
|
|
166
|
+
endpoint_uri: URI,
|
|
167
|
+
session: Optional[ClientSession] = None,
|
|
168
|
+
request_timeout: Optional[ClientTimeout] = None,
|
|
169
|
+
) -> ClientSession:
|
|
170
|
+
# cache key should have a unique thread identifier
|
|
171
|
+
cache_key = generate_cache_key(f"{threading.get_ident()}:{endpoint_uri}")
|
|
172
|
+
|
|
173
|
+
evicted_items = None
|
|
174
|
+
async with async_lock(self.session_pool, self._lock):
|
|
175
|
+
if cache_key not in self.session_cache:
|
|
176
|
+
if session is None:
|
|
177
|
+
session = ClientSession(raise_for_status=True)
|
|
178
|
+
|
|
179
|
+
cached_session, evicted_items = self.session_cache.cache(
|
|
180
|
+
cache_key, session
|
|
181
|
+
)
|
|
182
|
+
self.logger.debug(
|
|
183
|
+
f"Async session cached: {endpoint_uri}, {cached_session}"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
else:
|
|
187
|
+
# get the cached session
|
|
188
|
+
cached_session = self.session_cache.get_cache_entry(cache_key)
|
|
189
|
+
session_is_closed = cached_session.closed
|
|
190
|
+
session_loop_is_closed = cached_session._loop.is_closed()
|
|
191
|
+
|
|
192
|
+
warning = (
|
|
193
|
+
"Async session was closed"
|
|
194
|
+
if session_is_closed
|
|
195
|
+
else (
|
|
196
|
+
"Loop was closed for async session"
|
|
197
|
+
if session_loop_is_closed
|
|
198
|
+
else None
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
if warning:
|
|
202
|
+
self.logger.debug(
|
|
203
|
+
f"{warning}: {endpoint_uri}, {cached_session}. "
|
|
204
|
+
f"Creating and caching a new async session for uri."
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
self.session_cache._data.pop(cache_key)
|
|
208
|
+
if not session_is_closed:
|
|
209
|
+
# if loop was closed but not the session, close the session
|
|
210
|
+
await cached_session.close()
|
|
211
|
+
self.logger.debug(
|
|
212
|
+
f"Async session closed and evicted from cache: {cached_session}"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# replace stale session with a new session at the cache key
|
|
216
|
+
_session = ClientSession(raise_for_status=True)
|
|
217
|
+
cached_session, evicted_items = self.session_cache.cache(
|
|
218
|
+
cache_key, _session
|
|
219
|
+
)
|
|
220
|
+
self.logger.debug(
|
|
221
|
+
f"Async session cached: {endpoint_uri}, {cached_session}"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if evicted_items is not None:
|
|
225
|
+
# At this point the evicted sessions are already popped out of the cache and
|
|
226
|
+
# just stored in the `evicted_sessions` dict. So we can kick off a future
|
|
227
|
+
# task to close them and it should be safe to pop out of the lock here.
|
|
228
|
+
evicted_sessions = list(evicted_items.values())
|
|
229
|
+
for evicted_session in evicted_sessions:
|
|
230
|
+
self.logger.debug(
|
|
231
|
+
"Async session cache full. Session evicted from cache: "
|
|
232
|
+
f"{evicted_session}",
|
|
233
|
+
)
|
|
234
|
+
# Kick off an asyncio `Task` to close the evicted sessions. In the case
|
|
235
|
+
# that the cache filled very quickly and some sessions have been evicted
|
|
236
|
+
# before their original request has been made, we set the timer to a bit
|
|
237
|
+
# more than the `request_timeout` for a call. This should make it so that
|
|
238
|
+
# any call from an evicted session can still be made before the session
|
|
239
|
+
# is closed.
|
|
240
|
+
asyncio.create_task(
|
|
241
|
+
self._async_close_evicted_sessions(
|
|
242
|
+
# if `ClientTimeout.total` is `None`, don't wait forever for the
|
|
243
|
+
# closing session to finish the request. Instead, use the default
|
|
244
|
+
# timeout.
|
|
245
|
+
request_timeout.total or DEFAULT_HTTP_TIMEOUT + 0.1,
|
|
246
|
+
evicted_sessions,
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return cached_session
|
|
251
|
+
|
|
252
|
+
async def async_get_response_from_get_request(
|
|
253
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
254
|
+
) -> ClientResponse:
|
|
255
|
+
kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
|
|
256
|
+
session = await self.async_cache_and_return_session(
|
|
257
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
258
|
+
)
|
|
259
|
+
response = await session.get(endpoint_uri, *args, **kwargs)
|
|
260
|
+
return response
|
|
261
|
+
|
|
262
|
+
async def async_json_make_get_request(
|
|
263
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
264
|
+
) -> Dict[str, Any]:
|
|
265
|
+
response = await self.async_get_response_from_get_request(
|
|
266
|
+
endpoint_uri, *args, **kwargs
|
|
267
|
+
)
|
|
268
|
+
response.raise_for_status()
|
|
269
|
+
return await response.json()
|
|
270
|
+
|
|
271
|
+
async def async_get_response_from_post_request(
|
|
272
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
273
|
+
) -> ClientResponse:
|
|
274
|
+
kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
|
|
275
|
+
session = await self.async_cache_and_return_session(
|
|
276
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
277
|
+
)
|
|
278
|
+
response = await session.post(endpoint_uri, *args, **kwargs)
|
|
279
|
+
return response
|
|
280
|
+
|
|
281
|
+
async def async_make_post_request(
|
|
282
|
+
self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
|
|
283
|
+
) -> bytes:
|
|
284
|
+
response = await self.async_get_response_from_post_request(
|
|
285
|
+
endpoint_uri, data=data, **kwargs
|
|
286
|
+
)
|
|
287
|
+
response.raise_for_status()
|
|
288
|
+
return await response.read()
|
|
289
|
+
|
|
290
|
+
async def _async_close_evicted_sessions(
|
|
291
|
+
self, timeout: float, evicted_sessions: List[ClientSession]
|
|
292
|
+
) -> None:
|
|
293
|
+
await asyncio.sleep(timeout)
|
|
294
|
+
|
|
295
|
+
for evicted_session in evicted_sessions:
|
|
296
|
+
await evicted_session.close()
|
|
297
|
+
self.logger.debug(f"Closed evicted async session: {evicted_session}")
|
|
298
|
+
|
|
299
|
+
if any(not evicted_session.closed for evicted_session in evicted_sessions):
|
|
300
|
+
self.logger.warning(
|
|
301
|
+
"Some evicted async sessions were not properly closed: "
|
|
302
|
+
f"{evicted_sessions}"
|
|
303
|
+
)
|
web3/_utils/math.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import (
|
|
|
5
5
|
|
|
6
6
|
from web3.exceptions import (
|
|
7
7
|
InsufficientData,
|
|
8
|
+
Web3ValueError,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
|
|
@@ -17,23 +18,21 @@ def percentile(
|
|
|
17
18
|
f"Expected a sequence of at least 1 integers, got {values!r}"
|
|
18
19
|
)
|
|
19
20
|
if percentile is None:
|
|
20
|
-
raise
|
|
21
|
+
raise Web3ValueError(f"Expected a percentile choice, got {percentile}")
|
|
22
|
+
if percentile < 0 or percentile > 100:
|
|
23
|
+
raise Web3ValueError("percentile must be in the range [0, 100]")
|
|
21
24
|
|
|
22
25
|
sorted_values = sorted(values)
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
if index < 0:
|
|
28
|
-
return sorted_values[0]
|
|
29
|
-
else:
|
|
30
|
-
index = rank
|
|
27
|
+
index = len(values) * percentile / 100 - 1
|
|
28
|
+
if index < 0:
|
|
29
|
+
return sorted_values[0]
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
fractional = index % 1
|
|
32
|
+
if fractional == 0:
|
|
33
33
|
return sorted_values[int(index)]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return lower + fractional * (higher - lower)
|
|
34
|
+
|
|
35
|
+
integer = int(index - fractional)
|
|
36
|
+
lower = sorted_values[integer]
|
|
37
|
+
higher = sorted_values[integer + 1]
|
|
38
|
+
return lower + fractional * (higher - lower)
|