web3 7.0.0b5__py3-none-any.whl → 7.0.0b6__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.
- web3/_utils/batching.py +217 -0
- web3/_utils/caching.py +26 -2
- web3/_utils/compat/__init__.py +1 -0
- web3/_utils/events.py +2 -2
- web3/_utils/module_testing/eth_module.py +10 -10
- web3/_utils/module_testing/module_testing_utils.py +13 -0
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/contract/utils.py +112 -4
- web3/eth/async_eth.py +5 -4
- web3/eth/eth.py +5 -4
- 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/base.py +43 -0
- web3/module.py +47 -7
- web3/providers/async_base.py +55 -23
- web3/providers/base.py +59 -26
- web3/providers/ipc.py +23 -8
- web3/providers/legacy_websocket.py +26 -1
- 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/async_rpc.py +19 -1
- web3/providers/rpc/rpc.py +19 -1
- web3/types.py +7 -1
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/LICENSE +1 -1
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/METADATA +31 -20
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/RECORD +33 -32
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/WHEEL +0 -0
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/top_level.txt +0 -0
web3/eth/eth.py
CHANGED
|
@@ -30,6 +30,9 @@ from hexbytes import (
|
|
|
30
30
|
from web3._utils.blocks import (
|
|
31
31
|
select_method_for_block_identifier,
|
|
32
32
|
)
|
|
33
|
+
from web3._utils.compat import (
|
|
34
|
+
Unpack,
|
|
35
|
+
)
|
|
33
36
|
from web3._utils.fee_utils import (
|
|
34
37
|
fee_history_priority_fee,
|
|
35
38
|
)
|
|
@@ -596,10 +599,8 @@ class Eth(BaseEth):
|
|
|
596
599
|
current_transaction = get_required_transaction(self.w3, transaction_hash)
|
|
597
600
|
return replace_transaction(self.w3, current_transaction, new_transaction)
|
|
598
601
|
|
|
599
|
-
# todo: Update Any to stricter kwarg checking with TxParams
|
|
600
|
-
# https://github.com/python/mypy/issues/4441
|
|
601
602
|
def modify_transaction(
|
|
602
|
-
self, transaction_hash: _Hash32, **transaction_params:
|
|
603
|
+
self, transaction_hash: _Hash32, **transaction_params: Unpack[TxParams]
|
|
603
604
|
) -> HexBytes:
|
|
604
605
|
assert_valid_transaction_params(cast(TxParams, transaction_params))
|
|
605
606
|
current_transaction = get_required_transaction(self.w3, transaction_hash)
|
|
@@ -680,7 +681,7 @@ class Eth(BaseEth):
|
|
|
680
681
|
|
|
681
682
|
@overload
|
|
682
683
|
# type error: Overloaded function signatures 1 and 2 overlap with incompatible return types # noqa: E501
|
|
683
|
-
def contract(self, address: None = None, **kwargs: Any) -> Type[Contract]: # type: ignore[
|
|
684
|
+
def contract(self, address: None = None, **kwargs: Any) -> Type[Contract]: # type: ignore[overload-overlap] # noqa: E501
|
|
684
685
|
...
|
|
685
686
|
|
|
686
687
|
@overload
|
web3/exceptions.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import time
|
|
3
3
|
from typing import (
|
|
4
|
+
TYPE_CHECKING,
|
|
4
5
|
Any,
|
|
5
6
|
Dict,
|
|
6
7
|
Optional,
|
|
@@ -16,6 +17,9 @@ from web3.types import (
|
|
|
16
17
|
RPCResponse,
|
|
17
18
|
)
|
|
18
19
|
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
import asyncio
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
class Web3Exception(Exception):
|
|
21
25
|
"""
|
|
@@ -307,6 +311,22 @@ class BadResponseFormat(Web3Exception):
|
|
|
307
311
|
"""
|
|
308
312
|
|
|
309
313
|
|
|
314
|
+
class TaskNotRunning(Web3Exception):
|
|
315
|
+
"""
|
|
316
|
+
Used to signal between asyncio contexts that a task that is being awaited
|
|
317
|
+
is not currently running.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def __init__(
|
|
321
|
+
self, task: "asyncio.Task[Any]", message: Optional[str] = None
|
|
322
|
+
) -> None:
|
|
323
|
+
self.task = task
|
|
324
|
+
if message is None:
|
|
325
|
+
message = f"Task {task} is not running."
|
|
326
|
+
self.message = message
|
|
327
|
+
super().__init__(message)
|
|
328
|
+
|
|
329
|
+
|
|
310
330
|
class Web3RPCError(Web3Exception):
|
|
311
331
|
"""
|
|
312
332
|
Raised when a JSON-RPC response contains an error field.
|
|
@@ -110,11 +110,11 @@ def _aggregate_miner_data(
|
|
|
110
110
|
# types ignored b/c mypy has trouble inferring gas_prices: Sequence[Wei]
|
|
111
111
|
price_percentile = percentile(gas_prices, percentile=20) # type: ignore
|
|
112
112
|
except InsufficientData:
|
|
113
|
-
price_percentile = min(gas_prices)
|
|
113
|
+
price_percentile = min(gas_prices)
|
|
114
114
|
yield MinerData(
|
|
115
115
|
miner,
|
|
116
116
|
len(set(block_hashes)),
|
|
117
|
-
min(gas_prices),
|
|
117
|
+
min(gas_prices),
|
|
118
118
|
price_percentile,
|
|
119
119
|
)
|
|
120
120
|
|
web3/main.py
CHANGED
|
@@ -34,6 +34,7 @@ from typing import (
|
|
|
34
34
|
TYPE_CHECKING,
|
|
35
35
|
Any,
|
|
36
36
|
AsyncIterator,
|
|
37
|
+
Callable,
|
|
37
38
|
Dict,
|
|
38
39
|
Generator,
|
|
39
40
|
List,
|
|
@@ -100,6 +101,9 @@ from web3.manager import (
|
|
|
100
101
|
RequestManager as DefaultRequestManager,
|
|
101
102
|
)
|
|
102
103
|
from web3.middleware.base import MiddlewareOnion
|
|
104
|
+
from web3.method import (
|
|
105
|
+
Method,
|
|
106
|
+
)
|
|
103
107
|
from web3.module import (
|
|
104
108
|
Module,
|
|
105
109
|
)
|
|
@@ -143,7 +147,9 @@ from web3.types import (
|
|
|
143
147
|
)
|
|
144
148
|
|
|
145
149
|
if TYPE_CHECKING:
|
|
150
|
+
from web3._utils.batching import RequestBatcher # noqa: F401
|
|
146
151
|
from web3._utils.empty import Empty # noqa: F401
|
|
152
|
+
from web3.providers.persistent import PersistentConnectionProvider # noqa: F401
|
|
147
153
|
|
|
148
154
|
|
|
149
155
|
def get_async_default_modules() -> Dict[str, Union[Type[Module], Sequence[Any]]]:
|
|
@@ -336,6 +342,13 @@ class BaseWeb3:
|
|
|
336
342
|
def is_encodable(self, _type: TypeStr, value: Any) -> bool:
|
|
337
343
|
return self.codec.is_encodable(_type, value)
|
|
338
344
|
|
|
345
|
+
# -- APIs for high-level requests -- #
|
|
346
|
+
|
|
347
|
+
def batch_requests(
|
|
348
|
+
self,
|
|
349
|
+
) -> "RequestBatcher[Method[Callable[..., Any]]]":
|
|
350
|
+
return self.manager._batch_requests()
|
|
351
|
+
|
|
339
352
|
|
|
340
353
|
class Web3(BaseWeb3):
|
|
341
354
|
# mypy types
|
|
@@ -468,7 +481,7 @@ class AsyncWeb3(BaseWeb3):
|
|
|
468
481
|
new_ens.w3 = self # set self object reference for ``AsyncENS.w3``
|
|
469
482
|
self._ens = new_ens
|
|
470
483
|
|
|
471
|
-
|
|
484
|
+
# -- persistent connection methods -- #
|
|
472
485
|
|
|
473
486
|
@property
|
|
474
487
|
@persistent_connection_provider_method()
|
|
@@ -511,12 +524,11 @@ class AsyncWeb3(BaseWeb3):
|
|
|
511
524
|
"when instantiating via ``async for``."
|
|
512
525
|
)
|
|
513
526
|
async def __aiter__(self) -> AsyncIterator[Self]:
|
|
514
|
-
|
|
515
|
-
await self.provider.connect()
|
|
516
|
-
|
|
527
|
+
provider = self.provider
|
|
517
528
|
while True:
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
529
|
+
await provider.connect()
|
|
530
|
+
yield self
|
|
531
|
+
cast("PersistentConnectionProvider", provider).logger.error(
|
|
532
|
+
"Connection interrupted, attempting to reconnect..."
|
|
533
|
+
)
|
|
534
|
+
await provider.disconnect()
|
web3/manager.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
from typing import (
|
|
3
4
|
TYPE_CHECKING,
|
|
4
5
|
Any,
|
|
5
6
|
AsyncGenerator,
|
|
6
7
|
Callable,
|
|
8
|
+
Coroutine,
|
|
7
9
|
List,
|
|
8
10
|
Optional,
|
|
9
11
|
Sequence,
|
|
@@ -22,6 +24,9 @@ from websockets.exceptions import (
|
|
|
22
24
|
ConnectionClosedOK,
|
|
23
25
|
)
|
|
24
26
|
|
|
27
|
+
from web3._utils.batching import (
|
|
28
|
+
RequestBatcher,
|
|
29
|
+
)
|
|
25
30
|
from web3._utils.caching import (
|
|
26
31
|
generate_cache_key,
|
|
27
32
|
)
|
|
@@ -35,9 +40,13 @@ from web3.exceptions import (
|
|
|
35
40
|
BadResponseFormat,
|
|
36
41
|
MethodUnavailable,
|
|
37
42
|
ProviderConnectionError,
|
|
43
|
+
TaskNotRunning,
|
|
38
44
|
Web3RPCError,
|
|
39
45
|
Web3TypeError,
|
|
40
46
|
)
|
|
47
|
+
from web3.method import (
|
|
48
|
+
Method,
|
|
49
|
+
)
|
|
41
50
|
from web3.middleware import (
|
|
42
51
|
AttributeDictMiddleware,
|
|
43
52
|
BufferedGasEstimateMiddleware,
|
|
@@ -54,8 +63,12 @@ from web3.module import (
|
|
|
54
63
|
)
|
|
55
64
|
from web3.providers import (
|
|
56
65
|
AutoProvider,
|
|
66
|
+
JSONBaseProvider,
|
|
57
67
|
PersistentConnectionProvider,
|
|
58
68
|
)
|
|
69
|
+
from web3.providers.async_base import (
|
|
70
|
+
AsyncJSONBaseProvider,
|
|
71
|
+
)
|
|
59
72
|
from web3.types import (
|
|
60
73
|
RPCEndpoint,
|
|
61
74
|
RPCResponse,
|
|
@@ -375,6 +388,88 @@ class RequestManager:
|
|
|
375
388
|
response, params, error_formatters, null_result_formatters
|
|
376
389
|
)
|
|
377
390
|
|
|
391
|
+
# -- batch requests management -- #
|
|
392
|
+
|
|
393
|
+
def _batch_requests(self) -> RequestBatcher[Method[Callable[..., Any]]]:
|
|
394
|
+
"""
|
|
395
|
+
Context manager for making batch requests
|
|
396
|
+
"""
|
|
397
|
+
if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
|
|
398
|
+
raise Web3TypeError("Batch requests are not supported by this provider.")
|
|
399
|
+
return RequestBatcher(self.w3)
|
|
400
|
+
|
|
401
|
+
def _make_batch_request(
|
|
402
|
+
self, requests_info: List[Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
|
|
403
|
+
) -> List[RPCResponse]:
|
|
404
|
+
"""
|
|
405
|
+
Make a batch request using the provider
|
|
406
|
+
"""
|
|
407
|
+
provider = cast(JSONBaseProvider, self.provider)
|
|
408
|
+
request_func = provider.batch_request_func(
|
|
409
|
+
cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
410
|
+
)
|
|
411
|
+
responses = request_func(
|
|
412
|
+
[
|
|
413
|
+
(method, params)
|
|
414
|
+
for (method, params), _response_formatters in requests_info
|
|
415
|
+
]
|
|
416
|
+
)
|
|
417
|
+
formatted_responses = [
|
|
418
|
+
self._format_batched_response(info, resp)
|
|
419
|
+
for info, resp in zip(requests_info, responses)
|
|
420
|
+
]
|
|
421
|
+
return list(formatted_responses)
|
|
422
|
+
|
|
423
|
+
async def _async_make_batch_request(
|
|
424
|
+
self,
|
|
425
|
+
requests_info: List[
|
|
426
|
+
Coroutine[Any, Any, Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]]
|
|
427
|
+
],
|
|
428
|
+
) -> List[RPCResponse]:
|
|
429
|
+
"""
|
|
430
|
+
Make an asynchronous batch request using the provider
|
|
431
|
+
"""
|
|
432
|
+
provider = cast(AsyncJSONBaseProvider, self.provider)
|
|
433
|
+
request_func = await provider.batch_request_func(
|
|
434
|
+
cast("AsyncWeb3", self.w3),
|
|
435
|
+
cast("MiddlewareOnion", self.middleware_onion),
|
|
436
|
+
)
|
|
437
|
+
# since we add items to the batch without awaiting, we unpack the coroutines
|
|
438
|
+
# and await them all here
|
|
439
|
+
unpacked_requests_info = await asyncio.gather(*requests_info)
|
|
440
|
+
responses = await request_func(
|
|
441
|
+
[
|
|
442
|
+
(method, params)
|
|
443
|
+
for (method, params), _response_formatters in unpacked_requests_info
|
|
444
|
+
]
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
if isinstance(self.provider, PersistentConnectionProvider):
|
|
448
|
+
# call _process_response for each response in the batch
|
|
449
|
+
return [await self._process_response(resp) for resp in responses]
|
|
450
|
+
|
|
451
|
+
formatted_responses = [
|
|
452
|
+
self._format_batched_response(info, resp)
|
|
453
|
+
for info, resp in zip(unpacked_requests_info, responses)
|
|
454
|
+
]
|
|
455
|
+
return list(formatted_responses)
|
|
456
|
+
|
|
457
|
+
def _format_batched_response(
|
|
458
|
+
self,
|
|
459
|
+
requests_info: Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]],
|
|
460
|
+
response: RPCResponse,
|
|
461
|
+
) -> RPCResponse:
|
|
462
|
+
result_formatters, error_formatters, null_result_formatters = requests_info[1]
|
|
463
|
+
return apply_result_formatters(
|
|
464
|
+
result_formatters,
|
|
465
|
+
self.formatted_response(
|
|
466
|
+
response,
|
|
467
|
+
requests_info[0][1],
|
|
468
|
+
error_formatters,
|
|
469
|
+
null_result_formatters,
|
|
470
|
+
),
|
|
471
|
+
)
|
|
472
|
+
|
|
378
473
|
# -- persistent connection -- #
|
|
379
474
|
|
|
380
475
|
async def send(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
@@ -408,14 +503,24 @@ class RequestManager:
|
|
|
408
503
|
)
|
|
409
504
|
|
|
410
505
|
while True:
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
506
|
+
try:
|
|
507
|
+
response = await self._request_processor.pop_raw_response(
|
|
508
|
+
subscription=True
|
|
509
|
+
)
|
|
510
|
+
if (
|
|
511
|
+
response is not None
|
|
512
|
+
and response.get("params", {}).get("subscription")
|
|
513
|
+
in self._request_processor.active_subscriptions
|
|
514
|
+
):
|
|
515
|
+
# if response is an active subscription response, process it
|
|
516
|
+
yield await self._process_response(response)
|
|
517
|
+
except TaskNotRunning:
|
|
518
|
+
self._provider._handle_listener_task_exceptions()
|
|
519
|
+
self.logger.error(
|
|
520
|
+
"Message listener background task has stopped unexpectedly. "
|
|
521
|
+
"Stopping message stream."
|
|
522
|
+
)
|
|
523
|
+
raise StopAsyncIteration
|
|
419
524
|
|
|
420
525
|
async def _process_response(self, response: RPCResponse) -> RPCResponse:
|
|
421
526
|
provider = cast(PersistentConnectionProvider, self._provider)
|
web3/method.py
CHANGED
|
@@ -10,7 +10,6 @@ from typing import (
|
|
|
10
10
|
Sequence,
|
|
11
11
|
Tuple,
|
|
12
12
|
Type,
|
|
13
|
-
TypeVar,
|
|
14
13
|
Union,
|
|
15
14
|
)
|
|
16
15
|
import warnings
|
|
@@ -22,6 +21,9 @@ from eth_utils.toolz import (
|
|
|
22
21
|
pipe,
|
|
23
22
|
)
|
|
24
23
|
|
|
24
|
+
from web3._utils.batching import (
|
|
25
|
+
RPC_METHODS_UNSUPPORTED_DURING_BATCH,
|
|
26
|
+
)
|
|
25
27
|
from web3._utils.method_formatters import (
|
|
26
28
|
get_error_formatters,
|
|
27
29
|
get_null_result_formatters,
|
|
@@ -32,17 +34,22 @@ from web3._utils.rpc_abi import (
|
|
|
32
34
|
RPC,
|
|
33
35
|
)
|
|
34
36
|
from web3.exceptions import (
|
|
37
|
+
MethodNotSupported,
|
|
35
38
|
Web3TypeError,
|
|
36
39
|
Web3ValidationError,
|
|
37
40
|
Web3ValueError,
|
|
38
41
|
)
|
|
39
42
|
from web3.types import (
|
|
40
43
|
RPCEndpoint,
|
|
44
|
+
TFunc,
|
|
41
45
|
TReturn,
|
|
42
46
|
)
|
|
43
47
|
|
|
44
48
|
if TYPE_CHECKING:
|
|
45
|
-
from web3 import
|
|
49
|
+
from web3 import ( # noqa: F401
|
|
50
|
+
PersistentConnectionProvider,
|
|
51
|
+
Web3,
|
|
52
|
+
)
|
|
46
53
|
from web3.module import Module # noqa: F401
|
|
47
54
|
|
|
48
55
|
|
|
@@ -84,9 +91,6 @@ def default_root_munger(_module: "Module", *args: Any) -> List[Any]:
|
|
|
84
91
|
return [*args]
|
|
85
92
|
|
|
86
93
|
|
|
87
|
-
TFunc = TypeVar("TFunc", bound=Callable[..., Any])
|
|
88
|
-
|
|
89
|
-
|
|
90
94
|
class Method(Generic[TFunc]):
|
|
91
95
|
"""
|
|
92
96
|
Method object for web3 module methods
|
|
@@ -149,15 +153,31 @@ class Method(Generic[TFunc]):
|
|
|
149
153
|
self.is_property = is_property
|
|
150
154
|
|
|
151
155
|
def __get__(
|
|
152
|
-
self,
|
|
156
|
+
self,
|
|
157
|
+
module: Optional["Module"] = None,
|
|
158
|
+
_type: Optional[Type["Module"]] = None,
|
|
153
159
|
) -> TFunc:
|
|
154
|
-
|
|
160
|
+
self._module = module
|
|
161
|
+
if module is None:
|
|
155
162
|
raise Web3TypeError(
|
|
156
163
|
"Direct calls to methods are not supported. "
|
|
157
|
-
"Methods must be called from
|
|
164
|
+
"Methods must be called from a module instance, "
|
|
158
165
|
"usually attached to a web3 instance."
|
|
159
166
|
)
|
|
160
|
-
|
|
167
|
+
|
|
168
|
+
provider = module.w3.provider
|
|
169
|
+
if hasattr(provider, "_is_batching") and provider._is_batching:
|
|
170
|
+
if self.json_rpc_method in RPC_METHODS_UNSUPPORTED_DURING_BATCH:
|
|
171
|
+
raise MethodNotSupported(
|
|
172
|
+
f"Method `{self.json_rpc_method}` is not supported within a batch "
|
|
173
|
+
"request."
|
|
174
|
+
)
|
|
175
|
+
return module.retrieve_request_information(self)
|
|
176
|
+
else:
|
|
177
|
+
return module.retrieve_caller_fn(self)
|
|
178
|
+
|
|
179
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
180
|
+
return self.__get__(self._module)(*args, **kwargs)
|
|
161
181
|
|
|
162
182
|
@property
|
|
163
183
|
def method_selector_fn(
|
web3/middleware/base.py
CHANGED
|
@@ -4,6 +4,8 @@ from abc import (
|
|
|
4
4
|
from typing import (
|
|
5
5
|
TYPE_CHECKING,
|
|
6
6
|
Any,
|
|
7
|
+
List,
|
|
8
|
+
Tuple,
|
|
7
9
|
Type,
|
|
8
10
|
Union,
|
|
9
11
|
)
|
|
@@ -18,7 +20,9 @@ if TYPE_CHECKING:
|
|
|
18
20
|
Web3,
|
|
19
21
|
)
|
|
20
22
|
from web3.types import ( # noqa: F401
|
|
23
|
+
AsyncMakeBatchRequestFn,
|
|
21
24
|
AsyncMakeRequestFn,
|
|
25
|
+
MakeBatchRequestFn,
|
|
22
26
|
MakeRequestFn,
|
|
23
27
|
RPCEndpoint,
|
|
24
28
|
RPCResponse,
|
|
@@ -45,6 +49,25 @@ class Web3Middleware:
|
|
|
45
49
|
|
|
46
50
|
return middleware
|
|
47
51
|
|
|
52
|
+
def wrap_make_batch_request(
|
|
53
|
+
self, make_batch_request: "MakeBatchRequestFn"
|
|
54
|
+
) -> "MakeBatchRequestFn":
|
|
55
|
+
def middleware(
|
|
56
|
+
requests_info: List[Tuple["RPCEndpoint", Any]]
|
|
57
|
+
) -> List["RPCResponse"]:
|
|
58
|
+
req_processed = [
|
|
59
|
+
self.request_processor(method, params)
|
|
60
|
+
for (method, params) in requests_info
|
|
61
|
+
]
|
|
62
|
+
responses = make_batch_request(req_processed)
|
|
63
|
+
methods, _params = zip(*req_processed)
|
|
64
|
+
formatted_responses = [
|
|
65
|
+
self.response_processor(m, r) for m, r in zip(methods, responses)
|
|
66
|
+
]
|
|
67
|
+
return formatted_responses
|
|
68
|
+
|
|
69
|
+
return middleware
|
|
70
|
+
|
|
48
71
|
def request_processor(self, method: "RPCEndpoint", params: Any) -> Any:
|
|
49
72
|
return method, params
|
|
50
73
|
|
|
@@ -67,6 +90,26 @@ class Web3Middleware:
|
|
|
67
90
|
|
|
68
91
|
return middleware
|
|
69
92
|
|
|
93
|
+
async def async_wrap_make_batch_request(
|
|
94
|
+
self, make_batch_request: "AsyncMakeBatchRequestFn"
|
|
95
|
+
) -> "AsyncMakeBatchRequestFn":
|
|
96
|
+
async def middleware(
|
|
97
|
+
requests_info: List[Tuple["RPCEndpoint", Any]]
|
|
98
|
+
) -> List["RPCResponse"]:
|
|
99
|
+
req_processed = [
|
|
100
|
+
await self.async_request_processor(method, params)
|
|
101
|
+
for (method, params) in requests_info
|
|
102
|
+
]
|
|
103
|
+
responses = await make_batch_request(req_processed)
|
|
104
|
+
methods, _params = zip(*req_processed)
|
|
105
|
+
formatted_responses = [
|
|
106
|
+
await self.async_response_processor(m, r)
|
|
107
|
+
for m, r in zip(methods, responses)
|
|
108
|
+
]
|
|
109
|
+
return formatted_responses
|
|
110
|
+
|
|
111
|
+
return middleware
|
|
112
|
+
|
|
70
113
|
async def async_request_processor(
|
|
71
114
|
self,
|
|
72
115
|
method: "RPCEndpoint",
|
web3/module.py
CHANGED
|
@@ -5,6 +5,8 @@ from typing import (
|
|
|
5
5
|
Coroutine,
|
|
6
6
|
Dict,
|
|
7
7
|
Optional,
|
|
8
|
+
Sequence,
|
|
9
|
+
Tuple,
|
|
8
10
|
TypeVar,
|
|
9
11
|
Union,
|
|
10
12
|
cast,
|
|
@@ -55,9 +57,43 @@ def apply_result_formatters(
|
|
|
55
57
|
TReturn = TypeVar("TReturn")
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
@curry
|
|
61
|
+
def retrieve_request_information_for_batching(
|
|
62
|
+
w3: Union["AsyncWeb3", "Web3"],
|
|
63
|
+
module: "Module",
|
|
64
|
+
method: Method[Callable[..., Any]],
|
|
65
|
+
) -> Union[
|
|
66
|
+
Callable[..., Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]]],
|
|
67
|
+
Callable[..., Coroutine[Any, Any, Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]]]],
|
|
68
|
+
]:
|
|
69
|
+
async def async_inner(
|
|
70
|
+
*args: Any, **kwargs: Any
|
|
71
|
+
) -> Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]]:
|
|
72
|
+
(method_str, params), response_formatters = method.process_params(
|
|
73
|
+
module, *args, **kwargs
|
|
74
|
+
)
|
|
75
|
+
if isinstance(w3.provider, PersistentConnectionProvider):
|
|
76
|
+
w3.provider._request_processor.cache_request_information(
|
|
77
|
+
cast(RPCEndpoint, method_str), params, response_formatters
|
|
78
|
+
)
|
|
79
|
+
return (cast(RPCEndpoint, method_str), params), response_formatters
|
|
80
|
+
|
|
81
|
+
def inner(
|
|
82
|
+
*args: Any, **kwargs: Any
|
|
83
|
+
) -> Tuple[Tuple[RPCEndpoint, Any], Sequence[Any]]:
|
|
84
|
+
(method_str, params), response_formatters = method.process_params(
|
|
85
|
+
module, *args, **kwargs
|
|
86
|
+
)
|
|
87
|
+
return (cast(RPCEndpoint, method_str), params), response_formatters
|
|
88
|
+
|
|
89
|
+
return async_inner if module.is_async else inner
|
|
90
|
+
|
|
91
|
+
|
|
58
92
|
@curry
|
|
59
93
|
def retrieve_blocking_method_call_fn(
|
|
60
|
-
w3: "Web3",
|
|
94
|
+
w3: "Web3",
|
|
95
|
+
module: "Module",
|
|
96
|
+
method: Method[Callable[..., TReturn]],
|
|
61
97
|
) -> Callable[..., Union[TReturn, LogFilter]]:
|
|
62
98
|
def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]:
|
|
63
99
|
try:
|
|
@@ -82,7 +118,9 @@ def retrieve_blocking_method_call_fn(
|
|
|
82
118
|
|
|
83
119
|
@curry
|
|
84
120
|
def retrieve_async_method_call_fn(
|
|
85
|
-
async_w3: "AsyncWeb3",
|
|
121
|
+
async_w3: "AsyncWeb3",
|
|
122
|
+
module: "Module",
|
|
123
|
+
method: Method[Callable[..., Any]],
|
|
86
124
|
) -> Callable[..., Coroutine[Any, Any, Optional[Union[RPCResponse, AsyncLogFilter]]]]:
|
|
87
125
|
async def caller(*args: Any, **kwargs: Any) -> Union[RPCResponse, AsyncLogFilter]:
|
|
88
126
|
try:
|
|
@@ -93,12 +131,11 @@ def retrieve_async_method_call_fn(
|
|
|
93
131
|
return AsyncLogFilter(eth_module=module, filter_id=err.filter_id)
|
|
94
132
|
|
|
95
133
|
if isinstance(async_w3.provider, PersistentConnectionProvider):
|
|
96
|
-
# TODO: The typing does not seem to be correct for response_formatters.
|
|
97
|
-
# For now, keep the expected typing but ignore it here.
|
|
98
134
|
provider = async_w3.provider
|
|
99
135
|
cache_key = provider._request_processor.cache_request_information(
|
|
100
|
-
cast(RPCEndpoint, method_str), params, response_formatters
|
|
136
|
+
cast(RPCEndpoint, method_str), params, response_formatters
|
|
101
137
|
)
|
|
138
|
+
|
|
102
139
|
try:
|
|
103
140
|
method_str = cast(RPCEndpoint, method_str)
|
|
104
141
|
return await async_w3.manager.send(method_str, params)
|
|
@@ -139,6 +176,9 @@ class Module:
|
|
|
139
176
|
self.retrieve_caller_fn = retrieve_async_method_call_fn(w3, self)
|
|
140
177
|
else:
|
|
141
178
|
self.retrieve_caller_fn = retrieve_blocking_method_call_fn(w3, self)
|
|
179
|
+
self.retrieve_request_information = retrieve_request_information_for_batching(
|
|
180
|
+
w3, self
|
|
181
|
+
)
|
|
142
182
|
self.w3 = w3
|
|
143
183
|
|
|
144
184
|
@property
|
|
@@ -152,8 +192,8 @@ class Module:
|
|
|
152
192
|
) -> None:
|
|
153
193
|
for method_name, method_class in methods.items():
|
|
154
194
|
klass = (
|
|
155
|
-
method_class.__get__(
|
|
195
|
+
method_class.__get__(module=self)()
|
|
156
196
|
if method_class.is_property
|
|
157
|
-
else method_class.__get__(
|
|
197
|
+
else method_class.__get__(module=self)
|
|
158
198
|
)
|
|
159
199
|
setattr(self, method_name, klass)
|