web3 7.6.1__py3-none-any.whl → 7.8.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.
- ens/async_ens.py +1 -1
- ens/ens.py +1 -1
- web3/_utils/caching/caching_utils.py +64 -0
- web3/_utils/caching/request_caching_validation.py +1 -0
- web3/_utils/events.py +1 -1
- web3/_utils/http_session_manager.py +32 -3
- web3/_utils/module_testing/eth_module.py +5 -18
- web3/_utils/module_testing/module_testing_utils.py +1 -43
- web3/_utils/module_testing/persistent_connection_provider.py +696 -207
- web3/_utils/module_testing/utils.py +99 -33
- web3/beacon/api_endpoints.py +10 -0
- web3/beacon/async_beacon.py +47 -0
- web3/beacon/beacon.py +45 -0
- web3/contract/async_contract.py +2 -206
- web3/contract/base_contract.py +217 -13
- web3/contract/contract.py +2 -205
- web3/datastructures.py +15 -16
- web3/eth/async_eth.py +23 -5
- web3/exceptions.py +7 -0
- web3/main.py +24 -3
- web3/manager.py +140 -48
- web3/method.py +1 -1
- web3/middleware/attrdict.py +12 -22
- web3/middleware/base.py +14 -6
- web3/module.py +17 -21
- web3/providers/async_base.py +23 -14
- web3/providers/base.py +6 -8
- web3/providers/ipc.py +7 -6
- web3/providers/legacy_websocket.py +1 -1
- web3/providers/persistent/async_ipc.py +5 -3
- web3/providers/persistent/persistent.py +121 -17
- web3/providers/persistent/persistent_connection.py +11 -4
- web3/providers/persistent/request_processor.py +49 -41
- web3/providers/persistent/subscription_container.py +56 -0
- web3/providers/persistent/subscription_manager.py +298 -0
- web3/providers/persistent/websocket.py +4 -4
- web3/providers/rpc/async_rpc.py +16 -3
- web3/providers/rpc/rpc.py +9 -5
- web3/types.py +28 -14
- web3/utils/__init__.py +4 -0
- web3/utils/subscriptions.py +289 -0
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/LICENSE +1 -1
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/METADATA +68 -56
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/RECORD +46 -43
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/WHEEL +1 -1
- {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/top_level.txt +0 -0
web3/eth/async_eth.py
CHANGED
|
@@ -99,11 +99,16 @@ from web3.types import (
|
|
|
99
99
|
_Hash32,
|
|
100
100
|
)
|
|
101
101
|
from web3.utils import (
|
|
102
|
+
EthSubscription,
|
|
102
103
|
async_handle_offchain_lookup,
|
|
103
104
|
)
|
|
105
|
+
from web3.utils.subscriptions import (
|
|
106
|
+
EthSubscriptionHandler,
|
|
107
|
+
)
|
|
104
108
|
|
|
105
109
|
if TYPE_CHECKING:
|
|
106
110
|
from web3 import AsyncWeb3 # noqa: F401
|
|
111
|
+
from web3.contract.async_contract import AsyncContractEvent # noqa: F401
|
|
107
112
|
|
|
108
113
|
|
|
109
114
|
class AsyncEth(BaseEth):
|
|
@@ -714,6 +719,9 @@ class AsyncEth(BaseEth):
|
|
|
714
719
|
bool, # newPendingTransactions, full_transactions
|
|
715
720
|
]
|
|
716
721
|
] = None,
|
|
722
|
+
handler: Optional[EthSubscriptionHandler] = None,
|
|
723
|
+
handler_context: Optional[Dict[str, Any]] = None,
|
|
724
|
+
label: Optional[str] = None,
|
|
717
725
|
) -> HexStr:
|
|
718
726
|
if not isinstance(self.w3.provider, PersistentConnectionProvider):
|
|
719
727
|
raise MethodNotSupported(
|
|
@@ -721,10 +729,13 @@ class AsyncEth(BaseEth):
|
|
|
721
729
|
"persistent connections."
|
|
722
730
|
)
|
|
723
731
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
732
|
+
sub = EthSubscription._create_type_aware_subscription(
|
|
733
|
+
subscription_params=(subscription_type, subscription_arg),
|
|
734
|
+
handler=handler,
|
|
735
|
+
handler_context=handler_context or {},
|
|
736
|
+
label=label,
|
|
737
|
+
)
|
|
738
|
+
return await self.w3.subscription_manager.subscribe(sub)
|
|
728
739
|
|
|
729
740
|
_unsubscribe: Method[Callable[[HexStr], Awaitable[bool]]] = Method(
|
|
730
741
|
RPC.eth_unsubscribe,
|
|
@@ -738,7 +749,14 @@ class AsyncEth(BaseEth):
|
|
|
738
749
|
"persistent connections."
|
|
739
750
|
)
|
|
740
751
|
|
|
741
|
-
|
|
752
|
+
for sub in self.w3.subscription_manager.subscriptions:
|
|
753
|
+
if sub._id == subscription_id:
|
|
754
|
+
return await sub.unsubscribe()
|
|
755
|
+
|
|
756
|
+
raise Web3ValueError(
|
|
757
|
+
f"Cannot unsubscribe subscription with id `{subscription_id}`. "
|
|
758
|
+
"Subscription not found."
|
|
759
|
+
)
|
|
742
760
|
|
|
743
761
|
# -- contract methods -- #
|
|
744
762
|
|
web3/exceptions.py
CHANGED
|
@@ -353,6 +353,13 @@ class PersistentConnectionClosedOK(PersistentConnectionError):
|
|
|
353
353
|
"""
|
|
354
354
|
|
|
355
355
|
|
|
356
|
+
class SubscriptionProcessingFinished(Web3Exception):
|
|
357
|
+
"""
|
|
358
|
+
Raised to alert the subscription manager that the processing of subscriptions
|
|
359
|
+
has finished.
|
|
360
|
+
"""
|
|
361
|
+
|
|
362
|
+
|
|
356
363
|
class Web3RPCError(Web3Exception):
|
|
357
364
|
"""
|
|
358
365
|
Raised when a JSON-RPC response contains an error field.
|
web3/main.py
CHANGED
|
@@ -148,6 +148,9 @@ from web3.tracing import (
|
|
|
148
148
|
from web3.types import (
|
|
149
149
|
Wei,
|
|
150
150
|
)
|
|
151
|
+
from web3.providers.persistent.subscription_manager import (
|
|
152
|
+
SubscriptionManager,
|
|
153
|
+
)
|
|
151
154
|
|
|
152
155
|
if TYPE_CHECKING:
|
|
153
156
|
from web3._utils.batching import RequestBatcher # noqa: F401
|
|
@@ -508,12 +511,27 @@ class AsyncWeb3(BaseWeb3):
|
|
|
508
511
|
new_ens.w3 = self # set self object reference for ``AsyncENS.w3``
|
|
509
512
|
self._ens = new_ens
|
|
510
513
|
|
|
511
|
-
# -- persistent connection
|
|
514
|
+
# -- persistent connection settings -- #
|
|
515
|
+
|
|
516
|
+
_subscription_manager: Optional[SubscriptionManager] = None
|
|
517
|
+
_persistent_connection: Optional["PersistentConnection"] = None
|
|
518
|
+
|
|
519
|
+
@property
|
|
520
|
+
@persistent_connection_provider_method()
|
|
521
|
+
def subscription_manager(self) -> SubscriptionManager:
|
|
522
|
+
"""
|
|
523
|
+
Access the subscription manager for the current PersistentConnectionProvider.
|
|
524
|
+
"""
|
|
525
|
+
if not self._subscription_manager:
|
|
526
|
+
self._subscription_manager = SubscriptionManager(self)
|
|
527
|
+
return self._subscription_manager
|
|
512
528
|
|
|
513
529
|
@property
|
|
514
530
|
@persistent_connection_provider_method()
|
|
515
531
|
def socket(self) -> PersistentConnection:
|
|
516
|
-
|
|
532
|
+
if self._persistent_connection is None:
|
|
533
|
+
self._persistent_connection = PersistentConnection(self)
|
|
534
|
+
return self._persistent_connection
|
|
517
535
|
|
|
518
536
|
# w3 = await AsyncWeb3(PersistentConnectionProvider(...))
|
|
519
537
|
@persistent_connection_provider_method(
|
|
@@ -522,7 +540,10 @@ class AsyncWeb3(BaseWeb3):
|
|
|
522
540
|
)
|
|
523
541
|
def __await__(self) -> Generator[Any, None, Self]:
|
|
524
542
|
async def __async_init__() -> Self:
|
|
525
|
-
|
|
543
|
+
provider = cast("PersistentConnectionProvider", self.provider)
|
|
544
|
+
await provider.connect()
|
|
545
|
+
# set signal handlers since not within a context manager
|
|
546
|
+
provider._set_signal_handlers()
|
|
526
547
|
return self
|
|
527
548
|
|
|
528
549
|
return __async_init__().__await__()
|
web3/manager.py
CHANGED
|
@@ -6,7 +6,9 @@ from typing import (
|
|
|
6
6
|
AsyncGenerator,
|
|
7
7
|
Callable,
|
|
8
8
|
Coroutine,
|
|
9
|
+
Dict,
|
|
9
10
|
List,
|
|
11
|
+
NoReturn,
|
|
10
12
|
Optional,
|
|
11
13
|
Sequence,
|
|
12
14
|
Tuple,
|
|
@@ -69,7 +71,9 @@ from web3.providers.async_base import (
|
|
|
69
71
|
AsyncJSONBaseProvider,
|
|
70
72
|
)
|
|
71
73
|
from web3.types import (
|
|
74
|
+
FormattedEthSubscriptionResponse,
|
|
72
75
|
RPCEndpoint,
|
|
76
|
+
RPCRequest,
|
|
73
77
|
RPCResponse,
|
|
74
78
|
)
|
|
75
79
|
|
|
@@ -255,15 +259,38 @@ def _validate_response(
|
|
|
255
259
|
web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
|
|
256
260
|
|
|
257
261
|
response = apply_error_formatters(error_formatters, response)
|
|
258
|
-
|
|
259
|
-
logger.error(web3_rpc_error.user_message)
|
|
260
262
|
logger.debug(f"RPC error response: {response}")
|
|
263
|
+
|
|
261
264
|
raise web3_rpc_error
|
|
262
265
|
|
|
263
266
|
elif "result" not in response and not is_subscription_response:
|
|
264
267
|
_raise_bad_response_format(response)
|
|
265
268
|
|
|
266
269
|
|
|
270
|
+
def _raise_error_for_batch_response(
|
|
271
|
+
response: RPCResponse,
|
|
272
|
+
logger: Optional[logging.Logger] = None,
|
|
273
|
+
) -> NoReturn:
|
|
274
|
+
error = response.get("error")
|
|
275
|
+
if error is None:
|
|
276
|
+
_raise_bad_response_format(
|
|
277
|
+
response,
|
|
278
|
+
"Batch response must be formatted as a list of responses or "
|
|
279
|
+
"as a single JSON-RPC error response.",
|
|
280
|
+
)
|
|
281
|
+
_validate_response(
|
|
282
|
+
response,
|
|
283
|
+
None,
|
|
284
|
+
is_subscription_response=False,
|
|
285
|
+
logger=logger,
|
|
286
|
+
params=[],
|
|
287
|
+
)
|
|
288
|
+
# This should not be reached, but if it is, raise a generic `BadResponseFormat`
|
|
289
|
+
raise BadResponseFormat(
|
|
290
|
+
"Batch response was in an unexpected format and unable to be parsed."
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
267
294
|
class RequestManager:
|
|
268
295
|
logger = logging.getLogger("web3.manager.RequestManager")
|
|
269
296
|
|
|
@@ -293,9 +320,6 @@ class RequestManager:
|
|
|
293
320
|
provider = cast(PersistentConnectionProvider, self.provider)
|
|
294
321
|
self._request_processor: RequestProcessor = provider._request_processor
|
|
295
322
|
|
|
296
|
-
w3: Union["AsyncWeb3", "Web3"] = None
|
|
297
|
-
_provider = None
|
|
298
|
-
|
|
299
323
|
@property
|
|
300
324
|
def provider(self) -> Union["BaseProvider", "AsyncBaseProvider"]:
|
|
301
325
|
return self._provider
|
|
@@ -437,17 +461,23 @@ class RequestManager:
|
|
|
437
461
|
request_func = provider.batch_request_func(
|
|
438
462
|
cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
439
463
|
)
|
|
440
|
-
|
|
464
|
+
response = request_func(
|
|
441
465
|
[
|
|
442
466
|
(method, params)
|
|
443
467
|
for (method, params), _response_formatters in requests_info
|
|
444
468
|
]
|
|
445
469
|
)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
470
|
+
|
|
471
|
+
if isinstance(response, list):
|
|
472
|
+
# expected format
|
|
473
|
+
formatted_responses = [
|
|
474
|
+
self._format_batched_response(info, cast(RPCResponse, resp))
|
|
475
|
+
for info, resp in zip(requests_info, response)
|
|
476
|
+
]
|
|
477
|
+
return list(formatted_responses)
|
|
478
|
+
else:
|
|
479
|
+
# expect a single response with an error
|
|
480
|
+
_raise_error_for_batch_response(response, self.logger)
|
|
451
481
|
|
|
452
482
|
async def _async_make_batch_request(
|
|
453
483
|
self,
|
|
@@ -466,22 +496,31 @@ class RequestManager:
|
|
|
466
496
|
# since we add items to the batch without awaiting, we unpack the coroutines
|
|
467
497
|
# and await them all here
|
|
468
498
|
unpacked_requests_info = await asyncio.gather(*requests_info)
|
|
469
|
-
|
|
499
|
+
response = await request_func(
|
|
470
500
|
[
|
|
471
501
|
(method, params)
|
|
472
502
|
for (method, params), _response_formatters in unpacked_requests_info
|
|
473
503
|
]
|
|
474
504
|
)
|
|
475
505
|
|
|
476
|
-
if isinstance(
|
|
477
|
-
#
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
506
|
+
if isinstance(response, list):
|
|
507
|
+
# expected format
|
|
508
|
+
response = cast(List[RPCResponse], response)
|
|
509
|
+
if isinstance(self.provider, PersistentConnectionProvider):
|
|
510
|
+
# call _process_response for each response in the batch
|
|
511
|
+
return [
|
|
512
|
+
cast(RPCResponse, await self._process_response(resp))
|
|
513
|
+
for resp in response
|
|
514
|
+
]
|
|
515
|
+
|
|
516
|
+
formatted_responses = [
|
|
517
|
+
self._format_batched_response(info, resp)
|
|
518
|
+
for info, resp in zip(unpacked_requests_info, response)
|
|
519
|
+
]
|
|
520
|
+
return list(formatted_responses)
|
|
521
|
+
else:
|
|
522
|
+
# expect a single response with an error
|
|
523
|
+
_raise_error_for_batch_response(response, self.logger)
|
|
485
524
|
|
|
486
525
|
def _format_batched_response(
|
|
487
526
|
self,
|
|
@@ -489,6 +528,13 @@ class RequestManager:
|
|
|
489
528
|
response: RPCResponse,
|
|
490
529
|
) -> RPCResponse:
|
|
491
530
|
result_formatters, error_formatters, null_result_formatters = requests_info[1]
|
|
531
|
+
_validate_response(
|
|
532
|
+
response,
|
|
533
|
+
error_formatters,
|
|
534
|
+
is_subscription_response=False,
|
|
535
|
+
logger=self.logger,
|
|
536
|
+
params=requests_info[0][1],
|
|
537
|
+
)
|
|
492
538
|
return apply_result_formatters(
|
|
493
539
|
result_formatters,
|
|
494
540
|
self.formatted_response(
|
|
@@ -501,32 +547,65 @@ class RequestManager:
|
|
|
501
547
|
|
|
502
548
|
# -- persistent connection -- #
|
|
503
549
|
|
|
504
|
-
async def socket_request(
|
|
550
|
+
async def socket_request(
|
|
551
|
+
self,
|
|
552
|
+
method: RPCEndpoint,
|
|
553
|
+
params: Any,
|
|
554
|
+
response_formatters: Optional[
|
|
555
|
+
Tuple[Dict[str, Callable[..., Any]], Callable[..., Any], Callable[..., Any]]
|
|
556
|
+
] = None,
|
|
557
|
+
) -> RPCResponse:
|
|
505
558
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
506
|
-
request_func = await provider.request_func(
|
|
507
|
-
cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
508
|
-
)
|
|
509
559
|
self.logger.debug(
|
|
510
560
|
"Making request to open socket connection and waiting for response: "
|
|
511
|
-
f"{provider.get_endpoint_uri_or_ipc_path()}
|
|
561
|
+
f"{provider.get_endpoint_uri_or_ipc_path()},\n method: {method},\n"
|
|
562
|
+
f" params: {params}"
|
|
512
563
|
)
|
|
513
|
-
|
|
514
|
-
|
|
564
|
+
rpc_request = await self.send(method, params)
|
|
565
|
+
provider._request_processor.cache_request_information(
|
|
566
|
+
rpc_request["id"],
|
|
567
|
+
rpc_request["method"],
|
|
568
|
+
rpc_request["params"],
|
|
569
|
+
response_formatters=response_formatters or ((), (), ()),
|
|
570
|
+
)
|
|
571
|
+
return await self.recv_for_request(rpc_request)
|
|
515
572
|
|
|
516
|
-
async def send(self, method: RPCEndpoint, params: Any) ->
|
|
573
|
+
async def send(self, method: RPCEndpoint, params: Any) -> RPCRequest:
|
|
517
574
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
575
|
+
async_w3 = cast("AsyncWeb3", self.w3)
|
|
576
|
+
middleware_onion = cast("MiddlewareOnion", self.middleware_onion)
|
|
577
|
+
send_func = await provider.send_func(
|
|
578
|
+
async_w3,
|
|
579
|
+
middleware_onion,
|
|
580
|
+
)
|
|
523
581
|
self.logger.debug(
|
|
524
582
|
"Sending request to open socket connection: "
|
|
525
|
-
f"{provider.get_endpoint_uri_or_ipc_path()}
|
|
583
|
+
f"{provider.get_endpoint_uri_or_ipc_path()},\n method: {method},\n"
|
|
584
|
+
f" params: {params}"
|
|
526
585
|
)
|
|
527
|
-
await
|
|
586
|
+
return await send_func(method, params)
|
|
528
587
|
|
|
529
|
-
async def
|
|
588
|
+
async def recv_for_request(self, rpc_request: RPCRequest) -> RPCResponse:
|
|
589
|
+
provider = cast(PersistentConnectionProvider, self._provider)
|
|
590
|
+
async_w3 = cast("AsyncWeb3", self.w3)
|
|
591
|
+
middleware_onion = cast("MiddlewareOnion", self.middleware_onion)
|
|
592
|
+
recv_func = await provider.recv_func(
|
|
593
|
+
async_w3,
|
|
594
|
+
middleware_onion,
|
|
595
|
+
)
|
|
596
|
+
self.logger.debug(
|
|
597
|
+
"Getting response for request from open socket connection:\n"
|
|
598
|
+
f" request: {rpc_request}"
|
|
599
|
+
)
|
|
600
|
+
response = await recv_func(rpc_request)
|
|
601
|
+
try:
|
|
602
|
+
return cast(RPCResponse, await self._process_response(response))
|
|
603
|
+
except Exception:
|
|
604
|
+
response_id_key = generate_cache_key(response["id"])
|
|
605
|
+
provider._request_processor._request_information_cache.pop(response_id_key)
|
|
606
|
+
raise
|
|
607
|
+
|
|
608
|
+
async def recv(self) -> Union[RPCResponse, FormattedEthSubscriptionResponse]:
|
|
530
609
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
531
610
|
self.logger.debug(
|
|
532
611
|
"Getting next response from open socket connection: "
|
|
@@ -544,15 +623,18 @@ class RequestManager:
|
|
|
544
623
|
def _persistent_message_stream(self) -> "_AsyncPersistentMessageStream":
|
|
545
624
|
return _AsyncPersistentMessageStream(self)
|
|
546
625
|
|
|
547
|
-
async def _get_next_message(self) ->
|
|
626
|
+
async def _get_next_message(self) -> FormattedEthSubscriptionResponse:
|
|
548
627
|
return await self._message_stream().__anext__()
|
|
549
628
|
|
|
550
|
-
async def _message_stream(
|
|
629
|
+
async def _message_stream(
|
|
630
|
+
self,
|
|
631
|
+
) -> AsyncGenerator[FormattedEthSubscriptionResponse, None]:
|
|
551
632
|
if not isinstance(self._provider, PersistentConnectionProvider):
|
|
552
633
|
raise Web3TypeError(
|
|
553
634
|
"Only providers that maintain an open, persistent connection "
|
|
554
635
|
"can listen to streams."
|
|
555
636
|
)
|
|
637
|
+
async_w3 = cast("AsyncWeb3", self.w3)
|
|
556
638
|
|
|
557
639
|
if self._provider._message_listener_task is None:
|
|
558
640
|
raise ProviderConnectionError(
|
|
@@ -564,13 +646,21 @@ class RequestManager:
|
|
|
564
646
|
response = await self._request_processor.pop_raw_response(
|
|
565
647
|
subscription=True
|
|
566
648
|
)
|
|
567
|
-
if
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
yield
|
|
649
|
+
# if the subscription was unsubscribed from, we won't have a formatted
|
|
650
|
+
# response because we lost the request information.
|
|
651
|
+
sub_id = response.get(
|
|
652
|
+
"subscription", response.get("params", {}).get("subscription")
|
|
653
|
+
)
|
|
654
|
+
if async_w3.subscription_manager.get_by_id(sub_id):
|
|
655
|
+
# if active subscription, process and yield the formatted response
|
|
656
|
+
formatted_sub_response = cast(
|
|
657
|
+
FormattedEthSubscriptionResponse,
|
|
658
|
+
await self._process_response(response),
|
|
659
|
+
)
|
|
660
|
+
yield formatted_sub_response
|
|
661
|
+
else:
|
|
662
|
+
# if not an active sub, skip processing and continue
|
|
663
|
+
continue
|
|
574
664
|
except TaskNotRunning:
|
|
575
665
|
await asyncio.sleep(0)
|
|
576
666
|
self._provider._handle_listener_task_exceptions()
|
|
@@ -580,7 +670,9 @@ class RequestManager:
|
|
|
580
670
|
)
|
|
581
671
|
return
|
|
582
672
|
|
|
583
|
-
async def _process_response(
|
|
673
|
+
async def _process_response(
|
|
674
|
+
self, response: RPCResponse
|
|
675
|
+
) -> Union[RPCResponse, FormattedEthSubscriptionResponse]:
|
|
584
676
|
provider = cast(PersistentConnectionProvider, self._provider)
|
|
585
677
|
request_info = self._request_processor.get_request_information_for_response(
|
|
586
678
|
response
|
|
@@ -643,5 +735,5 @@ class _AsyncPersistentMessageStream:
|
|
|
643
735
|
def __aiter__(self) -> Self:
|
|
644
736
|
return self
|
|
645
737
|
|
|
646
|
-
async def __anext__(self) ->
|
|
738
|
+
async def __anext__(self) -> FormattedEthSubscriptionResponse:
|
|
647
739
|
return await self.manager._get_next_message()
|
web3/method.py
CHANGED
|
@@ -203,7 +203,7 @@ class Method(Generic[TFunc]):
|
|
|
203
203
|
def process_params(
|
|
204
204
|
self, module: "Module", *args: Any, **kwargs: Any
|
|
205
205
|
) -> Tuple[
|
|
206
|
-
Tuple[Union[RPCEndpoint, Callable[..., RPCEndpoint]], Tuple[
|
|
206
|
+
Tuple[Union[RPCEndpoint, Callable[..., RPCEndpoint]], Tuple[RPCEndpoint, ...]],
|
|
207
207
|
Tuple[
|
|
208
208
|
Union[TReturn, Dict[str, Callable[..., Any]]],
|
|
209
209
|
Callable[..., Any],
|
web3/middleware/attrdict.py
CHANGED
|
@@ -7,10 +7,6 @@ from typing import (
|
|
|
7
7
|
cast,
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
from eth_utils.toolz import (
|
|
11
|
-
assoc,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
10
|
from web3.datastructures import (
|
|
15
11
|
AttributeDict,
|
|
16
12
|
)
|
|
@@ -33,21 +29,18 @@ if TYPE_CHECKING:
|
|
|
33
29
|
|
|
34
30
|
|
|
35
31
|
def _handle_async_response(response: "RPCResponse") -> "RPCResponse":
|
|
32
|
+
"""
|
|
33
|
+
Process the RPC response by converting nested dictionaries into AttributeDict.
|
|
34
|
+
"""
|
|
36
35
|
if "result" in response:
|
|
37
|
-
|
|
36
|
+
response["result"] = AttributeDict.recursive(response["result"])
|
|
38
37
|
elif "params" in response and "result" in response["params"]:
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
response
|
|
42
|
-
"params",
|
|
43
|
-
assoc(
|
|
44
|
-
response["params"],
|
|
45
|
-
"result",
|
|
46
|
-
AttributeDict.recursive(response["params"]["result"]),
|
|
47
|
-
),
|
|
38
|
+
# subscription response
|
|
39
|
+
response["params"]["result"] = AttributeDict.recursive(
|
|
40
|
+
response["params"]["result"]
|
|
48
41
|
)
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
|
|
43
|
+
return response
|
|
51
44
|
|
|
52
45
|
|
|
53
46
|
class AttributeDictMiddleware(Web3Middleware, ABC):
|
|
@@ -60,11 +53,9 @@ class AttributeDictMiddleware(Web3Middleware, ABC):
|
|
|
60
53
|
|
|
61
54
|
def response_processor(self, method: "RPCEndpoint", response: "RPCResponse") -> Any:
|
|
62
55
|
if "result" in response:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
else:
|
|
67
|
-
return response
|
|
56
|
+
new_result = AttributeDict.recursive(response["result"])
|
|
57
|
+
response = {**response, "result": new_result}
|
|
58
|
+
return response
|
|
68
59
|
|
|
69
60
|
# -- async -- #
|
|
70
61
|
|
|
@@ -72,7 +63,6 @@ class AttributeDictMiddleware(Web3Middleware, ABC):
|
|
|
72
63
|
self, method: "RPCEndpoint", response: "RPCResponse"
|
|
73
64
|
) -> Any:
|
|
74
65
|
if self._w3.provider.has_persistent_connection:
|
|
75
|
-
# asynchronous response processing
|
|
76
66
|
provider = cast("PersistentConnectionProvider", self._w3.provider)
|
|
77
67
|
provider._request_processor.append_middleware_response_processor(
|
|
78
68
|
response, _handle_async_response
|
web3/middleware/base.py
CHANGED
|
@@ -62,15 +62,19 @@ class Web3Middleware:
|
|
|
62
62
|
) -> "MakeBatchRequestFn":
|
|
63
63
|
def middleware(
|
|
64
64
|
requests_info: List[Tuple["RPCEndpoint", Any]]
|
|
65
|
-
) -> List["RPCResponse"]:
|
|
65
|
+
) -> Union[List["RPCResponse"], "RPCResponse"]:
|
|
66
66
|
req_processed = [
|
|
67
67
|
self.request_processor(method, params)
|
|
68
68
|
for (method, params) in requests_info
|
|
69
69
|
]
|
|
70
|
-
|
|
70
|
+
response = make_batch_request(req_processed)
|
|
71
|
+
if not isinstance(response, list):
|
|
72
|
+
# RPC errors return only one response with the error object
|
|
73
|
+
return response
|
|
74
|
+
|
|
71
75
|
methods, _params = zip(*req_processed)
|
|
72
76
|
formatted_responses = [
|
|
73
|
-
self.response_processor(m, r) for m, r in zip(methods,
|
|
77
|
+
self.response_processor(m, r) for m, r in zip(methods, response)
|
|
74
78
|
]
|
|
75
79
|
return formatted_responses
|
|
76
80
|
|
|
@@ -103,16 +107,20 @@ class Web3Middleware:
|
|
|
103
107
|
) -> "AsyncMakeBatchRequestFn":
|
|
104
108
|
async def middleware(
|
|
105
109
|
requests_info: List[Tuple["RPCEndpoint", Any]]
|
|
106
|
-
) -> List["RPCResponse"]:
|
|
110
|
+
) -> Union[List["RPCResponse"], "RPCResponse"]:
|
|
107
111
|
req_processed = [
|
|
108
112
|
await self.async_request_processor(method, params)
|
|
109
113
|
for (method, params) in requests_info
|
|
110
114
|
]
|
|
111
|
-
|
|
115
|
+
response = await make_batch_request(req_processed)
|
|
116
|
+
if not isinstance(response, list):
|
|
117
|
+
# RPC errors return only one response with the error object
|
|
118
|
+
return response
|
|
119
|
+
|
|
112
120
|
methods, _params = zip(*req_processed)
|
|
113
121
|
formatted_responses = [
|
|
114
122
|
await self.async_response_processor(m, r)
|
|
115
|
-
for m, r in zip(methods,
|
|
123
|
+
for m, r in zip(methods, response)
|
|
116
124
|
]
|
|
117
125
|
return formatted_responses
|
|
118
126
|
|
web3/module.py
CHANGED
|
@@ -32,6 +32,7 @@ from web3.providers.persistent import (
|
|
|
32
32
|
PersistentConnectionProvider,
|
|
33
33
|
)
|
|
34
34
|
from web3.types import (
|
|
35
|
+
FormattedEthSubscriptionResponse,
|
|
35
36
|
RPCEndpoint,
|
|
36
37
|
RPCResponse,
|
|
37
38
|
)
|
|
@@ -74,7 +75,7 @@ def retrieve_request_information_for_batching(
|
|
|
74
75
|
)
|
|
75
76
|
if isinstance(w3.provider, PersistentConnectionProvider):
|
|
76
77
|
w3.provider._request_processor.cache_request_information(
|
|
77
|
-
cast(RPCEndpoint, method_str), params, response_formatters
|
|
78
|
+
None, cast(RPCEndpoint, method_str), params, response_formatters
|
|
78
79
|
)
|
|
79
80
|
return (cast(RPCEndpoint, method_str), params), response_formatters
|
|
80
81
|
|
|
@@ -121,8 +122,17 @@ def retrieve_async_method_call_fn(
|
|
|
121
122
|
async_w3: "AsyncWeb3",
|
|
122
123
|
module: "Module",
|
|
123
124
|
method: Method[Callable[..., Any]],
|
|
124
|
-
) -> Callable[
|
|
125
|
-
|
|
125
|
+
) -> Callable[
|
|
126
|
+
...,
|
|
127
|
+
Coroutine[
|
|
128
|
+
Any,
|
|
129
|
+
Any,
|
|
130
|
+
Optional[Union[RPCResponse, FormattedEthSubscriptionResponse, AsyncLogFilter]],
|
|
131
|
+
],
|
|
132
|
+
]:
|
|
133
|
+
async def caller(
|
|
134
|
+
*args: Any, **kwargs: Any
|
|
135
|
+
) -> Union[RPCResponse, FormattedEthSubscriptionResponse, AsyncLogFilter]:
|
|
126
136
|
try:
|
|
127
137
|
(method_str, params), response_formatters = method.process_params(
|
|
128
138
|
module, *args, **kwargs
|
|
@@ -131,31 +141,17 @@ def retrieve_async_method_call_fn(
|
|
|
131
141
|
return AsyncLogFilter(eth_module=module, filter_id=err.filter_id)
|
|
132
142
|
|
|
133
143
|
if isinstance(async_w3.provider, PersistentConnectionProvider):
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
return await async_w3.manager.socket_request(
|
|
145
|
+
cast(RPCEndpoint, method_str),
|
|
146
|
+
params,
|
|
147
|
+
response_formatters=response_formatters,
|
|
137
148
|
)
|
|
138
|
-
|
|
139
|
-
try:
|
|
140
|
-
method_str = cast(RPCEndpoint, method_str)
|
|
141
|
-
return await async_w3.manager.socket_request(method_str, params)
|
|
142
|
-
except Exception as e:
|
|
143
|
-
if (
|
|
144
|
-
cache_key is not None
|
|
145
|
-
and cache_key
|
|
146
|
-
in provider._request_processor._request_information_cache
|
|
147
|
-
):
|
|
148
|
-
provider._request_processor.pop_cached_request_information(
|
|
149
|
-
cache_key
|
|
150
|
-
)
|
|
151
|
-
raise e
|
|
152
149
|
else:
|
|
153
150
|
(
|
|
154
151
|
result_formatters,
|
|
155
152
|
error_formatters,
|
|
156
153
|
null_result_formatters,
|
|
157
154
|
) = response_formatters
|
|
158
|
-
|
|
159
155
|
result = await async_w3.manager.coro_request(
|
|
160
156
|
method_str, params, error_formatters, null_result_formatters
|
|
161
157
|
)
|