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.
- ens/__init__.py +13 -2
- web3/__init__.py +21 -5
- web3/_utils/batching.py +217 -0
- web3/_utils/caching.py +26 -2
- web3/_utils/compat/__init__.py +1 -0
- web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
- web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/event_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
- web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
- web3/_utils/events.py +2 -2
- web3/_utils/http.py +3 -0
- web3/_utils/http_session_manager.py +280 -0
- web3/_utils/method_formatters.py +0 -2
- web3/_utils/module_testing/eth_module.py +92 -119
- web3/_utils/module_testing/module_testing_utils.py +27 -9
- web3/_utils/module_testing/persistent_connection_provider.py +1 -0
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/_utils/rpc_abi.py +0 -3
- web3/beacon/__init__.py +5 -0
- web3/beacon/async_beacon.py +9 -5
- web3/beacon/beacon.py +7 -5
- web3/contract/__init__.py +7 -0
- web3/contract/base_contract.py +10 -1
- web3/contract/utils.py +112 -4
- web3/eth/__init__.py +7 -0
- web3/eth/async_eth.py +5 -37
- web3/eth/eth.py +7 -57
- web3/exceptions.py +20 -0
- web3/gas_strategies/time_based.py +2 -2
- web3/main.py +21 -9
- web3/manager.py +113 -8
- web3/method.py +29 -9
- web3/middleware/__init__.py +17 -0
- web3/middleware/base.py +43 -0
- web3/module.py +47 -7
- web3/providers/__init__.py +21 -0
- web3/providers/async_base.py +55 -23
- web3/providers/base.py +59 -26
- web3/providers/eth_tester/__init__.py +5 -0
- web3/providers/eth_tester/defaults.py +0 -6
- web3/providers/eth_tester/middleware.py +3 -8
- web3/providers/ipc.py +23 -8
- web3/providers/legacy_websocket.py +26 -1
- web3/providers/persistent/__init__.py +7 -0
- web3/providers/persistent/async_ipc.py +60 -76
- web3/providers/persistent/persistent.py +134 -10
- web3/providers/persistent/request_processor.py +98 -14
- web3/providers/persistent/websocket.py +43 -66
- web3/providers/rpc/__init__.py +5 -0
- web3/providers/rpc/async_rpc.py +34 -12
- web3/providers/rpc/rpc.py +34 -12
- web3/providers/rpc/utils.py +0 -3
- web3/tools/benchmark/main.py +7 -6
- web3/tools/benchmark/node.py +1 -1
- web3/types.py +7 -1
- web3/utils/__init__.py +14 -5
- web3/utils/async_exception_handling.py +19 -7
- web3/utils/exception_handling.py +7 -5
- {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/LICENSE +1 -1
- {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/METADATA +33 -20
- {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/RECORD +80 -80
- {web3-7.0.0b5.dist-info → web3-7.0.0b7.dist-info}/WHEEL +1 -1
- web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
- web3/_utils/request.py +0 -265
- {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:
|
|
48
|
-
|
|
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[
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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,
|
|
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={
|
|
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
|
|
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.
|
|
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 =
|
|
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
|
|
120
|
-
|
|
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
|
|
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
|
|
180
|
-
self
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
+
)
|
web3/providers/rpc/__init__.py
CHANGED
web3/providers/rpc/async_rpc.py
CHANGED
|
@@ -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:
|
|
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 =
|
|
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
|
|
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:
|
|
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 =
|
|
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(
|
|
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)
|
web3/providers/rpc/utils.py
CHANGED
web3/tools/benchmark/main.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
130
|
+
"exec": lambda w3_http=w3_http, account=account: w3_http.eth.send_transaction( # noqa: E501
|
|
130
131
|
{
|
|
131
132
|
"to": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
|
|
132
|
-
"from":
|
|
133
|
+
"from": account,
|
|
133
134
|
"value": Wei(1),
|
|
134
135
|
}
|
|
135
136
|
),
|
|
136
|
-
"async_exec": lambda async_w3_http=async_w3_http,
|
|
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":
|
|
140
|
+
"from": async_account,
|
|
140
141
|
"value": Wei(1),
|
|
141
142
|
}
|
|
142
143
|
),
|
web3/tools/benchmark/node.py
CHANGED
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
|
-
|
|
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):
|