Skip to content

Commit 82a2fd3

Browse files
authored
retry cases where the error is an rpc timeout (#15)
* retry cases where the error is an rpc timeout * use forked version of eth-brownie, update python version to 3.9.16 * increment minor version
1 parent 6bb74ca commit 82a2fd3

7 files changed

+35
-6
lines changed

.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.1.0
2+
current_version = 0.2.0
33
commit = True
44
tag = True
55
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<stage>[^.]*)\.(?P<devnum>\d+))?

.python-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.9.0
1+
3.9.16

requirements-dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
black == 22.10.0
22
bumpversion == 0.6.0
33
isort == 5.10.1
4-
eth-brownie == 1.19.2
4+
eth-brownie @ git+https://github.com/stakefish/brownie.git@v1.19.3-forked
55
pytest-asyncio == 0.20.3
66
pytest-mock == 3.10.0
77
twine == 4.0.2

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
web3==5.31.1
1+
web3==5.31.3
22
tenacity==8.1.0

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = stakefish-web3-utils
3-
version = 0.1.0
3+
version = 0.2.0
44
description = Stakefish’s web3 utils for Python
55
author = Michal Baranowski <[email protected]>, Mateusz Sokola <[email protected]>
66
url = https://github.com/stakefish/web3-utils

tests/test_retryable_eth_module.py

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from web3.eth import Eth
1111
from web3.exceptions import BlockNotFound, TransactionNotFound
1212
from web3.main import get_default_modules, Web3
13+
from web3.types import RPCError
1314

1415
from web3_utils.retryable_eth_module import get_retryable_eth_module
1516

@@ -141,3 +142,15 @@ def test_stop_retry_on_shutdown(mocker: MockerFixture, event_loop):
141142
web3.eth.get_block(123)
142143

143144
mocked_fn.assert_called()
145+
146+
147+
def test_rpc_timeout_error_retry(mocker):
148+
rpc_timeout_error = RPCError(code=-32603, message="request failed or timed out")
149+
mocked_fn: MagicMock = mocker.patch(
150+
"web3.module.retrieve_blocking_method_call_fn",
151+
return_value=trigger_fake_error(error_to_raise=ValueError(rpc_timeout_error)),
152+
)
153+
154+
web3 = retryable_web3()
155+
web3.eth.get_block(123)
156+
mocked_fn.assert_called()

web3_utils/retryable_eth_module.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22
import asyncio
3+
from http import HTTPStatus
34
import logging
45
import time
56
from typing import Type
@@ -50,6 +51,20 @@ def before(retry_state: "RetryCallState"):
5051
retry_state.start_time = time.monotonic()
5152

5253

54+
def is_retryable_http_error(e) -> bool:
55+
"""Check for retryable HTTP status codes"""
56+
return isinstance(e, HTTPError) and e.response.status_code in [
57+
HTTPStatus.TOO_MANY_REQUESTS,
58+
HTTPStatus.BAD_GATEWAY,
59+
HTTPStatus.GATEWAY_TIMEOUT,
60+
]
61+
62+
63+
def is_timeout_value_error(e) -> bool:
64+
"""Check if a ValueError is due to an rpc request timeout."""
65+
return isinstance(e, ValueError) and "request failed or timed out" in str(e)
66+
67+
5368
def get_retryable_eth_module(base: Type[Eth] | Type[AsyncEth], logger: logging.Logger, retry_stop: typing.Callable or None = None):
5469
class RetryableModule(base):
5570
def __getattribute__(self, name):
@@ -63,7 +78,8 @@ def __getattribute__(self, name):
6378
retry_if_exception_type(
6479
(BlockNotFound, TransactionNotFound, ConnectionError, ClientConnectorError, asyncio.TimeoutError)
6580
),
66-
retry_if_exception(lambda e: isinstance(e, HTTPError) and e.response.status_code in [429, 502, 504]),
81+
retry_if_exception(is_retryable_http_error),
82+
retry_if_exception(is_timeout_value_error),
6783
),
6884
wait=wait_fixed(5),
6985
reraise=True,

0 commit comments

Comments
 (0)