Skip to content

Commit 851de85

Browse files
committed
Replace tenacity.retry with our own decorator
Tenacity is a 1600+ LOC dependency which is only used for a convenient `@retry` decorator... which is used a grand total of two times. This is a lot of code for what can be trivially reimplemented by us. This also incidentally improves startup performance as tenacity imports asyncio which is expensive and unnecessary for us.
1 parent af00870 commit 851de85

File tree

4 files changed

+45
-10
lines changed

4 files changed

+45
-10
lines changed

src/pip/_internal/utils/filesystem.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
from tempfile import NamedTemporaryFile
88
from typing import Any, BinaryIO, Generator, List, Union, cast
99

10-
from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
11-
1210
from pip._internal.utils.compat import get_path_uid
1311
from pip._internal.utils.misc import format_size
12+
from pip._internal.utils.retry import retry
1413

1514

1615
def check_path_owner(path: str) -> bool:
@@ -65,10 +64,7 @@ def adjacent_tmp_file(path: str, **kwargs: Any) -> Generator[BinaryIO, None, Non
6564
os.fsync(result.fileno())
6665

6766

68-
# Tenacity raises RetryError by default, explicitly raise the original exception
69-
_replace_retry = retry(reraise=True, stop=stop_after_delay(1), wait=wait_fixed(0.25))
70-
71-
replace = _replace_retry(os.replace)
67+
replace = retry(stop_after_delay=1, wait=0.25)(os.replace)
7268

7369

7470
# test_writable_dir and _test_writable_dir_win are copied from Flit,

src/pip/_internal/utils/misc.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@
3636

3737
from pip._vendor.packaging.requirements import Requirement
3838
from pip._vendor.pyproject_hooks import BuildBackendHookCaller
39-
from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
4039

4140
from pip import __version__
4241
from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment
4342
from pip._internal.locations import get_major_minor_version
4443
from pip._internal.utils.compat import WINDOWS
44+
from pip._internal.utils.retry import retry
4545
from pip._internal.utils.virtualenv import running_under_virtualenv
4646

4747
__all__ = [
@@ -120,8 +120,7 @@ def get_prog() -> str:
120120

121121

122122
# Retry every half second for up to 3 seconds
123-
# Tenacity raises RetryError by default, explicitly raise the original exception
124-
@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
123+
@retry(stop_after_delay=3, wait=0.5)
125124
def rmtree(
126125
dir: str,
127126
ignore_errors: bool = False,

src/pip/_internal/utils/retry.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import functools
2+
from time import monotonic, sleep
3+
from typing import Callable, TypeVar
4+
5+
from pip._vendor.typing_extensions import ParamSpec
6+
7+
T = TypeVar("T")
8+
P = ParamSpec("P")
9+
10+
11+
def retry(
12+
wait: float, stop_after_delay: float
13+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
14+
"""Decorator to automatically retry a function on error.
15+
16+
If the function raises, the function is recalled with the same arguments
17+
until it returns or the time limit is reached. When the time limit is
18+
surpassed, the last exception raised is reraised.
19+
20+
:param wait: The time to wait after an error before retrying, in seconds.
21+
:param stop_after_delay: The time limit after which retries will cease,
22+
in seconds.
23+
"""
24+
25+
def wrapper(func: Callable[P, T]) -> Callable[P, T]:
26+
27+
@functools.wraps(func)
28+
def retry_wrapped(*args: P.args, **kwargs: P.kwargs) -> T:
29+
start_time = monotonic()
30+
while True:
31+
try:
32+
return func(*args, **kwargs)
33+
except Exception:
34+
if monotonic() - start_time > stop_after_delay:
35+
raise
36+
sleep(wait)
37+
38+
return retry_wrapped
39+
40+
return wrapper

src/pip/_internal/utils/temp_dir.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def onerror(
208208

209209
if self.ignore_cleanup_errors:
210210
try:
211-
# first try with tenacity; retrying to handle ephemeral errors
211+
# first try with @retry; retrying to handle ephemeral errors
212212
rmtree(self._path, ignore_errors=False)
213213
except OSError:
214214
# last pass ignore/log all errors

0 commit comments

Comments
 (0)