diff -pruN 8.4.2+really8.4.1-2/.github/workflows/ci.yaml 9.0.0-0ubuntu1/.github/workflows/ci.yaml
--- 8.4.2+really8.4.1-2/.github/workflows/ci.yaml	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/.github/workflows/ci.yaml	2024-07-29 12:12:16.000000000 +0000
@@ -34,7 +34,7 @@ jobs:
             tox: mypy
     steps:
       - name: Checkout 🛎️
-        uses: actions/checkout@v4.1.6
+        uses: actions/checkout@v4.1.7
         with:
           fetch-depth: 0
 
diff -pruN 8.4.2+really8.4.1-2/.github/workflows/deploy.yaml 9.0.0-0ubuntu1/.github/workflows/deploy.yaml
--- 8.4.2+really8.4.1-2/.github/workflows/deploy.yaml	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/.github/workflows/deploy.yaml	2024-07-29 12:12:16.000000000 +0000
@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout 🛎️
-        uses: actions/checkout@v4.1.6
+        uses: actions/checkout@v4.1.7
         with:
           fetch-depth: 0
 
diff -pruN 8.4.2+really8.4.1-2/PKG-INFO 9.0.0-0ubuntu1/PKG-INFO
--- 8.4.2+really8.4.1-2/PKG-INFO	2024-06-17 12:16:58.784843200 +0000
+++ 9.0.0-0ubuntu1/PKG-INFO	2024-07-29 12:12:25.123712800 +0000
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tenacity
-Version: 8.4.1
+Version: 9.0.0
 Summary: Retry code until it succeeds
 Home-page: https://github.com/jd/tenacity
 Author: Julien Danjou
diff -pruN 8.4.2+really8.4.1-2/README.rst 9.0.0-0ubuntu1/README.rst
--- 8.4.2+really8.4.1-2/README.rst	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/README.rst	2024-07-29 12:12:16.000000000 +0000
@@ -124,8 +124,8 @@ retrying stuff.
         print("Stopping after 10 seconds")
         raise Exception
 
-If you're on a tight deadline, and exceeding your delay time isn't ok, 
-then you can give up on retries one attempt before you would exceed the delay. 
+If you're on a tight deadline, and exceeding your delay time isn't ok,
+then you can give up on retries one attempt before you would exceed the delay.
 
 .. testcode::
 
@@ -362,7 +362,7 @@ Statistics
 ~~~~~~~~~~
 
 You can access the statistics about the retry made over a function by using the
-`retry` attribute attached to the function and its `statistics` attribute:
+`statistics` attribute attached to the function:
 
 .. testcode::
 
@@ -375,7 +375,7 @@ You can access the statistics about the
     except Exception:
         pass
 
-    print(raise_my_exception.retry.statistics)
+    print(raise_my_exception.statistics)
 
 .. testoutput::
    :hide:
@@ -495,7 +495,7 @@ using the `retry_with` function attached
     except Exception:
         pass
 
-    print(raise_my_exception.retry.statistics)
+    print(raise_my_exception.statistics)
 
 .. testoutput::
    :hide:
@@ -514,6 +514,32 @@ to use the `retry` decorator - you can i
         retryer = Retrying(stop=stop_after_attempt(max_attempts), reraise=True)
         retryer(never_good_enough, 'I really do try')
 
+You may also want to change the behaviour of a decorated function temporarily,
+like in tests to avoid unnecessary wait times. You can modify/patch the `retry`
+attribute attached to the function. Bear in mind this is a write-only attribute,
+statistics should be read from the function `statistics` attribute.
+
+.. testcode::
+
+    @retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
+    def raise_my_exception():
+        raise MyException("Fail")
+
+    from unittest import mock
+
+    with mock.patch.object(raise_my_exception.retry, "wait", wait_fixed(0)):
+        try:
+            raise_my_exception()
+        except Exception:
+            pass
+
+    print(raise_my_exception.statistics)
+
+.. testoutput::
+   :hide:
+
+   ...
+
 Retrying code block
 ~~~~~~~~~~~~~~~~~~~
 
