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
web3/manager.py CHANGED
@@ -1,9 +1,11 @@
1
+ import asyncio
1
2
  import logging
2
3
  from typing import (
3
4
  TYPE_CHECKING,
4
5
  Any,
5
6
  AsyncGenerator,
6
7
  Callable,
8
+ Coroutine,
7
9
  List,
8
10
  Optional,
9
11
  Sequence,
@@ -18,10 +20,10 @@ from eth_utils.toolz import (
18
20
  from hexbytes import (
19
21
  HexBytes,
20
22
  )
21
- from websockets.exceptions import (
22
- ConnectionClosedOK,
23
- )
24
23
 
24
+ from web3._utils.batching import (
25
+ RequestBatcher,
26
+ )
25
27
  from web3._utils.caching import (
26
28
  generate_cache_key,
27
29
  )
@@ -35,33 +37,37 @@ from web3.exceptions import (
35
37
  BadResponseFormat,
36
38
  MethodUnavailable,
37
39
  ProviderConnectionError,
40
+ RequestTimedOut,
38
41
  TaskNotRunning,
42
+ Web3RPCError,
43
+ Web3TypeError,
44
+ )
45
+ from web3.method import (
46
+ Method,
39
47
  )
40
48
  from web3.middleware import (
41
- abi_middleware,
42
- async_attrdict_middleware,
43
- async_buffered_gas_estimate_middleware,
44
- async_gas_price_strategy_middleware,
45
- async_name_to_address_middleware,
46
- async_validation_middleware,
47
- attrdict_middleware,
48
- buffered_gas_estimate_middleware,
49
- gas_price_strategy_middleware,
50
- name_to_address_middleware,
51
- validation_middleware,
49
+ AttributeDictMiddleware,
50
+ BufferedGasEstimateMiddleware,
51
+ ENSNameToAddressMiddleware,
52
+ GasPriceStrategyMiddleware,
53
+ ValidationMiddleware,
54
+ )
55
+ from web3.middleware.base import (
56
+ Middleware,
57
+ MiddlewareOnion,
52
58
  )
53
59
  from web3.module import (
54
60
  apply_result_formatters,
55
61
  )
56
62
  from web3.providers import (
57
63
  AutoProvider,
64
+ JSONBaseProvider,
58
65
  PersistentConnectionProvider,
59
66
  )
67
+ from web3.providers.async_base import (
68
+ AsyncJSONBaseProvider,
69
+ )
60
70
  from web3.types import (
61
- AsyncMiddleware,
62
- AsyncMiddlewareOnion,
63
- Middleware,
64
- MiddlewareOnion,
65
71
  RPCEndpoint,
66
72
  RPCResponse,
67
73
  )
@@ -71,16 +77,26 @@ if TYPE_CHECKING:
71
77
  AsyncWeb3,
72
78
  Web3,
73
79
  )
80
+ from web3.middleware.base import ( # noqa: F401
81
+ Web3Middleware,
82
+ )
74
83
  from web3.providers import ( # noqa: F401
75
84
  AsyncBaseProvider,
76
85
  BaseProvider,
77
86
  )
78
- from web3.providers.websocket.request_processor import ( # noqa: F401
87
+ from web3.providers.persistent.request_processor import ( # noqa: F401
79
88
  RequestProcessor,
80
89
  )
81
90
 
82
91
 
83
92
  NULL_RESPONSES = [None, HexBytes("0x"), "0x"]
93
+ KNOWN_REQUEST_TIMEOUT_MESSAGING = {
94
+ # Note: It's important to be very explicit here and not too broad. We don't want
95
+ # to accidentally catch a message that is not for a request timeout. In the worst
96
+ # case, we raise something more generic like `Web3RPCError`. JSON-RPC unfortunately
97
+ # has not standardized error codes for request timeouts.
98
+ "request timed out", # go-ethereum
99
+ }
84
100
  METHOD_NOT_FOUND = -32601
85
101
 
86
102
 
@@ -89,6 +105,7 @@ def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
89
105
  raw_response = f"The raw response is: {response}"
90
106
 
91
107
  if error is not None and error != "":
108
+ error = error[:-1] if error.endswith(".") else error
92
109
  message = f"{message} {error}. {raw_response}"
93
110
  else:
94
111
  message = f"{message} {raw_response}"
@@ -119,22 +136,135 @@ def apply_null_result_formatters(
119
136
  return response
120
137
 
121
138
 
139
+ def _validate_subscription_fields(response: RPCResponse) -> None:
140
+ params = response["params"]
141
+ subscription = params["subscription"]
142
+ if not isinstance(subscription, str) and not len(subscription) == 34:
143
+ _raise_bad_response_format(
144
+ response, "eth_subscription 'params' must include a 'subscription' field."
145
+ )
146
+
147
+
148
+ def _validate_response(
149
+ response: RPCResponse,
150
+ error_formatters: Optional[Callable[..., Any]],
151
+ is_subscription_response: bool = False,
152
+ logger: Optional[logging.Logger] = None,
153
+ ) -> None:
154
+ if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
155
+ _raise_bad_response_format(
156
+ response, 'The "jsonrpc" field must be present with a value of "2.0".'
157
+ )
158
+
159
+ response_id = response.get("id")
160
+ if "id" in response:
161
+ int_error_msg = (
162
+ '"id" must be an integer or a string representation of an integer.'
163
+ )
164
+ if response_id is None and "error" in response:
165
+ # errors can sometimes have null `id`, according to the JSON-RPC spec
166
+ pass
167
+ elif not isinstance(response_id, (str, int)):
168
+ _raise_bad_response_format(response, int_error_msg)
169
+ elif isinstance(response_id, str):
170
+ try:
171
+ int(response_id)
172
+ except ValueError:
173
+ _raise_bad_response_format(response, int_error_msg)
174
+ elif is_subscription_response:
175
+ # if `id` is not present, this must be a subscription response
176
+ _validate_subscription_fields(response)
177
+ else:
178
+ _raise_bad_response_format(
179
+ response,
180
+ 'Response must include an "id" field or be formatted as an '
181
+ "`eth_subscription` response.",
182
+ )
183
+
184
+ if all(key in response for key in {"error", "result"}):
185
+ _raise_bad_response_format(
186
+ response, 'Response cannot include both "error" and "result".'
187
+ )
188
+ elif (
189
+ not any(key in response for key in {"error", "result"})
190
+ and not is_subscription_response
191
+ ):
192
+ _raise_bad_response_format(
193
+ response, 'Response must include either "error" or "result".'
194
+ )
195
+ elif "error" in response:
196
+ web3_rpc_error: Optional[Web3RPCError] = None
197
+ error = response["error"]
198
+
199
+ # raise the error when the value is a string
200
+ if error is None or not isinstance(error, dict):
201
+ _raise_bad_response_format(
202
+ response,
203
+ 'response["error"] must be a valid object as defined by the '
204
+ "JSON-RPC 2.0 specification.",
205
+ )
206
+
207
+ # errors must include a message
208
+ error_message = error.get("message")
209
+ if not isinstance(error_message, str):
210
+ _raise_bad_response_format(
211
+ response, 'error["message"] is required and must be a string value.'
212
+ )
213
+
214
+ # errors must include an integer code
215
+ code = error.get("code")
216
+ if not isinstance(code, int):
217
+ _raise_bad_response_format(
218
+ response, 'error["code"] is required and must be an integer value.'
219
+ )
220
+ elif code == METHOD_NOT_FOUND:
221
+ web3_rpc_error = MethodUnavailable(
222
+ repr(error),
223
+ rpc_response=response,
224
+ user_message=(
225
+ "This method is not available. Check your node provider or your "
226
+ "client's API docs to see what methods are supported and / or "
227
+ "currently enabled."
228
+ ),
229
+ )
230
+ elif any(
231
+ # parse specific timeout messages
232
+ timeout_str in error_message.lower()
233
+ for timeout_str in KNOWN_REQUEST_TIMEOUT_MESSAGING
234
+ ):
235
+ web3_rpc_error = RequestTimedOut(
236
+ repr(error),
237
+ rpc_response=response,
238
+ user_message=(
239
+ "The request timed out. Check the connection to your node and "
240
+ "try again."
241
+ ),
242
+ )
243
+
244
+ if web3_rpc_error is None:
245
+ # if no condition was met above, raise a more generic `Web3RPCError`
246
+ web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
247
+
248
+ response = apply_error_formatters(error_formatters, response)
249
+
250
+ logger.error(web3_rpc_error.user_message)
251
+ logger.debug(f"RPC error response: {response}")
252
+ raise web3_rpc_error
253
+
254
+ elif "result" not in response and not is_subscription_response:
255
+ _raise_bad_response_format(response)
256
+
257
+
122
258
  class RequestManager:
123
- logger = logging.getLogger("web3.RequestManager")
259
+ logger = logging.getLogger("web3.manager.RequestManager")
124
260
 
125
- middleware_onion: Union[
126
- MiddlewareOnion, AsyncMiddlewareOnion, NamedElementOnion[None, None]
127
- ]
261
+ middleware_onion: Union["MiddlewareOnion", NamedElementOnion[None, None]]
128
262
 
129
263
  def __init__(
130
264
  self,
131
265
  w3: Union["AsyncWeb3", "Web3"],
132
266
  provider: Optional[Union["BaseProvider", "AsyncBaseProvider"]] = None,
133
- middlewares: Optional[
134
- Union[
135
- Sequence[Tuple[Middleware, str]], Sequence[Tuple[AsyncMiddleware, str]]
136
- ]
137
- ] = None,
267
+ middleware: Optional[Sequence[Tuple[Middleware, str]]] = None,
138
268
  ) -> None:
139
269
  self.w3 = w3
140
270
 
@@ -143,14 +273,10 @@ class RequestManager:
143
273
  else:
144
274
  self.provider = provider
145
275
 
146
- if middlewares is None:
147
- middlewares = (
148
- self.async_default_middlewares()
149
- if self.provider.is_async
150
- else self.default_middlewares(cast("Web3", w3))
151
- )
276
+ if middleware is None:
277
+ middleware = self.get_default_middleware()
152
278
 
153
- self.middleware_onion = NamedElementOnion(middlewares)
279
+ self.middleware_onion = NamedElementOnion(middleware)
154
280
 
155
281
  if isinstance(provider, PersistentConnectionProvider):
156
282
  # set up the request processor to be able to properly process ordered
@@ -170,33 +296,17 @@ class RequestManager:
170
296
  self._provider = provider
171
297
 
172
298
  @staticmethod
173
- def default_middlewares(w3: "Web3") -> List[Tuple[Middleware, str]]:
174
- """
175
- List the default middlewares for the request manager.
176
- Leaving w3 unspecified will prevent the middleware from resolving names.
177
- Documentation should remain in sync with these defaults.
178
- """
179
- return [
180
- (gas_price_strategy_middleware, "gas_price_strategy"),
181
- (name_to_address_middleware(w3), "name_to_address"),
182
- (attrdict_middleware, "attrdict"),
183
- (validation_middleware, "validation"),
184
- (abi_middleware, "abi"),
185
- (buffered_gas_estimate_middleware, "gas_estimate"),
186
- ]
187
-
188
- @staticmethod
189
- def async_default_middlewares() -> List[Tuple[AsyncMiddleware, str]]:
299
+ def get_default_middleware() -> List[Tuple[Middleware, str]]:
190
300
  """
191
- List the default async middlewares for the request manager.
301
+ List the default middleware for the request manager.
192
302
  Documentation should remain in sync with these defaults.
193
303
  """
194
304
  return [
195
- (async_gas_price_strategy_middleware, "gas_price_strategy"),
196
- (async_name_to_address_middleware, "name_to_address"),
197
- (async_attrdict_middleware, "attrdict"),
198
- (async_validation_middleware, "validation"),
199
- (async_buffered_gas_estimate_middleware, "gas_estimate"),
305
+ (GasPriceStrategyMiddleware, "gas_price_strategy"),
306
+ (ENSNameToAddressMiddleware, "ens_name_to_address"),
307
+ (AttributeDictMiddleware, "attrdict"),
308
+ (ValidationMiddleware, "validation"),
309
+ (BufferedGasEstimateMiddleware, "gas_estimate"),
200
310
  ]
201
311
 
202
312
  #
@@ -207,7 +317,7 @@ class RequestManager:
207
317
  ) -> RPCResponse:
208
318
  provider = cast("BaseProvider", self.provider)
209
319
  request_func = provider.request_func(
210
- cast("Web3", self.w3), cast(MiddlewareOnion, self.middleware_onion)
320
+ cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
211
321
  )
212
322
  self.logger.debug(f"Making request. Method: {method}")
213
323
  return request_func(method, params)
@@ -217,8 +327,7 @@ class RequestManager:
217
327
  ) -> RPCResponse:
218
328
  provider = cast("AsyncBaseProvider", self.provider)
219
329
  request_func = await provider.request_func(
220
- cast("AsyncWeb3", self.w3),
221
- cast(AsyncMiddlewareOnion, self.middleware_onion),
330
+ cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
222
331
  )
223
332
  self.logger.debug(f"Making request. Method: {method}")
224
333
  return await request_func(method, params)
@@ -232,90 +341,42 @@ class RequestManager:
232
341
  #
233
342
  # See also: https://www.jsonrpc.org/specification
234
343
  #
235
- @staticmethod
236
344
  def formatted_response(
345
+ self,
237
346
  response: RPCResponse,
238
347
  params: Any,
239
348
  error_formatters: Optional[Callable[..., Any]] = None,
240
349
  null_result_formatters: Optional[Callable[..., Any]] = None,
241
350
  ) -> Any:
242
- # jsonrpc is not enforced (as per the spec) but if present, it must be 2.0
243
- if "jsonrpc" in response and response["jsonrpc"] != "2.0":
244
- _raise_bad_response_format(
245
- response, 'The "jsonrpc" field must be present with a value of "2.0"'
246
- )
247
-
248
- # id is not enforced (as per the spec) but if present, it must be a
249
- # string or integer
250
- # TODO: v7 - enforce id per the spec
251
- if "id" in response:
252
- response_id = response["id"]
253
- # id is always None for errors
254
- if response_id is None and "error" not in response:
255
- _raise_bad_response_format(
256
- response, '"id" must be None when an error is present'
257
- )
258
- elif not isinstance(response_id, (str, int, type(None))):
259
- _raise_bad_response_format(response, '"id" must be a string or integer')
260
-
261
- # Response may not include both "error" and "result"
262
- if "error" in response and "result" in response:
263
- _raise_bad_response_format(
264
- response, 'Response cannot include both "error" and "result"'
265
- )
266
-
267
- # Format and validate errors
268
- elif "error" in response:
269
- error = response.get("error")
270
- # Raise the error when the value is a string
271
- if error is None or isinstance(error, str):
272
- raise ValueError(error)
273
-
274
- # Errors must include an integer code
275
- code = error.get("code")
276
- if not isinstance(code, int):
277
- _raise_bad_response_format(response, "error['code'] must be an integer")
278
- elif code == METHOD_NOT_FOUND:
279
- raise MethodUnavailable(
280
- error,
281
- user_message="Check your node provider's API docs to see what "
282
- "methods are supported",
283
- )
284
-
285
- # Errors must include a message
286
- if not isinstance(error.get("message"), str):
287
- _raise_bad_response_format(
288
- response, "error['message'] must be a string"
289
- )
290
-
291
- apply_error_formatters(error_formatters, response)
351
+ is_subscription_response = (
352
+ response.get("method") == "eth_subscription"
353
+ and response.get("params") is not None
354
+ and response["params"].get("subscription") is not None
355
+ and response["params"].get("result") is not None
356
+ )
292
357
 
293
- raise ValueError(error)
358
+ _validate_response(
359
+ response,
360
+ error_formatters,
361
+ is_subscription_response=is_subscription_response,
362
+ logger=self.logger,
363
+ )
294
364
 
295
- # Format and validate results
296
- elif "result" in response:
365
+ # format results
366
+ if "result" in response:
297
367
  # Null values for result should apply null_result_formatters
298
368
  # Skip when result not present in the response (fallback to False)
299
369
  if response.get("result", False) in NULL_RESPONSES:
300
370
  apply_null_result_formatters(null_result_formatters, response, params)
301
371
  return response.get("result")
302
372
 
303
- # Response from eth_subscription includes response["params"]["result"]
304
- elif (
305
- response.get("method") == "eth_subscription"
306
- and response.get("params") is not None
307
- and response["params"].get("subscription") is not None
308
- and response["params"].get("result") is not None
309
- ):
373
+ # response from eth_subscription includes response["params"]["result"]
374
+ elif is_subscription_response:
310
375
  return {
311
376
  "subscription": response["params"]["subscription"],
312
377
  "result": response["params"]["result"],
313
378
  }
314
379
 
315
- # Any other response type raises BadResponseFormat
316
- else:
317
- _raise_bad_response_format(response)
318
-
319
380
  def request_blocking(
320
381
  self,
321
382
  method: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
@@ -346,36 +407,147 @@ class RequestManager:
346
407
  response, params, error_formatters, null_result_formatters
347
408
  )
348
409
 
410
+ # -- batch requests management -- #
411
+
412
+ def _batch_requests(self) -> RequestBatcher[Method[Callable[..., Any]]]:
413
+ """
414
+ Context manager for making batch requests
415
+ """
416
+ if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
417
+ raise Web3TypeError("Batch requests are not supported by this provider.")
418
+ return RequestBatcher(self.w3)
419
+
420
+ def _make_batch_request(
421
+ self, requests_info: List[Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
422
+ ) -> List[RPCResponse]:
423
+ """
424
+ Make a batch request using the provider
425
+ """
426
+ provider = cast(JSONBaseProvider, self.provider)
427
+ request_func = provider.batch_request_func(
428
+ cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
429
+ )
430
+ responses = request_func(
431
+ [
432
+ (method, params)
433
+ for (method, params), _response_formatters in requests_info
434
+ ]
435
+ )
436
+ formatted_responses = [
437
+ self._format_batched_response(info, resp)
438
+ for info, resp in zip(requests_info, responses)
439
+ ]
440
+ return list(formatted_responses)
441
+
442
+ async def _async_make_batch_request(
443
+ self,
444
+ requests_info: List[
445
+ Coroutine[Any, Any, Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
446
+ ],
447
+ ) -> List[RPCResponse]:
448
+ """
449
+ Make an asynchronous batch request using the provider
450
+ """
451
+ provider = cast(AsyncJSONBaseProvider, self.provider)
452
+ request_func = await provider.batch_request_func(
453
+ cast("AsyncWeb3", self.w3),
454
+ cast("MiddlewareOnion", self.middleware_onion),
455
+ )
456
+ # since we add items to the batch without awaiting, we unpack the coroutines
457
+ # and await them all here
458
+ unpacked_requests_info = await asyncio.gather(*requests_info)
459
+ responses = await request_func(
460
+ [
461
+ (method, params)
462
+ for (method, params), _response_formatters in unpacked_requests_info
463
+ ]
464
+ )
465
+
466
+ if isinstance(self.provider, PersistentConnectionProvider):
467
+ # call _process_response for each response in the batch
468
+ return [await self._process_response(resp) for resp in responses]
469
+
470
+ formatted_responses = [
471
+ self._format_batched_response(info, resp)
472
+ for info, resp in zip(unpacked_requests_info, responses)
473
+ ]
474
+ return list(formatted_responses)
475
+
476
+ def _format_batched_response(
477
+ self,
478
+ requests_info: Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]],
479
+ response: RPCResponse,
480
+ ) -> RPCResponse:
481
+ result_formatters, error_formatters, null_result_formatters = requests_info[1]
482
+ return apply_result_formatters(
483
+ result_formatters,
484
+ self.formatted_response(
485
+ response,
486
+ requests_info[0][1],
487
+ error_formatters,
488
+ null_result_formatters,
489
+ ),
490
+ )
491
+
349
492
  # -- persistent connection -- #
350
493
 
351
- async def ws_send(self, method: RPCEndpoint, params: Any) -> RPCResponse:
494
+ async def socket_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
352
495
  provider = cast(PersistentConnectionProvider, self._provider)
353
496
  request_func = await provider.request_func(
354
- cast("AsyncWeb3", self.w3),
355
- cast(AsyncMiddlewareOnion, self.middleware_onion),
497
+ cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
356
498
  )
357
499
  self.logger.debug(
358
- "Making request to open websocket connection - "
359
- f"uri: {provider.endpoint_uri}, method: {method}"
500
+ "Making request to open socket connection and waiting for response: "
501
+ f"{provider.get_endpoint_uri_or_ipc_path()}, method: {method}"
360
502
  )
361
503
  response = await request_func(method, params)
362
- return await self._process_ws_response(response)
504
+ return await self._process_response(response)
505
+
506
+ async def send(self, method: RPCEndpoint, params: Any) -> None:
507
+ provider = cast(PersistentConnectionProvider, self._provider)
508
+ # run through the request processors of the middleware
509
+ for mw_class in self.middleware_onion.as_tuple_of_middleware():
510
+ mw = mw_class(self.w3)
511
+ method, params = mw.request_processor(method, params)
512
+
513
+ self.logger.debug(
514
+ "Sending request to open socket connection: "
515
+ f"{provider.get_endpoint_uri_or_ipc_path()}, method: {method}"
516
+ )
517
+ await provider.socket_send(provider.encode_rpc_request(method, params))
518
+
519
+ async def recv(self) -> RPCResponse:
520
+ provider = cast(PersistentConnectionProvider, self._provider)
521
+ self.logger.debug(
522
+ "Getting next response from open socket connection: "
523
+ f"{provider.get_endpoint_uri_or_ipc_path()}"
524
+ )
525
+ # pop from the queue since the listener task is responsible for reading
526
+ # directly from the socket
527
+ request_response_cache = self._request_processor._request_response_cache
528
+ _key, response = await request_response_cache.async_await_and_popitem(
529
+ last=False,
530
+ timeout=provider.request_timeout,
531
+ )
532
+ return await self._process_response(response)
363
533
 
364
534
  def _persistent_message_stream(self) -> "_AsyncPersistentMessageStream":
365
535
  return _AsyncPersistentMessageStream(self)
366
536
 
367
- async def _get_next_ws_message(self) -> Any:
368
- return await self._ws_message_stream().__anext__()
537
+ async def _get_next_message(self) -> RPCResponse:
538
+ return await self._message_stream().__anext__()
369
539
 
370
- async def _ws_message_stream(self) -> AsyncGenerator[RPCResponse, None]:
540
+ async def _message_stream(self) -> AsyncGenerator[RPCResponse, None]:
371
541
  if not isinstance(self._provider, PersistentConnectionProvider):
372
- raise TypeError(
373
- "Only websocket providers that maintain an open, persistent connection "
374
- "can listen to websocket recv streams."
542
+ raise Web3TypeError(
543
+ "Only providers that maintain an open, persistent connection "
544
+ "can listen to streams."
375
545
  )
376
546
 
377
547
  if self._provider._message_listener_task is None:
378
- raise ProviderConnectionError("No listener found for websocket connection.")
548
+ raise ProviderConnectionError(
549
+ "No listener found for persistent connection."
550
+ )
379
551
 
380
552
  while True:
381
553
  try:
@@ -388,16 +560,17 @@ class RequestManager:
388
560
  in self._request_processor.active_subscriptions
389
561
  ):
390
562
  # if response is an active subscription response, process it
391
- yield await self._process_ws_response(response)
563
+ yield await self._process_response(response)
392
564
  except TaskNotRunning:
565
+ await asyncio.sleep(0)
393
566
  self._provider._handle_listener_task_exceptions()
394
567
  self.logger.error(
395
568
  "Message listener background task has stopped unexpectedly. "
396
569
  "Stopping message stream."
397
570
  )
398
- raise StopAsyncIteration
571
+ return
399
572
 
400
- async def _process_ws_response(self, response: RPCResponse) -> RPCResponse:
573
+ async def _process_response(self, response: RPCResponse) -> RPCResponse:
401
574
  provider = cast(PersistentConnectionProvider, self._provider)
402
575
  request_info = self._request_processor.get_request_information_for_response(
403
576
  response
@@ -461,7 +634,4 @@ class _AsyncPersistentMessageStream:
461
634
  return self
462
635
 
463
636
  async def __anext__(self) -> RPCResponse:
464
- try:
465
- return await self.manager._get_next_ws_message()
466
- except ConnectionClosedOK:
467
- raise StopAsyncIteration
637
+ return await self.manager._get_next_message()