diff --git a/tenacity/__init__.py b/tenacity/__init__.py index dd4eb48..c6484a8 100644 --- a/tenacity/__init__.py +++ b/tenacity/__init__.py @@ -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]": diff --git a/tenacity/asyncio/__init__.py b/tenacity/asyncio/__init__.py index 0ff4885..b2b203e 100644 --- a/tenacity/asyncio/__init__.py +++ b/tenacity/asyncio/__init__.py @@ -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 diff --git a/tests/test_asyncio.py b/tests/test_asyncio.py index 749c883..e2e2cd4 100644 --- a/tests/test_asyncio.py +++ b/tests/test_asyncio.py @@ -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( @@ -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( @@ -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") diff --git a/tests/test_tenacity.py b/tests/test_tenacity.py index 34c7ada..d518231 100644 --- a/tests/test_tenacity.py +++ b/tests/test_tenacity.py @@ -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))) @@ -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, @@ -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") @@ -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: