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.
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.3.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.3.dist-info → web3-7.0.0.dist-info}/top_level.txt +0 -1
  138. ethpm/__init__.py +0 -20
  139. ethpm/_utils/__init__.py +0 -0
  140. ethpm/_utils/backend.py +0 -93
  141. ethpm/_utils/cache.py +0 -44
  142. ethpm/_utils/chains.py +0 -119
  143. ethpm/_utils/contract.py +0 -35
  144. ethpm/_utils/deployments.py +0 -145
  145. ethpm/_utils/ipfs.py +0 -116
  146. ethpm/_utils/protobuf/__init__.py +0 -0
  147. ethpm/_utils/protobuf/ipfs_file_pb2.py +0 -33
  148. ethpm/_utils/registry.py +0 -29
  149. ethpm/assets/__init__.py +0 -0
  150. ethpm/assets/ens/v3.json +0 -1
  151. ethpm/assets/escrow/with_bytecode_v3.json +0 -1
  152. ethpm/assets/ipfs_file.proto +0 -32
  153. ethpm/assets/owned/output_v3.json +0 -1
  154. ethpm/assets/owned/with_contract_type_v3.json +0 -1
  155. ethpm/assets/registry/contracts/Authority.sol +0 -156
  156. ethpm/assets/registry/contracts/IndexedOrderedSetLib.sol +0 -106
  157. ethpm/assets/registry/contracts/PackageDB.sol +0 -225
  158. ethpm/assets/registry/contracts/PackageRegistry.sol +0 -361
  159. ethpm/assets/registry/contracts/PackageRegistryInterface.sol +0 -97
  160. ethpm/assets/registry/contracts/ReleaseDB.sol +0 -309
  161. ethpm/assets/registry/contracts/ReleaseValidator.sol +0 -152
  162. ethpm/assets/registry/solc_input.json +0 -1
  163. ethpm/assets/registry/solc_output.json +0 -1
  164. ethpm/assets/registry/v3.json +0 -1
  165. ethpm/assets/safe-math-lib/v3-strict-no-deployments.json +0 -1
  166. ethpm/assets/simple-registry/contracts/Ownable.sol +0 -63
  167. ethpm/assets/simple-registry/contracts/PackageRegistry.sol +0 -373
  168. ethpm/assets/simple-registry/contracts/PackageRegistryInterface.sol +0 -96
  169. ethpm/assets/simple-registry/solc_input.json +0 -33
  170. ethpm/assets/simple-registry/solc_output.json +0 -1
  171. ethpm/assets/simple-registry/v3.json +0 -1
  172. ethpm/assets/standard-token/output_v3.json +0 -1
  173. ethpm/assets/standard-token/with_bytecode_v3.json +0 -1
  174. ethpm/assets/vyper_registry/0.1.0.json +0 -1
  175. ethpm/assets/vyper_registry/registry.vy +0 -216
  176. ethpm/assets/vyper_registry/registry_with_delete.vy +0 -244
  177. ethpm/backends/__init__.py +0 -0
  178. ethpm/backends/base.py +0 -43
  179. ethpm/backends/http.py +0 -108
  180. ethpm/backends/ipfs.py +0 -219
  181. ethpm/backends/registry.py +0 -154
  182. ethpm/constants.py +0 -17
  183. ethpm/contract.py +0 -187
  184. ethpm/dependencies.py +0 -58
  185. ethpm/deployments.py +0 -80
  186. ethpm/ethpm-spec/examples/escrow/1.0.0-pretty.json +0 -146
  187. ethpm/ethpm-spec/examples/escrow/1.0.0.json +0 -1
  188. ethpm/ethpm-spec/examples/escrow/contracts/Escrow.sol +0 -32
  189. ethpm/ethpm-spec/examples/escrow/contracts/SafeSendLib.sol +0 -20
  190. ethpm/ethpm-spec/examples/escrow/v3-pretty.json +0 -171
  191. ethpm/ethpm-spec/examples/escrow/v3.json +0 -1
  192. ethpm/ethpm-spec/examples/owned/1.0.0-pretty.json +0 -21
  193. ethpm/ethpm-spec/examples/owned/1.0.0.json +0 -1
  194. ethpm/ethpm-spec/examples/owned/contracts/Owned.sol +0 -12
  195. ethpm/ethpm-spec/examples/owned/v3-pretty.json +0 -27
  196. ethpm/ethpm-spec/examples/owned/v3.json +0 -1
  197. ethpm/ethpm-spec/examples/piper-coin/1.0.0-pretty.json +0 -31
  198. ethpm/ethpm-spec/examples/piper-coin/1.0.0.json +0 -1
  199. ethpm/ethpm-spec/examples/piper-coin/v3-pretty.json +0 -21
  200. ethpm/ethpm-spec/examples/piper-coin/v3.json +0 -1
  201. ethpm/ethpm-spec/examples/safe-math-lib/1.0.0-pretty.json +0 -85
  202. ethpm/ethpm-spec/examples/safe-math-lib/1.0.0.json +0 -1
  203. ethpm/ethpm-spec/examples/safe-math-lib/contracts/SafeMathLib.sol +0 -24
  204. ethpm/ethpm-spec/examples/safe-math-lib/v3-pretty.json +0 -117
  205. ethpm/ethpm-spec/examples/safe-math-lib/v3.json +0 -1
  206. ethpm/ethpm-spec/examples/standard-token/1.0.0-pretty.json +0 -55
  207. ethpm/ethpm-spec/examples/standard-token/1.0.0.json +0 -1
  208. ethpm/ethpm-spec/examples/standard-token/contracts/AbstractToken.sol +0 -20
  209. ethpm/ethpm-spec/examples/standard-token/contracts/StandardToken.sol +0 -84
  210. ethpm/ethpm-spec/examples/standard-token/v3-pretty.json +0 -460
  211. ethpm/ethpm-spec/examples/standard-token/v3.json +0 -1
  212. ethpm/ethpm-spec/examples/transferable/1.0.0-pretty.json +0 -21
  213. ethpm/ethpm-spec/examples/transferable/1.0.0.json +0 -1
  214. ethpm/ethpm-spec/examples/transferable/contracts/Transferable.sol +0 -14
  215. ethpm/ethpm-spec/examples/transferable/v3-pretty.json +0 -27
  216. ethpm/ethpm-spec/examples/transferable/v3.json +0 -1
  217. ethpm/ethpm-spec/examples/wallet/1.0.0-pretty.json +0 -120
  218. ethpm/ethpm-spec/examples/wallet/1.0.0.json +0 -1
  219. ethpm/ethpm-spec/examples/wallet/contracts/Wallet.sol +0 -41
  220. ethpm/ethpm-spec/examples/wallet/v3-pretty.json +0 -181
  221. ethpm/ethpm-spec/examples/wallet/v3.json +0 -1
  222. ethpm/ethpm-spec/examples/wallet-with-send/1.0.0-pretty.json +0 -135
  223. ethpm/ethpm-spec/examples/wallet-with-send/1.0.0.json +0 -1
  224. ethpm/ethpm-spec/examples/wallet-with-send/contracts/WalletWithSend.sol +0 -18
  225. ethpm/ethpm-spec/examples/wallet-with-send/v3-pretty.json +0 -207
  226. ethpm/ethpm-spec/examples/wallet-with-send/v3.json +0 -1
  227. ethpm/ethpm-spec/spec/package.spec.json +0 -379
  228. ethpm/ethpm-spec/spec/v3.spec.json +0 -483
  229. ethpm/exceptions.py +0 -68
  230. ethpm/package.py +0 -438
  231. ethpm/tools/__init__.py +0 -4
  232. ethpm/tools/builder.py +0 -930
  233. ethpm/tools/checker.py +0 -312
  234. ethpm/tools/get_manifest.py +0 -19
  235. ethpm/uri.py +0 -141
  236. ethpm/validation/__init__.py +0 -0
  237. ethpm/validation/manifest.py +0 -146
  238. ethpm/validation/misc.py +0 -39
  239. ethpm/validation/package.py +0 -80
  240. ethpm/validation/uri.py +0 -163
  241. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  242. web3/_utils/miner.py +0 -88
  243. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -323
  244. web3/_utils/request.py +0 -265
  245. web3/middleware/abi.py +0 -11
  246. web3/middleware/async_cache.py +0 -99
  247. web3/middleware/cache.py +0 -374
  248. web3/middleware/exception_handling.py +0 -49
  249. web3/middleware/exception_retry_request.py +0 -188
  250. web3/middleware/fixture.py +0 -190
  251. web3/middleware/geth_poa.py +0 -81
  252. web3/middleware/normalize_request_parameters.py +0 -11
  253. web3/middleware/simulate_unmined_transaction.py +0 -43
  254. web3/pm.py +0 -602
  255. web3/providers/async_rpc.py +0 -99
  256. web3/providers/rpc.py +0 -98
  257. web3/providers/websocket/__init__.py +0 -11
  258. web3/providers/websocket/websocket_connection.py +0 -42
  259. web3/tools/__init__.py +0 -4
  260. web3/tools/pytest_ethereum/__init__.py +0 -0
  261. web3/tools/pytest_ethereum/_utils.py +0 -145
  262. web3/tools/pytest_ethereum/deployer.py +0 -48
  263. web3/tools/pytest_ethereum/exceptions.py +0 -22
  264. web3/tools/pytest_ethereum/linker.py +0 -128
  265. web3/tools/pytest_ethereum/plugins.py +0 -33
  266. web3-6.20.3.dist-info/METADATA +0 -104
  267. web3-6.20.3.dist-info/RECORD +0 -283
  268. web3-6.20.3.dist-info/entry_points.txt +0 -2
  269. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
  270. {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[Callable[..., Any], ...],
101
- ) -> str:
102
- # copy the request counter and find the next request id without incrementing
103
- # since this is done when / if the request is successfully sent
104
- request_id = next(copy(self._provider.request_counter))
105
- cache_key = generate_cache_key(request_id)
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, request_id)
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={request_id},\n"
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 ValueError("Subscription response must have params field")
191
+ raise Web3ValueError("Subscription response must have params field")
172
192
  if "subscription" not in response["params"]:
173
- raise ValueError(
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 request id
206
+ # retrieve the request info from the cache using the response id
188
207
  cache_key = generate_cache_key(response["id"])
189
- request_info = (
190
- # pop the request info from the cache since we don't need to keep it
191
- # this keeps the cache size bounded
192
- self.pop_cached_request_information(cache_key)
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 ValueError(
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 WebsocketProviderV2(PersistentConnectionProvider):
55
- logger = logging.getLogger("web3.providers.WebsocketProviderV2")
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 _provider_specific_connect(self) -> None:
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
- current_request_id = json.loads(request_data)["id"]
134
- response = await self._get_response_for_request_id(current_request_id)
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
- return response
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 _provider_specific_message_listener(self) -> None:
139
- while True:
140
- raw_message = await self._ws.recv()
141
- response = json.loads(raw_message)
142
- subscription = response.get("method") == "eth_subscription"
143
- await self._request_processor.cache_raw_response(
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,11 @@
1
+ from .async_rpc import (
2
+ AsyncHTTPProvider,
3
+ )
4
+ from .rpc import (
5
+ HTTPProvider,
6
+ )
7
+
8
+ __all__ = [
9
+ "AsyncHTTPProvider",
10
+ "HTTPProvider",
11
+ ]
@@ -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)