web3 7.11.1__py3-none-any.whl → 7.12.1__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 +2 -2
- ens/ens.py +2 -2
- ens/specs/.DS_Store +0 -0
- ens/utils.py +14 -3
- web3/_utils/abi.py +24 -20
- web3/_utils/batching.py +22 -68
- web3/_utils/caching/request_caching_validation.py +8 -4
- web3/_utils/decorators.py +12 -9
- web3/_utils/http_session_manager.py +18 -15
- web3/_utils/method_formatters.py +17 -24
- web3/_utils/module_testing/eth_module.py +39 -54
- web3/_utils/module_testing/web3_module.py +78 -4
- web3/_utils/validation.py +1 -1
- web3/contract/utils.py +20 -35
- web3/manager.py +108 -26
- web3/method.py +7 -7
- web3/providers/async_base.py +15 -1
- web3/providers/auto.py +28 -6
- web3/providers/base.py +18 -5
- web3/providers/ipc.py +4 -6
- web3/providers/legacy_websocket.py +4 -5
- web3/providers/persistent/persistent.py +110 -40
- web3/providers/persistent/request_processor.py +34 -51
- web3/providers/persistent/subscription_manager.py +12 -7
- web3/providers/rpc/async_rpc.py +7 -7
- web3/providers/rpc/rpc.py +6 -6
- web3/utils/subscriptions.py +7 -4
- {web3-7.11.1.dist-info → web3-7.12.1.dist-info}/METADATA +69 -56
- {web3-7.11.1.dist-info → web3-7.12.1.dist-info}/RECORD +32 -31
- {web3-7.11.1.dist-info → web3-7.12.1.dist-info}/WHEEL +1 -1
- {web3-7.11.1.dist-info → web3-7.12.1.dist-info/licenses}/LICENSE +0 -0
- {web3-7.11.1.dist-info → web3-7.12.1.dist-info}/top_level.txt +0 -0
web3/providers/async_base.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextvars
|
|
2
3
|
import itertools
|
|
3
4
|
import logging
|
|
4
5
|
from typing import (
|
|
@@ -61,6 +62,9 @@ if TYPE_CHECKING:
|
|
|
61
62
|
AsyncWeb3,
|
|
62
63
|
WebSocketProvider,
|
|
63
64
|
)
|
|
65
|
+
from web3._utils.batching import ( # noqa: F401
|
|
66
|
+
RequestBatcher,
|
|
67
|
+
)
|
|
64
68
|
from web3.providers.persistent import ( # noqa: F401
|
|
65
69
|
RequestProcessor,
|
|
66
70
|
)
|
|
@@ -94,12 +98,19 @@ class AsyncBaseProvider:
|
|
|
94
98
|
self.cache_allowed_requests = cache_allowed_requests
|
|
95
99
|
self.cacheable_requests = cacheable_requests or CACHEABLE_REQUESTS
|
|
96
100
|
self.request_cache_validation_threshold = request_cache_validation_threshold
|
|
97
|
-
|
|
101
|
+
|
|
102
|
+
self._batching_context: contextvars.ContextVar[
|
|
103
|
+
Optional["RequestBatcher[Any]"]
|
|
104
|
+
] = contextvars.ContextVar("batching_context", default=None)
|
|
98
105
|
self._batch_request_func_cache: Tuple[
|
|
99
106
|
Tuple[Middleware, ...],
|
|
100
107
|
Callable[..., Coroutine[Any, Any, Union[List[RPCResponse], RPCResponse]]],
|
|
101
108
|
] = (None, None)
|
|
102
109
|
|
|
110
|
+
@property
|
|
111
|
+
def _is_batching(self) -> bool:
|
|
112
|
+
return self._batching_context.get() is not None
|
|
113
|
+
|
|
103
114
|
async def request_func(
|
|
104
115
|
self, async_w3: "AsyncWeb3", middleware_onion: MiddlewareOnion
|
|
105
116
|
) -> Callable[..., Coroutine[Any, Any, RPCResponse]]:
|
|
@@ -240,3 +251,6 @@ class AsyncJSONBaseProvider(AsyncBaseProvider):
|
|
|
240
251
|
)
|
|
241
252
|
+ b"]"
|
|
242
253
|
)
|
|
254
|
+
|
|
255
|
+
def encode_batch_request_dicts(self, request_dicts: List[RPCRequest]) -> bytes:
|
|
256
|
+
return b"[" + b",".join(self.encode_rpc_dict(d) for d in request_dicts) + b"]"
|
web3/providers/auto.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import (
|
|
|
3
3
|
Any,
|
|
4
4
|
Callable,
|
|
5
5
|
Dict,
|
|
6
|
+
List,
|
|
6
7
|
Optional,
|
|
7
8
|
Sequence,
|
|
8
9
|
Tuple,
|
|
@@ -21,9 +22,9 @@ from web3.exceptions import (
|
|
|
21
22
|
CannotHandleRequest,
|
|
22
23
|
)
|
|
23
24
|
from web3.providers import (
|
|
24
|
-
BaseProvider,
|
|
25
25
|
HTTPProvider,
|
|
26
26
|
IPCProvider,
|
|
27
|
+
JSONBaseProvider,
|
|
27
28
|
LegacyWebSocketProvider,
|
|
28
29
|
)
|
|
29
30
|
from web3.types import (
|
|
@@ -35,7 +36,7 @@ HTTP_SCHEMES = {"http", "https"}
|
|
|
35
36
|
WS_SCHEMES = {"ws", "wss"}
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def load_provider_from_environment() ->
|
|
39
|
+
def load_provider_from_environment() -> Optional[JSONBaseProvider]:
|
|
39
40
|
uri_string = URI(os.environ.get("WEB3_PROVIDER_URI", ""))
|
|
40
41
|
if not uri_string:
|
|
41
42
|
return None
|
|
@@ -45,7 +46,7 @@ def load_provider_from_environment() -> BaseProvider:
|
|
|
45
46
|
|
|
46
47
|
def load_provider_from_uri(
|
|
47
48
|
uri_string: URI, headers: Optional[Dict[str, Tuple[str, str]]] = None
|
|
48
|
-
) ->
|
|
49
|
+
) -> JSONBaseProvider:
|
|
49
50
|
uri = urlparse(uri_string)
|
|
50
51
|
if uri.scheme == "file":
|
|
51
52
|
return IPCProvider(uri.path)
|
|
@@ -60,7 +61,7 @@ def load_provider_from_uri(
|
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
class AutoProvider(
|
|
64
|
+
class AutoProvider(JSONBaseProvider):
|
|
64
65
|
default_providers = (
|
|
65
66
|
load_provider_from_environment,
|
|
66
67
|
IPCProvider,
|
|
@@ -72,7 +73,7 @@ class AutoProvider(BaseProvider):
|
|
|
72
73
|
def __init__(
|
|
73
74
|
self,
|
|
74
75
|
potential_providers: Optional[
|
|
75
|
-
Sequence[Union[Callable[...,
|
|
76
|
+
Sequence[Union[Callable[..., JSONBaseProvider], Type[JSONBaseProvider]]]
|
|
76
77
|
] = None,
|
|
77
78
|
) -> None:
|
|
78
79
|
"""
|
|
@@ -83,6 +84,7 @@ class AutoProvider(BaseProvider):
|
|
|
83
84
|
in an attempt to find an active node. The list will default to
|
|
84
85
|
:attribute:`default_providers`.
|
|
85
86
|
"""
|
|
87
|
+
super().__init__()
|
|
86
88
|
if potential_providers:
|
|
87
89
|
self._potential_providers = potential_providers
|
|
88
90
|
else:
|
|
@@ -94,6 +96,14 @@ class AutoProvider(BaseProvider):
|
|
|
94
96
|
except OSError:
|
|
95
97
|
return self._proxy_request(method, params, use_cache=False)
|
|
96
98
|
|
|
99
|
+
def make_batch_request(
|
|
100
|
+
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
101
|
+
) -> Union[List[RPCResponse], RPCResponse]:
|
|
102
|
+
try:
|
|
103
|
+
return self._proxy_batch_request(requests)
|
|
104
|
+
except OSError:
|
|
105
|
+
return self._proxy_batch_request(requests, use_cache=False)
|
|
106
|
+
|
|
97
107
|
def is_connected(self, show_traceback: bool = False) -> bool:
|
|
98
108
|
provider = self._get_active_provider(use_cache=True)
|
|
99
109
|
return provider is not None and provider.is_connected(show_traceback)
|
|
@@ -110,7 +120,19 @@ class AutoProvider(BaseProvider):
|
|
|
110
120
|
|
|
111
121
|
return provider.make_request(method, params)
|
|
112
122
|
|
|
113
|
-
def
|
|
123
|
+
def _proxy_batch_request(
|
|
124
|
+
self, requests: List[Tuple[RPCEndpoint, Any]], use_cache: bool = True
|
|
125
|
+
) -> Union[List[RPCResponse], RPCResponse]:
|
|
126
|
+
provider = self._get_active_provider(use_cache)
|
|
127
|
+
if provider is None:
|
|
128
|
+
raise CannotHandleRequest(
|
|
129
|
+
"Could not discover provider while making batch request: "
|
|
130
|
+
f"requests:{requests}\n"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return provider.make_batch_request(requests)
|
|
134
|
+
|
|
135
|
+
def _get_active_provider(self, use_cache: bool) -> Optional[JSONBaseProvider]:
|
|
114
136
|
if use_cache and self._active_provider is not None:
|
|
115
137
|
return self._active_provider
|
|
116
138
|
|
web3/providers/base.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextvars
|
|
1
2
|
import itertools
|
|
2
3
|
import logging
|
|
3
4
|
import threading
|
|
@@ -50,6 +51,9 @@ from web3.utils import (
|
|
|
50
51
|
|
|
51
52
|
if TYPE_CHECKING:
|
|
52
53
|
from web3 import Web3 # noqa: F401
|
|
54
|
+
from web3._utils.batching import (
|
|
55
|
+
RequestBatcher,
|
|
56
|
+
)
|
|
53
57
|
|
|
54
58
|
|
|
55
59
|
class BaseProvider:
|
|
@@ -81,6 +85,20 @@ class BaseProvider:
|
|
|
81
85
|
self.cacheable_requests = cacheable_requests or CACHEABLE_REQUESTS
|
|
82
86
|
self.request_cache_validation_threshold = request_cache_validation_threshold
|
|
83
87
|
|
|
88
|
+
self._batching_context: contextvars.ContextVar[
|
|
89
|
+
Optional["RequestBatcher[Any]"]
|
|
90
|
+
] = contextvars.ContextVar("batching_context", default=None)
|
|
91
|
+
self._batch_request_func_cache: Tuple[
|
|
92
|
+
Tuple[Middleware, ...], Callable[..., Union[List[RPCResponse], RPCResponse]]
|
|
93
|
+
] = (None, None)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def _is_batching(self) -> bool:
|
|
97
|
+
"""
|
|
98
|
+
Check if the provider is currently batching requests.
|
|
99
|
+
"""
|
|
100
|
+
return self._batching_context.get() is not None
|
|
101
|
+
|
|
84
102
|
def request_func(
|
|
85
103
|
self, w3: "Web3", middleware_onion: MiddlewareOnion
|
|
86
104
|
) -> Callable[..., RPCResponse]:
|
|
@@ -120,11 +138,6 @@ class JSONBaseProvider(BaseProvider):
|
|
|
120
138
|
super().__init__(**kwargs)
|
|
121
139
|
self.request_counter = itertools.count()
|
|
122
140
|
|
|
123
|
-
self._is_batching: bool = False
|
|
124
|
-
self._batch_request_func_cache: Tuple[
|
|
125
|
-
Tuple[Middleware, ...], Callable[..., Union[List[RPCResponse], RPCResponse]]
|
|
126
|
-
] = (None, None)
|
|
127
|
-
|
|
128
141
|
def encode_rpc_request(self, method: RPCEndpoint, params: Any) -> bytes:
|
|
129
142
|
rpc_dict = {
|
|
130
143
|
"jsonrpc": "2.0",
|
web3/providers/ipc.py
CHANGED
|
@@ -30,7 +30,6 @@ from web3.types import (
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
from .._utils.batching import (
|
|
33
|
-
batching_context,
|
|
34
33
|
sort_batch_response_by_response_ids,
|
|
35
34
|
)
|
|
36
35
|
from .._utils.caching import (
|
|
@@ -60,7 +59,7 @@ def get_ipc_socket(ipc_path: str, timeout: float = 2.0) -> socket.socket:
|
|
|
60
59
|
return sock
|
|
61
60
|
|
|
62
61
|
|
|
63
|
-
class
|
|
62
|
+
class PersistentSocket:
|
|
64
63
|
sock = None
|
|
65
64
|
|
|
66
65
|
def __init__(self, ipc_path: str) -> None:
|
|
@@ -158,7 +157,7 @@ class IPCProvider(JSONBaseProvider):
|
|
|
158
157
|
|
|
159
158
|
self.timeout = timeout
|
|
160
159
|
self._lock = threading.Lock()
|
|
161
|
-
self._socket =
|
|
160
|
+
self._socket = PersistentSocket(self.ipc_path)
|
|
162
161
|
|
|
163
162
|
def __str__(self) -> str:
|
|
164
163
|
return f"<{self.__class__.__name__} {self.ipc_path}>"
|
|
@@ -197,16 +196,15 @@ class IPCProvider(JSONBaseProvider):
|
|
|
197
196
|
@handle_request_caching
|
|
198
197
|
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
199
198
|
self.logger.debug(
|
|
200
|
-
|
|
199
|
+
"Making request IPC. Path: %s, Method: %s", self.ipc_path, method
|
|
201
200
|
)
|
|
202
201
|
request = self.encode_rpc_request(method, params)
|
|
203
202
|
return self._make_request(request)
|
|
204
203
|
|
|
205
|
-
@batching_context
|
|
206
204
|
def make_batch_request(
|
|
207
205
|
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
208
206
|
) -> List[RPCResponse]:
|
|
209
|
-
self.logger.debug(
|
|
207
|
+
self.logger.debug("Making batch request IPC. Path: %s", self.ipc_path)
|
|
210
208
|
request_data = self.encode_batch_rpc_request(requests)
|
|
211
209
|
response = cast(List[RPCResponse], self._make_request(request_data))
|
|
212
210
|
return sort_batch_response_by_response_ids(response)
|
|
@@ -27,7 +27,6 @@ from websockets.legacy.client import (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
from web3._utils.batching import (
|
|
30
|
-
batching_context,
|
|
31
30
|
sort_batch_response_by_response_ids,
|
|
32
31
|
)
|
|
33
32
|
from web3._utils.caching import (
|
|
@@ -136,7 +135,7 @@ class LegacyWebSocketProvider(JSONBaseProvider):
|
|
|
136
135
|
@handle_request_caching
|
|
137
136
|
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
138
137
|
self.logger.debug(
|
|
139
|
-
|
|
138
|
+
"Making request WebSocket. URI: %s, Method: %s", self.endpoint_uri, method
|
|
140
139
|
)
|
|
141
140
|
request_data = self.encode_rpc_request(method, params)
|
|
142
141
|
future = asyncio.run_coroutine_threadsafe(
|
|
@@ -144,13 +143,13 @@ class LegacyWebSocketProvider(JSONBaseProvider):
|
|
|
144
143
|
)
|
|
145
144
|
return future.result()
|
|
146
145
|
|
|
147
|
-
@batching_context
|
|
148
146
|
def make_batch_request(
|
|
149
147
|
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
150
148
|
) -> List[RPCResponse]:
|
|
151
149
|
self.logger.debug(
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
"Making batch request WebSocket. URI: %s, Methods: %s",
|
|
151
|
+
self.endpoint_uri,
|
|
152
|
+
requests,
|
|
154
153
|
)
|
|
155
154
|
request_data = self.encode_batch_rpc_request(requests)
|
|
156
155
|
future = asyncio.run_coroutine_threadsafe(
|
|
@@ -24,7 +24,6 @@ from websockets import (
|
|
|
24
24
|
|
|
25
25
|
from web3._utils.batching import (
|
|
26
26
|
BATCH_REQUEST_ID,
|
|
27
|
-
async_batching_context,
|
|
28
27
|
sort_batch_response_by_response_ids,
|
|
29
28
|
)
|
|
30
29
|
from web3._utils.caching import (
|
|
@@ -71,16 +70,16 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
71
70
|
|
|
72
71
|
_send_func_cache: Tuple[
|
|
73
72
|
Optional[int], Optional[Callable[..., Coroutine[Any, Any, RPCRequest]]]
|
|
74
|
-
] = (
|
|
75
|
-
None,
|
|
76
|
-
None,
|
|
77
|
-
)
|
|
73
|
+
] = (None, None)
|
|
78
74
|
_recv_func_cache: Tuple[
|
|
79
75
|
Optional[int], Optional[Callable[..., Coroutine[Any, Any, RPCResponse]]]
|
|
80
|
-
] = (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
76
|
+
] = (None, None)
|
|
77
|
+
_send_batch_func_cache: Tuple[
|
|
78
|
+
Optional[int], Optional[Callable[..., Coroutine[Any, Any, List[RPCRequest]]]]
|
|
79
|
+
] = (None, None)
|
|
80
|
+
_recv_batch_func_cache: Tuple[
|
|
81
|
+
Optional[int], Optional[Callable[..., Coroutine[Any, Any, List[RPCResponse]]]]
|
|
82
|
+
] = (None, None)
|
|
84
83
|
|
|
85
84
|
def __init__(
|
|
86
85
|
self,
|
|
@@ -98,13 +97,14 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
98
97
|
request_information_cache_size=request_information_cache_size,
|
|
99
98
|
)
|
|
100
99
|
self._message_listener_task: Optional["asyncio.Task[None]"] = None
|
|
101
|
-
self._batch_request_counter: Optional[int] = None
|
|
102
100
|
self._listen_event: asyncio.Event = asyncio.Event()
|
|
103
101
|
self._max_connection_retries = max_connection_retries
|
|
104
102
|
|
|
105
103
|
self.request_timeout = request_timeout
|
|
106
104
|
self.silence_listener_task_exceptions = silence_listener_task_exceptions
|
|
107
105
|
|
|
106
|
+
# -- cached middleware request/response functions -- #
|
|
107
|
+
|
|
108
108
|
async def send_func(
|
|
109
109
|
self, async_w3: "AsyncWeb3", middleware_onion: "MiddlewareOnion"
|
|
110
110
|
) -> Callable[..., Coroutine[Any, Any, RPCRequest]]:
|
|
@@ -155,6 +155,60 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
155
155
|
|
|
156
156
|
return self._recv_func_cache[1]
|
|
157
157
|
|
|
158
|
+
async def send_batch_func(
|
|
159
|
+
self, async_w3: "AsyncWeb3", middleware_onion: "MiddlewareOnion"
|
|
160
|
+
) -> Callable[..., Coroutine[Any, Any, List[RPCRequest]]]:
|
|
161
|
+
middleware = middleware_onion.as_tuple_of_middleware()
|
|
162
|
+
cache_key = hash(tuple(id(mw) for mw in middleware))
|
|
163
|
+
|
|
164
|
+
if cache_key != self._send_batch_func_cache[0]:
|
|
165
|
+
|
|
166
|
+
async def send_func(
|
|
167
|
+
requests: List[Tuple[RPCEndpoint, Any]]
|
|
168
|
+
) -> List[RPCRequest]:
|
|
169
|
+
for mw in middleware:
|
|
170
|
+
initialized = mw(async_w3)
|
|
171
|
+
requests = [
|
|
172
|
+
await initialized.async_request_processor(method, params)
|
|
173
|
+
for (method, params) in requests
|
|
174
|
+
]
|
|
175
|
+
return await self.send_batch_request(requests)
|
|
176
|
+
|
|
177
|
+
self._send_batch_func_cache = (cache_key, send_func)
|
|
178
|
+
|
|
179
|
+
return self._send_batch_func_cache[1]
|
|
180
|
+
|
|
181
|
+
async def recv_batch_func(
|
|
182
|
+
self, async_w3: "AsyncWeb3", middleware_onion: "MiddlewareOnion"
|
|
183
|
+
) -> Callable[..., Coroutine[Any, Any, List[RPCResponse]]]:
|
|
184
|
+
middleware = middleware_onion.as_tuple_of_middleware()
|
|
185
|
+
cache_key = hash(tuple(id(mw) for mw in middleware))
|
|
186
|
+
|
|
187
|
+
if cache_key != self._recv_batch_func_cache[0]:
|
|
188
|
+
|
|
189
|
+
async def recv_function(
|
|
190
|
+
rpc_requests: List[RPCRequest],
|
|
191
|
+
) -> List[RPCResponse]:
|
|
192
|
+
methods = [rpc_request["method"] for rpc_request in rpc_requests]
|
|
193
|
+
responses = await self.recv_for_batch_request(rpc_requests)
|
|
194
|
+
for mw in reversed(middleware):
|
|
195
|
+
if not isinstance(responses, list):
|
|
196
|
+
# RPC errors return only one response with the error object
|
|
197
|
+
return responses
|
|
198
|
+
|
|
199
|
+
initialized = mw(async_w3)
|
|
200
|
+
responses = [
|
|
201
|
+
await initialized.async_response_processor(m, r)
|
|
202
|
+
for m, r in zip(methods, responses)
|
|
203
|
+
]
|
|
204
|
+
return responses
|
|
205
|
+
|
|
206
|
+
self._recv_batch_func_cache = (cache_key, recv_function)
|
|
207
|
+
|
|
208
|
+
return self._recv_batch_func_cache[1]
|
|
209
|
+
|
|
210
|
+
# -- connection management -- #
|
|
211
|
+
|
|
158
212
|
def get_endpoint_uri_or_ipc_path(self) -> str:
|
|
159
213
|
if hasattr(self, "endpoint_uri"):
|
|
160
214
|
return str(self.endpoint_uri)
|
|
@@ -167,6 +221,7 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
167
221
|
)
|
|
168
222
|
|
|
169
223
|
async def connect(self) -> None:
|
|
224
|
+
endpoint = self.get_endpoint_uri_or_ipc_path()
|
|
170
225
|
_connection_attempts = 0
|
|
171
226
|
_backoff_rate_change = 1.75
|
|
172
227
|
_backoff_time = 1.75
|
|
@@ -174,9 +229,7 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
174
229
|
while _connection_attempts != self._max_connection_retries:
|
|
175
230
|
try:
|
|
176
231
|
_connection_attempts += 1
|
|
177
|
-
self.logger.info(
|
|
178
|
-
f"Connecting to: {self.get_endpoint_uri_or_ipc_path()}"
|
|
179
|
-
)
|
|
232
|
+
self.logger.info("Connecting to: %s", endpoint)
|
|
180
233
|
await self._provider_specific_connect()
|
|
181
234
|
self._message_listener_task = asyncio.create_task(
|
|
182
235
|
self._message_listener()
|
|
@@ -184,19 +237,18 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
184
237
|
self._message_listener_task.add_done_callback(
|
|
185
238
|
self._message_listener_callback
|
|
186
239
|
)
|
|
187
|
-
self.logger.info(
|
|
188
|
-
f"Successfully connected to: {self.get_endpoint_uri_or_ipc_path()}"
|
|
189
|
-
)
|
|
240
|
+
self.logger.info("Successfully connected to: %s", endpoint)
|
|
190
241
|
break
|
|
191
242
|
except (WebSocketException, OSError) as e:
|
|
192
243
|
if _connection_attempts == self._max_connection_retries:
|
|
193
244
|
raise ProviderConnectionError(
|
|
194
|
-
f"Could not connect to: {
|
|
245
|
+
f"Could not connect to: {endpoint}. "
|
|
195
246
|
f"Retries exceeded max of {self._max_connection_retries}."
|
|
196
247
|
) from e
|
|
197
248
|
self.logger.info(
|
|
198
|
-
|
|
199
|
-
|
|
249
|
+
"Could not connect to: %s. Retrying in %s seconds.",
|
|
250
|
+
endpoint,
|
|
251
|
+
round(_backoff_time, 1),
|
|
200
252
|
exc_info=True,
|
|
201
253
|
)
|
|
202
254
|
await asyncio.sleep(_backoff_time)
|
|
@@ -217,9 +269,12 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
217
269
|
await self._provider_specific_disconnect()
|
|
218
270
|
self._request_processor.clear_caches()
|
|
219
271
|
self.logger.info(
|
|
220
|
-
|
|
272
|
+
"Successfully disconnected from: %s",
|
|
273
|
+
self.get_endpoint_uri_or_ipc_path(),
|
|
221
274
|
)
|
|
222
275
|
|
|
276
|
+
# -- request methods -- #
|
|
277
|
+
|
|
223
278
|
@async_handle_send_caching
|
|
224
279
|
async def send_request(self, method: RPCEndpoint, params: Any) -> RPCRequest:
|
|
225
280
|
request_dict = self.form_request(method, params)
|
|
@@ -238,20 +293,33 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
238
293
|
rpc_request = await self.send_request(method, params)
|
|
239
294
|
return await self.recv_for_request(rpc_request)
|
|
240
295
|
|
|
241
|
-
|
|
242
|
-
|
|
296
|
+
# -- batch requests -- #
|
|
297
|
+
|
|
298
|
+
async def send_batch_request(
|
|
243
299
|
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
244
|
-
) -> List[
|
|
245
|
-
|
|
300
|
+
) -> List[RPCRequest]:
|
|
301
|
+
request_dicts = [
|
|
302
|
+
self.form_request(method, params) for (method, params) in requests
|
|
303
|
+
]
|
|
304
|
+
request_data = self.encode_batch_request_dicts(request_dicts)
|
|
246
305
|
await self.socket_send(request_data)
|
|
306
|
+
return request_dicts
|
|
247
307
|
|
|
248
|
-
|
|
308
|
+
async def recv_for_batch_request(
|
|
309
|
+
self, _request_dicts: List[RPCRequest]
|
|
310
|
+
) -> List[RPCResponse]:
|
|
249
311
|
response = cast(
|
|
250
312
|
List[RPCResponse],
|
|
251
313
|
await self._get_response_for_request_id(BATCH_REQUEST_ID),
|
|
252
314
|
)
|
|
253
315
|
return response
|
|
254
316
|
|
|
317
|
+
async def make_batch_request(
|
|
318
|
+
self, requests: List[Tuple[RPCEndpoint, Any]]
|
|
319
|
+
) -> List[RPCResponse]:
|
|
320
|
+
request_dicts = await self.send_batch_request(requests)
|
|
321
|
+
return await self.recv_for_batch_request(request_dicts)
|
|
322
|
+
|
|
255
323
|
# -- abstract methods -- #
|
|
256
324
|
|
|
257
325
|
@abstractmethod
|
|
@@ -320,26 +388,26 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
320
388
|
Check the request response cache for any errors not tied to current requests
|
|
321
389
|
and raise them if found.
|
|
322
390
|
"""
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
response
|
|
326
|
-
|
|
327
|
-
|
|
391
|
+
for response in self._request_processor._request_response_cache._data.values():
|
|
392
|
+
if isinstance(response, dict):
|
|
393
|
+
if "id" not in response:
|
|
394
|
+
validate_rpc_response_and_raise_if_error(
|
|
395
|
+
cast(RPCResponse, response), None, logger=self.logger
|
|
396
|
+
)
|
|
397
|
+
else:
|
|
328
398
|
request = self._request_processor._request_information_cache.get_cache_entry( # noqa: E501
|
|
329
399
|
generate_cache_key(response["id"])
|
|
330
400
|
)
|
|
331
401
|
if "error" in response and request is None:
|
|
332
|
-
# if we find an error response in the cache without a
|
|
333
|
-
# corresponding request, raise the error
|
|
334
402
|
validate_rpc_response_and_raise_if_error(
|
|
335
403
|
cast(RPCResponse, response), None, logger=self.logger
|
|
336
404
|
)
|
|
337
405
|
|
|
338
406
|
async def _message_listener(self) -> None:
|
|
339
407
|
self.logger.info(
|
|
340
|
-
|
|
341
|
-
"
|
|
342
|
-
|
|
408
|
+
"%s listener background task started. Storing all messages in "
|
|
409
|
+
"appropriate request processor queues / caches to be processed.",
|
|
410
|
+
self.__class__.__qualname__,
|
|
343
411
|
)
|
|
344
412
|
while True:
|
|
345
413
|
# the use of sleep(0) seems to be the most efficient way to yield control
|
|
@@ -363,8 +431,8 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
363
431
|
self._raise_stray_errors_from_cache()
|
|
364
432
|
except PersistentConnectionClosedOK as e:
|
|
365
433
|
self.logger.info(
|
|
366
|
-
"Message listener background task has ended gracefully: "
|
|
367
|
-
|
|
434
|
+
"Message listener background task has ended gracefully: %s",
|
|
435
|
+
e.user_message,
|
|
368
436
|
)
|
|
369
437
|
# trigger a return to end the listener task and initiate the callback fn
|
|
370
438
|
return
|
|
@@ -382,8 +450,9 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
382
450
|
"""
|
|
383
451
|
self.logger.error(
|
|
384
452
|
"Exception caught in listener, error logging and keeping "
|
|
385
|
-
"listener background task alive
|
|
386
|
-
|
|
453
|
+
"listener background task alive.\n error=%s: %s",
|
|
454
|
+
e.__class__.__name__,
|
|
455
|
+
e,
|
|
387
456
|
)
|
|
388
457
|
|
|
389
458
|
def _handle_listener_task_exceptions(self) -> None:
|
|
@@ -416,7 +485,8 @@ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
|
|
|
416
485
|
|
|
417
486
|
if request_cache_key in self._request_processor._request_response_cache:
|
|
418
487
|
self.logger.debug(
|
|
419
|
-
|
|
488
|
+
"Popping response for id %s from cache.",
|
|
489
|
+
request_id,
|
|
420
490
|
)
|
|
421
491
|
popped_response = await self._request_processor.pop_raw_response(
|
|
422
492
|
cache_key=request_cache_key,
|