diff -pruN 8.4.2+really8.4.1-2/debian/.gitignore 9.0.0-0ubuntu1/debian/.gitignore
--- 8.4.2+really8.4.1-2/debian/.gitignore	2024-12-18 13:07:28.000000000 +0000
+++ 9.0.0-0ubuntu1/debian/.gitignore	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-/files
diff -pruN 8.4.2+really8.4.1-2/debian/changelog 9.0.0-0ubuntu1/debian/changelog
--- 8.4.2+really8.4.1-2/debian/changelog	2024-12-18 13:07:28.000000000 +0000
+++ 9.0.0-0ubuntu1/debian/changelog	2025-02-13 15:48:21.000000000 +0000
@@ -1,18 +1,14 @@
-python-tenacity (8.4.2+really8.4.1-2) unstable; urgency=medium
+python-tenacity (9.0.0-0ubuntu1) plucky; urgency=medium
 
-  * Team upload.
-  * Simplify a debhelper override, avoiding use of the deprecated
-    python_distutils buildsystem.
+  * New upstream release.
 
- -- Colin Watson <cjwatson@debian.org>  Wed, 18 Dec 2024 13:07:28 +0000
+ -- James Page <james.page@ubuntu.com>  Thu, 13 Feb 2025 15:48:21 +0000
 
-python-tenacity (8.4.2+really8.4.1-1) unstable; urgency=medium
+python-tenacity (8.5.0-0ubuntu1) oracular; urgency=medium
 
-  * Team upload.
-  * Revert to 8.4.1, since 8.4.2 breaks several OpenStack packages (closes:
-    #1074690, #1074730).
+  * New upstream release.
 
- -- Colin Watson <cjwatson@debian.org>  Sun, 14 Jul 2024 12:35:48 +0100
+ -- James Page <james.page@ubuntu.com>  Thu, 11 Jul 2024 10:53:00 +0100
 
 python-tenacity (8.4.2-1) unstable; urgency=medium
 
diff -pruN 8.4.2+really8.4.1-2/debian/control 9.0.0-0ubuntu1/debian/control
--- 8.4.2+really8.4.1-2/debian/control	2024-12-18 13:07:28.000000000 +0000
+++ 9.0.0-0ubuntu1/debian/control	2024-07-11 09:51:32.000000000 +0000
@@ -1,7 +1,8 @@
 Source: python-tenacity
 Section: python
 Priority: optional
-Maintainer: Debian Python Team <team+python@tracker.debian.org>
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Debian Python Team <team+python@tracker.debian.org>
 Uploaders:
  Thomas Goirand <zigo@debian.org>,
  Michal Arbet <michal.arbet@ultimum.io>,
diff -pruN 8.4.2+really8.4.1-2/debian/rules 9.0.0-0ubuntu1/debian/rules
--- 8.4.2+really8.4.1-2/debian/rules	2024-12-18 13:07:28.000000000 +0000
+++ 9.0.0-0ubuntu1/debian/rules	2024-06-28 08:24:47.000000000 +0000
@@ -10,7 +10,8 @@ export PYBUILD_AFTER_TEST = rm -r '{buil
 override_dh_installchangelogs:
 	dh_installchangelogs $(CURDIR)/debian/CHANGELOG
 
-execute_before_dh_sphinxdoc:
+override_dh_sphinxdoc:
 ifeq (,$(findstring nodocs, $(DEB_BUILD_OPTIONS)))
 	PYTHON=python3 python3 -m sphinx -b html doc/source debian/python-tenacity-doc/usr/share/doc/python-tenacity-doc/html
+	dh_sphinxdoc -O--buildsystem=python_distutils
 endif
diff -pruN 8.4.2+really8.4.1-2/doc/source/index.rst 9.0.0-0ubuntu1/doc/source/index.rst
--- 8.4.2+really8.4.1-2/doc/source/index.rst	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/doc/source/index.rst	2024-07-29 12:12:16.000000000 +0000
@@ -124,8 +124,8 @@ retrying stuff.
         print("Stopping after 10 seconds")
         raise Exception
 
-If you're on a tight deadline, and exceeding your delay time isn't ok, 
-then you can give up on retries one attempt before you would exceed the delay. 
+If you're on a tight deadline, and exceeding your delay time isn't ok,
+then you can give up on retries one attempt before you would exceed the delay.
 
 .. testcode::
 
@@ -362,7 +362,7 @@ Statistics
 ~~~~~~~~~~
 
 You can access the statistics about the retry made over a function by using the
-`retry` attribute attached to the function and its `statistics` attribute:
+`statistics` attribute attached to the function:
 
 .. testcode::
 
@@ -375,7 +375,7 @@ You can access the statistics about the
     except Exception:
         pass
 
-    print(raise_my_exception.retry.statistics)
+    print(raise_my_exception.statistics)
 
 .. testoutput::
    :hide:
@@ -495,7 +495,7 @@ using the `retry_with` function attached
     except Exception:
         pass
 
-    print(raise_my_exception.retry.statistics)
+    print(raise_my_exception.statistics)
 
 .. testoutput::
    :hide:
@@ -514,6 +514,32 @@ to use the `retry` decorator - you can i
         retryer = Retrying(stop=stop_after_attempt(max_attempts), reraise=True)
         retryer(never_good_enough, 'I really do try')
 
+You may also want to change the behaviour of a decorated function temporarily,
+like in tests to avoid unnecessary wait times. You can modify/patch the `retry`
+attribute attached to the function. Bear in mind this is a write-only attribute,
+statistics should be read from the function `statistics` attribute.
+
+.. testcode::
+
+    @retry(stop=stop_after_attempt(3), wait=wait_fixed(3))
+    def raise_my_exception():
+        raise MyException("Fail")
+
+    from unittest import mock
+
+    with mock.patch.object(raise_my_exception.retry, "wait", wait_fixed(0)):
+        try:
+            raise_my_exception()
+        except Exception:
+            pass
+
+    print(raise_my_exception.statistics)
+
+.. testoutput::
+   :hide:
+
+   ...
+
 Retrying code block
 ~~~~~~~~~~~~~~~~~~~
 
diff -pruN 8.4.2+really8.4.1-2/releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml 9.0.0-0ubuntu1/releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml
--- 8.4.2+really8.4.1-2/releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 9.0.0-0ubuntu1/releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml	2024-07-29 12:12:16.000000000 +0000
@@ -0,0 +1,4 @@
+---
+fixes:
+  - |
+    Avoid overwriting local contexts when applying the retry decorator.
diff -pruN 8.4.2+really8.4.1-2/releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml 9.0.0-0ubuntu1/releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml
--- 8.4.2+really8.4.1-2/releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 9.0.0-0ubuntu1/releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml	2024-07-29 12:12:16.000000000 +0000
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Restore the value of the `retry` attribute for wrapped functions. Also,
+    clarify that those attributes are write-only and statistics should be
+    read from the function attribute directly.
diff -pruN 8.4.2+really8.4.1-2/releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml 9.0.0-0ubuntu1/releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml
--- 8.4.2+really8.4.1-2/releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml	1970-01-01 00:00:00.000000000 +0000
+++ 9.0.0-0ubuntu1/releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml	2024-07-29 12:12:16.000000000 +0000
@@ -0,0 +1,4 @@
+---
+fixes:
+  - |
+    Respects `min` arg for `wait_random_exponential`
diff -pruN 8.4.2+really8.4.1-2/tenacity/__init__.py 9.0.0-0ubuntu1/tenacity/__init__.py
--- 8.4.2+really8.4.1-2/tenacity/__init__.py	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/tenacity/__init__.py	2024-07-29 12:12:16.000000000 +0000
@@ -329,13 +329,19 @@ class BaseRetrying(ABC):
             f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
         )
         def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
