Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tenacity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
# calling the same wrapped functions multiple times in the same stack
copy = self.copy()
wrapped_f.statistics = copy.statistics # type: ignore[attr-defined]
self._local.statistics = copy.statistics
return copy(f, *args, **kw)

def retry_with(*args: t.Any, **kwargs: t.Any) -> "_RetryDecorated[P, R]":
Expand Down
1 change: 1 addition & 0 deletions tenacity/asyncio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
# calling the same wrapped functions multiple times in the same stack
copy = self.copy()
async_wrapped.statistics = copy.statistics # type: ignore[attr-defined]
self._local.statistics = copy.statistics
return await copy(fn, *args, **kwargs) # type: ignore[type-var]

# Preserve attributes
Expand Down
6 changes: 3 additions & 3 deletions tests/test_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ async def test_retry_function_attributes(self) -> None:

- statistics contains the value for the latest function run
- retry object can be modified to change its behaviour (useful to patch in tests)
- retry object statistics do not contain valid information
- retry object statistics are synced with function statistics
"""

self.assertTrue(
Expand All @@ -467,7 +467,7 @@ async def test_retry_function_attributes(self) -> None:
)
self.assertEqual(
_retryable_coroutine_with_2_attempts.retry.statistics,
{},
expected_stats,
)

with mock.patch.object(
Expand All @@ -493,7 +493,7 @@ async def test_retry_function_attributes(self) -> None:
self.assertEqual(exc.last_attempt.attempt_number, 1)
self.assertEqual(
_retryable_coroutine_with_2_attempts.retry.statistics,
{},
expected_stats,
)
else:
self.fail("RetryError should have been raised after 1 attempt")
Expand Down
39 changes: 36 additions & 3 deletions tests/test_tenacity.py
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ def test_retry_function_attributes(self) -> None:

- statistics contains the value for the latest function run
- retry object can be modified to change its behaviour (useful to patch in tests)
- retry object statistics do not contain valid information
- retry object statistics are synced with function statistics
"""

self.assertTrue(_retryable_test_with_stop(NoneReturnUntilAfterCount(2)))
Expand All @@ -1387,7 +1387,7 @@ def test_retry_function_attributes(self) -> None:
"start_time": mock.ANY,
}
self.assertEqual(_retryable_test_with_stop.statistics, expected_stats)
self.assertEqual(_retryable_test_with_stop.retry.statistics, {})
self.assertEqual(_retryable_test_with_stop.retry.statistics, expected_stats)

with mock.patch.object(
_retryable_test_with_stop.retry,
Expand All @@ -1405,7 +1405,9 @@ def test_retry_function_attributes(self) -> None:
}
self.assertEqual(_retryable_test_with_stop.statistics, expected_stats)
self.assertEqual(exc.last_attempt.attempt_number, 1)
self.assertEqual(_retryable_test_with_stop.retry.statistics, {})
self.assertEqual(
_retryable_test_with_stop.retry.statistics, expected_stats
)
else:
self.fail("RetryError should have been raised after 1 attempt")

Expand Down Expand Up @@ -1765,6 +1767,37 @@ def _foobar() -> None:
_foobar()
self.assertEqual(2, _foobar.statistics["attempt_number"])

def test_retry_object_statistics_synced(self) -> None:
"""Test that func.retry.statistics is synced with func.statistics."""

@retry(stop=tenacity.stop_after_attempt(3))
def _foobar() -> int:
return 42

_foobar()
self.assertEqual(
_foobar.retry.statistics["attempt_number"],
_foobar.statistics["attempt_number"],
)

def test_retry_object_statistics_during_execution(self) -> None:
"""Test that func.retry.statistics is accessible during execution."""
attempts: list[int] = []

@retry(
stop=tenacity.stop_after_attempt(3),
retry=tenacity.retry_if_exception_type(ValueError),
reraise=True,
)
def _foobar() -> int:
attempts.append(_foobar.retry.statistics["attempt_number"])
if len(attempts) < 3:
raise ValueError("retry")
return 42

_foobar()
self.assertEqual(attempts, [1, 2, 3])


class TestRetryErrorCallback(unittest.TestCase):
def setUp(self) -> None:
Expand Down
Loading