Skip to content

Commit d7295c6

Browse files
committed
bugfix: Fix batching for AutoProvider
- We can't set the provider, we must let `AutoProvider` decide when it makes proxy calls using the "active" provider. - For the reason above, we need to implement a proxy batch request so that it can instead pull the active provider and make the batch request using it. - Make sure current tests fail with the old implementation and pass with the new one. closes #3711
1 parent e18f6e9 commit d7295c6

File tree

5 files changed

+56
-13
lines changed

5 files changed

+56
-13
lines changed

newsfragments/3712.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ``AutoProvider`` batching setup by adding a proxy batch request.

tests/integration/go_ethereum/test_goethereum_http.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from web3 import (
99
AsyncWeb3,
10+
AutoProvider,
1011
Web3,
1112
)
1213
from web3._utils.module_testing.go_ethereum_admin_module import (
@@ -102,8 +103,17 @@ class TestGoEthereumDebugModuleTest(GoEthereumDebugModuleTest):
102103

103104
class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest):
104105
def test_auto_provider_batching(self, auto_w3: "Web3") -> None:
105-
# test that batch_requests doesn't error out when using the auto provider
106-
auto_w3.batch_requests()
106+
with auto_w3.batch_requests() as batch:
107+
assert isinstance(auto_w3.provider, AutoProvider)
108+
assert auto_w3.provider._is_batching
109+
assert auto_w3.provider._batching_context is not None
110+
batch.add(auto_w3.eth.get_block("latest"))
111+
batch.add(auto_w3.eth.get_block("earliest"))
112+
batch.add(auto_w3.eth.get_block("pending"))
113+
results = batch.execute()
114+
115+
assert not auto_w3.provider._is_batching
116+
assert len(results) == 3
107117

108118

109119
class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest):

tests/integration/go_ethereum/test_goethereum_ipc.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from web3 import (
88
AsyncIPCProvider,
99
AsyncWeb3,
10+
AutoProvider,
1011
Web3,
1112
)
1213
from web3._utils.module_testing.persistent_connection_provider import (
@@ -81,8 +82,17 @@ class TestGoEthereumDebugModuleTest(GoEthereumDebugModuleTest):
8182

8283
class TestGoEthereumEthModuleTest(GoEthereumEthModuleTest):
8384
def test_auto_provider_batching(self, auto_w3: "Web3") -> None:
84-
# test that batch_requests doesn't error out when using the auto provider
85-
auto_w3.batch_requests()
85+
with auto_w3.batch_requests() as batch:
86+
assert isinstance(auto_w3.provider, AutoProvider)
87+
assert auto_w3.provider._is_batching
88+
assert auto_w3.provider._batching_context is not None
89+
batch.add(auto_w3.eth.get_block("latest"))
90+
batch.add(auto_w3.eth.get_block("earliest"))
91+
batch.add(auto_w3.eth.get_block("pending"))
92+
results = batch.execute()
93+
94+
assert not auto_w3.provider._is_batching
95+
assert len(results) == 3
8696

8797

8898
class TestGoEthereumNetModuleTest(GoEthereumNetModuleTest):

web3/manager.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,9 @@ def _batch_requests(self) -> RequestBatcher[Method[Callable[..., Any]]]:
254254
"""
255255
Context manager for making batch requests
256256
"""
257-
if isinstance(self.provider, AutoProvider):
258-
self.provider = self.provider._get_active_provider(use_cache=True)
259-
if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
257+
if not isinstance(
258+
self.provider, (AsyncJSONBaseProvider, JSONBaseProvider, AutoProvider)
259+
):
260260
raise Web3TypeError("Batch requests are not supported by this provider.")
261261
return RequestBatcher(self.w3)
262262

web3/providers/auto.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Any,
44
Callable,
55
Dict,
6+
List,
67
Optional,
78
Sequence,
89
Tuple,
@@ -21,9 +22,9 @@
2122
CannotHandleRequest,
2223
)
2324
from web3.providers import (
24-
BaseProvider,
2525
HTTPProvider,
2626
IPCProvider,
27+
JSONBaseProvider,
2728
LegacyWebSocketProvider,
2829
)
2930
from web3.types import (
@@ -35,7 +36,7 @@
3536
WS_SCHEMES = {"ws", "wss"}
3637

3738

38-
def load_provider_from_environment() -> BaseProvider:
39+
def load_provider_from_environment() -> Optional[JSONBaseProvider]:
3940
uri_string = URI(os.environ.get("WEB3_PROVIDER_URI", ""))
4041
if not uri_string:
4142
return None
@@ -45,7 +46,7 @@ def load_provider_from_environment() -> BaseProvider:
4546

4647
def load_provider_from_uri(
4748
uri_string: URI, headers: Optional[Dict[str, Tuple[str, str]]] = None
48-
) -> BaseProvider:
49+
) -> JSONBaseProvider:
4950
uri = urlparse(uri_string)
5051
if uri.scheme == "file":
5152
return IPCProvider(uri.path)
@@ -60,7 +61,7 @@ def load_provider_from_uri(
6061
)
6162

6263

63-
class AutoProvider(BaseProvider):
64+
class AutoProvider(JSONBaseProvider):
6465
default_providers = (
6566
load_provider_from_environment,
6667
IPCProvider,
@@ -72,7 +73,7 @@ class AutoProvider(BaseProvider):
7273
def __init__(
7374
self,
7475
potential_providers: Optional[
75-
Sequence[Union[Callable[..., BaseProvider], Type[BaseProvider]]]
76+
Sequence[Union[Callable[..., JSONBaseProvider], Type[JSONBaseProvider]]]
7677
] = None,
7778
) -> None:
7879
"""
@@ -83,6 +84,7 @@ def __init__(
8384
in an attempt to find an active node. The list will default to
8485
:attribute:`default_providers`.
8586
"""
87+
super().__init__()
8688
if potential_providers:
8789
self._potential_providers = potential_providers
8890
else:
@@ -94,6 +96,14 @@ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
9496
except OSError:
9597
return self._proxy_request(method, params, use_cache=False)
9698

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+
97107
def is_connected(self, show_traceback: bool = False) -> bool:
98108
provider = self._get_active_provider(use_cache=True)
99109
return provider is not None and provider.is_connected(show_traceback)
@@ -110,7 +120,19 @@ def _proxy_request(
110120

111121
return provider.make_request(method, params)
112122

113-
def _get_active_provider(self, use_cache: bool) -> Optional[BaseProvider]:
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]:
114136
if use_cache and self._active_provider is not None:
115137
return self._active_provider
116138

0 commit comments

Comments
 (0)