web3 7.0.0b1__py3-none-any.whl → 7.7.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 (261) hide show
  1. ens/__init__.py +13 -2
  2. ens/_normalization.py +4 -4
  3. ens/async_ens.py +31 -21
  4. ens/base_ens.py +3 -1
  5. ens/contract_data.py +2 -2
  6. ens/ens.py +14 -11
  7. ens/exceptions.py +16 -29
  8. ens/specs/nf.json +1 -1
  9. ens/specs/normalization_spec.json +1 -1
  10. ens/utils.py +33 -41
  11. web3/__init__.py +23 -12
  12. web3/_utils/abi.py +162 -274
  13. web3/_utils/async_transactions.py +34 -20
  14. web3/_utils/batching.py +217 -0
  15. web3/_utils/blocks.py +6 -2
  16. web3/_utils/caching/__init__.py +12 -0
  17. web3/_utils/caching/caching_utils.py +433 -0
  18. web3/_utils/caching/request_caching_validation.py +287 -0
  19. web3/_utils/compat/__init__.py +2 -3
  20. web3/_utils/contract_sources/compile_contracts.py +1 -1
  21. web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +42 -0
  22. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  24. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  25. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  26. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  27. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  28. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  29. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  30. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  31. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  32. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  33. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  34. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  35. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  36. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  37. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  38. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  39. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  40. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  41. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  42. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  43. web3/_utils/contracts.py +172 -220
  44. web3/_utils/datatypes.py +5 -1
  45. web3/_utils/decorators.py +6 -1
  46. web3/_utils/empty.py +1 -1
  47. web3/_utils/encoding.py +16 -12
  48. web3/_utils/error_formatters_utils.py +5 -3
  49. web3/_utils/events.py +78 -72
  50. web3/_utils/fee_utils.py +1 -3
  51. web3/_utils/filters.py +24 -22
  52. web3/_utils/formatters.py +2 -2
  53. web3/_utils/http.py +8 -2
  54. web3/_utils/http_session_manager.py +314 -0
  55. web3/_utils/math.py +14 -15
  56. web3/_utils/method_formatters.py +161 -34
  57. web3/_utils/module.py +2 -1
  58. web3/_utils/module_testing/__init__.py +3 -2
  59. web3/_utils/module_testing/eth_module.py +736 -583
  60. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  61. web3/_utils/module_testing/module_testing_utils.py +81 -24
  62. web3/_utils/module_testing/persistent_connection_provider.py +702 -220
  63. web3/_utils/module_testing/utils.py +114 -33
  64. web3/_utils/module_testing/web3_module.py +438 -17
  65. web3/_utils/normalizers.py +13 -11
  66. web3/_utils/rpc_abi.py +10 -22
  67. web3/_utils/threads.py +8 -7
  68. web3/_utils/transactions.py +32 -25
  69. web3/_utils/type_conversion.py +5 -1
  70. web3/_utils/validation.py +20 -17
  71. web3/beacon/__init__.py +5 -0
  72. web3/beacon/api_endpoints.py +3 -0
  73. web3/beacon/async_beacon.py +29 -6
  74. web3/beacon/beacon.py +24 -6
  75. web3/contract/__init__.py +7 -0
  76. web3/contract/async_contract.py +285 -82
  77. web3/contract/base_contract.py +556 -258
  78. web3/contract/contract.py +295 -84
  79. web3/contract/utils.py +251 -55
  80. web3/datastructures.py +56 -41
  81. web3/eth/__init__.py +7 -0
  82. web3/eth/async_eth.py +89 -69
  83. web3/eth/base_eth.py +7 -3
  84. web3/eth/eth.py +43 -66
  85. web3/exceptions.py +158 -83
  86. web3/gas_strategies/time_based.py +8 -6
  87. web3/geth.py +53 -184
  88. web3/main.py +77 -43
  89. web3/manager.py +368 -101
  90. web3/method.py +43 -15
  91. web3/middleware/__init__.py +26 -8
  92. web3/middleware/attrdict.py +12 -22
  93. web3/middleware/base.py +55 -2
  94. web3/middleware/filter.py +45 -23
  95. web3/middleware/formatting.py +6 -3
  96. web3/middleware/names.py +4 -1
  97. web3/middleware/signing.py +15 -6
  98. web3/middleware/stalecheck.py +2 -1
  99. web3/module.py +62 -26
  100. web3/providers/__init__.py +21 -0
  101. web3/providers/async_base.py +93 -38
  102. web3/providers/base.py +85 -40
  103. web3/providers/eth_tester/__init__.py +5 -0
  104. web3/providers/eth_tester/defaults.py +2 -55
  105. web3/providers/eth_tester/main.py +57 -35
  106. web3/providers/eth_tester/middleware.py +16 -17
  107. web3/providers/ipc.py +42 -18
  108. web3/providers/legacy_websocket.py +27 -2
  109. web3/providers/persistent/__init__.py +7 -0
  110. web3/providers/persistent/async_ipc.py +61 -121
  111. web3/providers/persistent/persistent.py +324 -17
  112. web3/providers/persistent/persistent_connection.py +54 -5
  113. web3/providers/persistent/request_processor.py +136 -56
  114. web3/providers/persistent/subscription_container.py +56 -0
  115. web3/providers/persistent/subscription_manager.py +233 -0
  116. web3/providers/persistent/websocket.py +29 -92
  117. web3/providers/rpc/__init__.py +5 -0
  118. web3/providers/rpc/async_rpc.py +73 -18
  119. web3/providers/rpc/rpc.py +73 -30
  120. web3/providers/rpc/utils.py +1 -13
  121. web3/scripts/install_pre_releases.py +33 -0
  122. web3/scripts/parse_pygeth_version.py +16 -0
  123. web3/testing.py +4 -4
  124. web3/tracing.py +9 -5
  125. web3/types.py +141 -74
  126. web3/utils/__init__.py +64 -5
  127. web3/utils/abi.py +790 -10
  128. web3/utils/address.py +8 -0
  129. web3/utils/async_exception_handling.py +20 -11
  130. web3/utils/caching.py +34 -4
  131. web3/utils/exception_handling.py +9 -12
  132. web3/utils/subscriptions.py +285 -0
  133. {web3-7.0.0b1.dist-info → web3-7.7.0.dist-info}/LICENSE +1 -1
  134. web3-7.7.0.dist-info/METADATA +130 -0
  135. web3-7.7.0.dist-info/RECORD +171 -0
  136. {web3-7.0.0b1.dist-info → web3-7.7.0.dist-info}/WHEEL +1 -1
  137. {web3-7.0.0b1.dist-info → web3-7.7.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/caching.py +0 -155
  242. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  243. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
  244. web3/_utils/request.py +0 -265
  245. web3/pm.py +0 -602
  246. web3/tools/__init__.py +0 -4
  247. web3/tools/benchmark/__init__.py +0 -0
  248. web3/tools/benchmark/main.py +0 -185
  249. web3/tools/benchmark/node.py +0 -126
  250. web3/tools/benchmark/reporting.py +0 -39
  251. web3/tools/benchmark/utils.py +0 -69
  252. web3/tools/pytest_ethereum/__init__.py +0 -0
  253. web3/tools/pytest_ethereum/_utils.py +0 -145
  254. web3/tools/pytest_ethereum/deployer.py +0 -48
  255. web3/tools/pytest_ethereum/exceptions.py +0 -22
  256. web3/tools/pytest_ethereum/linker.py +0 -128
  257. web3/tools/pytest_ethereum/plugins.py +0 -33
  258. web3-7.0.0b1.dist-info/METADATA +0 -114
  259. web3-7.0.0b1.dist-info/RECORD +0 -280
  260. web3-7.0.0b1.dist-info/entry_points.txt +0 -2
  261. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
web3/providers/ipc.py CHANGED
@@ -14,8 +14,11 @@ from types import (
14
14
  )
15
15
  from typing import (
16
16
  Any,
17
+ List,
18
+ Tuple,
17
19
  Type,
18
20
  Union,
21
+ cast,
19
22
  )
20
23
 
21
24
  from web3._utils.threads import (
@@ -26,6 +29,16 @@ from web3.types import (
26
29
  RPCResponse,
27
30
  )
28
31
 
32
+ from .._utils.batching import (
33
+ sort_batch_response_by_response_ids,
34
+ )
35
+ from .._utils.caching import (
36
+ handle_request_caching,
37
+ )
38
+ from ..exceptions import (
39
+ Web3TypeError,
40
+ Web3ValueError,
41
+ )
29
42
  from .base import (
30
43
  JSONBaseProvider,
31
44
  )
@@ -96,28 +109,29 @@ def get_default_ipc_path() -> str:
96
109
  return r"\\.\pipe\geth.ipc"
97
110
 
98
111
  else:
99
- raise ValueError(
112
+ raise Web3ValueError(
100
113
  f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/"
101
114
  "freebsd are supported. You must specify the ipc_path"
102
115
  )
103
116
 
104
117
 
105
118
  def get_dev_ipc_path() -> str:
106
- if os.environ.get("WEB3_PROVIDER_URI", ""):
107
- return os.environ.get("WEB3_PROVIDER_URI")
119
+ web3_provider_uri = os.environ.get("WEB3_PROVIDER_URI", "")
120
+ if web3_provider_uri and "geth.ipc" in web3_provider_uri:
121
+ return web3_provider_uri
108
122
 
109
- elif sys.platform == "darwin":
110
- tmpdir = os.environ.get("TMPDIR", "")
123
+ elif sys.platform == "darwin" or sys.platform.startswith("linux"):
124
+ tmpdir = os.environ.get("TMPDIR", "/tmp")
111
125
  return os.path.expanduser(os.path.join(tmpdir, "geth.ipc"))
112
126
 
113
- elif sys.platform.startswith("linux") or sys.platform.startswith("freebsd"):
127
+ elif sys.platform.endswith("freebsd"):
114
128
  return os.path.expanduser(os.path.join("/tmp", "geth.ipc"))
115
129
 
116
130
  elif sys.platform == "win32":
117
131
  return r"\\.\pipe\geth.ipc"
118
132
 
119
133
  else:
120
- raise ValueError(
134
+ raise Web3ValueError(
121
135
  f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/"
122
136
  "freebsd are supported. You must specify the ipc_path"
123
137
  )
@@ -130,34 +144,28 @@ class IPCProvider(JSONBaseProvider):
130
144
  def __init__(
131
145
  self,
132
146
  ipc_path: Union[str, Path] = None,
133
- timeout: int = 10,
134
- *args: Any,
147
+ timeout: int = 30,
135
148
  **kwargs: Any,
136
149
  ) -> None:
150
+ super().__init__(**kwargs)
137
151
  if ipc_path is None:
138
152
  self.ipc_path = get_default_ipc_path()
139
153
  elif isinstance(ipc_path, str) or isinstance(ipc_path, Path):
140
154
  self.ipc_path = str(Path(ipc_path).expanduser().resolve())
141
155
  else:
142
- raise TypeError("ipc_path must be of type string or pathlib.Path")
156
+ raise Web3TypeError("ipc_path must be of type string or pathlib.Path")
143
157
 
144
158
  self.timeout = timeout
145
159
  self._lock = threading.Lock()
146
160
  self._socket = PersistantSocket(self.ipc_path)
147
- super().__init__()
148
161
 
149
162
  def __str__(self) -> str:
150
163
  return f"<{self.__class__.__name__} {self.ipc_path}>"
151
164
 
152
- def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
153
- self.logger.debug(
154
- f"Making request IPC. Path: {self.ipc_path}, Method: {method}"
155
- )
156
- request = self.encode_rpc_request(method, params)
157
-
165
+ def _make_request(self, request: bytes) -> RPCResponse:
158
166
  with self._lock, self._socket as sock:
159
167
  try:
160
- sock.sendall(request)
168
+ sock.sendall(request + b"\n")
161
169
  except BrokenPipeError:
162
170
  # one extra attempt, then give up
163
171
  sock = self._socket.reset()
@@ -185,6 +193,22 @@ class IPCProvider(JSONBaseProvider):
185
193
  timeout.sleep(0)
186
194
  continue
187
195
 
196
+ @handle_request_caching
197
+ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
198
+ self.logger.debug(
199
+ f"Making request IPC. Path: {self.ipc_path}, Method: {method}"
200
+ )
201
+ request = self.encode_rpc_request(method, params)
202
+ return self._make_request(request)
203
+
204
+ def make_batch_request(
205
+ self, requests: List[Tuple[RPCEndpoint, Any]]
206
+ ) -> List[RPCResponse]:
207
+ self.logger.debug(f"Making batch request IPC. Path: {self.ipc_path}")
208
+ request_data = self.encode_batch_rpc_request(requests)
209
+ response = cast(List[RPCResponse], self._make_request(request_data))
210
+ return sort_batch_response_by_response_ids(response)
211
+
188
212
 
189
213
  # A valid JSON RPC response can only end in } or ] http://www.jsonrpc.org/specification
190
214
  def has_valid_json_rpc_ending(raw_response: bytes) -> bool:
@@ -10,9 +10,12 @@ from types import (
10
10
  )
11
11
  from typing import (
12
12
  Any,
13
+ List,
13
14
  Optional,
15
+ Tuple,
14
16
  Type,
15
17
  Union,
18
+ cast,
16
19
  )
17
20
 
18
21
  from eth_typing import (
@@ -25,6 +28,12 @@ from websockets.legacy.client import (
25
28
  WebSocketClientProtocol,
26
29
  )
27
30
 
31
+ from web3._utils.batching import (
32
+ sort_batch_response_by_response_ids,
33
+ )
34
+ from web3._utils.caching import (
35
+ handle_request_caching,
36
+ )
28
37
  from web3.exceptions import (
29
38
  Web3ValidationError,
30
39
  )
@@ -37,7 +46,7 @@ from web3.types import (
37
46
  )
38
47
 
39
48
  RESTRICTED_WEBSOCKET_KWARGS = {"uri", "loop"}
40
- DEFAULT_WEBSOCKET_TIMEOUT = 10
49
+ DEFAULT_WEBSOCKET_TIMEOUT = 30
41
50
 
42
51
 
43
52
  def _start_event_loop(loop: asyncio.AbstractEventLoop) -> None:
@@ -91,7 +100,9 @@ class LegacyWebSocketProvider(JSONBaseProvider):
91
100
  endpoint_uri: Optional[Union[URI, str]] = None,
92
101
  websocket_kwargs: Optional[Any] = None,
93
102
  websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,
103
+ **kwargs: Any,
94
104
  ) -> None:
105
+ super().__init__(**kwargs)
95
106
  self.endpoint_uri = URI(endpoint_uri)
96
107
  self.websocket_timeout = websocket_timeout
97
108
  if self.endpoint_uri is None:
@@ -110,7 +121,6 @@ class LegacyWebSocketProvider(JSONBaseProvider):
110
121
  f"in websocket_kwargs, found: {found_restricted_keys}"
111
122
  )
112
123
  self.conn = PersistentWebSocket(self.endpoint_uri, websocket_kwargs)
113
- super().__init__()
114
124
 
115
125
  def __str__(self) -> str:
116
126
  return f"WS connection {self.endpoint_uri}"
@@ -124,6 +134,7 @@ class LegacyWebSocketProvider(JSONBaseProvider):
124
134
  await asyncio.wait_for(conn.recv(), timeout=self.websocket_timeout)
125
135
  )
126
136
 
137
+ @handle_request_caching
127
138
  def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
128
139
  self.logger.debug(
129
140
  f"Making request WebSocket. URI: {self.endpoint_uri}, " f"Method: {method}"
@@ -133,3 +144,17 @@ class LegacyWebSocketProvider(JSONBaseProvider):
133
144
  self.coro_make_request(request_data), LegacyWebSocketProvider._loop
134
145
  )
135
146
  return future.result()
147
+
148
+ def make_batch_request(
149
+ self, requests: List[Tuple[RPCEndpoint, Any]]
150
+ ) -> List[RPCResponse]:
151
+ self.logger.debug(
152
+ f"Making batch request WebSocket. URI: {self.endpoint_uri}, "
153
+ f"Methods: {requests}"
154
+ )
155
+ request_data = self.encode_batch_rpc_request(requests)
156
+ future = asyncio.run_coroutine_threadsafe(
157
+ self.coro_make_request(request_data), LegacyWebSocketProvider._loop
158
+ )
159
+ response = cast(List[RPCResponse], future.result())
160
+ return sort_batch_response_by_response_ids(response)
@@ -13,3 +13,10 @@ from .async_ipc import (
13
13
  from .websocket import (
14
14
  WebSocketProvider,
15
15
  )
16
+
17
+ __all__ = [
18
+ "PersistentConnectionProvider",
19
+ "PersistentConnection",
20
+ "AsyncIPCProvider",
21
+ "WebSocketProvider",
22
+ ]
@@ -1,9 +1,6 @@
1
1
  import asyncio
2
2
  import errno
3
3
  import json
4
- from json import (
5
- JSONDecodeError,
6
- )
7
4
  import logging
8
5
  from pathlib import (
9
6
  Path,
@@ -16,10 +13,6 @@ from typing import (
16
13
  Union,
17
14
  )
18
15
 
19
- from eth_utils import (
20
- to_text,
21
- )
22
-
23
16
  from web3.types import (
24
17
  RPCEndpoint,
25
18
  RPCResponse,
@@ -28,11 +21,11 @@ from web3.types import (
28
21
  from . import (
29
22
  PersistentConnectionProvider,
30
23
  )
31
- from ..._utils.caching import (
32
- async_handle_request_caching,
33
- )
34
24
  from ...exceptions import (
25
+ PersistentConnectionClosedOK,
35
26
  ProviderConnectionError,
27
+ ReadBufferLimitReached,
28
+ Web3TypeError,
36
29
  )
37
30
  from ..ipc import (
38
31
  get_default_ipc_path,
@@ -40,7 +33,7 @@ from ..ipc import (
40
33
 
41
34
 
42
35
  async def async_get_ipc_socket(
43
- ipc_path: str,
36
+ ipc_path: str, read_buffer_limit: int
44
37
  ) -> Tuple[asyncio.StreamReader, asyncio.StreamWriter]:
45
38
  if sys.platform == "win32":
46
39
  # On Windows named pipe is used. Simulate socket with it.
@@ -50,7 +43,7 @@ async def async_get_ipc_socket(
50
43
 
51
44
  return NamedPipe(ipc_path)
52
45
  else:
53
- return await asyncio.open_unix_connection(ipc_path)
46
+ return await asyncio.open_unix_connection(ipc_path, limit=read_buffer_limit)
54
47
 
55
48
 
56
49
  class AsyncIPCProvider(PersistentConnectionProvider):
@@ -58,23 +51,24 @@ class AsyncIPCProvider(PersistentConnectionProvider):
58
51
 
59
52
  _reader: Optional[asyncio.StreamReader] = None
60
53
  _writer: Optional[asyncio.StreamWriter] = None
54
+ _decoder: json.JSONDecoder = json.JSONDecoder()
61
55
 
62
56
  def __init__(
63
57
  self,
64
58
  ipc_path: Optional[Union[str, Path]] = None,
65
- max_connection_retries: int = 5,
59
+ read_buffer_limit: int = 20 * 1024 * 1024, # 20 MB
66
60
  # `PersistentConnectionProvider` kwargs can be passed through
67
61
  **kwargs: Any,
68
62
  ) -> None:
63
+ # initialize the ipc_path before calling the super constructor
69
64
  if ipc_path is None:
70
65
  self.ipc_path = get_default_ipc_path()
71
66
  elif isinstance(ipc_path, str) or isinstance(ipc_path, Path):
72
67
  self.ipc_path = str(Path(ipc_path).expanduser().resolve())
73
68
  else:
74
- raise TypeError("ipc_path must be of type string or pathlib.Path")
75
-
76
- self._max_connection_retries = max_connection_retries
69
+ raise Web3TypeError("ipc_path must be of type string or pathlib.Path")
77
70
  super().__init__(**kwargs)
71
+ self.read_buffer_limit = read_buffer_limit
78
72
 
79
73
  def __str__(self) -> str:
80
74
  return f"<{self.__class__.__name__} {self.ipc_path}>"
@@ -84,81 +78,46 @@ class AsyncIPCProvider(PersistentConnectionProvider):
84
78
  return False
85
79
 
86
80
  try:
87
- request_data = self.encode_rpc_request(
88
- RPCEndpoint("web3_clientVersions"), []
89
- )
90
- self._writer.write(request_data)
91
- current_request_id = json.loads(request_data)["id"]
92
- await self._get_response_for_request_id(current_request_id, timeout=2)
81
+ await self.make_request(RPCEndpoint("web3_clientVersion"), [])
93
82
  return True
94
- except (OSError, BrokenPipeError, ProviderConnectionError) as e:
83
+ except (OSError, ProviderConnectionError) as e:
95
84
  if show_traceback:
96
85
  raise ProviderConnectionError(
97
86
  f"Problem connecting to provider with error: {type(e)}: {e}"
98
87
  )
99
88
  return False
100
89
 
101
- async def connect(self) -> None:
102
- _connection_attempts = 0
103
- _backoff_rate_change = 1.75
104
- _backoff_time = 1.75
105
-
106
- while _connection_attempts != self._max_connection_retries:
107
- try:
108
- _connection_attempts += 1
109
- self._reader, self._writer = await async_get_ipc_socket(self.ipc_path)
110
- self._message_listener_task = asyncio.create_task(
111
- self._message_listener()
112
- )
113
- break
114
- except OSError as e:
115
- if _connection_attempts == self._max_connection_retries:
116
- raise ProviderConnectionError(
117
- f"Could not connect to endpoint: {self.endpoint_uri}. "
118
- f"Retries exceeded max of {self._max_connection_retries}."
119
- ) from e
120
- self.logger.info(
121
- f"Could not connect to endpoint: {self.endpoint_uri}. Retrying in "
122
- f"{round(_backoff_time, 1)} seconds.",
123
- exc_info=True,
124
- )
125
- await asyncio.sleep(_backoff_time)
126
- _backoff_time *= _backoff_rate_change
127
-
128
- async def disconnect(self) -> None:
129
- if self._writer and not self._writer.is_closing():
130
- self._writer.close()
131
- await self._writer.wait_closed()
132
- self._writer = None
133
- self.logger.debug(
134
- f'Successfully disconnected from endpoint: "{self.endpoint_uri}'
135
- )
136
-
137
- try:
138
- self._message_listener_task.cancel()
139
- await self._message_listener_task
140
- self._reader = None
141
- except (asyncio.CancelledError, StopAsyncIteration):
142
- pass
143
-
144
- self._request_processor.clear_caches()
145
-
146
- async def _reset_socket(self) -> None:
147
- self._writer.close()
148
- await self._writer.wait_closed()
149
- self._reader, self._writer = await async_get_ipc_socket(self.ipc_path)
150
-
151
- @async_handle_request_caching
152
- async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
153
- request_data = self.encode_rpc_request(method, params)
154
-
90
+ async def socket_send(self, request_data: bytes) -> None:
155
91
  if self._writer is None:
156
92
  raise ProviderConnectionError(
157
93
  "Connection to ipc socket has not been initiated for the provider."
158
94
  )
159
95
 
96
+ return await asyncio.wait_for(
97
+ self._socket_send(request_data), timeout=self.request_timeout
98
+ )
99
+
100
+ async def socket_recv(self) -> RPCResponse:
160
101
  try:
161
- self._writer.write(request_data)
102
+ data = await self._reader.readline()
103
+ except ValueError as e:
104
+ if all(kw in str(e) for kw in ("limit", "chunk")):
105
+ raise ReadBufferLimitReached(
106
+ f"Read buffer limit of `{self.read_buffer_limit}` bytes was "
107
+ "reached. Consider increasing the ``read_buffer_limit`` on the "
108
+ "AsyncIPCProvider."
109
+ ) from e
110
+ raise
111
+
112
+ if not data:
113
+ raise PersistentConnectionClosedOK("Socket reader received end of stream.")
114
+ return self.decode_rpc_response(data)
115
+
116
+ # -- private methods -- #
117
+
118
+ async def _socket_send(self, request_data: bytes) -> None:
119
+ try:
120
+ self._writer.write(request_data + b"\n")
162
121
  await self._writer.drain()
163
122
  except OSError as e:
164
123
  # Broken pipe
@@ -168,48 +127,29 @@ class AsyncIPCProvider(PersistentConnectionProvider):
168
127
  self._writer.write(request_data)
169
128
  await self._writer.drain()
170
129
 
171
- current_request_id = json.loads(request_data)["id"]
172
- response = await self._get_response_for_request_id(current_request_id)
173
-
174
- return response
130
+ async def _reset_socket(self) -> None:
131
+ self._writer.close()
132
+ await self._writer.wait_closed()
133
+ self._reader, self._writer = await async_get_ipc_socket(
134
+ self.ipc_path, self.read_buffer_limit
135
+ )
175
136
 
176
- async def _message_listener(self) -> None:
177
- self.logger.info(
178
- "IPC socket listener background task started. Storing all messages in "
179
- "appropriate request processor queues / caches to be processed."
137
+ async def _provider_specific_connect(self) -> None:
138
+ self._reader, self._writer = await async_get_ipc_socket(
139
+ self.ipc_path, self.read_buffer_limit
180
140
  )
181
- raw_message = ""
182
- decoder = json.JSONDecoder()
183
-
184
- while True:
185
- # the use of sleep(0) seems to be the most efficient way to yield control
186
- # back to the event loop to share the loop with other tasks.
187
- await asyncio.sleep(0)
188
-
189
- try:
190
- raw_message += to_text(await self._reader.read(4096)).lstrip()
191
-
192
- while raw_message:
193
- try:
194
- response, pos = decoder.raw_decode(raw_message)
195
- except JSONDecodeError:
196
- break
197
-
198
- is_subscription = response.get("method") == "eth_subscription"
199
- await self._request_processor.cache_raw_response(
200
- response, subscription=is_subscription
201
- )
202
- raw_message = raw_message[pos:].lstrip()
203
- except Exception as e:
204
- if not self.silence_listener_task_exceptions:
205
- loop = asyncio.get_event_loop()
206
- for task in asyncio.all_tasks(loop=loop):
207
- task.cancel()
208
- raise e
209
-
210
- self.logger.error(
211
- "Exception caught in listener, error logging and keeping listener "
212
- f"background task alive.\n error={e}"
213
- )
214
- # if only error logging, reset the ``raw_message`` buffer and continue
215
- raw_message = ""
141
+
142
+ async def _provider_specific_disconnect(self) -> None:
143
+ # this should remain idempotent
144
+ if self._writer and not self._writer.is_closing():
145
+ self._writer.close()
146
+ await self._writer.wait_closed()
147
+ self._writer = None
148
+ if self._reader:
149
+ self._reader = None
150
+
151
+ async def _provider_specific_socket_reader(self) -> RPCResponse:
152
+ return await self.socket_recv()
153
+
154
+ def _error_log_listener_task_exception(self, e: Exception) -> None:
155
+ super()._error_log_listener_task_exception(e)