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
@@ -0,0 +1,314 @@
1
+ import asyncio
2
+ from concurrent.futures import (
3
+ ThreadPoolExecutor,
4
+ )
5
+ import logging
6
+ import os
7
+ import threading
8
+ import time
9
+ from typing import (
10
+ Any,
11
+ Dict,
12
+ List,
13
+ Optional,
14
+ Union,
15
+ )
16
+
17
+ from aiohttp import (
18
+ ClientResponse,
19
+ ClientSession,
20
+ ClientTimeout,
21
+ TCPConnector,
22
+ )
23
+ from eth_typing import (
24
+ URI,
25
+ )
26
+ import requests
27
+
28
+ from web3._utils.async_caching import (
29
+ async_lock,
30
+ )
31
+ from web3._utils.caching import (
32
+ generate_cache_key,
33
+ )
34
+ from web3._utils.http import (
35
+ DEFAULT_HTTP_TIMEOUT,
36
+ )
37
+ from web3.exceptions import (
38
+ TimeExhausted,
39
+ )
40
+ from web3.utils.caching import (
41
+ SimpleCache,
42
+ )
43
+
44
+
45
+ class HTTPSessionManager:
46
+ logger = logging.getLogger("web3._utils.http_session_manager.HTTPSessionManager")
47
+ _lock: threading.Lock = threading.Lock()
48
+
49
+ def __init__(
50
+ self,
51
+ cache_size: int = 100,
52
+ session_pool_max_workers: int = 5,
53
+ ) -> None:
54
+ self.session_cache = SimpleCache(cache_size)
55
+ self.session_pool = ThreadPoolExecutor(max_workers=session_pool_max_workers)
56
+
57
+ @staticmethod
58
+ def get_default_http_endpoint() -> URI:
59
+ return URI(os.environ.get("WEB3_HTTP_PROVIDER_URI", "http://localhost:8545"))
60
+
61
+ def cache_and_return_session(
62
+ self,
63
+ endpoint_uri: URI,
64
+ session: requests.Session = None,
65
+ request_timeout: Optional[float] = None,
66
+ ) -> requests.Session:
67
+ # cache key should have a unique thread identifier
68
+ cache_key = generate_cache_key(f"{threading.get_ident()}:{endpoint_uri}")
69
+
70
+ cached_session = self.session_cache.get_cache_entry(cache_key)
71
+ if cached_session is not None:
72
+ # If read from cache yields a session, no need to lock; return the session.
73
+ # Sync is a bit simpler in this way since a `requests.Session` doesn't
74
+ # really "close" in the same way that an async `ClientSession` does.
75
+ # When "closed", it still uses http / https adapters successfully if a
76
+ # request is made.
77
+ return cached_session
78
+
79
+ if session is None:
80
+ session = requests.Session()
81
+
82
+ with self._lock:
83
+ cached_session, evicted_items = self.session_cache.cache(cache_key, session)
84
+ self.logger.debug(f"Session cached: {endpoint_uri}, {cached_session}")
85
+
86
+ if evicted_items is not None:
87
+ evicted_sessions = evicted_items.values()
88
+ for evicted_session in evicted_sessions:
89
+ self.logger.debug(
90
+ "Session cache full. Session evicted from cache: "
91
+ f"{evicted_session}",
92
+ )
93
+ threading.Timer(
94
+ # If `request_timeout` is `None`, don't wait forever for the closing
95
+ # session to finish the request. Instead, wait over the default timeout.
96
+ request_timeout or DEFAULT_HTTP_TIMEOUT + 0.1,
97
+ self._close_evicted_sessions,
98
+ args=[evicted_sessions],
99
+ ).start()
100
+
101
+ return cached_session
102
+
103
+ def get_response_from_get_request(
104
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
105
+ ) -> requests.Response:
106
+ kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
107
+ session = self.cache_and_return_session(
108
+ endpoint_uri, request_timeout=kwargs["timeout"]
109
+ )
110
+ response = session.get(endpoint_uri, *args, **kwargs)
111
+ return response
112
+
113
+ def json_make_get_request(
114
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
115
+ ) -> Dict[str, Any]:
116
+ response = self.get_response_from_get_request(endpoint_uri, *args, **kwargs)
117
+ response.raise_for_status()
118
+ return response.json()
119
+
120
+ def get_response_from_post_request(
121
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
122
+ ) -> requests.Response:
123
+ session = self.cache_and_return_session(
124
+ endpoint_uri, request_timeout=kwargs["timeout"]
125
+ )
126
+ return session.post(endpoint_uri, *args, **kwargs)
127
+
128
+ def make_post_request(
129
+ self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
130
+ ) -> bytes:
131
+ kwargs.setdefault("timeout", DEFAULT_HTTP_TIMEOUT)
132
+ kwargs.setdefault("stream", False)
133
+
134
+ start = time.time()
135
+ timeout = kwargs["timeout"]
136
+
137
+ with self.get_response_from_post_request(
138
+ endpoint_uri, data=data, **kwargs
139
+ ) as response:
140
+ response.raise_for_status()
141
+ if kwargs.get("stream"):
142
+ return self._handle_streaming_response(response, start, timeout)
143
+ else:
144
+ return response.content
145
+
146
+ def _handle_streaming_response(
147
+ self, response: requests.Response, start: float, timeout: float
148
+ ) -> bytes:
149
+ response_body = b""
150
+ for data in response.iter_content():
151
+ response_body += data
152
+ # Manually manage timeout so streaming responses time out
153
+ # rather than resetting the timeout each time a response comes back
154
+ if (time.time() - start) > timeout:
155
+ raise TimeExhausted
156
+ return response_body
157
+
158
+ def _close_evicted_sessions(self, evicted_sessions: List[requests.Session]) -> None:
159
+ for evicted_session in evicted_sessions:
160
+ evicted_session.close()
161
+ self.logger.debug(f"Closed evicted session: {evicted_session}")
162
+
163
+ # -- async -- #
164
+
165
+ async def async_cache_and_return_session(
166
+ self,
167
+ endpoint_uri: URI,
168
+ session: Optional[ClientSession] = None,
169
+ request_timeout: Optional[ClientTimeout] = None,
170
+ ) -> ClientSession:
171
+ # cache key should have a unique thread identifier
172
+ cache_key = generate_cache_key(f"{threading.get_ident()}:{endpoint_uri}")
173
+
174
+ evicted_items = None
175
+ async with async_lock(self.session_pool, self._lock):
176
+ if cache_key not in self.session_cache:
177
+ if session is None:
178
+ session = ClientSession(
179
+ raise_for_status=True,
180
+ connector=TCPConnector(
181
+ force_close=True, enable_cleanup_closed=True
182
+ ),
183
+ )
184
+
185
+ cached_session, evicted_items = self.session_cache.cache(
186
+ cache_key, session
187
+ )
188
+ self.logger.debug(
189
+ f"Async session cached: {endpoint_uri}, {cached_session}"
190
+ )
191
+
192
+ else:
193
+ # get the cached session
194
+ cached_session = self.session_cache.get_cache_entry(cache_key)
195
+ session_is_closed = cached_session.closed
196
+ session_loop_is_closed = cached_session._loop.is_closed()
197
+
198
+ warning = (
199
+ "Async session was closed"
200
+ if session_is_closed
201
+ else (
202
+ "Loop was closed for async session"
203
+ if session_loop_is_closed
204
+ else None
205
+ )
206
+ )
207
+ if warning:
208
+ self.logger.debug(
209
+ f"{warning}: {endpoint_uri}, {cached_session}. "
210
+ f"Creating and caching a new async session for uri."
211
+ )
212
+
213
+ self.session_cache._data.pop(cache_key)
214
+ if not session_is_closed:
215
+ # if loop was closed but not the session, close the session
216
+ await cached_session.close()
217
+ self.logger.debug(
218
+ f"Async session closed and evicted from cache: {cached_session}"
219
+ )
220
+
221
+ # replace stale session with a new session at the cache key
222
+ _session = ClientSession(
223
+ raise_for_status=True,
224
+ connector=TCPConnector(
225
+ force_close=True, enable_cleanup_closed=True
226
+ ),
227
+ )
228
+ cached_session, evicted_items = self.session_cache.cache(
229
+ cache_key, _session
230
+ )
231
+ self.logger.debug(
232
+ f"Async session cached: {endpoint_uri}, {cached_session}"
233
+ )
234
+
235
+ if evicted_items is not None:
236
+ # At this point the evicted sessions are already popped out of the cache and
237
+ # just stored in the `evicted_sessions` dict. So we can kick off a future
238
+ # task to close them and it should be safe to pop out of the lock here.
239
+ evicted_sessions = list(evicted_items.values())
240
+ for evicted_session in evicted_sessions:
241
+ self.logger.debug(
242
+ "Async session cache full. Session evicted from cache: "
243
+ f"{evicted_session}",
244
+ )
245
+ # Kick off an asyncio `Task` to close the evicted sessions. In the case
246
+ # that the cache filled very quickly and some sessions have been evicted
247
+ # before their original request has been made, we set the timer to a bit
248
+ # more than the `request_timeout` for a call. This should make it so that
249
+ # any call from an evicted session can still be made before the session
250
+ # is closed.
251
+ asyncio.create_task(
252
+ self._async_close_evicted_sessions(
253
+ # if `ClientTimeout.total` is `None`, don't wait forever for the
254
+ # closing session to finish the request. Instead, use the default
255
+ # timeout.
256
+ request_timeout.total or DEFAULT_HTTP_TIMEOUT + 0.1,
257
+ evicted_sessions,
258
+ )
259
+ )
260
+
261
+ return cached_session
262
+
263
+ async def async_get_response_from_get_request(
264
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
265
+ ) -> ClientResponse:
266
+ kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
267
+ session = await self.async_cache_and_return_session(
268
+ endpoint_uri, request_timeout=kwargs["timeout"]
269
+ )
270
+ response = await session.get(endpoint_uri, *args, **kwargs)
271
+ return response
272
+
273
+ async def async_json_make_get_request(
274
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
275
+ ) -> Dict[str, Any]:
276
+ response = await self.async_get_response_from_get_request(
277
+ endpoint_uri, *args, **kwargs
278
+ )
279
+ response.raise_for_status()
280
+ return await response.json()
281
+
282
+ async def async_get_response_from_post_request(
283
+ self, endpoint_uri: URI, *args: Any, **kwargs: Any
284
+ ) -> ClientResponse:
285
+ kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
286
+ session = await self.async_cache_and_return_session(
287
+ endpoint_uri, request_timeout=kwargs["timeout"]
288
+ )
289
+ response = await session.post(endpoint_uri, *args, **kwargs)
290
+ return response
291
+
292
+ async def async_make_post_request(
293
+ self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
294
+ ) -> bytes:
295
+ response = await self.async_get_response_from_post_request(
296
+ endpoint_uri, data=data, **kwargs
297
+ )
298
+ response.raise_for_status()
299
+ return await response.read()
300
+
301
+ async def _async_close_evicted_sessions(
302
+ self, timeout: float, evicted_sessions: List[ClientSession]
303
+ ) -> None:
304
+ await asyncio.sleep(timeout)
305
+
306
+ for evicted_session in evicted_sessions:
307
+ await evicted_session.close()
308
+ self.logger.debug(f"Closed evicted async session: {evicted_session}")
309
+
310
+ if any(not evicted_session.closed for evicted_session in evicted_sessions):
311
+ self.logger.warning(
312
+ "Some evicted async sessions were not properly closed: "
313
+ f"{evicted_sessions}"
314
+ )
web3/_utils/math.py CHANGED
@@ -5,6 +5,7 @@ from typing import (
5
5
 
6
6
  from web3.exceptions import (
7
7
  InsufficientData,
8
+ Web3ValueError,
8
9
  )
9
10
 
10
11
 
@@ -17,23 +18,21 @@ def percentile(
17
18
  f"Expected a sequence of at least 1 integers, got {values!r}"
18
19
  )
19
20
  if percentile is None:
20
- raise ValueError(f"Expected a percentile choice, got {percentile}")
21
+ raise Web3ValueError(f"Expected a percentile choice, got {percentile}")
22
+ if percentile < 0 or percentile > 100:
23
+ raise Web3ValueError("percentile must be in the range [0, 100]")
21
24
 
22
25
  sorted_values = sorted(values)
23
26
 
24
- rank = len(values) * percentile / 100
25
- if rank > 0:
26
- index = rank - 1
27
- if index < 0:
28
- return sorted_values[0]
29
- else:
30
- index = rank
27
+ index = len(values) * percentile / 100 - 1
28
+ if index < 0:
29
+ return sorted_values[0]
31
30
 
32
- if index % 1 == 0:
31
+ fractional = index % 1
32
+ if fractional == 0:
33
33
  return sorted_values[int(index)]
34
- else:
35
- fractional = index % 1
36
- integer = int(index - fractional)
37
- lower = sorted_values[integer]
38
- higher = sorted_values[integer + 1]
39
- return lower + fractional * (higher - lower)
34
+
35
+ integer = int(index - fractional)
36
+ lower = sorted_values[integer]
37
+ higher = sorted_values[integer + 1]
38
+ return lower + fractional * (higher - lower)