|
1 | 1 | import contextlib
|
2 | 2 | import errno
|
| 3 | +import functools |
3 | 4 | import getpass
|
4 | 5 | import hashlib
|
5 | 6 | import io
|
|
14 | 15 | from functools import partial
|
15 | 16 | from io import StringIO
|
16 | 17 | from itertools import filterfalse, tee, zip_longest
|
17 |
| -from types import TracebackType |
| 18 | +from pathlib import Path |
| 19 | +from types import FunctionType, TracebackType |
18 | 20 | from typing import (
|
19 | 21 | Any,
|
20 | 22 | BinaryIO,
|
|
67 | 69 | ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
|
68 | 70 | VersionInfo = Tuple[int, int, int]
|
69 | 71 | NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
|
| 72 | +OnExc = Callable[[FunctionType, Path, BaseException], Any] |
| 73 | +OnErr = Callable[[FunctionType, Path, ExcInfo], Any] |
70 | 74 |
|
71 | 75 |
|
72 | 76 | def get_pip_version() -> str:
|
@@ -121,22 +125,44 @@ def get_prog() -> str:
|
121 | 125 | return "pip"
|
122 | 126 |
|
123 | 127 |
|
| 128 | +def bare_exc_to_onexc(exc_val: BaseException) -> ExcInfo: |
| 129 | + exc_ty = type(exc_val) |
| 130 | + tb = exc_val.__traceback__ |
| 131 | + if tb is None: |
| 132 | + import inspect |
| 133 | + |
| 134 | + frame = inspect.currentframe() |
| 135 | + assert frame is not None |
| 136 | + tb = TracebackType(None, frame, frame.f_lasti, frame.f_lineno) |
| 137 | + return (exc_ty, exc_val, tb) |
| 138 | + |
| 139 | + |
| 140 | +def extract_exc_info_arg(f: OnErr) -> OnExc: |
| 141 | + def g(fn: FunctionType, p: Path, e: BaseException) -> Any: |
| 142 | + info = bare_exc_to_onexc(e) |
| 143 | + return f(fn, p, info) |
| 144 | + |
| 145 | + return functools.update_wrapper(g, f) |
| 146 | + |
| 147 | + |
124 | 148 | # Retry every half second for up to 3 seconds
|
125 | 149 | # Tenacity raises RetryError by default, explicitly raise the original exception
|
126 | 150 | @retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
|
127 | 151 | def rmtree(
|
128 | 152 | dir: str,
|
129 | 153 | ignore_errors: bool = False,
|
130 |
| - onexc: Optional[Callable[[Any, Any, Any], Any]] = None, |
| 154 | + onexc: Optional[OnErr] = None, |
131 | 155 | ) -> None:
|
132 | 156 | if ignore_errors:
|
133 | 157 | onexc = _onerror_ignore
|
134 |
| - elif onexc is None: |
| 158 | + if onexc is None: |
135 | 159 | onexc = _onerror_reraise
|
| 160 | + handler: OnErr = partial(rmtree_errorhandler, onexc=onexc) |
136 | 161 | if sys.version_info >= (3, 12):
|
137 |
| - shutil.rmtree(dir, onexc=partial(rmtree_errorhandler, onexc=onexc)) |
| 162 | + exc_handler = extract_exc_info_arg(handler) |
| 163 | + shutil.rmtree(dir, onexc=exc_handler) |
138 | 164 | else:
|
139 |
| - shutil.rmtree(dir, onerror=partial(rmtree_errorhandler, onexc=onexc)) |
| 165 | + shutil.rmtree(dir, onerror=handler) |
140 | 166 |
|
141 | 167 |
|
142 | 168 | def _onerror_ignore(*_args: Any) -> None:
|
|
0 commit comments