-            return self(f, *args, **kw)
+            # Always create a copy to prevent overwriting the local contexts when
+            # calling the same wrapped functions multiple times in the same stack
+            copy = self.copy()
+            wrapped_f.statistics = copy.statistics  # type: ignore[attr-defined]
+            return copy(f, *args, **kw)
 
         def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
             return self.copy(*args, **kwargs).wraps(f)
 
+        # Preserve attributes
         wrapped_f.retry = self  # type: ignore[attr-defined]
         wrapped_f.retry_with = retry_with  # type: ignore[attr-defined]
+        wrapped_f.statistics = {}  # type: ignore[attr-defined]
 
         return wrapped_f  # type: ignore[return-value]
 
diff -pruN 8.4.2+really8.4.1-2/tenacity/asyncio/__init__.py 9.0.0-0ubuntu1/tenacity/asyncio/__init__.py
--- 8.4.2+really8.4.1-2/tenacity/asyncio/__init__.py	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/tenacity/asyncio/__init__.py	2024-07-29 12:12:16.000000000 +0000
@@ -175,18 +175,23 @@ class AsyncRetrying(BaseRetrying):
                 raise StopAsyncIteration
 
     def wraps(self, fn: WrappedFn) -> WrappedFn:
-        fn = super().wraps(fn)
+        wrapped = super().wraps(fn)
         # Ensure wrapper is recognized as a coroutine function.
 
         @functools.wraps(
             fn, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
         )
         async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
