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.
Files changed (270) hide show
  1. ens/__init__.py +13 -2
  2. ens/_normalization.py +2 -17
  3. ens/async_ens.py +33 -21
  4. ens/base_ens.py +3 -1
  5. ens/ens.py +16 -11
  6. ens/exceptions.py +16 -29
  7. ens/specs/nf.json +1 -1
  8. ens/specs/normalization_spec.json +1 -1
  9. ens/utils.py +52 -63
  10. web3/__init__.py +20 -24
  11. web3/_utils/abi.py +115 -271
  12. web3/_utils/async_transactions.py +7 -4
  13. web3/_utils/batching.py +217 -0
  14. web3/_utils/blocks.py +6 -2
  15. web3/_utils/caching.py +128 -5
  16. web3/_utils/compat/__init__.py +2 -3
  17. web3/_utils/contract_sources/compile_contracts.py +1 -1
  18. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  19. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  20. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  21. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  22. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
  24. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  25. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  26. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  27. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  28. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  29. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  30. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  31. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  32. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  33. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  34. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  35. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  36. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  37. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  38. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  39. web3/_utils/contracts.py +130 -236
  40. web3/_utils/datatypes.py +5 -1
  41. web3/_utils/decorators.py +13 -23
  42. web3/_utils/empty.py +1 -1
  43. web3/_utils/encoding.py +16 -12
  44. web3/_utils/ens.py +2 -1
  45. web3/_utils/error_formatters_utils.py +5 -3
  46. web3/_utils/events.py +66 -69
  47. web3/_utils/fee_utils.py +1 -3
  48. web3/_utils/filters.py +24 -22
  49. web3/_utils/formatters.py +2 -2
  50. web3/_utils/http.py +5 -3
  51. web3/_utils/http_session_manager.py +303 -0
  52. web3/_utils/math.py +14 -15
  53. web3/_utils/method_formatters.py +34 -36
  54. web3/_utils/module.py +2 -1
  55. web3/_utils/module_testing/__init__.py +0 -3
  56. web3/_utils/module_testing/eth_module.py +695 -643
  57. web3/_utils/module_testing/module_testing_utils.py +61 -34
  58. web3/_utils/module_testing/persistent_connection_provider.py +56 -25
  59. web3/_utils/module_testing/utils.py +258 -0
  60. web3/_utils/module_testing/web3_module.py +438 -17
  61. web3/_utils/normalizers.py +13 -11
  62. web3/_utils/rpc_abi.py +5 -32
  63. web3/_utils/threads.py +8 -7
  64. web3/_utils/transactions.py +14 -12
  65. web3/_utils/type_conversion.py +5 -1
  66. web3/_utils/validation.py +17 -17
  67. web3/auto/gethdev.py +7 -2
  68. web3/beacon/__init__.py +6 -1
  69. web3/beacon/async_beacon.py +9 -5
  70. web3/beacon/{main.py → beacon.py} +7 -5
  71. web3/contract/__init__.py +7 -0
  72. web3/contract/async_contract.py +47 -46
  73. web3/contract/base_contract.py +183 -158
  74. web3/contract/contract.py +49 -43
  75. web3/contract/utils.py +203 -59
  76. web3/datastructures.py +79 -31
  77. web3/eth/__init__.py +7 -0
  78. web3/eth/async_eth.py +39 -51
  79. web3/eth/base_eth.py +17 -10
  80. web3/eth/eth.py +30 -68
  81. web3/exceptions.py +108 -82
  82. web3/gas_strategies/time_based.py +8 -6
  83. web3/geth.py +1 -254
  84. web3/main.py +75 -122
  85. web3/manager.py +316 -146
  86. web3/method.py +38 -31
  87. web3/middleware/__init__.py +67 -89
  88. web3/middleware/attrdict.py +36 -49
  89. web3/middleware/base.py +174 -0
  90. web3/middleware/buffered_gas_estimate.py +20 -21
  91. web3/middleware/filter.py +157 -117
  92. web3/middleware/formatting.py +124 -108
  93. web3/middleware/gas_price_strategy.py +20 -32
  94. web3/middleware/names.py +29 -26
  95. web3/middleware/proof_of_authority.py +68 -0
  96. web3/middleware/pythonic.py +2 -2
  97. web3/middleware/signing.py +74 -89
  98. web3/middleware/stalecheck.py +52 -79
  99. web3/middleware/validation.py +5 -13
  100. web3/module.py +54 -10
  101. web3/providers/__init__.py +10 -6
  102. web3/providers/async_base.py +117 -39
  103. web3/providers/auto.py +3 -3
  104. web3/providers/base.py +89 -33
  105. web3/providers/eth_tester/__init__.py +5 -0
  106. web3/providers/eth_tester/defaults.py +1 -64
  107. web3/providers/eth_tester/main.py +99 -31
  108. web3/providers/eth_tester/middleware.py +45 -73
  109. web3/providers/ipc.py +42 -46
  110. web3/providers/{websocket/websocket.py → legacy_websocket.py} +32 -7
  111. web3/providers/persistent/__init__.py +22 -0
  112. web3/providers/persistent/async_ipc.py +153 -0
  113. web3/providers/{persistent.py → persistent/persistent.py} +106 -25
  114. web3/providers/persistent/persistent_connection.py +84 -0
  115. web3/providers/{websocket → persistent}/request_processor.py +94 -32
  116. web3/providers/persistent/utils.py +43 -0
  117. web3/providers/{websocket/websocket_v2.py → persistent/websocket.py} +29 -28
  118. web3/providers/rpc/__init__.py +11 -0
  119. web3/providers/rpc/async_rpc.py +171 -0
  120. web3/providers/rpc/rpc.py +179 -0
  121. web3/providers/rpc/utils.py +92 -0
  122. web3/testing.py +4 -4
  123. web3/tools/benchmark/main.py +22 -22
  124. web3/tools/benchmark/node.py +2 -8
  125. web3/tools/benchmark/reporting.py +2 -2
  126. web3/tools/benchmark/utils.py +1 -1
  127. web3/tracing.py +9 -5
  128. web3/types.py +30 -107
  129. web3/utils/__init__.py +58 -5
  130. web3/utils/abi.py +575 -10
  131. web3/utils/async_exception_handling.py +19 -7
  132. web3/utils/caching.py +32 -13
  133. web3/utils/exception_handling.py +7 -5
  134. {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/LICENSE +1 -1
  135. web3-7.0.0.dist-info/METADATA +112 -0
  136. web3-7.0.0.dist-info/RECORD +167 -0
  137. {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/WHEEL +1 -1
  138. {web3-6.20.2.dist-info → web3-7.0.0.dist-info}/top_level.txt +0 -1
  139. ethpm/__init__.py +0 -20
  140. ethpm/_utils/__init__.py +0 -0
  141. ethpm/_utils/backend.py +0 -93
  142. ethpm/_utils/cache.py +0 -44
  143. ethpm/_utils/chains.py +0 -119
  144. ethpm/_utils/contract.py +0 -35
  145. ethpm/_utils/deployments.py +0 -145
  146. ethpm/_utils/ipfs.py +0 -116
  147. ethpm/_utils/protobuf/__init__.py +0 -0
  148. ethpm/_utils/protobuf/ipfs_file_pb2.py +0 -33
  149. ethpm/_utils/registry.py +0 -29
  150. ethpm/assets/__init__.py +0 -0
  151. ethpm/assets/ens/v3.json +0 -1
  152. ethpm/assets/escrow/with_bytecode_v3.json +0 -1
  153. ethpm/assets/ipfs_file.proto +0 -32
  154. ethpm/assets/owned/output_v3.json +0 -1
  155. ethpm/assets/owned/with_contract_type_v3.json +0 -1
  156. ethpm/assets/registry/contracts/Authority.sol +0 -156
  157. ethpm/assets/registry/contracts/IndexedOrderedSetLib.sol +0 -106
  158. ethpm/assets/registry/contracts/PackageDB.sol +0 -225
  159. ethpm/assets/registry/contracts/PackageRegistry.sol +0 -361
  160. ethpm/assets/registry/contracts/PackageRegistryInterface.sol +0 -97
  161. ethpm/assets/registry/contracts/ReleaseDB.sol +0 -309
  162. ethpm/assets/registry/contracts/ReleaseValidator.sol +0 -152
  163. ethpm/assets/registry/solc_input.json +0 -1
  164. ethpm/assets/registry/solc_output.json +0 -1
  165. ethpm/assets/registry/v3.json +0 -1
  166. ethpm/assets/safe-math-lib/v3-strict-no-deployments.json +0 -1
  167. ethpm/assets/simple-registry/contracts/Ownable.sol +0 -63
  168. ethpm/assets/simple-registry/contracts/PackageRegistry.sol +0 -373
  169. ethpm/assets/simple-registry/contracts/PackageRegistryInterface.sol +0 -96
  170. ethpm/assets/simple-registry/solc_input.json +0 -33
  171. ethpm/assets/simple-registry/solc_output.json +0 -1
  172. ethpm/assets/simple-registry/v3.json +0 -1
  173. ethpm/assets/standard-token/output_v3.json +0 -1
  174. ethpm/assets/standard-token/with_bytecode_v3.json +0 -1
  175. ethpm/assets/vyper_registry/0.1.0.json +0 -1
  176. ethpm/assets/vyper_registry/registry.vy +0 -216
  177. ethpm/assets/vyper_registry/registry_with_delete.vy +0 -244
  178. ethpm/backends/__init__.py +0 -0
  179. ethpm/backends/base.py +0 -43
  180. ethpm/backends/http.py +0 -108
  181. ethpm/backends/ipfs.py +0 -219
  182. ethpm/backends/registry.py +0 -154
  183. ethpm/constants.py +0 -17
  184. ethpm/contract.py +0 -187
  185. ethpm/dependencies.py +0 -58
  186. ethpm/deployments.py +0 -80
  187. ethpm/ethpm-spec/examples/escrow/1.0.0-pretty.json +0 -146
  188. ethpm/ethpm-spec/examples/escrow/1.0.0.json +0 -1
  189. ethpm/ethpm-spec/examples/escrow/contracts/Escrow.sol +0 -32
  190. ethpm/ethpm-spec/examples/escrow/contracts/SafeSendLib.sol +0 -20
  191. ethpm/ethpm-spec/examples/escrow/v3-pretty.json +0 -171
  192. ethpm/ethpm-spec/examples/escrow/v3.json +0 -1
  193. ethpm/ethpm-spec/examples/owned/1.0.0-pretty.json +0 -21
  194. ethpm/ethpm-spec/examples/owned/1.0.0.json +0 -1
  195. ethpm/ethpm-spec/examples/owned/contracts/Owned.sol +0 -12
  196. ethpm/ethpm-spec/examples/owned/v3-pretty.json +0 -27
  197. ethpm/ethpm-spec/examples/owned/v3.json +0 -1
  198. ethpm/ethpm-spec/examples/piper-coin/1.0.0-pretty.json +0 -31
  199. ethpm/ethpm-spec/examples/piper-coin/1.0.0.json +0 -1
  200. ethpm/ethpm-spec/examples/piper-coin/v3-pretty.json +0 -21
  201. ethpm/ethpm-spec/examples/piper-coin/v3.json +0 -1
  202. ethpm/ethpm-spec/examples/safe-math-lib/1.0.0-pretty.json +0 -85
  203. ethpm/ethpm-spec/examples/safe-math-lib/1.0.0.json +0 -1
  204. ethpm/ethpm-spec/examples/safe-math-lib/contracts/SafeMathLib.sol +0 -24
  205. ethpm/ethpm-spec/examples/safe-math-lib/v3-pretty.json +0 -117
  206. ethpm/ethpm-spec/examples/safe-math-lib/v3.json +0 -1
  207. ethpm/ethpm-spec/examples/standard-token/1.0.0-pretty.json +0 -55
  208. ethpm/ethpm-spec/examples/standard-token/1.0.0.json +0 -1
  209. ethpm/ethpm-spec/examples/standard-token/contracts/AbstractToken.sol +0 -20
  210. ethpm/ethpm-spec/examples/standard-token/contracts/StandardToken.sol +0 -84
  211. ethpm/ethpm-spec/examples/standard-token/v3-pretty.json +0 -460
  212. ethpm/ethpm-spec/examples/standard-token/v3.json +0 -1
  213. ethpm/ethpm-spec/examples/transferable/1.0.0-pretty.json +0 -21
  214. ethpm/ethpm-spec/examples/transferable/1.0.0.json +0 -1
  215. ethpm/ethpm-spec/examples/transferable/contracts/Transferable.sol +0 -14
  216. ethpm/ethpm-spec/examples/transferable/v3-pretty.json +0 -27
  217. ethpm/ethpm-spec/examples/transferable/v3.json +0 -1
  218. ethpm/ethpm-spec/examples/wallet/1.0.0-pretty.json +0 -120
  219. ethpm/ethpm-spec/examples/wallet/1.0.0.json +0 -1
  220. ethpm/ethpm-spec/examples/wallet/contracts/Wallet.sol +0 -41
  221. ethpm/ethpm-spec/examples/wallet/v3-pretty.json +0 -181
  222. ethpm/ethpm-spec/examples/wallet/v3.json +0 -1
  223. ethpm/ethpm-spec/examples/wallet-with-send/1.0.0-pretty.json +0 -135
  224. ethpm/ethpm-spec/examples/wallet-with-send/1.0.0.json +0 -1
  225. ethpm/ethpm-spec/examples/wallet-with-send/contracts/WalletWithSend.sol +0 -18
  226. ethpm/ethpm-spec/examples/wallet-with-send/v3-pretty.json +0 -207
  227. ethpm/ethpm-spec/examples/wallet-with-send/v3.json +0 -1
  228. ethpm/ethpm-spec/spec/package.spec.json +0 -379
  229. ethpm/ethpm-spec/spec/v3.spec.json +0 -483
  230. ethpm/exceptions.py +0 -68
  231. ethpm/package.py +0 -438
  232. ethpm/tools/__init__.py +0 -4
  233. ethpm/tools/builder.py +0 -930
  234. ethpm/tools/checker.py +0 -312
  235. ethpm/tools/get_manifest.py +0 -19
  236. ethpm/uri.py +0 -141
  237. ethpm/validation/__init__.py +0 -0
  238. ethpm/validation/manifest.py +0 -146
  239. ethpm/validation/misc.py +0 -39
  240. ethpm/validation/package.py +0 -80
  241. ethpm/validation/uri.py +0 -163
  242. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  243. web3/_utils/miner.py +0 -88
  244. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -323
  245. web3/_utils/request.py +0 -265
  246. web3/middleware/abi.py +0 -11
  247. web3/middleware/async_cache.py +0 -99
  248. web3/middleware/cache.py +0 -374
  249. web3/middleware/exception_handling.py +0 -49
  250. web3/middleware/exception_retry_request.py +0 -188
  251. web3/middleware/fixture.py +0 -190
  252. web3/middleware/geth_poa.py +0 -81
  253. web3/middleware/normalize_request_parameters.py +0 -11
  254. web3/middleware/simulate_unmined_transaction.py +0 -43
  255. web3/pm.py +0 -602
  256. web3/providers/async_rpc.py +0 -99
  257. web3/providers/rpc.py +0 -98
  258. web3/providers/websocket/__init__.py +0 -11
  259. web3/providers/websocket/websocket_connection.py +0 -42
  260. web3/tools/__init__.py +0 -4
  261. web3/tools/pytest_ethereum/__init__.py +0 -0
  262. web3/tools/pytest_ethereum/_utils.py +0 -145
  263. web3/tools/pytest_ethereum/deployer.py +0 -48
  264. web3/tools/pytest_ethereum/exceptions.py +0 -22
  265. web3/tools/pytest_ethereum/linker.py +0 -128
  266. web3/tools/pytest_ethereum/plugins.py +0 -33
  267. web3-6.20.2.dist-info/METADATA +0 -103
  268. web3-6.20.2.dist-info/RECORD +0 -283
  269. web3-6.20.2.dist-info/entry_points.txt +0 -2
  270. /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
- fromBlock: Optional[BlockIdentifier] = None,
75
- toBlock: Optional[BlockIdentifier] = None,
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 TypeError(
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
- if len(topic_set) == 1 and is_list_like(topic_set[0]):
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 ValueError(
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 fromBlock is not None:
124
- filter_params["fromBlock"] = fromBlock
121
+ if from_block is not None:
122
+ filter_params["fromBlock"] = from_block
125
123
 
126
- if toBlock is not None:
127
- filter_params["toBlock"] = 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(Filter, self).__init__(filter_id)
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(AsyncFilter, self).__init__(filter_id)
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
- """Sets the data filters (non indexed argument filters)
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
- """Sets the data filters (non indexed argument filters)
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
- """Decodes utf-8 bytes to strings for abi string values.
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((normalize_to_text(value) for value in data_value))
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
- """Match function used for filtering non-indexed event arguments.
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 ValueError(
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((is_string(item) for item in value))
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((is_dict(item) for item in value))
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
- def construct_user_agent(class_name: str) -> str:
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
- user_agent = f"web3.py/{web3_version}/{class_name}"
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 ValueError(f"Expected a percentile choice, got {percentile}")
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
- rank = len(values) * percentile / 100
25
- if rank > 0:
26
- index = rank - 1
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
- if index % 1 == 0:
31
+ fractional = index % 1
32
+ if fractional == 0:
33
33
  return sorted_values[int(index)]
34
- else:
35
- fractional = index % 1
36
- integer = int(index - fractional)
37
- lower = sorted_values[integer]
38
- higher = sorted_values[integer + 1]
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)