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
@@ -1,17 +1,22 @@
1
+ import asyncio
2
+ import functools
1
3
  import pytest
2
- import time
3
4
  from typing import (
4
5
  TYPE_CHECKING,
5
6
  Any,
7
+ Callable,
6
8
  Collection,
7
9
  Dict,
8
10
  Generator,
9
- Optional,
11
+ Literal,
10
12
  Sequence,
13
+ Tuple,
14
+ Type,
11
15
  Union,
12
16
  )
13
17
 
14
18
  from aiohttp import (
19
+ ClientSession,
15
20
  ClientTimeout,
16
21
  )
17
22
  from eth_typing import (
@@ -27,14 +32,10 @@ from flaky import (
27
32
  from hexbytes import (
28
33
  HexBytes,
29
34
  )
35
+ import requests
30
36
 
31
- from web3._utils.compat import (
32
- Literal,
33
- )
34
- from web3._utils.request import (
35
- async_cache_and_return_session,
36
- asyncio,
37
- cache_and_return_session,
37
+ from web3._utils.http import (
38
+ DEFAULT_HTTP_TIMEOUT,
38
39
  )
39
40
  from web3.types import (
40
41
  BlockData,
@@ -43,7 +44,9 @@ from web3.types import (
43
44
 
44
45
  if TYPE_CHECKING:
45
46
  from _pytest.monkeypatch import MonkeyPatch # noqa: F401
46
- from aiohttp import ClientResponse # noqa: F401
47
+ from aiohttp import ( # noqa: F401
48
+ ClientResponse,
49
+ )
47
50
  from requests import Response # noqa: F401
48
51
 
49
52
  from web3 import Web3 # noqa: F401
@@ -57,19 +60,44 @@ flaky_geth_dev_mining decorator for tests requiring a pending block
57
60
  for the duration of the test. This behavior can be flaky
58
61
  due to timing of the test running as a block is mined.
59
62
  """
60
- flaky_geth_dev_mining = flaky(max_runs=3)
61
-
62
-
63
- def mine_pending_block(w3: "Web3") -> None:
64
- with pytest.warns(DeprecationWarning):
65
- timeout = 10
66
-
67
- w3.geth.miner.start() # type: ignore
68
- start = time.time()
69
- while time.time() < start + timeout:
70
- if len(w3.eth.get_block("pending")["transactions"]) == 0:
71
- break
72
- w3.geth.miner.stop() # type: ignore
63
+ flaky_geth_dev_mining = flaky(max_runs=3, min_passes=1)
64
+
65
+
66
+ def flaky_with_xfail_on_exception(
67
+ reason: str,
68
+ exception: Union[Type[Exception], Tuple[Type[Exception], ...]],
69
+ max_runs: int = 3,
70
+ min_passes: int = 1,
71
+ ) -> Callable[[Any], Any]:
72
+ """
73
+ Some tests inconsistently fail hard with a particular exception and retrying
74
+ these tests often times does not get them "unstuck". If we've exhausted all flaky
75
+ retries and this expected exception is raised, `xfail` the test with the given
76
+ reason.
77
+ """
78
+ runs = max_runs
79
+
80
+ def decorator(func: Any) -> Any:
81
+ @flaky(max_runs=max_runs, min_passes=min_passes)
82
+ @functools.wraps(func)
83
+ async def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
84
+ nonlocal runs
85
+ try:
86
+ return await func(self, *args, **kwargs)
87
+ except exception:
88
+ # xfail the test only if the exception is raised and we have exhausted
89
+ # all flaky retries
90
+ if runs == 1:
91
+ pytest.xfail(reason)
92
+ runs -= 1
93
+ pytest.fail(f"xfailed but {runs} run(s) remaining with flaky...")
94
+ except Exception as e:
95
+ # let flaky handle it
96
+ raise e
97
+
98
+ return wrapper
99
+
100
+ return decorator
73
101
 
74
102
 
75
103
  def assert_contains_log(
@@ -117,13 +145,13 @@ def mock_offchain_lookup_request_response(
117
145
 
118
146
  # mock response only to specified url while validating appropriate fields
119
147
  if url_from_args == mocked_request_url:
120
- assert kwargs["timeout"] == 10
148
+ assert kwargs["timeout"] == DEFAULT_HTTP_TIMEOUT
121
149
  if http_method.upper() == "POST":
122
150
  assert kwargs["data"] == {"data": calldata, "sender": sender}
123
151
  return MockedResponse()
124
152
 
125
153
  # else, make a normal request (no mocking)
126
- session = cache_and_return_session(url_from_args)
154
+ session = requests.Session()
127
155
  return session.request(method=http_method.upper(), url=url_from_args, **kwargs)
128
156
 
129
157
  monkeypatch.setattr(
@@ -167,29 +195,29 @@ def async_mock_offchain_lookup_request_response(
167
195
 
168
196
  # mock response only to specified url while validating appropriate fields
169
197
  if url_from_args == mocked_request_url:
170
- assert kwargs["timeout"] == ClientTimeout(10)
198
+ assert kwargs["timeout"] == ClientTimeout(DEFAULT_HTTP_TIMEOUT)
171
199
  if http_method.upper() == "post":
172
200
  assert kwargs["data"] == {"data": calldata, "sender": sender}
173
201
  return AsyncMockedResponse()
174
202
 
175
203
  # else, make a normal request (no mocking)
176
- session = await async_cache_and_return_session(url_from_args)
177
- return await session.request(
204
+ session = ClientSession()
205
+ response = await session.request(
178
206
  method=http_method.upper(), url=url_from_args, **kwargs
179
207
  )
208
+ await session.close()
209
+ return response
180
210
 
181
211
  monkeypatch.setattr(
182
212
  f"aiohttp.ClientSession.{http_method.lower()}", _mock_specific_request
183
213
  )
184
214
 
185
215
 
186
- class WebsocketMessageStreamMock:
216
+ class WebSocketMessageStreamMock:
187
217
  closed: bool = False
188
218
 
189
219
  def __init__(
190
- self,
191
- messages: Optional[Collection[bytes]] = None,
192
- raise_exception: Optional[Exception] = None,
220
+ self, messages: Collection[bytes] = None, raise_exception: Exception = None
193
221
  ) -> None:
194
222
  self.queue = asyncio.Queue() # type: ignore # py38 issue
195
223
  for msg in messages or []:
@@ -206,12 +234,11 @@ class WebsocketMessageStreamMock:
206
234
  return self
207
235
 
208
236
  async def __anext__(self) -> bytes:
209
- return await self.recv()
237
+ return await self.queue.get()
210
238
 
211
239
  async def recv(self) -> bytes:
212
240
  if self.raise_exception:
213
241
  raise self.raise_exception
214
-
215
242
  return await self.queue.get()
216
243
 
217
244
  @staticmethod
@@ -19,18 +19,35 @@ from web3.datastructures import (
19
19
  AttributeDict,
20
20
  )
21
21
  from web3.middleware import (
22
- async_geth_poa_middleware,
22
+ ExtraDataToPOAMiddleware,
23
23
  )
24
24
  from web3.types import (
25
25
  FormattedEthSubscriptionResponse,
26
+ RPCEndpoint,
26
27
  )
27
28
 
28
29
  if TYPE_CHECKING:
29
30
  from web3.main import (
30
- _PersistentConnectionWeb3,
31
+ AsyncWeb3,
31
32
  )
32
33
 
33
34
 
35
+ SOME_BLOCK_KEYS = [
36
+ "number",
37
+ "hash",
38
+ "parentHash",
39
+ "transactionsRoot",
40
+ "stateRoot",
41
+ "receiptsRoot",
42
+ "size",
43
+ "gasLimit",
44
+ "gasUsed",
45
+ "timestamp",
46
+ "transactions",
47
+ "baseFeePerGas",
48
+ ]
49
+
50
+
34
51
  class PersistentConnectionProviderTest:
35
52
  @pytest.mark.asyncio
36
53
  @pytest.mark.parametrize(
@@ -278,7 +295,7 @@ class PersistentConnectionProviderTest:
278
295
  )
279
296
  async def test_async_eth_subscribe_mocked(
280
297
  self,
281
- async_w3: "_PersistentConnectionWeb3",
298
+ async_w3: "AsyncWeb3",
282
299
  subscription_params: Tuple[Any, ...],
283
300
  ws_subscription_response: Dict[str, Any],
284
301
  expected_formatted_result: Any,
@@ -295,21 +312,22 @@ class PersistentConnectionProviderTest:
295
312
  ws_subscription_response, subscription=True
296
313
  )
297
314
 
298
- async for msg in async_w3.ws.process_subscriptions():
315
+ async for msg in async_w3.socket.process_subscriptions():
299
316
  response = cast(FormattedEthSubscriptionResponse, msg)
300
317
  assert response["subscription"] == sub_id
301
318
  assert response["result"] == expected_formatted_result
302
319
 
303
320
  # only testing one message, so break here
321
+ await async_w3.eth.unsubscribe(sub_id)
304
322
  break
305
323
 
306
324
  @pytest.mark.asyncio
307
- async def test_async_geth_poa_middleware_on_eth_subscription(
325
+ async def test_async_extradata_poa_middleware_on_eth_subscription(
308
326
  self,
309
- async_w3: "_PersistentConnectionWeb3",
327
+ async_w3: "AsyncWeb3",
310
328
  ) -> None:
311
329
  async_w3.middleware_onion.inject(
312
- async_geth_poa_middleware, "poa_middleware", layer=0
330
+ ExtraDataToPOAMiddleware, "poa_middleware", layer=0
313
331
  )
314
332
 
315
333
  sub_id = await async_w3.eth.subscribe("newHeads")
@@ -331,7 +349,7 @@ class PersistentConnectionProviderTest:
331
349
  subscription=True,
332
350
  )
333
351
 
334
- async for msg in async_w3.ws.process_subscriptions():
352
+ async for msg in async_w3.socket.process_subscriptions():
335
353
  response = cast(FormattedEthSubscriptionResponse, msg)
336
354
  assert response.keys() == {"subscription", "result"}
337
355
  assert response["subscription"] == sub_id
@@ -348,7 +366,7 @@ class PersistentConnectionProviderTest:
348
366
  @pytest.mark.asyncio
349
367
  async def test_asyncio_gather_for_multiple_requests_matches_the_responses(
350
368
  self,
351
- async_w3: "_PersistentConnectionWeb3",
369
+ async_w3: "AsyncWeb3",
352
370
  ) -> None:
353
371
  (
354
372
  latest,
@@ -371,22 +389,10 @@ class PersistentConnectionProviderTest:
371
389
  assert isinstance(pending, AttributeDict)
372
390
 
373
391
  # assert block values
374
- some_block_keys = [
375
- "number",
376
- "hash",
377
- "parentHash",
378
- "transactionsRoot",
379
- "stateRoot",
380
- "receiptsRoot",
381
- "size",
382
- "gasLimit",
383
- "gasUsed",
384
- "timestamp",
385
- "transactions",
386
- "baseFeePerGas",
387
- ]
388
- assert all(k in latest.keys() for k in some_block_keys)
389
- assert all(k in pending.keys() for k in some_block_keys)
392
+ assert latest is not None
393
+ assert all(k in latest.keys() for k in SOME_BLOCK_KEYS)
394
+ assert pending is not None
395
+ assert all(k in pending.keys() for k in SOME_BLOCK_KEYS)
390
396
 
391
397
  assert isinstance(block_num, int)
392
398
  assert latest["number"] == block_num
@@ -394,3 +400,28 @@ class PersistentConnectionProviderTest:
394
400
  assert isinstance(chain_id, int)
395
401
  assert isinstance(chain_id2, int)
396
402
  assert isinstance(chain_id3, int)
403
+
404
+ @pytest.mark.asyncio
405
+ async def test_public_socket_api(self, async_w3: "AsyncWeb3") -> None:
406
+ # send a request over the socket
407
+ await async_w3.socket.send(
408
+ RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
409
+ )
410
+
411
+ # recv and validate the unprocessed response
412
+ response = await async_w3.socket.recv()
413
+ assert "id" in response, "Expected 'id' key in response."
414
+ assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
415
+ assert "result" in response, "Expected 'result' key in response."
416
+ assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
417
+ assert not isinstance(response["result"]["number"], int) # assert not processed
418
+
419
+ # make a request over the socket
420
+ response = await async_w3.socket.make_request(
421
+ RPCEndpoint("eth_getBlockByNumber"), ["latest", True]
422
+ )
423
+ assert "id" in response, "Expected 'id' key in response."
424
+ assert "jsonrpc" in response, "Expected 'jsonrpc' key in response."
425
+ assert "result" in response, "Expected 'result' key in response."
426
+ assert all(k in response["result"].keys() for k in SOME_BLOCK_KEYS)
427
+ assert not isinstance(response["result"]["number"], int) # assert not processed
@@ -0,0 +1,258 @@
1
+ from asyncio import (
2
+ iscoroutinefunction,
3
+ )
4
+ import copy
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Dict,
9
+ Union,
10
+ cast,
11
+ )
12
+
13
+ from toolz import (
14
+ merge,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from web3 import ( # noqa: F401
19
+ AsyncWeb3,
20
+ Web3,
21
+ )
22
+ from web3._utils.compat import ( # noqa: F401
23
+ Self,
24
+ )
25
+ from web3.types import ( # noqa: F401
26
+ AsyncMakeRequestFn,
27
+ MakeRequestFn,
28
+ RPCEndpoint,
29
+ RPCResponse,
30
+ )
31
+
32
+
33
+ class RequestMocker:
34
+ """
35
+ Context manager to mock requests made by a web3 instance. This is meant to be used
36
+ via a ``request_mocker`` fixture defined within the appropriate context.
37
+
38
+ Example:
39
+ -------
40
+
41
+ def test_my_w3(w3, request_mocker):
42
+ assert w3.eth.block_number == 0
43
+
44
+ with request_mocker(w3, mock_results={"eth_blockNumber": "0x1"}):
45
+ assert w3.eth.block_number == 1
46
+
47
+ assert w3.eth.block_number == 0
48
+
49
+ Example with async and a mocked response object:
50
+ -----------------------------------------------
51
+
52
+ async def test_my_w3(async_w3, request_mocker):
53
+ def _iter_responses():
54
+ while True:
55
+ yield {"error": {"message": "transaction indexing in progress"}}
56
+ yield {"error": {"message": "transaction indexing in progress"}}
57
+ yield {"result": {"status": "0x1"}}
58
+
59
+ iter_responses = _iter_responses()
60
+
61
+ async with request_mocker(
62
+ async_w3,
63
+ mock_responses={
64
+ "eth_getTransactionReceipt": lambda *_: next(iter_responses)
65
+ },
66
+ ):
67
+ # assert that the first two error responses are handled and the result
68
+ # is eventually returned when present
69
+ assert await w3.eth.get_transaction_receipt("0x1") == "0x1"
70
+
71
+
72
+ - ``mock_results`` is a dict mapping method names to the desired "result" object of
73
+ the RPC response.
74
+ - ``mock_errors`` is a dict mapping method names to the desired
75
+ "error" object of the RPC response.
76
+ -``mock_responses`` is a dict mapping method names to the entire RPC response
77
+ object. This can be useful if you wish to return an iterator which returns
78
+ different responses on each call to the method.
79
+
80
+ If a method name is not present in any of the dicts above, the request is made as
81
+ usual.
82
+
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ w3: Union["AsyncWeb3", "Web3"],
88
+ mock_results: Dict[Union["RPCEndpoint", str], Any] = None,
89
+ mock_errors: Dict[Union["RPCEndpoint", str], Any] = None,
90
+ mock_responses: Dict[Union["RPCEndpoint", str], Any] = None,
91
+ ):
92
+ self.w3 = w3
93
+ self.mock_results = mock_results or {}
94
+ self.mock_errors = mock_errors or {}
95
+ self.mock_responses = mock_responses or {}
96
+ self._make_request: Union[
97
+ "AsyncMakeRequestFn", "MakeRequestFn"
98
+ ] = w3.provider.make_request
99
+
100
+ def __enter__(self) -> "Self":
101
+ # mypy error: Cannot assign to a method
102
+ self.w3.provider.make_request = self._mock_request_handler # type: ignore[method-assign] # noqa: E501
103
+ # reset request func cache to re-build request_func with mocked make_request
104
+ self.w3.provider._request_func_cache = (None, None)
105
+
106
+ return self
107
+
108
+ # define __exit__ with typing information
109
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
110
+ # mypy error: Cannot assign to a method
111
+ self.w3.provider.make_request = self._make_request # type: ignore[method-assign] # noqa: E501
112
+ # reset request func cache to re-build request_func with original make_request
113
+ self.w3.provider._request_func_cache = (None, None)
114
+
115
+ def _mock_request_handler(
116
+ self, method: "RPCEndpoint", params: Any
117
+ ) -> "RPCResponse":
118
+ self.w3 = cast("Web3", self.w3)
119
+ self._make_request = cast("MakeRequestFn", self._make_request)
120
+
121
+ if all(
122
+ method not in mock_dict
123
+ for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
124
+ ):
125
+ return self._make_request(method, params)
126
+
127
+ request_id = (
128
+ next(copy.deepcopy(self.w3.provider.request_counter))
129
+ if hasattr(self.w3.provider, "request_counter")
130
+ else 1
131
+ )
132
+ response_dict = {"jsonrpc": "2.0", "id": request_id}
133
+
134
+ if method in self.mock_responses:
135
+ mock_return = self.mock_responses[method]
136
+ if callable(mock_return):
137
+ mock_return = mock_return(method, params)
138
+
139
+ if "result" in mock_return:
140
+ mock_return = {"result": mock_return["result"]}
141
+ elif "error" in mock_return:
142
+ mock_return = self._create_error_object(mock_return["error"])
143
+
144
+ mocked_response = merge(response_dict, mock_return)
145
+ elif method in self.mock_results:
146
+ mock_return = self.mock_results[method]
147
+ if callable(mock_return):
148
+ mock_return = mock_return(method, params)
149
+ mocked_response = merge(response_dict, {"result": mock_return})
150
+ elif method in self.mock_errors:
151
+ error = self.mock_errors[method]
152
+ if callable(error):
153
+ error = error(method, params)
154
+ mocked_response = merge(response_dict, self._create_error_object(error))
155
+ else:
156
+ raise Exception("Invariant: unreachable code path")
157
+
158
+ decorator = getattr(self._make_request, "_decorator", None)
159
+ if decorator is not None:
160
+ # If the original make_request was decorated, we need to re-apply
161
+ # the decorator to the mocked make_request. This is necessary for
162
+ # the request caching decorator to work properly.
163
+ return decorator(lambda *_: mocked_response)(
164
+ self.w3.provider, method, params
165
+ )
166
+ else:
167
+ return mocked_response
168
+
169
+ # -- async -- #
170
+ async def __aenter__(self) -> "Self":
171
+ # mypy error: Cannot assign to a method
172
+ self.w3.provider.make_request = self._async_mock_request_handler # type: ignore[method-assign] # noqa: E501
173
+ # reset request func cache to re-build request_func with mocked make_request
174
+ self.w3.provider._request_func_cache = (None, None)
175
+ return self
176
+
177
+ async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
178
+ # mypy error: Cannot assign to a method
179
+ self.w3.provider.make_request = self._make_request # type: ignore[method-assign] # noqa: E501
180
+ # reset request func cache to re-build request_func with original make_request
181
+ self.w3.provider._request_func_cache = (None, None)
182
+
183
+ async def _async_mock_request_handler(
184
+ self, method: "RPCEndpoint", params: Any
185
+ ) -> "RPCResponse":
186
+ self.w3 = cast("AsyncWeb3", self.w3)
187
+ self._make_request = cast("AsyncMakeRequestFn", self._make_request)
188
+
189
+ if all(
190
+ method not in mock_dict
191
+ for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
192
+ ):
193
+ return await self._make_request(method, params)
194
+
195
+ request_id = (
196
+ next(copy.deepcopy(self.w3.provider.request_counter))
197
+ if hasattr(self.w3.provider, "request_counter")
198
+ else 1
199
+ )
200
+ response_dict = {"jsonrpc": "2.0", "id": request_id}
201
+
202
+ if method in self.mock_responses:
203
+ mock_return = self.mock_responses[method]
204
+
205
+ if callable(mock_return):
206
+ mock_return = mock_return(method, params)
207
+ elif iscoroutinefunction(mock_return):
208
+ # this is the "correct" way to mock the async make_request
209
+ mock_return = await mock_return(method, params)
210
+
211
+ if "result" in mock_return:
212
+ mock_return = {"result": mock_return["result"]}
213
+ elif "error" in mock_return:
214
+ mock_return = self._create_error_object(mock_return["error"])
215
+
216
+ mocked_result = merge(response_dict, mock_return)
217
+ elif method in self.mock_results:
218
+ mock_return = self.mock_results[method]
219
+ if callable(mock_return):
220
+ # handle callable to make things easier since we're mocking
221
+ mock_return = mock_return(method, params)
222
+ elif iscoroutinefunction(mock_return):
223
+ # this is the "correct" way to mock the async make_request
224
+ mock_return = await mock_return(method, params)
225
+
226
+ mocked_result = merge(response_dict, {"result": mock_return})
227
+
228
+ elif method in self.mock_errors:
229
+ error = self.mock_errors[method]
230
+ if callable(error):
231
+ error = error(method, params)
232
+ elif iscoroutinefunction(error):
233
+ error = await error(method, params)
234
+ mocked_result = merge(response_dict, self._create_error_object(error))
235
+
236
+ else:
237
+ raise Exception("Invariant: unreachable code path")
238
+
239
+ decorator = getattr(self._make_request, "_decorator", None)
240
+ if decorator is not None:
241
+ # If the original make_request was decorated, we need to re-apply
242
+ # the decorator to the mocked make_request. This is necessary for
243
+ # the request caching decorator to work properly.
244
+
245
+ async def _coro(
246
+ _provider: Any, _method: "RPCEndpoint", _params: Any
247
+ ) -> "RPCResponse":
248
+ return mocked_result
249
+
250
+ return await decorator(_coro)(self.w3.provider, method, params)
251
+ else:
252
+ return mocked_result
253
+
254
+ @staticmethod
255
+ def _create_error_object(error: Dict[str, Any]) -> Dict[str, Any]:
256
+ code = error.get("code", -32000)
257
+ message = error.get("message", "Mocked error")
258
+ return {"error": merge({"code": code, "message": message}, error)}