tenacity 8.4.2__tar.gz → 9.0.0__tar.gz
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.
- {tenacity-8.4.2 → tenacity-9.0.0}/.github/workflows/ci.yaml +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/.github/workflows/deploy.yaml +1 -1
- {tenacity-8.4.2/tenacity.egg-info → tenacity-9.0.0}/PKG-INFO +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/README.rst +31 -5
- {tenacity-8.4.2 → tenacity-9.0.0}/doc/source/index.rst +31 -5
- tenacity-9.0.0/releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml +6 -0
- tenacity-9.0.0/releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml +4 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/__init__.py +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/asyncio/__init__.py +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/wait.py +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0/tenacity.egg-info}/PKG-INFO +1 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity.egg-info/SOURCES.txt +2 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_asyncio.py +60 -1
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_tenacity.py +58 -12
- {tenacity-8.4.2 → tenacity-9.0.0}/.editorconfig +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/.github/dependabot.yml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/.gitignore +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/.mergify.yml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/.readthedocs.yml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/LICENSE +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/doc/source/api.rst +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/doc/source/changelog.rst +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/doc/source/conf.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/pyproject.toml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/Fix-tests-for-typeguard-3.x-6eebfea546b6207e.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/Use--for-formatting-and-validate-using-black-39ec9d57d4691778.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-async-actions-b249c527d99723bb.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-reno-d1ab5710f272650a.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-retry_except_exception_type-31b31da1924d55f4.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-stop-before-delay-a775f88ac872c923.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-test-extra-55e869261b03e56d.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add_omitted_modules_to_import_all-2ab282f20a2c22f7.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add_retry_if_exception_cause_type-d16b918ace4ae0ad.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/added_a_link_to_documentation-eefaf8f074b539f8.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/after_log-50f4d73b24ce9203.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/allow-mocking-of-nap-sleep-6679c50e702446f1.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/annotate_code-197b93130df14042.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/before_sleep_log-improvements-d8149274dfb37d7c.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/clarify-reraise-option-6829667eacf4f599.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/dependabot-for-github-actions-4d2464f3c0928463.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/do_not_package_tests-fe5ac61940b0a5ed.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/drop-deprecated-python-versions-69a05cb2e0f1034c.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/drop_deprecated-7ea90b212509b082.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/export-convenience-symbols-981d9611c8b754f3.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix-async-loop-with-result-f68e913ccb425aca.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix-setuptools-config-3af71aa3592b6948.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix-wait-typing-b26eecdb6cc0a1de.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix_async-52b6594c8e75c4bc.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/make-logger-more-compatible-5da1ddf1bab77047.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/no-async-iter-6132a42e52348a75.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/pr320-py3-only-wheel-tag.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/py36_plus-c425fb3aa17c6682.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/remove-py36-876c0416cf279d15.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/retrycallstate-repr-94947f7b00ee15e1.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/some-slug-for-preserve-defaults-86682846dfa18005.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/timedelta-for-stop-ef6bf71b88ce9988.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/trio-support-retry-22bd544800cd1f36.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/reno.yaml +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/setup.cfg +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/setup.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/_utils.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/after.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/asyncio/retry.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/before.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/before_sleep.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/nap.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/py.typed +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/retry.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/stop.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity/tornadoweb.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity.egg-info/dependency_links.txt +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity.egg-info/requires.txt +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tenacity.egg-info/top_level.txt +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/__init__.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_after.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_issue_478.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_tornado.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tests/test_utils.py +0 -0
- {tenacity-8.4.2 → tenacity-9.0.0}/tox.ini +0 -0
|
@@ -124,8 +124,8 @@ retrying stuff.
|
|
|
124
124
|
print("Stopping after 10 seconds")
|
|
125
125
|
raise Exception
|
|
126
126
|
|
|
127
|
-
If you're on a tight deadline, and exceeding your delay time isn't ok,
|
|
128
|
-
then you can give up on retries one attempt before you would exceed the delay.
|
|
127
|
+
If you're on a tight deadline, and exceeding your delay time isn't ok,
|
|
128
|
+
then you can give up on retries one attempt before you would exceed the delay.
|
|
129
129
|
|
|
130
130
|
.. testcode::
|
|
131
131
|
|
|
@@ -362,7 +362,7 @@ Statistics
|
|
|
362
362
|
~~~~~~~~~~
|
|
363
363
|
|
|
364
364
|
You can access the statistics about the retry made over a function by using the
|
|
365
|
-
`
|
|
365
|
+
`statistics` attribute attached to the function:
|
|
366
366
|
|
|
367
367
|
.. testcode::
|
|
368
368
|
|
|
@@ -375,7 +375,7 @@ You can access the statistics about the retry made over a function by using the
|
|
|
375
375
|
except Exception:
|
|
376
376
|
pass
|
|
377
377
|
|
|
378
|
-
print(raise_my_exception.
|
|
378
|
+
print(raise_my_exception.statistics)
|
|
379
379
|
|
|
380
380
|
.. testoutput::
|
|
381
381
|
:hide:
|
|
@@ -495,7 +495,7 @@ using the `retry_with` function attached to the wrapped function:
|
|
|
495
495
|
except Exception:
|
|
496
496
|
pass
|
|
497
497
|
|
|
498
|
-
print(raise_my_exception.
|
|
498
|
+
print(raise_my_exception.statistics)
|
|
499
499
|
|
|
500
500
|
.. testoutput::
|
|
501
501
|
:hide:
|
|
@@ -514,6 +514,32 @@ to use the `retry` decorator - you can instead use `Retrying` directly:
|
|
|
514
514
|
retryer = Retrying(stop=stop_after_attempt(max_attempts), reraise=True)
|
|
515
515
|
retryer(never_good_enough, 'I really do try')
|
|
516
516
|
|
|
517
|
+
You may also want to change the behaviour of a decorated function temporarily,
|
|
518
|
+
like in tests to avoid unnecessary wait times. You can modify/patch the `retry`
|
|
519
|
+
attribute attached to the function. Bear in mind this is a write-only attribute,
|
|
520
|
+
statistics should be read from the function `statistics` attribute.
|
|
521
|
+
|
|
522
|
+
.. testcode::
|
|
523
|
+
|
|
524
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
|
|
525
|
+
def raise_my_exception():
|
|
526
|
+
raise MyException("Fail")
|
|
527
|
+
|
|
528
|
+
from unittest import mock
|
|
529
|
+
|
|
530
|
+
with mock.patch.object(raise_my_exception.retry, "wait", wait_fixed(0)):
|
|
531
|
+
try:
|
|
532
|
+
raise_my_exception()
|
|
533
|
+
except Exception:
|
|
534
|
+
pass
|
|
535
|
+
|
|
536
|
+
print(raise_my_exception.statistics)
|
|
537
|
+
|
|
538
|
+
.. testoutput::
|
|
539
|
+
:hide:
|
|
540
|
+
|
|
541
|
+
...
|
|
542
|
+
|
|
517
543
|
Retrying code block
|
|
518
544
|
~~~~~~~~~~~~~~~~~~~
|
|
519
545
|
|
|
@@ -124,8 +124,8 @@ retrying stuff.
|
|
|
124
124
|
print("Stopping after 10 seconds")
|
|
125
125
|
raise Exception
|
|
126
126
|
|
|
127
|
-
If you're on a tight deadline, and exceeding your delay time isn't ok,
|
|
128
|
-
then you can give up on retries one attempt before you would exceed the delay.
|
|
127
|
+
If you're on a tight deadline, and exceeding your delay time isn't ok,
|
|
128
|
+
then you can give up on retries one attempt before you would exceed the delay.
|
|
129
129
|
|
|
130
130
|
.. testcode::
|
|
131
131
|
|
|
@@ -362,7 +362,7 @@ Statistics
|
|
|
362
362
|
~~~~~~~~~~
|
|
363
363
|
|
|
364
364
|
You can access the statistics about the retry made over a function by using the
|
|
365
|
-
`
|
|
365
|
+
`statistics` attribute attached to the function:
|
|
366
366
|
|
|
367
367
|
.. testcode::
|
|
368
368
|
|
|
@@ -375,7 +375,7 @@ You can access the statistics about the retry made over a function by using the
|
|
|
375
375
|
except Exception:
|
|
376
376
|
pass
|
|
377
377
|
|
|
378
|
-
print(raise_my_exception.
|
|
378
|
+
print(raise_my_exception.statistics)
|
|
379
379
|
|
|
380
380
|
.. testoutput::
|
|
381
381
|
:hide:
|
|
@@ -495,7 +495,7 @@ using the `retry_with` function attached to the wrapped function:
|
|
|
495
495
|
except Exception:
|
|
496
496
|
pass
|
|
497
497
|
|
|
498
|
-
print(raise_my_exception.
|
|
498
|
+
print(raise_my_exception.statistics)
|
|
499
499
|
|
|
500
500
|
.. testoutput::
|
|
501
501
|
:hide:
|
|
@@ -514,6 +514,32 @@ to use the `retry` decorator - you can instead use `Retrying` directly:
|
|
|
514
514
|
retryer = Retrying(stop=stop_after_attempt(max_attempts), reraise=True)
|
|
515
515
|
retryer(never_good_enough, 'I really do try')
|
|
516
516
|
|
|
517
|
+
You may also want to change the behaviour of a decorated function temporarily,
|
|
518
|
+
like in tests to avoid unnecessary wait times. You can modify/patch the `retry`
|
|
519
|
+
attribute attached to the function. Bear in mind this is a write-only attribute,
|
|
520
|
+
statistics should be read from the function `statistics` attribute.
|
|
521
|
+
|
|
522
|
+
.. testcode::
|
|
523
|
+
|
|
524
|
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
|
|
525
|
+
def raise_my_exception():
|
|
526
|
+
raise MyException("Fail")
|
|
527
|
+
|
|
528
|
+
from unittest import mock
|
|
529
|
+
|
|
530
|
+
with mock.patch.object(raise_my_exception.retry, "wait", wait_fixed(0)):
|
|
531
|
+
try:
|
|
532
|
+
raise_my_exception()
|
|
533
|
+
except Exception:
|
|
534
|
+
pass
|
|
535
|
+
|
|
536
|
+
print(raise_my_exception.statistics)
|
|
537
|
+
|
|
538
|
+
.. testoutput::
|
|
539
|
+
:hide:
|
|
540
|
+
|
|
541
|
+
...
|
|
542
|
+
|
|
517
543
|
Retrying code block
|
|
518
544
|
~~~~~~~~~~~~~~~~~~~
|
|
519
545
|
|
|
@@ -339,7 +339,7 @@ class BaseRetrying(ABC):
|
|
|
339
339
|
return self.copy(*args, **kwargs).wraps(f)
|
|
340
340
|
|
|
341
341
|
# Preserve attributes
|
|
342
|
-
wrapped_f.retry =
|
|
342
|
+
wrapped_f.retry = self # type: ignore[attr-defined]
|
|
343
343
|
wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
|
|
344
344
|
wrapped_f.statistics = {} # type: ignore[attr-defined]
|
|
345
345
|
|
|
@@ -189,7 +189,7 @@ class AsyncRetrying(BaseRetrying):
|
|
|
189
189
|
return await copy(fn, *args, **kwargs)
|
|
190
190
|
|
|
191
191
|
# Preserve attributes
|
|
192
|
-
async_wrapped.retry =
|
|
192
|
+
async_wrapped.retry = self # type: ignore[attr-defined]
|
|
193
193
|
async_wrapped.retry_with = wrapped.retry_with # type: ignore[attr-defined]
|
|
194
194
|
async_wrapped.statistics = {} # type: ignore[attr-defined]
|
|
195
195
|
|
|
@@ -197,7 +197,7 @@ class wait_random_exponential(wait_exponential):
|
|
|
197
197
|
|
|
198
198
|
def __call__(self, retry_state: "RetryCallState") -> float:
|
|
199
199
|
high = super().__call__(retry_state=retry_state)
|
|
200
|
-
return random.uniform(
|
|
200
|
+
return random.uniform(self.min, high)
|
|
201
201
|
|
|
202
202
|
|
|
203
203
|
class wait_exponential_jitter(wait_base):
|
|
@@ -38,6 +38,7 @@ releasenotes/notes/drop_deprecated-7ea90b212509b082.yaml
|
|
|
38
38
|
releasenotes/notes/export-convenience-symbols-981d9611c8b754f3.yaml
|
|
39
39
|
releasenotes/notes/fix-async-loop-with-result-f68e913ccb425aca.yaml
|
|
40
40
|
releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml
|
|
41
|
+
releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml
|
|
41
42
|
releasenotes/notes/fix-setuptools-config-3af71aa3592b6948.yaml
|
|
42
43
|
releasenotes/notes/fix-wait-typing-b26eecdb6cc0a1de.yaml
|
|
43
44
|
releasenotes/notes/fix_async-52b6594c8e75c4bc.yaml
|
|
@@ -52,6 +53,7 @@ releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml
|
|
|
52
53
|
releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml
|
|
53
54
|
releasenotes/notes/timedelta-for-stop-ef6bf71b88ce9988.yaml
|
|
54
55
|
releasenotes/notes/trio-support-retry-22bd544800cd1f36.yaml
|
|
56
|
+
releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml
|
|
55
57
|
releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml
|
|
56
58
|
tenacity/__init__.py
|
|
57
59
|
tenacity/_utils.py
|
|
@@ -17,6 +17,7 @@ import asyncio
|
|
|
17
17
|
import inspect
|
|
18
18
|
import unittest
|
|
19
19
|
from functools import wraps
|
|
20
|
+
from unittest import mock
|
|
20
21
|
|
|
21
22
|
try:
|
|
22
23
|
import trio
|
|
@@ -59,7 +60,7 @@ async def _retryable_coroutine(thing):
|
|
|
59
60
|
@retry(stop=stop_after_attempt(2))
|
|
60
61
|
async def _retryable_coroutine_with_2_attempts(thing):
|
|
61
62
|
await asyncio.sleep(0.00001)
|
|
62
|
-
thing.go()
|
|
63
|
+
return thing.go()
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class TestAsyncio(unittest.TestCase):
|
|
@@ -394,6 +395,64 @@ class TestContextManager(unittest.TestCase):
|
|
|
394
395
|
await _async_function(thing)
|
|
395
396
|
|
|
396
397
|
|
|
398
|
+
class TestDecoratorWrapper(unittest.TestCase):
|
|
399
|
+
@asynctest
|
|
400
|
+
async def test_retry_function_attributes(self):
|
|
401
|
+
"""Test that the wrapped function attributes are exposed as intended.
|
|
402
|
+
|
|
403
|
+
- statistics contains the value for the latest function run
|
|
404
|
+
- retry object can be modified to change its behaviour (useful to patch in tests)
|
|
405
|
+
- retry object statistics do not contain valid information
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
self.assertTrue(
|
|
409
|
+
await _retryable_coroutine_with_2_attempts(NoIOErrorAfterCount(1))
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
expected_stats = {
|
|
413
|
+
"attempt_number": 2,
|
|
414
|
+
"delay_since_first_attempt": mock.ANY,
|
|
415
|
+
"idle_for": mock.ANY,
|
|
416
|
+
"start_time": mock.ANY,
|
|
417
|
+
}
|
|
418
|
+
self.assertEqual(
|
|
419
|
+
_retryable_coroutine_with_2_attempts.statistics, # type: ignore[attr-defined]
|
|
420
|
+
expected_stats,
|
|
421
|
+
)
|
|
422
|
+
self.assertEqual(
|
|
423
|
+
_retryable_coroutine_with_2_attempts.retry.statistics, # type: ignore[attr-defined]
|
|
424
|
+
{},
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
with mock.patch.object(
|
|
428
|
+
_retryable_coroutine_with_2_attempts.retry, # type: ignore[attr-defined]
|
|
429
|
+
"stop",
|
|
430
|
+
tenacity.stop_after_attempt(1),
|
|
431
|
+
):
|
|
432
|
+
try:
|
|
433
|
+
self.assertTrue(
|
|
434
|
+
await _retryable_coroutine_with_2_attempts(NoIOErrorAfterCount(2))
|
|
435
|
+
)
|
|
436
|
+
except RetryError as exc:
|
|
437
|
+
expected_stats = {
|
|
438
|
+
"attempt_number": 1,
|
|
439
|
+
"delay_since_first_attempt": mock.ANY,
|
|
440
|
+
"idle_for": mock.ANY,
|
|
441
|
+
"start_time": mock.ANY,
|
|
442
|
+
}
|
|
443
|
+
self.assertEqual(
|
|
444
|
+
_retryable_coroutine_with_2_attempts.statistics, # type: ignore[attr-defined]
|
|
445
|
+
expected_stats,
|
|
446
|
+
)
|
|
447
|
+
self.assertEqual(exc.last_attempt.attempt_number, 1)
|
|
448
|
+
self.assertEqual(
|
|
449
|
+
_retryable_coroutine_with_2_attempts.retry.statistics, # type: ignore[attr-defined]
|
|
450
|
+
{},
|
|
451
|
+
)
|
|
452
|
+
else:
|
|
453
|
+
self.fail("RetryError should have been raised after 1 attempt")
|
|
454
|
+
|
|
455
|
+
|
|
397
456
|
# make sure mypy accepts passing an async sleep function
|
|
398
457
|
# https://github.com/jd/tenacity/issues/399
|
|
399
458
|
async def my_async_sleep(x: float) -> None:
|
|
@@ -25,6 +25,7 @@ import warnings
|
|
|
25
25
|
from contextlib import contextmanager
|
|
26
26
|
from copy import copy
|
|
27
27
|
from fractions import Fraction
|
|
28
|
+
from unittest import mock
|
|
28
29
|
|
|
29
30
|
import pytest
|
|
30
31
|
|
|
@@ -471,9 +472,17 @@ class TestWaitConditions(unittest.TestCase):
|
|
|
471
472
|
self._assert_inclusive_range(fn(make_retry_state(8, 0)), 0, 60.0)
|
|
472
473
|
self._assert_inclusive_range(fn(make_retry_state(9, 0)), 0, 60.0)
|
|
473
474
|
|
|
474
|
-
|
|
475
|
+
# max wait
|
|
476
|
+
max_wait = 5
|
|
477
|
+
fn = tenacity.wait_random_exponential(10, max_wait)
|
|
475
478
|
for _ in range(1000):
|
|
476
|
-
self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0.00,
|
|
479
|
+
self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0.00, max_wait)
|
|
480
|
+
|
|
481
|
+
# min wait
|
|
482
|
+
min_wait = 5
|
|
483
|
+
fn = tenacity.wait_random_exponential(min=min_wait)
|
|
484
|
+
for _ in range(1000):
|
|
485
|
+
self._assert_inclusive_range(fn(make_retry_state(1, 0)), min_wait, 5)
|
|
477
486
|
|
|
478
487
|
# Default arguments exist
|
|
479
488
|
fn = tenacity.wait_random_exponential()
|
|
@@ -1073,7 +1082,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1073
1082
|
_retryable_test_with_unless_exception_type_name(NameErrorUntilCount(5))
|
|
1074
1083
|
)
|
|
1075
1084
|
except NameError as e:
|
|
1076
|
-
s = _retryable_test_with_unless_exception_type_name.
|
|
1085
|
+
s = _retryable_test_with_unless_exception_type_name.statistics
|
|
1077
1086
|
self.assertTrue(s["attempt_number"] == 6)
|
|
1078
1087
|
print(e)
|
|
1079
1088
|
else:
|
|
@@ -1088,7 +1097,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1088
1097
|
)
|
|
1089
1098
|
)
|
|
1090
1099
|
except NameError as e:
|
|
1091
|
-
s = _retryable_test_with_unless_exception_type_no_input.
|
|
1100
|
+
s = _retryable_test_with_unless_exception_type_no_input.statistics
|
|
1092
1101
|
self.assertTrue(s["attempt_number"] == 6)
|
|
1093
1102
|
print(e)
|
|
1094
1103
|
else:
|
|
@@ -1111,7 +1120,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1111
1120
|
_retryable_test_if_exception_message_message(NoCustomErrorAfterCount(3))
|
|
1112
1121
|
)
|
|
1113
1122
|
except CustomError:
|
|
1114
|
-
print(_retryable_test_if_exception_message_message.
|
|
1123
|
+
print(_retryable_test_if_exception_message_message.statistics)
|
|
1115
1124
|
self.fail("CustomError should've been retried from errormessage")
|
|
1116
1125
|
|
|
1117
1126
|
def test_retry_if_not_exception_message(self):
|
|
@@ -1122,7 +1131,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1122
1131
|
)
|
|
1123
1132
|
)
|
|
1124
1133
|
except CustomError:
|
|
1125
|
-
s = _retryable_test_if_not_exception_message_message.
|
|
1134
|
+
s = _retryable_test_if_not_exception_message_message.statistics
|
|
1126
1135
|
self.assertTrue(s["attempt_number"] == 1)
|
|
1127
1136
|
|
|
1128
1137
|
def test_retry_if_not_exception_message_delay(self):
|
|
@@ -1131,7 +1140,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1131
1140
|
_retryable_test_not_exception_message_delay(NameErrorUntilCount(3))
|
|
1132
1141
|
)
|
|
1133
1142
|
except NameError:
|
|
1134
|
-
s = _retryable_test_not_exception_message_delay.
|
|
1143
|
+
s = _retryable_test_not_exception_message_delay.statistics
|
|
1135
1144
|
print(s["attempt_number"])
|
|
1136
1145
|
self.assertTrue(s["attempt_number"] == 4)
|
|
1137
1146
|
|
|
@@ -1151,7 +1160,7 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1151
1160
|
)
|
|
1152
1161
|
)
|
|
1153
1162
|
except CustomError:
|
|
1154
|
-
s = _retryable_test_if_not_exception_message_message.
|
|
1163
|
+
s = _retryable_test_if_not_exception_message_message.statistics
|
|
1155
1164
|
self.assertTrue(s["attempt_number"] == 1)
|
|
1156
1165
|
|
|
1157
1166
|
def test_retry_if_exception_cause_type(self):
|
|
@@ -1209,6 +1218,43 @@ class TestDecoratorWrapper(unittest.TestCase):
|
|
|
1209
1218
|
h = retrying.wraps(Hello())
|
|
1210
1219
|
self.assertEqual(h(), "Hello")
|
|
1211
1220
|
|
|
1221
|
+
def test_retry_function_attributes(self):
|
|
1222
|
+
"""Test that the wrapped function attributes are exposed as intended.
|
|
1223
|
+
|
|
1224
|
+
- statistics contains the value for the latest function run
|
|
1225
|
+
- retry object can be modified to change its behaviour (useful to patch in tests)
|
|
1226
|
+
- retry object statistics do not contain valid information
|
|
1227
|
+
"""
|
|
1228
|
+
|
|
1229
|
+
self.assertTrue(_retryable_test_with_stop(NoneReturnUntilAfterCount(2)))
|
|
1230
|
+
|
|
1231
|
+
expected_stats = {
|
|
1232
|
+
"attempt_number": 3,
|
|
1233
|
+
"delay_since_first_attempt": mock.ANY,
|
|
1234
|
+
"idle_for": mock.ANY,
|
|
1235
|
+
"start_time": mock.ANY,
|
|
1236
|
+
}
|
|
1237
|
+
self.assertEqual(_retryable_test_with_stop.statistics, expected_stats)
|
|
1238
|
+
self.assertEqual(_retryable_test_with_stop.retry.statistics, {})
|
|
1239
|
+
|
|
1240
|
+
with mock.patch.object(
|
|
1241
|
+
_retryable_test_with_stop.retry, "stop", tenacity.stop_after_attempt(1)
|
|
1242
|
+
):
|
|
1243
|
+
try:
|
|
1244
|
+
self.assertTrue(_retryable_test_with_stop(NoneReturnUntilAfterCount(2)))
|
|
1245
|
+
except RetryError as exc:
|
|
1246
|
+
expected_stats = {
|
|
1247
|
+
"attempt_number": 1,
|
|
1248
|
+
"delay_since_first_attempt": mock.ANY,
|
|
1249
|
+
"idle_for": mock.ANY,
|
|
1250
|
+
"start_time": mock.ANY,
|
|
1251
|
+
}
|
|
1252
|
+
self.assertEqual(_retryable_test_with_stop.statistics, expected_stats)
|
|
1253
|
+
self.assertEqual(exc.last_attempt.attempt_number, 1)
|
|
1254
|
+
self.assertEqual(_retryable_test_with_stop.retry.statistics, {})
|
|
1255
|
+
else:
|
|
1256
|
+
self.fail("RetryError should have been raised after 1 attempt")
|
|
1257
|
+
|
|
1212
1258
|
|
|
1213
1259
|
class TestRetryWith:
|
|
1214
1260
|
def test_redefine_wait(self):
|
|
@@ -1479,21 +1525,21 @@ class TestStatistics(unittest.TestCase):
|
|
|
1479
1525
|
def _foobar():
|
|
1480
1526
|
return 42
|
|
1481
1527
|
|
|
1482
|
-
self.assertEqual({}, _foobar.
|
|
1528
|
+
self.assertEqual({}, _foobar.statistics)
|
|
1483
1529
|
_foobar()
|
|
1484
|
-
self.assertEqual(1, _foobar.
|
|
1530
|
+
self.assertEqual(1, _foobar.statistics["attempt_number"])
|
|
1485
1531
|
|
|
1486
1532
|
def test_stats_failing(self):
|
|
1487
1533
|
@retry(stop=tenacity.stop_after_attempt(2))
|
|
1488
1534
|
def _foobar():
|
|
1489
1535
|
raise ValueError(42)
|
|
1490
1536
|
|
|
1491
|
-
self.assertEqual({}, _foobar.
|
|
1537
|
+
self.assertEqual({}, _foobar.statistics)
|
|
1492
1538
|
try:
|
|
1493
1539
|
_foobar()
|
|
1494
1540
|
except Exception: # noqa: B902
|
|
1495
1541
|
pass
|
|
1496
|
-
self.assertEqual(2, _foobar.
|
|
1542
|
+
self.assertEqual(2, _foobar.statistics["attempt_number"])
|
|
1497
1543
|
|
|
1498
1544
|
|
|
1499
1545
|
class TestRetryErrorCallback(unittest.TestCase):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-async-actions-b249c527d99723bb.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/add-stop-before-delay-a775f88ac872c923.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/clarify-reraise-option-6829667eacf4f599.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/do_not_package_tests-fe5ac61940b0a5ed.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/fix-setuptools-config-3af71aa3592b6948.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/retrycallstate-repr-94947f7b00ee15e1.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/sphinx_define_error-642c9cd5c165d39a.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/timedelta-for-stop-ef6bf71b88ce9988.yaml
RENAMED
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/trio-support-retry-22bd544800cd1f36.yaml
RENAMED
|
File without changes
|
{tenacity-8.4.2 → tenacity-9.0.0}/releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|