-            return await fn(*args, **kwargs)
+            # Always create a copy to prevent overwriting the local contexts when
+            # calling the same wrapped functions multiple times in the same stack
+            copy = self.copy()
+            async_wrapped.statistics = copy.statistics  # type: ignore[attr-defined]
+            return await copy(fn, *args, **kwargs)
 
         # Preserve attributes
-        async_wrapped.retry = fn.retry  # type: ignore[attr-defined]
-        async_wrapped.retry_with = fn.retry_with  # type: ignore[attr-defined]
+        async_wrapped.retry = self  # type: ignore[attr-defined]
+        async_wrapped.retry_with = wrapped.retry_with  # type: ignore[attr-defined]
+        async_wrapped.statistics = {}  # type: ignore[attr-defined]
 
         return async_wrapped  # type: ignore[return-value]
 
diff -pruN 8.4.2+really8.4.1-2/tenacity/wait.py 9.0.0-0ubuntu1/tenacity/wait.py
--- 8.4.2+really8.4.1-2/tenacity/wait.py	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/tenacity/wait.py	2024-07-29 12:12:16.000000000 +0000
@@ -197,7 +197,7 @@ class wait_random_exponential(wait_expon
 
     def __call__(self, retry_state: "RetryCallState") -> float:
         high = super().__call__(retry_state=retry_state)
-        return random.uniform(0, high)
+        return random.uniform(self.min, high)
 
 
 class wait_exponential_jitter(wait_base):
diff -pruN 8.4.2+really8.4.1-2/tenacity.egg-info/PKG-INFO 9.0.0-0ubuntu1/tenacity.egg-info/PKG-INFO
--- 8.4.2+really8.4.1-2/tenacity.egg-info/PKG-INFO	2024-06-17 12:16:58.000000000 +0000
+++ 9.0.0-0ubuntu1/tenacity.egg-info/PKG-INFO	2024-07-29 12:12:25.000000000 +0000
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: tenacity
-Version: 8.4.1
+Version: 9.0.0
 Summary: Retry code until it succeeds
 Home-page: https://github.com/jd/tenacity
 Author: Julien Danjou
diff -pruN 8.4.2+really8.4.1-2/tenacity.egg-info/SOURCES.txt 9.0.0-0ubuntu1/tenacity.egg-info/SOURCES.txt
--- 8.4.2+really8.4.1-2/tenacity.egg-info/SOURCES.txt	2024-06-17 12:16:58.000000000 +0000
+++ 9.0.0-0ubuntu1/tenacity.egg-info/SOURCES.txt	2024-07-29 12:12:25.000000000 +0000
@@ -37,6 +37,8 @@ releasenotes/notes/drop-deprecated-pytho
 releasenotes/notes/drop_deprecated-7ea90b212509b082.yaml
 releasenotes/notes/export-convenience-symbols-981d9611c8b754f3.yaml
 releasenotes/notes/fix-async-loop-with-result-f68e913ccb425aca.yaml
+releasenotes/notes/fix-local-context-overwrite-94190ba06a481631.yaml
+releasenotes/notes/fix-retry-wrapper-attributes-f7a3a45b8e90f257.yaml
 releasenotes/notes/fix-setuptools-config-3af71aa3592b6948.yaml
 releasenotes/notes/fix-wait-typing-b26eecdb6cc0a1de.yaml
 releasenotes/notes/fix_async-52b6594c8e75c4bc.yaml
@@ -51,6 +53,7 @@ releasenotes/notes/sphinx_define_error-6
 releasenotes/notes/support-timedelta-wait-unit-type-5ba1e9fc0fe45523.yaml
 releasenotes/notes/timedelta-for-stop-ef6bf71b88ce9988.yaml
 releasenotes/notes/trio-support-retry-22bd544800cd1f36.yaml
+releasenotes/notes/wait-random-exponential-min-2a4b7eed9f002436.yaml
 releasenotes/notes/wait_exponential_jitter-6ffc81dddcbaa6d3.yaml
 tenacity/__init__.py
 tenacity/_utils.py
@@ -73,6 +76,7 @@ tenacity/asyncio/retry.py
 tests/__init__.py
 tests/test_after.py
 tests/test_asyncio.py
+tests/test_issue_478.py
 tests/test_tenacity.py
 tests/test_tornado.py
 tests/test_utils.py
\ No newline at end of file
diff -pruN 8.4.2+really8.4.1-2/tests/test_asyncio.py 9.0.0-0ubuntu1/tests/test_asyncio.py
--- 8.4.2+really8.4.1-2/tests/test_asyncio.py	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/tests/test_asyncio.py	2024-07-29 12:12:16.000000000 +0000
@@ -17,6 +17,7 @@ import asyncio
 import inspect
 import unittest
 from functools import wraps
+from unittest import mock
 
 try:
     import trio
@@ -59,7 +60,7 @@ async def _retryable_coroutine(thing):
 @retry(stop=stop_after_attempt(2))
 async def _retryable_coroutine_with_2_attempts(thing):
     await asyncio.sleep(0.00001)
-    thing.go()
+    return thing.go()
 
 
 class TestAsyncio(unittest.TestCase):
@@ -394,6 +395,64 @@ class TestContextManager(unittest.TestCa
                     await _async_function(thing)
 
 
+class TestDecoratorWrapper(unittest.TestCase):
+    @asynctest
+    async def test_retry_function_attributes(self):
+        """Test that the wrapped function attributes are exposed as intended.
+
+        - 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
+        """
+
+        self.assertTrue(
+            await _retryable_coroutine_with_2_attempts(NoIOErrorAfterCount(1))
+        )
+
+        expected_stats = {
+            "attempt_number": 2,
+            "delay_since_first_attempt": mock.ANY,
+            "idle_for": mock.ANY,
+            "start_time": mock.ANY,
+        }
+        self.assertEqual(
+            _retryable_coroutine_with_2_attempts.statistics,  # type: ignore[attr-defined]
+            expected_stats,
+        )
+        self.assertEqual(
+            _retryable_coroutine_with_2_attempts.retry.statistics,  # type: ignore[attr-defined]
+            {},
+        )
+
+        with mock.patch.object(
+            _retryable_coroutine_with_2_attempts.retry,  # type: ignore[attr-defined]
+            "stop",
+            tenacity.stop_after_attempt(1),
+        ):
+            try:
+                self.assertTrue(
+                    await _retryable_coroutine_with_2_attempts(NoIOErrorAfterCount(2))
+                )
+            except RetryError as exc:
+                expected_stats = {
+                    "attempt_number": 1,
+                    "delay_since_first_attempt": mock.ANY,
+                    "idle_for": mock.ANY,
+                    "start_time": mock.ANY,
+                }
+                self.assertEqual(
+                    _retryable_coroutine_with_2_attempts.statistics,  # type: ignore[attr-defined]
+                    expected_stats,
+                )
+                self.assertEqual(exc.last_attempt.attempt_number, 1)
+                self.assertEqual(
+                    _retryable_coroutine_with_2_attempts.retry.statistics,  # type: ignore[attr-defined]
+                    {},
+                )
+            else:
+                self.fail("RetryError should have been raised after 1 attempt")
+
+
 # make sure mypy accepts passing an async sleep function
 # https://github.com/jd/tenacity/issues/399
 async def my_async_sleep(x: float) -> None:
diff -pruN 8.4.2+really8.4.1-2/tests/test_issue_478.py 9.0.0-0ubuntu1/tests/test_issue_478.py
--- 8.4.2+really8.4.1-2/tests/test_issue_478.py	1970-01-01 00:00:00.000000000 +0000
+++ 9.0.0-0ubuntu1/tests/test_issue_478.py	2024-07-29 12:12:16.000000000 +0000
@@ -0,0 +1,118 @@
+import asyncio
+import typing
+import unittest
+
+from functools import wraps
+
+from tenacity import RetryCallState, retry
+
+
+def asynctest(
+    callable_: typing.Callable[..., typing.Any],
+) -> typing.Callable[..., typing.Any]:
+    @wraps(callable_)
+    def wrapper(*a: typing.Any, **kw: typing.Any) -> typing.Any:
+        loop = asyncio.get_event_loop()
+        return loop.run_until_complete(callable_(*a, **kw))
+
+    return wrapper
+
+
+MAX_RETRY_FIX_ATTEMPTS = 2
+
+
+class TestIssue478(unittest.TestCase):
+    def test_issue(self) -> None:
+        results = []
+
+        def do_retry(retry_state: RetryCallState) -> bool:
+            outcome = retry_state.outcome
+            assert outcome
+            ex = outcome.exception()
+            _subject_: str = retry_state.args[0]
+
+            if _subject_ == "Fix":  # no retry on fix failure
+                return False
+
+            if retry_state.attempt_number >= MAX_RETRY_FIX_ATTEMPTS:
+                return False
+
+            if ex:
+                do_fix_work()
+                return True
+
+            return False
+
+        @retry(reraise=True, retry=do_retry)
+        def _do_work(subject: str) -> None:
+            if subject == "Error":
+                results.append(f"{subject} is not working")
+                raise Exception(f"{subject} is not working")
+            results.append(f"{subject} is working")
+
+        def do_any_work(subject: str) -> None:
+            _do_work(subject)
+
+        def do_fix_work() -> None:
+            _do_work("Fix")
+
+        try:
+            do_any_work("Error")
+        except Exception as exc:
+            assert str(exc) == "Error is not working"
+        else:
+            assert False, "No exception caught"
+
+        assert results == [
+            "Error is not working",
+            "Fix is working",
+            "Error is not working",
+        ]
+
+    @asynctest
+    async def test_async(self) -> None:
+        results = []
+
+        async def do_retry(retry_state: RetryCallState) -> bool:
+            outcome = retry_state.outcome
+            assert outcome
+            ex = outcome.exception()
+            _subject_: str = retry_state.args[0]
+
+            if _subject_ == "Fix":  # no retry on fix failure
+                return False
+
+            if retry_state.attempt_number >= MAX_RETRY_FIX_ATTEMPTS:
+                return False
+
+            if ex:
+                await do_fix_work()
+                return True
+
+            return False
+
+        @retry(reraise=True, retry=do_retry)
+        async def _do_work(subject: str) -> None:
+            if subject == "Error":
+                results.append(f"{subject} is not working")
+                raise Exception(f"{subject} is not working")
+            results.append(f"{subject} is working")
+
+        async def do_any_work(subject: str) -> None:
+            await _do_work(subject)
+
+        async def do_fix_work() -> None:
+            await _do_work("Fix")
+
+        try:
+            await do_any_work("Error")
+        except Exception as exc:
+            assert str(exc) == "Error is not working"
+        else:
+            assert False, "No exception caught"
+
+        assert results == [
+            "Error is not working",
+            "Fix is working",
+            "Error is not working",
+        ]
diff -pruN 8.4.2+really8.4.1-2/tests/test_tenacity.py 9.0.0-0ubuntu1/tests/test_tenacity.py
--- 8.4.2+really8.4.1-2/tests/test_tenacity.py	2024-06-17 12:16:47.000000000 +0000
+++ 9.0.0-0ubuntu1/tests/test_tenacity.py	2024-07-29 12:12:16.000000000 +0000
@@ -25,6 +25,7 @@ import warnings
 from contextlib import contextmanager
 from copy import copy
 from fractions import Fraction
+from unittest import mock
 
 import pytest
 
@@ -471,9 +472,17 @@ class TestWaitConditions(unittest.TestCa
             self._assert_inclusive_range(fn(make_retry_state(8, 0)), 0, 60.0)
             self._assert_inclusive_range(fn(make_retry_state(9, 0)), 0, 60.0)
 
-        fn = tenacity.wait_random_exponential(10, 5)
+        # max wait
+        max_wait = 5
+        fn = tenacity.wait_random_exponential(10, max_wait)
         for _ in range(1000):
-            self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0.00, 5.00)
+            self._assert_inclusive_range(fn(make_retry_state(1, 0)), 0.00, max_wait)
+
+        # min wait
+        min_wait = 5
+        fn = tenacity.wait_random_exponential(min=min_wait)
+        for _ in range(1000):
+            self._assert_inclusive_range(fn(make_retry_state(1, 0)), min_wait, 5)
 
         # Default arguments exist
         fn = tenacity.wait_random_exponential()
@@ -1073,7 +1082,7 @@ class TestDecoratorWrapper(unittest.Test
                 _retryable_test_with_unless_exception_type_name(NameErrorUntilCount(5))
             )
         except NameError as e:
-            s = _retryable_test_with_unless_exception_type_name.retry.statistics
+            s = _retryable_test_with_unless_exception_type_name.statistics
             self.assertTrue(s["attempt_number"] == 6)
             print(e)
         else:
@@ -1088,7 +1097,7 @@ class TestDecoratorWrapper(unittest.Test
                 )
             )
         except NameError as e:
-            s = _retryable_test_with_unless_exception_type_no_input.retry.statistics
+            s = _retryable_test_with_unless_exception_type_no_input.statistics
             self.assertTrue(s["attempt_number"] == 6)
             print(e)
         else:
@@ -1111,7 +1120,7 @@ class TestDecoratorWrapper(unittest.Test
                 _retryable_test_if_exception_message_message(NoCustomErrorAfterCount(3))
             )
         except CustomError:
-            print(_retryable_test_if_exception_message_message.retry.statistics)
+            print(_retryable_test_if_exception_message_message.statistics)
             self.fail("CustomError should've been retried from errormessage")
 
     def test_retry_if_not_exception_message(self):
@@ -1122,7 +1131,7 @@ class TestDecoratorWrapper(unittest.Test
                 )
             )
         except CustomError:
