This repository was archived by the owner on Aug 8, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 154
drop python<=3.7 support #219
Open
kloczek
wants to merge
4
commits into
litl:master
Choose a base branch
from
kloczek:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
According to https://endoflife.date/python python 3.7 has been EOSed 27 Jun 2023. Filter all code over `pyupgracde --py38-plus`. Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
from whole patch generated by --- a/backoff/_wait_gen.py
+++ b/backoff/_wait_gen.py
@@ -71,8 +69,7 @@
else:
itr = iter(interval) # type: ignore
- for val in itr:
- yield val
+ yield from itr
def runtime( Because after that pytest fails with: pytest output:+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-backoff-2.2.1-2.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-backoff-2.2.1-2.fc37.x86_64/usr/lib/python3.10/site-packages
+ /usr/bin/pytest -ra -m 'not network'
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.10.14, pytest-8.2.2, pluggy-1.5.0
rootdir: /home/tkloczko/rpmbuild/BUILD/backoff-2.2.1
configfile: pyproject.toml
plugins: asyncio-0.23.8
asyncio: mode=strict
collected 119 items
tests/test_backoff.py ........F..FFFFF..FFFF..F....................................................... [ 67%]
tests/test_backoff_async.py .......F..FFFFFF..FFFFF.... [ 89%]
tests/test_integration.py .. [ 91%]
tests/test_jitter.py . [ 92%]
tests/test_wait_gen.py ......... [100%]
========================================================================================= FAILURES ==========================================================================================
____________________________________________________________________________ test_on_exception_constant_iterable ____________________________________________________________________________
@backoff.on_exception(
backoff.constant,
KeyError,
interval=(1, 2, 3),
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
)
def endless_exceptions():
> raise KeyError('foo')
E KeyError: 'foo'
tests/test_backoff.py:205: KeyError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29997f10>
def test_on_exception_constant_iterable(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
backoffs = []
giveups = []
successes = []
@backoff.on_exception(
backoff.constant,
KeyError,
interval=(1, 2, 3),
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
)
def endless_exceptions():
raise KeyError('foo')
with pytest.raises(KeyError):
> endless_exceptions()
tests/test_backoff.py:208:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = (1, 2, 3)
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'tuple_iterator' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off endless_exceptions(...) for 0.8s (KeyError: 'foo')
_________________________________________________________________________________ test_on_exception_success _________________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
Exception,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=None,
interval=0)
@_save_target
def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(backoffs) < 2:
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff.py:289: ValueError
During handling of the above exception, another exception occurred:
def test_on_exception_success():
backoffs, giveups, successes = [], [], []
@backoff.on_exception(backoff.constant,
Exception,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=None,
interval=0)
@_save_target
def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(backoffs) < 2:
raise ValueError("catch me")
> succeeder(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:291:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off succeeder(...) for 0.0s (ValueError: catch me)
______________________________________________________________________________ test_on_exception_giveup[True] _______________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
ValueError,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
max_tries=3,
jitter=None,
raise_on_giveup=raise_on_giveup,
interval=0)
@_save_target
def exceptor(*args, **kwargs):
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff.py:334: ValueError
During handling of the above exception, another exception occurred:
raise_on_giveup = True
@pytest.mark.parametrize('raise_on_giveup', [True, False])
def test_on_exception_giveup(raise_on_giveup):
backoffs, giveups, successes = [], [], []
@backoff.on_exception(backoff.constant,
ValueError,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
max_tries=3,
jitter=None,
raise_on_giveup=raise_on_giveup,
interval=0)
@_save_target
def exceptor(*args, **kwargs):
raise ValueError("catch me")
if raise_on_giveup:
with pytest.raises(ValueError):
> exceptor(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:338:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.0s (ValueError: catch me)
______________________________________________________________________________ test_on_exception_giveup[False] ______________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
ValueError,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
max_tries=3,
jitter=None,
raise_on_giveup=raise_on_giveup,
interval=0)
@_save_target
def exceptor(*args, **kwargs):
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff.py:334: ValueError
During handling of the above exception, another exception occurred:
raise_on_giveup = False
@pytest.mark.parametrize('raise_on_giveup', [True, False])
def test_on_exception_giveup(raise_on_giveup):
backoffs, giveups, successes = [], [], []
@backoff.on_exception(backoff.constant,
ValueError,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
max_tries=3,
jitter=None,
raise_on_giveup=raise_on_giveup,
interval=0)
@_save_target
def exceptor(*args, **kwargs):
raise ValueError("catch me")
if raise_on_giveup:
with pytest.raises(ValueError):
exceptor(1, 2, 3, foo=1, bar=2)
else:
> exceptor(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:340:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.0s (ValueError: catch me)
____________________________________________________________________________ test_on_exception_giveup_predicate _____________________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
def foo_bar_baz():
> raise ValueError(vals.pop())
E ValueError: bar
tests/test_backoff.py:370: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f298b3940>
def test_on_exception_giveup_predicate(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
def on_baz(e):
return str(e) == "baz"
vals = ["baz", "bar", "foo"]
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
def foo_bar_baz():
raise ValueError(vals.pop())
with pytest.raises(ValueError):
> foo_bar_baz()
tests/test_backoff.py:373:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off foo_bar_baz(...) for 0.1s (ValueError: foo)
_________________________________________________________________________________ test_on_predicate_success _________________________________________________________________________________
def test_on_predicate_success():
backoffs, giveups, successes = [], [], []
@backoff.on_predicate(backoff.constant,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=None,
interval=0)
@_save_target
def success(*args, **kwargs):
# succeed after we've backed off twice
return len(backoffs) == 2
> success(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:392:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:58: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off success(...) for 0.0s (False)
__________________________________________________________________________ test_on_exception_success_0_arg_jitter ___________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
Exception,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=random.random,
interval=0)
@_save_target
def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(backoffs) < 2:
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff.py:510: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f298730d0>
def test_on_exception_success_0_arg_jitter(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
monkeypatch.setattr('random.random', lambda: 0)
backoffs, giveups, successes = [], [], []
@backoff.on_exception(backoff.constant,
Exception,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=random.random,
interval=0)
@_save_target
def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(backoffs) < 2:
raise ValueError("catch me")
with pytest.deprecated_call():
> succeeder(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:513:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off succeeder(...) for 0.0s (ValueError: catch me)
__________________________________________________________________________ test_on_predicate_success_0_arg_jitter ___________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29808460>
def test_on_predicate_success_0_arg_jitter(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
monkeypatch.setattr('random.random', lambda: 0)
backoffs, giveups, successes = [], [], []
@backoff.on_predicate(backoff.constant,
on_success=successes.append,
on_backoff=backoffs.append,
on_giveup=giveups.append,
jitter=random.random,
interval=0)
@_save_target
def success(*args, **kwargs):
# succeed after we've backed off twice
return len(backoffs) == 2
with pytest.deprecated_call():
> success(1, 2, 3, foo=1, bar=2)
tests/test_backoff.py:561:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:58: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off success(...) for 0.0s (False)
___________________________________________________________________________ test_on_exception_callable_max_tries ____________________________________________________________________________
@backoff.on_exception(backoff.constant, ValueError, max_tries=lambda: 3)
def exceptor():
log.append(True)
> raise ValueError()
E ValueError
tests/test_backoff.py:598: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f298596f0>
def test_on_exception_callable_max_tries(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
log = []
@backoff.on_exception(backoff.constant, ValueError, max_tries=lambda: 3)
def exceptor():
log.append(True)
raise ValueError()
with pytest.raises(ValueError):
> exceptor()
tests/test_backoff.py:601:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.9s (ValueError)
___________________________________________________________________ test_on_exception_callable_max_tries_reads_every_time ___________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
def exceptor():
> raise ValueError()
E ValueError
tests/test_backoff.py:619: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f298b3d90>
def test_on_exception_callable_max_tries_reads_every_time(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
lookups = []
def lookup_max_tries():
lookups.append(True)
return 3
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
def exceptor():
raise ValueError()
with pytest.raises(ValueError):
> exceptor()
tests/test_backoff.py:622:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:117: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.5s (ValueError)
____________________________________________________________________________ test_on_predicate_constant_iterable ____________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29820a30>
def test_on_predicate_constant_iterable(monkeypatch):
monkeypatch.setattr('time.sleep', lambda x: None)
waits = [1, 2, 3, 6, 9]
backoffs = []
giveups = []
successes = []
@backoff.on_predicate(
backoff.constant,
interval=waits,
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
jitter=None,
)
def falsey():
return False
> assert not falsey()
tests/test_backoff.py:704:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_sync.py:58: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = [1, 2, 3, 6, 9]
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'list_iterator' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off falsey(...) for 1.0s (False)
____________________________________________________________________________ test_on_exception_constant_iterable ____________________________________________________________________________
@backoff.on_exception(
backoff.constant,
KeyError,
interval=(1, 2, 3),
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
)
async def endless_exceptions():
> raise KeyError('foo')
E KeyError: 'foo'
tests/test_backoff_async.py:157: KeyError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29997d60>
@pytest.mark.asyncio
async def test_on_exception_constant_iterable(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
backoffs = []
giveups = []
successes = []
@backoff.on_exception(
backoff.constant,
KeyError,
interval=(1, 2, 3),
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
)
async def endless_exceptions():
raise KeyError('foo')
with pytest.raises(KeyError):
> await endless_exceptions()
tests/test_backoff_async.py:160:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = (1, 2, 3)
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'tuple_iterator' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off endless_exceptions(...) for 0.1s (KeyError: 'foo')
Backing off endless_exceptions(...) for 0.1s (KeyError: 'foo')
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off endless_exceptions(...) for 0.1s (KeyError: 'foo')
_________________________________________________________________________________ test_on_exception_success _________________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
Exception,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=None,
interval=0)
@_save_target
async def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(log['backoff']) < 2:
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff_async.py:244: ValueError
During handling of the above exception, another exception occurred:
@pytest.mark.asyncio
async def test_on_exception_success():
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_exception(backoff.constant,
Exception,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=None,
interval=0)
@_save_target
async def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(log['backoff']) < 2:
raise ValueError("catch me")
> await succeeder(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:246:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off succeeder(...) for 0.0s (ValueError: catch me)
Backing off succeeder(...) for 0.0s (ValueError: catch me)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off succeeder(...) for 0.0s (ValueError: catch me)
______________________________________________________________________________ test_on_exception_giveup[True] _______________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
ValueError,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
raise_on_giveup=raise_on_giveup,
max_tries=3,
jitter=None,
interval=0)
@_save_target
async def exceptor(*args, **kwargs):
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff_async.py:290: ValueError
During handling of the above exception, another exception occurred:
raise_on_giveup = True
@pytest.mark.asyncio
@pytest.mark.parametrize('raise_on_giveup', [True, False])
async def test_on_exception_giveup(raise_on_giveup):
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_exception(backoff.constant,
ValueError,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
raise_on_giveup=raise_on_giveup,
max_tries=3,
jitter=None,
interval=0)
@_save_target
async def exceptor(*args, **kwargs):
raise ValueError("catch me")
if raise_on_giveup:
with pytest.raises(ValueError):
> await exceptor(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:294:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off exceptor(...) for 0.0s (ValueError: catch me)
Backing off exceptor(...) for 0.0s (ValueError: catch me)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.0s (ValueError: catch me)
______________________________________________________________________________ test_on_exception_giveup[False] ______________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
ValueError,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
raise_on_giveup=raise_on_giveup,
max_tries=3,
jitter=None,
interval=0)
@_save_target
async def exceptor(*args, **kwargs):
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff_async.py:290: ValueError
During handling of the above exception, another exception occurred:
raise_on_giveup = False
@pytest.mark.asyncio
@pytest.mark.parametrize('raise_on_giveup', [True, False])
async def test_on_exception_giveup(raise_on_giveup):
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_exception(backoff.constant,
ValueError,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
raise_on_giveup=raise_on_giveup,
max_tries=3,
jitter=None,
interval=0)
@_save_target
async def exceptor(*args, **kwargs):
raise ValueError("catch me")
if raise_on_giveup:
with pytest.raises(ValueError):
await exceptor(1, 2, 3, foo=1, bar=2)
else:
> await exceptor(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:296:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off exceptor(...) for 0.0s (ValueError: catch me)
Backing off exceptor(...) for 0.0s (ValueError: catch me)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.0s (ValueError: catch me)
____________________________________________________________________________ test_on_exception_giveup_predicate _____________________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
async def foo_bar_baz():
> raise ValueError(vals.pop())
E ValueError: bar
tests/test_backoff_async.py:327: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29864df0>
@pytest.mark.asyncio
async def test_on_exception_giveup_predicate(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
def on_baz(e):
return str(e) == "baz"
vals = ["baz", "bar", "foo"]
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
async def foo_bar_baz():
raise ValueError(vals.pop())
with pytest.raises(ValueError):
> await foo_bar_baz()
tests/test_backoff_async.py:330:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off foo_bar_baz(...) for 0.6s (ValueError: foo)
Backing off foo_bar_baz(...) for 0.6s (ValueError: foo)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off foo_bar_baz(...) for 0.6s (ValueError: foo)
_______________________________________________________________________________ test_on_exception_giveup_coro _______________________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
async def foo_bar_baz():
> raise ValueError(vals.pop())
E ValueError: bar
tests/test_backoff_async.py:348: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f2973abc0>
@pytest.mark.asyncio
async def test_on_exception_giveup_coro(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
async def on_baz(e):
return str(e) == "baz"
vals = ["baz", "bar", "foo"]
@backoff.on_exception(backoff.constant,
ValueError,
giveup=on_baz)
async def foo_bar_baz():
raise ValueError(vals.pop())
with pytest.raises(ValueError):
> await foo_bar_baz()
tests/test_backoff_async.py:351:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off foo_bar_baz(...) for 0.1s (ValueError: foo)
Backing off foo_bar_baz(...) for 0.1s (ValueError: foo)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off foo_bar_baz(...) for 0.1s (ValueError: foo)
_________________________________________________________________________________ test_on_predicate_success _________________________________________________________________________________
@pytest.mark.asyncio
async def test_on_predicate_success():
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_predicate(backoff.constant,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=None,
interval=0)
@_save_target
async def success(*args, **kwargs):
# succeed after we've backed off twice
return len(log['backoff']) == 2
> await success(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:371:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:86: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off success(...) for 0.0s (False)
Backing off success(...) for 0.0s (False)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off success(...) for 0.0s (False)
____________________________________________________________________________ test_on_predicate_constant_iterable ____________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29854190>
@pytest.mark.asyncio
async def test_on_predicate_constant_iterable(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
waits = [1, 2, 3, 6, 9]
backoffs = []
giveups = []
successes = []
@backoff.on_predicate(
backoff.constant,
interval=waits,
on_backoff=backoffs.append,
on_giveup=giveups.append,
on_success=successes.append,
jitter=None,
)
async def falsey():
return False
> assert not await falsey()
tests/test_backoff_async.py:483:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:86: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = [1, 2, 3, 6, 9]
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'list_iterator' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off falsey(...) for 1.0s (False)
Backing off falsey(...) for 1.0s (False)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off falsey(...) for 1.0s (False)
__________________________________________________________________________ test_on_exception_success_0_arg_jitter ___________________________________________________________________________
args = (1, 2, 3), kwargs = {'bar': 2, 'foo': 1}
@backoff.on_exception(backoff.constant,
Exception,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=random.random,
interval=0)
@_save_target
async def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(log['backoff']) < 2:
> raise ValueError("catch me")
E ValueError: catch me
tests/test_backoff_async.py:513: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f298145e0>
@pytest.mark.asyncio
async def test_on_exception_success_0_arg_jitter(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
monkeypatch.setattr('random.random', lambda: 0)
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_exception(backoff.constant,
Exception,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=random.random,
interval=0)
@_save_target
async def succeeder(*args, **kwargs):
# succeed after we've backed off twice
if len(log['backoff']) < 2:
raise ValueError("catch me")
with pytest.deprecated_call():
> await succeeder(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:516:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off succeeder(...) for 0.0s (ValueError: catch me)
Backing off succeeder(...) for 0.0s (ValueError: catch me)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off succeeder(...) for 0.0s (ValueError: catch me)
__________________________________________________________________________ test_on_predicate_success_0_arg_jitter ___________________________________________________________________________
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f29823520>
@pytest.mark.asyncio
async def test_on_predicate_success_0_arg_jitter(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
monkeypatch.setattr('random.random', lambda: 0)
log, log_success, log_backoff, log_giveup = _log_hdlrs()
@backoff.on_predicate(backoff.constant,
on_success=log_success,
on_backoff=log_backoff,
on_giveup=log_giveup,
jitter=random.random,
interval=0)
@_save_target
async def success(*args, **kwargs):
# succeed after we've backed off twice
return len(log['backoff']) == 2
with pytest.deprecated_call():
> await success(1, 2, 3, foo=1, bar=2)
tests/test_backoff_async.py:565:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:86: in retry
seconds = _next_wait(wait, ret, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 0
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off success(...) for 0.0s (False)
Backing off success(...) for 0.0s (False)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off success(...) for 0.0s (False)
___________________________________________________________________________ test_on_exception_callable_max_tries ____________________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
async def exceptor():
log.append(True)
> raise ValueError()
E ValueError
tests/test_backoff_async.py:607: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f299ceb00>
@pytest.mark.asyncio
async def test_on_exception_callable_max_tries(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
def lookup_max_tries():
return 3
log = []
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
async def exceptor():
log.append(True)
raise ValueError()
with pytest.raises(ValueError):
> await exceptor()
tests/test_backoff_async.py:610:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off exceptor(...) for 0.6s (ValueError)
Backing off exceptor(...) for 0.6s (ValueError)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.6s (ValueError)
___________________________________________________________________ test_on_exception_callable_max_tries_reads_every_time ___________________________________________________________________
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
async def exceptor():
> raise ValueError()
E ValueError
tests/test_backoff_async.py:629: ValueError
During handling of the above exception, another exception occurred:
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7f3f299a09a0>
@pytest.mark.asyncio
async def test_on_exception_callable_max_tries_reads_every_time(monkeypatch):
monkeypatch.setattr('asyncio.sleep', _await_none)
lookups = []
def lookup_max_tries():
lookups.append(True)
return 3
@backoff.on_exception(backoff.constant,
ValueError,
max_tries=lookup_max_tries)
async def exceptor():
raise ValueError()
with pytest.raises(ValueError):
> await exceptor()
tests/test_backoff_async.py:632:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
backoff/_async.py:164: in retry
seconds = _next_wait(wait, e, jitter, elapsed,
backoff/_common.py:33: in _next_wait
value = wait.send(send_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
interval = 1
def constant(
interval: Union[int, Iterable[float]] = 1
) -> Generator[float, None, None]:
"""Generator for constant intervals.
Args:
interval: A constant value to yield or an iterable of such values.
"""
# Advance past initial .send() call
yield # type: ignore[misc]
try:
itr = iter(interval) # type: ignore
except TypeError:
itr = itertools.repeat(interval) # type: ignore
> yield from itr
E AttributeError: 'itertools.repeat' object has no attribute 'send'
backoff/_wait_gen.py:69: AttributeError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
Backing off exceptor(...) for 0.0s (ValueError)
Backing off exceptor(...) for 0.0s (ValueError)
------------------------------------------------------------------------------------- Captured log call -------------------------------------------------------------------------------------
INFO backoff:_common.py:103 Backing off exceptor(...) for 0.0s (ValueError)
===================================================================================== warnings summary ======================================================================================
tests/test_backoff_async.py:665
tests/test_backoff_async.py:665: PytestDeprecationWarning: test_on_exception_coro_cancelling is asynchronous and explicitly requests the "event_loop" fixture. Asynchronous fixtures and test functions should use "asyncio.get_running_loop()" instead.
@pytest.mark.asyncio
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================================================================== short test summary info ==================================================================================
FAILED tests/test_backoff.py::test_on_exception_constant_iterable - AttributeError: 'tuple_iterator' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_success - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_giveup[True] - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_giveup[False] - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_giveup_predicate - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_predicate_success - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_success_0_arg_jitter - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_predicate_success_0_arg_jitter - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_callable_max_tries - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_exception_callable_max_tries_reads_every_time - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff.py::test_on_predicate_constant_iterable - AttributeError: 'list_iterator' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_constant_iterable - AttributeError: 'tuple_iterator' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_success - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_giveup[True] - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_giveup[False] - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_giveup_predicate - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_giveup_coro - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_predicate_success - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_predicate_constant_iterable - AttributeError: 'list_iterator' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_success_0_arg_jitter - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_predicate_success_0_arg_jitter - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_callable_max_tries - AttributeError: 'itertools.repeat' object has no attribute 'send'
FAILED tests/test_backoff_async.py::test_on_exception_callable_max_tries_reads_every_time - AttributeError: 'itertools.repeat' object has no attribute 'send'
========================================================================= 23 failed, 96 passed, 1 warning in 1.21s ========================================================================== I'm not sure how to rewrite |
Just found jet another issue in backoff/_wait_gen.py in context of |
Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
BTW end of this month python 3.8 will be EOSed so it would be good to merge those changes and resolve pyupgrade issue in backoff/_wait_gen.py before end of this month, |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
According to https://endoflife.date/python python 3.7 has been EOSed 27 Jun 2023.
Filter all code over
pyupgracde --py38-plus
.