web3 7.0.0b5__py3-none-any.whl → 7.0.0b7__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 (82) hide show
  1. ens/__init__.py +13 -2
  2. web3/__init__.py +21 -5
  3. web3/_utils/batching.py +217 -0
  4. web3/_utils/caching.py +26 -2
  5. web3/_utils/compat/__init__.py +1 -0
  6. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  7. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  8. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  9. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  10. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  11. web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
  12. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  13. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  14. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  15. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  16. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  17. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  18. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  19. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  20. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  21. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  22. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  24. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  25. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  26. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  27. web3/_utils/events.py +2 -2
  28. web3/_utils/http.py +3 -0
  29. web3/_utils/http_session_manager.py +280 -0
  30. web3/_utils/method_formatters.py +0 -2
  31. web3/_utils/module_testing/eth_module.py +92 -119
  32. web3/_utils/module_testing/module_testing_utils.py +27 -9
  33. web3/_utils/module_testing/persistent_connection_provider.py +1 -0
  34. web3/_utils/module_testing/web3_module.py +438 -17
  35. web3/_utils/rpc_abi.py +0 -3
  36. web3/beacon/__init__.py +5 -0
  37. web3/beacon/async_beacon.py +9 -5
  38. web3/beacon/beacon.py +7 -5
  39. web3/contract/__init__.py +7 -0
  40. web3/contract/base_contract.py +10 -1
  41. web3/contract/utils.py +112 -4
  42. web3/eth/__init__.py +7 -0
  43. web3/eth/async_eth.py +5 -37
  44. web3/eth/eth.py +7 -57
  45. web3/exceptions.py +20 -0
  46. web3/gas_strategies/time_based.py +2 -2
  47. web3/main.py +21 -9
  48. web3/manager.py +113 -8
  49. web3/method.py +29 -9
  50. web3/middleware/__init__.py +17 -0
  51. web3/middleware/base.py +43 -0
  52. web3/module.py +47 -7
  53. web3/providers/__init__.py +21 -0
  54. web3/providers/async_base.py +55 -23
  55. web3/providers/base.py +59 -26
  56. web3/providers/eth_tester/__init__.py +5 -0
  57. web3/providers/eth_tester/defaults.py +0 -6
  58. web3/providers/eth_tester/middleware.py +3 -8
  59. web3/providers/ipc.py +23 -8
  60. web3/providers/legacy_websocket.py +26 -1
  61. web3/providers/persistent/__init__.py +7 -0
  62. web3/providers/persistent/async_ipc.py +60 -76
  63. web3/providers/persistent/persistent.py +134 -10
  64. web3/providers/persistent/request_processor.py +98 -14
  65. web3/providers/persistent/websocket.py +43 -66
  66. web3/providers/rpc/__init__.py +5 -0
  67. web3/providers/rpc/async_rpc.py +34 -12
  68. web3/providers/rpc/rpc.py +34 -12
  69. web3/providers/rpc/utils.py +0 -3
  70. web3/tools/benchmark/main.py +7 -6
  71. web3/tools/benchmark/node.py +1 -1
  72. web3/types.py +7 -1
  73. web3/utils/__init__.py +14 -5
  74. web3/utils/async_exception_handling.py +19 -7
  75. web3/utils/exception_handling.py +7 -5
  76. {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/LICENSE +1 -1
  77. {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/METADATA +33 -20
  78. {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/RECORD +80 -80
  79. {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/WHEEL +1 -1
  80. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  81. web3/_utils/request.py +0 -265
  82. {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/top_level.txt +0 -0
@@ -2,20 +2,32 @@ import asyncio
2
2
  from copy import (
3
3
  copy,
4
4
  )
5
+ import sys
5
6
  from typing import (
6
7
  TYPE_CHECKING,
7
8
  Any,
8
9
  Callable,
9
10
  Dict,
11
+ Generic,
10
12
  Optional,
11
13
  Tuple,
14
+ TypeVar,
15
+ Union,
12
16
  )
13
17
 
18
+ from eth_utils.toolz import (
19
+ compose,
20
+ )
21
+
22
+ from web3._utils.batching import (
23
+ BATCH_REQUEST_ID,
24
+ )
14
25
  from web3._utils.caching import (
15
26
  RequestInformation,
16
27
  generate_cache_key,
17
28
  )
18
29
  from web3.exceptions import (
30
+ TaskNotRunning,
19
31
  Web3ValueError,
20
32
  )
21
33
  from web3.types import (
@@ -31,6 +43,34 @@ if TYPE_CHECKING:
31
43
  PersistentConnectionProvider,
32
44
  )
33
45
 
46
+ T = TypeVar("T")
47
+
48
+ # TODO: This is an ugly hack for python 3.8. Remove this after we drop support for it
49
+ # and use `asyncio.Queue[T]` type directly in the `TaskReliantQueue` class.
50
+ if sys.version_info >= (3, 9):
51
+
52
+ class _TaskReliantQueue(asyncio.Queue[T], Generic[T]):
53
+ pass
54
+
55
+ else:
56
+
57
+ class _TaskReliantQueue(asyncio.Queue, Generic[T]): # type: ignore
58
+ pass
59
+
60
+
61
+ class TaskReliantQueue(_TaskReliantQueue[T]):
62
+ """
63
+ A queue that relies on a task to be running to process items in the queue.
64
+ """
65
+
66
+ async def get(self) -> T:
67
+ item = await super().get()
68
+ if isinstance(item, Exception):
69
+ # if the item is an exception, raise it so the task can handle this case
70
+ # more gracefully
71
+ raise item
72
+ return item
73
+
34
74
 
35
75
  class RequestProcessor:
36
76
  _subscription_queue_synced_with_ws_stream: bool = False
@@ -44,9 +84,9 @@ class RequestProcessor:
44
84
 
45
85
  self._request_information_cache: SimpleCache = SimpleCache(500)
46
86
  self._request_response_cache: SimpleCache = SimpleCache(500)
47
- self._subscription_response_queue: asyncio.Queue[RPCResponse] = asyncio.Queue(
48
- maxsize=subscription_response_queue_size
49
- )
87
+ self._subscription_response_queue: TaskReliantQueue[
88
+ Union[RPCResponse, TaskNotRunning]
89
+ ] = TaskReliantQueue(maxsize=subscription_response_queue_size)
50
90
 
51
91
  @property
52
92
  def active_subscriptions(self) -> Dict[str, Any]:
@@ -62,7 +102,11 @@ class RequestProcessor:
62
102
  self,
63
103
  method: RPCEndpoint,
64
104
  params: Any,
65
- response_formatters: Tuple[Callable[..., Any], ...],
105
+ response_formatters: Tuple[
106
+ Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
107
+ Callable[..., Any],
108
+ Callable[..., Any],
109
+ ],
66
110
  ) -> Optional[str]:
67
111
  cached_requests_key = generate_cache_key((method, params))
68
112
  if cached_requests_key in self._provider._request_cache._data:
@@ -76,12 +120,17 @@ class RequestProcessor:
76
120
  )
77
121
  return None
78
122
 
79
- # copy the request counter and find the next request id without incrementing
80
- # since this is done when / if the request is successfully sent
81
- request_id = next(copy(self._provider.request_counter))
82
- cache_key = generate_cache_key(request_id)
123
+ if self._provider._is_batching:
124
+ # the _batch_request_counter is set when entering the context manager
125
+ current_request_id = self._provider._batch_request_counter
126
+ self._provider._batch_request_counter += 1
127
+ else:
128
+ # copy the request counter and find the next request id without incrementing
129
+ # since this is done when / if the request is successfully sent
130
+ current_request_id = next(copy(self._provider.request_counter))
131
+ cache_key = generate_cache_key(current_request_id)
83
132
 
84
- self._bump_cache_if_key_present(cache_key, request_id)
133
+ self._bump_cache_if_key_present(cache_key, current_request_id)
85
134
 
86
135
  request_info = RequestInformation(
87
136
  method,
@@ -89,7 +138,7 @@ class RequestProcessor:
89
138
  response_formatters,
90
139
  )
91
140
  self._provider.logger.debug(
92
- f"Caching request info:\n request_id={request_id},\n"
141
+ f"Caching request info:\n request_id={current_request_id},\n"
93
142
  f" cache_key={cache_key},\n request_info={request_info.__dict__}"
94
143
  )
95
144
  self._request_information_cache.cache(
@@ -153,9 +202,8 @@ class RequestProcessor:
153
202
  # i.e. subscription request information remains in the cache
154
203
  self._request_information_cache.get_cache_entry(cache_key)
155
204
  )
156
-
157
205
  else:
158
- # retrieve the request info from the cache using the request id
206
+ # retrieve the request info from the cache using the response id
159
207
  cache_key = generate_cache_key(response["id"])
160
208
  if response in self._provider._request_cache._data.values():
161
209
  request_info = (
@@ -184,6 +232,33 @@ class RequestProcessor:
184
232
 
185
233
  return request_info
186
234
 
235
+ def append_result_formatter_for_request(
236
+ self, request_id: int, result_formatter: Callable[..., Any]
237
+ ) -> None:
238
+ cache_key = generate_cache_key(request_id)
239
+ cached_request_info_for_id: RequestInformation = (
240
+ self._request_information_cache.get_cache_entry(cache_key)
241
+ )
242
+ if cached_request_info_for_id is not None:
243
+ (
244
+ current_result_formatters,
245
+ error_formatters,
246
+ null_result_formatters,
247
+ ) = cached_request_info_for_id.response_formatters
248
+ cached_request_info_for_id.response_formatters = (
249
+ compose(
250
+ result_formatter,
251
+ current_result_formatters,
252
+ ),
253
+ error_formatters,
254
+ null_result_formatters,
255
+ )
256
+ else:
257
+ self._provider.logger.debug(
258
+ f"No cached request info for response id `{request_id}`. Cannot "
259
+ f"append response formatter for response."
260
+ )
261
+
187
262
  def append_middleware_response_processor(
188
263
  self,
189
264
  response: RPCResponse,
@@ -218,7 +293,7 @@ class RequestProcessor:
218
293
  ) -> None:
219
294
  if subscription:
220
295
  if self._subscription_response_queue.full():
221
- self._provider.logger.info(
296
+ self._provider.logger.debug(
222
297
  "Subscription queue is full. Waiting for provider to consume "
223
298
  "messages before caching."
224
299
  )
@@ -229,6 +304,15 @@ class RequestProcessor:
229
304
  f"Caching subscription response:\n response={raw_response}"
230
305
  )
231
306
  await self._subscription_response_queue.put(raw_response)
307
+ elif isinstance(raw_response, list):
308
+ # Since only one batch should be in the cache at all times, we use a
309
+ # constant cache key for the batch response.
310
+ cache_key = generate_cache_key(BATCH_REQUEST_ID)
311
+ self._provider.logger.debug(
312
+ f"Caching batch response:\n cache_key={cache_key},\n"
313
+ f" response={raw_response}"
314
+ )
315
+ self._request_response_cache.cache(cache_key, raw_response)
232
316
  else:
233
317
  response_id = raw_response.get("id")
234
318
  cache_key = generate_cache_key(response_id)
@@ -289,6 +373,6 @@ class RequestProcessor:
289
373
  """Clear the request processor caches."""
290
374
  self._request_information_cache.clear()
291
375
  self._request_response_cache.clear()
292
- self._subscription_response_queue = asyncio.Queue(
376
+ self._subscription_response_queue = TaskReliantQueue(
293
377
  maxsize=self._subscription_response_queue.maxsize
294
378
  )
@@ -5,8 +5,11 @@ import os
5
5
  from typing import (
6
6
  Any,
7
7
  Dict,
8
+ List,
8
9
  Optional,
10
+ Tuple,
9
11
  Union,
12
+ cast,
10
13
  )
11
14
 
12
15
  from eth_typing import (
@@ -25,6 +28,10 @@ from websockets.exceptions import (
25
28
  WebSocketException,
26
29
  )
27
30
 
31
+ from web3._utils.batching import (
32
+ BATCH_REQUEST_ID,
33
+ sort_batch_response_by_response_ids,
34
+ )
28
35
  from web3._utils.caching import (
29
36
  async_handle_request_caching,
30
37
  )
@@ -61,7 +68,6 @@ class WebSocketProvider(PersistentConnectionProvider):
61
68
  logger = logging.getLogger("web3.providers.WebSocketProvider")
62
69
  is_async: bool = True
63
70
 
64
- _max_connection_retries: int = 5
65
71
  _ws: Optional[WebSocketClientProtocol] = None
66
72
 
67
73
  def __init__(
@@ -116,47 +122,13 @@ class WebSocketProvider(PersistentConnectionProvider):
116
122
  ) from e
117
123
  return False
118
124
 
119
- async def connect(self) -> None:
120
- _connection_attempts = 0
121
- _backoff_rate_change = 1.75
122
- _backoff_time = 1.75
123
-
124
- while _connection_attempts != self._max_connection_retries:
125
- try:
126
- _connection_attempts += 1
127
- self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
128
- self._message_listener_task = asyncio.create_task(
129
- self._message_listener()
130
- )
131
- break
132
- except WebSocketException as e:
133
- if _connection_attempts == self._max_connection_retries:
134
- raise ProviderConnectionError(
135
- f"Could not connect to endpoint: {self.endpoint_uri}. "
136
- f"Retries exceeded max of {self._max_connection_retries}."
137
- ) from e
138
- self.logger.info(
139
- f"Could not connect to endpoint: {self.endpoint_uri}. Retrying in "
140
- f"{round(_backoff_time, 1)} seconds.",
141
- exc_info=True,
142
- )
143
- await asyncio.sleep(_backoff_time)
144
- _backoff_time *= _backoff_rate_change
125
+ async def _provider_specific_connect(self) -> None:
126
+ self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
145
127
 
146
- async def disconnect(self) -> None:
128
+ async def _provider_specific_disconnect(self) -> None:
147
129
  if self._ws is not None and not self._ws.closed:
148
130
  await self._ws.close()
149
131
  self._ws = None
150
- self.logger.debug(
151
- f'Successfully disconnected from endpoint: "{self.endpoint_uri}'
152
- )
153
-
154
- try:
155
- self._message_listener_task.cancel()
156
- await self._message_listener_task
157
- except (asyncio.CancelledError, StopAsyncIteration):
158
- pass
159
- self._request_processor.clear_caches()
160
132
 
161
133
  @async_handle_request_caching
162
134
  async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
@@ -176,34 +148,39 @@ class WebSocketProvider(PersistentConnectionProvider):
176
148
 
177
149
  return response
178
150
 
179
- async def _message_listener(self) -> None:
180
- self.logger.info(
181
- "WebSocket listener background task started. Storing all messages in "
182
- "appropriate request processor queues / caches to be processed."
151
+ async def make_batch_request(
152
+ self, requests: List[Tuple[RPCEndpoint, Any]]
153
+ ) -> List[RPCResponse]:
154
+ request_data = self.encode_batch_rpc_request(requests)
155
+
156
+ if self._ws is None:
157
+ raise ProviderConnectionError(
158
+ "Connection to websocket has not been initiated for the provider."
159
+ )
160
+
161
+ await asyncio.wait_for(
162
+ self._ws.send(request_data), timeout=self.request_timeout
163
+ )
164
+
165
+ response = cast(
166
+ List[RPCResponse],
167
+ await self._get_response_for_request_id(BATCH_REQUEST_ID),
183
168
  )
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.
169
+ return response
170
+
171
+ async def _provider_specific_message_listener(self) -> None:
172
+ async for raw_message in self._ws:
187
173
  await asyncio.sleep(0)
188
174
 
189
- try:
190
- async for raw_message in self._ws:
191
- await asyncio.sleep(0)
192
-
193
- response = json.loads(raw_message)
194
- subscription = response.get("method") == "eth_subscription"
195
- await self._request_processor.cache_raw_response(
196
- response, subscription=subscription
197
- )
198
- except Exception as e:
199
- if not self.silence_listener_task_exceptions:
200
- loop = asyncio.get_event_loop()
201
- for task in asyncio.all_tasks(loop=loop):
202
- task.cancel()
203
- raise e
204
-
205
- self.logger.error(
206
- "Exception caught in listener, error logging and keeping "
207
- "listener background task alive."
208
- f"\n error={e.__class__.__name__}: {e}"
209
- )
175
+ response = json.loads(raw_message)
176
+ if isinstance(response, list):
177
+ response = sort_batch_response_by_response_ids(response)
178
+
179
+ subscription = (
180
+ response.get("method") == "eth_subscription"
181
+ if not isinstance(response, list)
182
+ else False
183
+ )
184
+ await self._request_processor.cache_raw_response(
185
+ response, subscription=subscription
186
+ )
@@ -4,3 +4,8 @@ from .async_rpc import (
4
4
  from .rpc import (
5
5
  HTTPProvider,
6
6
  )
7
+
8
+ __all__ = [
9
+ "AsyncHTTPProvider",
10
+ "HTTPProvider",
11
+ ]
@@ -4,9 +4,11 @@ from typing import (
4
4
  Any,
5
5
  Dict,
6
6
  Iterable,
7
+ List,
7
8
  Optional,
8
9
  Tuple,
9
10
  Union,
11
+ cast,
10
12
  )
11
13
 
12
14
  from aiohttp import (
@@ -27,19 +29,20 @@ from web3._utils.empty import (
27
29
  from web3._utils.http import (
28
30
  construct_user_agent,
29
31
  )
30
- from web3._utils.request import (
31
- async_cache_and_return_session as _async_cache_and_return_session,
32
- async_make_post_request,
33
- get_default_http_endpoint,
34
- )
35
32
  from web3.types import (
36
33
  RPCEndpoint,
37
34
  RPCResponse,
38
35
  )
39
36
 
37
+ from ..._utils.batching import (
38
+ sort_batch_response_by_response_ids,
39
+ )
40
40
  from ..._utils.caching import (
41
41
  async_handle_request_caching,
42
42
  )
43
+ from ..._utils.http_session_manager import (
44
+ HTTPSessionManager,
45
+ )
43
46
  from ..async_base import (
44
47
  AsyncJSONBaseProvider,
45
48
  )
@@ -58,22 +61,29 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
58
61
  self,
59
62
  endpoint_uri: Optional[Union[URI, str]] = None,
60
63
  request_kwargs: Optional[Any] = None,
61
- exception_retry_configuration: Union[
62
- ExceptionRetryConfiguration, Empty
64
+ exception_retry_configuration: Optional[
65
+ Union[ExceptionRetryConfiguration, Empty]
63
66
  ] = empty,
67
+ **kwargs: Any,
64
68
  ) -> None:
69
+ self._request_session_manager = HTTPSessionManager()
70
+
65
71
  if endpoint_uri is None:
66
- self.endpoint_uri = get_default_http_endpoint()
72
+ self.endpoint_uri = (
73
+ self._request_session_manager.get_default_http_endpoint()
74
+ )
67
75
  else:
68
76
  self.endpoint_uri = URI(endpoint_uri)
69
77
 
70
78
  self._request_kwargs = request_kwargs or {}
71
79
  self._exception_retry_configuration = exception_retry_configuration
72
80
 
73
- super().__init__()
81
+ super().__init__(**kwargs)
74
82
 
75
83
  async def cache_async_session(self, session: ClientSession) -> ClientSession:
76
- return await _async_cache_and_return_session(self.endpoint_uri, session)
84
+ return await self._request_session_manager.async_cache_and_return_session(
85
+ self.endpoint_uri, session
86
+ )
77
87
 
78
88
  def __str__(self) -> str:
79
89
  return f"RPC connection {self.endpoint_uri}"
@@ -117,7 +127,7 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
117
127
  ):
118
128
  for i in range(self.exception_retry_configuration.retries):
119
129
  try:
120
- return await async_make_post_request(
130
+ return await self._request_session_manager.async_make_post_request(
121
131
  self.endpoint_uri, request_data, **self.get_request_kwargs()
122
132
  )
123
133
  except tuple(self.exception_retry_configuration.errors):
@@ -130,7 +140,7 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
130
140
  raise
131
141
  return None
132
142
  else:
133
- return await async_make_post_request(
143
+ return await self._request_session_manager.async_make_post_request(
134
144
  self.endpoint_uri, request_data, **self.get_request_kwargs()
135
145
  )
136
146
 
@@ -147,3 +157,15 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
147
157
  f"Method: {method}, Response: {response}"
148
158
  )
149
159
  return response
160
+
161
+ async def make_batch_request(
162
+ self, batch_requests: List[Tuple[RPCEndpoint, Any]]
163
+ ) -> List[RPCResponse]:
164
+ self.logger.debug(f"Making batch request HTTP - uri: `{self.endpoint_uri}`")
165
+ request_data = self.encode_batch_rpc_request(batch_requests)
166
+ raw_response = await self._request_session_manager.async_make_post_request(
167
+ self.endpoint_uri, request_data, **self.get_request_kwargs()
168
+ )
169
+ self.logger.debug("Received batch response HTTP.")
170
+ responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
171
+ return sort_batch_response_by_response_ids(responses_list)
web3/providers/rpc/rpc.py CHANGED
@@ -5,9 +5,11 @@ from typing import (
5
5
  Any,
6
6
  Dict,
7
7
  Iterable,
8
+ List,
8
9
  Optional,
9
10
  Tuple,
10
11
  Union,
12
+ cast,
11
13
  )
12
14
 
13
15
  from eth_typing import (
@@ -25,19 +27,20 @@ from web3._utils.empty import (
25
27
  from web3._utils.http import (
26
28
  construct_user_agent,
27
29
  )
28
- from web3._utils.request import (
29
- cache_and_return_session,
30
- get_default_http_endpoint,
31
- make_post_request,
32
- )
33
30
  from web3.types import (
34
31
  RPCEndpoint,
35
32
  RPCResponse,
36
33
  )
37
34
 
35
+ from ..._utils.batching import (
36
+ sort_batch_response_by_response_ids,
37
+ )
38
38
  from ..._utils.caching import (
39
39
  handle_request_caching,
40
40
  )
41
+ from ..._utils.http_session_manager import (
42
+ HTTPSessionManager,
43
+ )
41
44
  from ..base import (
42
45
  JSONBaseProvider,
43
46
  )
@@ -62,12 +65,17 @@ class HTTPProvider(JSONBaseProvider):
62
65
  endpoint_uri: Optional[Union[URI, str]] = None,
63
66
  request_kwargs: Optional[Any] = None,
64
67
  session: Optional[Any] = None,
65
- exception_retry_configuration: Union[
66
- ExceptionRetryConfiguration, Empty
68
+ exception_retry_configuration: Optional[
69
+ Union[ExceptionRetryConfiguration, Empty]
67
70
  ] = empty,
71
+ **kwargs: Any,
68
72
  ) -> None:
73
+ self._request_session_manager = HTTPSessionManager()
74
+
69
75
  if endpoint_uri is None:
70
- self.endpoint_uri = get_default_http_endpoint()
76
+ self.endpoint_uri = (
77
+ self._request_session_manager.get_default_http_endpoint()
78
+ )
71
79
  else:
72
80
  self.endpoint_uri = URI(endpoint_uri)
73
81
 
@@ -75,9 +83,11 @@ class HTTPProvider(JSONBaseProvider):
75
83
  self._exception_retry_configuration = exception_retry_configuration
76
84
 
77
85
  if session:
78
- cache_and_return_session(self.endpoint_uri, session)
86
+ self._request_session_manager.cache_and_return_session(
87
+ self.endpoint_uri, session
88
+ )
79
89
 
80
- super().__init__()
90
+ super().__init__(**kwargs)
81
91
 
82
92
  def __str__(self) -> str:
83
93
  return f"RPC connection {self.endpoint_uri}"
@@ -125,7 +135,7 @@ class HTTPProvider(JSONBaseProvider):
125
135
  ):
126
136
  for i in range(self.exception_retry_configuration.retries):
127
137
  try:
128
- return make_post_request(
138
+ return self._request_session_manager.make_post_request(
129
139
  self.endpoint_uri, request_data, **self.get_request_kwargs()
130
140
  )
131
141
  except tuple(self.exception_retry_configuration.errors) as e:
@@ -138,7 +148,7 @@ class HTTPProvider(JSONBaseProvider):
138
148
  raise e
139
149
  return None
140
150
  else:
141
- return make_post_request(
151
+ return self._request_session_manager.make_post_request(
142
152
  self.endpoint_uri, request_data, **self.get_request_kwargs()
143
153
  )
144
154
 
@@ -155,3 +165,15 @@ class HTTPProvider(JSONBaseProvider):
155
165
  f"Method: {method}, Response: {response}"
156
166
  )
157
167
  return response
168
+
169
+ def make_batch_request(
170
+ self, batch_requests: List[Tuple[RPCEndpoint, Any]]
171
+ ) -> List[RPCResponse]:
172
+ self.logger.debug(f"Making batch request HTTP, uri: `{self.endpoint_uri}`")
173
+ request_data = self.encode_batch_rpc_request(batch_requests)
174
+ raw_response = self._request_session_manager.make_post_request(
175
+ self.endpoint_uri, request_data, **self.get_request_kwargs()
176
+ )
177
+ self.logger.debug("Received batch response HTTP.")
178
+ responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
179
+ return sort_batch_response_by_response_ids(responses_list)
@@ -19,9 +19,6 @@ REQUEST_RETRY_ALLOWLIST = [
19
19
  "evm",
20
20
  "eth_protocolVersion",
21
21
  "eth_syncing",
22
- "eth_coinbase",
23
- "eth_mining",
24
- "eth_hashrate",
25
22
  "eth_chainId",
26
23
  "eth_gasPrice",
27
24
  "eth_accounts",
@@ -108,13 +108,14 @@ def main(logger: logging.Logger, num_calls: int) -> None:
108
108
  asyncio.set_event_loop(loop)
109
109
 
110
110
  # -- sync -- #
111
- coinbase = w3_http.eth.coinbase
111
+ account = w3_http.eth.accounts[0]
112
112
 
113
113
  # -- async -- #
114
114
  async_w3_http = loop.run_until_complete(
115
115
  build_async_w3_http(fixture.endpoint_uri)
116
116
  )
117
- async_coinbase = loop.run_until_complete(async_w3_http.eth.coinbase)
117
+ async_accounts = loop.run_until_complete(async_w3_http.eth.accounts)
118
+ async_account = async_accounts[0]
118
119
 
119
120
  methods = [
120
121
  {
@@ -126,17 +127,17 @@ def main(logger: logging.Logger, num_calls: int) -> None:
126
127
  {
127
128
  "name": "eth_sendTransaction",
128
129
  "params": {},
129
- "exec": lambda w3_http=w3_http, coinbase=coinbase: w3_http.eth.send_transaction( # noqa: E501
130
+ "exec": lambda w3_http=w3_http, account=account: w3_http.eth.send_transaction( # noqa: E501
130
131
  {
131
132
  "to": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
132
- "from": coinbase,
133
+ "from": account,
133
134
  "value": Wei(1),
134
135
  }
135
136
  ),
136
- "async_exec": lambda async_w3_http=async_w3_http, async_coinbase=async_coinbase: async_w3_http.eth.send_transaction( # noqa: E501
137
+ "async_exec": lambda async_w3_http=async_w3_http, async_account=async_account: async_w3_http.eth.send_transaction( # noqa: E501
137
138
  {
138
139
  "to": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
139
- "from": async_coinbase,
140
+ "from": async_account,
140
141
  "value": Wei(1),
141
142
  }
142
143
  ),
@@ -24,7 +24,7 @@ from web3.tools.benchmark.utils import (
24
24
  kill_proc_gracefully,
25
25
  )
26
26
 
27
- GETH_FIXTURE_ZIP = "geth-1.13.14-fixture.zip"
27
+ GETH_FIXTURE_ZIP = "geth-1.14.5-fixture.zip"
28
28
 
29
29
 
30
30
  class GethBenchmarkFixture:
web3/types.py CHANGED
@@ -9,6 +9,7 @@ from typing import (
9
9
  NewType,
10
10
  Optional,
11
11
  Sequence,
12
+ Tuple,
12
13
  Type,
13
14
  TypedDict,
14
15
  TypeVar,
@@ -43,8 +44,9 @@ if TYPE_CHECKING:
43
44
  )
44
45
 
45
46
 
46
- TReturn = TypeVar("TReturn")
47
+ TFunc = TypeVar("TFunc", bound=Callable[..., Any])
47
48
  TParams = TypeVar("TParams")
49
+ TReturn = TypeVar("TReturn")
48
50
  TValue = TypeVar("TValue")
49
51
 
50
52
  BlockParams = Literal["latest", "earliest", "pending", "safe", "finalized"]
@@ -318,7 +320,11 @@ class CreateAccessListResponse(TypedDict):
318
320
 
319
321
 
320
322
  MakeRequestFn = Callable[[RPCEndpoint, Any], RPCResponse]
323
+ MakeBatchRequestFn = Callable[[List[Tuple[RPCEndpoint, Any]]], List[RPCResponse]]
321
324
  AsyncMakeRequestFn = Callable[[RPCEndpoint, Any], Coroutine[Any, Any, RPCResponse]]
325
+ AsyncMakeBatchRequestFn = Callable[
326
+ [List[Tuple[RPCEndpoint, Any]]], Coroutine[Any, Any, List[RPCResponse]]
327
+ ]
322
328
 
323
329
 
324
330
  class FormattersDict(TypedDict, total=False):