-            s = _retryable_test_if_not_exception_message_message.retry.statistics
+            s = _retryable_test_if_not_exception_message_message.statistics
             self.assertTrue(s["attempt_number"] == 1)
 
     def test_retry_if_not_exception_message_delay(self):
@@ -1131,7 +1140,7 @@ class TestDecoratorWrapper(unittest.Test
                 _retryable_test_not_exception_message_delay(NameErrorUntilCount(3))
             )
         except NameError:
-            s = _retryable_test_not_exception_message_delay.retry.statistics
+            s = _retryable_test_not_exception_message_delay.statistics
             print(s["attempt_number"])
             self.assertTrue(s["attempt_number"] == 4)
 
@@ -1151,7 +1160,7 @@ class TestDecoratorWrapper(unittest.Test
                 )
             )
         except CustomError:
-            s = _retryable_test_if_not_exception_message_message.retry.statistics
+            s = _retryable_test_if_not_exception_message_message.statistics
             self.assertTrue(s["attempt_number"] == 1)
 
     def test_retry_if_exception_cause_type(self):
@@ -1209,6 +1218,43 @@ class TestDecoratorWrapper(unittest.Test
         h = retrying.wraps(Hello())
         self.assertEqual(h(), "Hello")
 
+    def test_retry_function_attributes(self):
+        """Test that the wrapped function attributes are exposed as intended.
+
+        - 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
+        """
+
+        self.assertTrue(_retryable_test_with_stop(NoneReturnUntilAfterCount(2)))
+
+        expected_stats = {
+            "attempt_number": 3,
+            "delay_since_first_attempt": mock.ANY,
+            "idle_for": mock.ANY,
+            "start_time": mock.ANY,
+        }
+        self.assertEqual(_retryable_test_with_stop.statistics, expected_stats)
+        self.assertEqual(_retryable_test_with_stop.retry.statistics, {})
+
+        with mock.patch.object(
+            _retryable_test_with_stop.retry, "stop", tenacity.stop_after_attempt(1)
+        ):
+            try:
+                self.assertTrue(_retryable_test_with_stop(NoneReturnUntilAfterCount(2)))
+            except RetryError as exc:
+                expected_stats = {
+                    "attempt_number": 1,
+                    "delay_since_first_attempt": mock.ANY,
+                    "idle_for": mock.ANY,
+                    "start_time": mock.ANY,
+                }
+                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, {})
+            else:
+                self.fail("RetryError should have been raised after 1 attempt")
+
 
 class TestRetryWith:
     def test_redefine_wait(self):
@@ -1479,21 +1525,21 @@ class TestStatistics(unittest.TestCase):
         def _foobar():
             return 42
 
-        self.assertEqual({}, _foobar.retry.statistics)
+        self.assertEqual({}, _foobar.statistics)
         _foobar()
-        self.assertEqual(1, _foobar.retry.statistics["attempt_number"])
+        self.assertEqual(1, _foobar.statistics["attempt_number"])
 
     def test_stats_failing(self):
         @retry(stop=tenacity.stop_after_attempt(2))
         def _foobar():
             raise ValueError(42)
 
-        self.assertEqual({}, _foobar.retry.statistics)
+        self.assertEqual({}, _foobar.statistics)
         try:
             _foobar()
         except Exception:  # noqa: B902
             pass
-        self.assertEqual(2, _foobar.retry.statistics["attempt_number"])
+        self.assertEqual(2, _foobar.statistics["attempt_number"])
 
 
 class TestRetryErrorCallback(unittest.TestCase):
