Skip to content

Commit b69ed81

Browse files
authored
Merge pull request #12187 from cosmicexplorer/fix-tempdir-cleanup
2 parents d064174 + 6cc961e commit b69ed81

File tree

3 files changed

+33
-19
lines changed

3 files changed

+33
-19
lines changed

news/12187.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix improper handling of the new onexc argument of ``shutil.rmtree()`` in Python 3.12.

src/pip/_internal/utils/misc.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from functools import partial
1515
from io import StringIO
1616
from itertools import filterfalse, tee, zip_longest
17-
from types import TracebackType
17+
from pathlib import Path
18+
from types import FunctionType, TracebackType
1819
from typing import (
1920
Any,
2021
BinaryIO,
@@ -67,6 +68,8 @@
6768
ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
6869
VersionInfo = Tuple[int, int, int]
6970
NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
71+
OnExc = Callable[[FunctionType, Path, BaseException], Any]
72+
OnErr = Callable[[FunctionType, Path, ExcInfo], Any]
7073

7174

7275
def get_pip_version() -> str:
@@ -127,16 +130,23 @@ def get_prog() -> str:
127130
def rmtree(
128131
dir: str,
129132
ignore_errors: bool = False,
130-
onexc: Optional[Callable[[Any, Any, Any], Any]] = None,
133+
onexc: Optional[OnExc] = None,
131134
) -> None:
132135
if ignore_errors:
133136
onexc = _onerror_ignore
134-
elif onexc is None:
137+
if onexc is None:
135138
onexc = _onerror_reraise
139+
handler: OnErr = partial(
140+
# `[func, path, Union[ExcInfo, BaseException]] -> Any` is equivalent to
141+
# `Union[([func, path, ExcInfo] -> Any), ([func, path, BaseException] -> Any)]`.
142+
cast(Union[OnExc, OnErr], rmtree_errorhandler),
143+
onexc=onexc,
144+
)
136145
if sys.version_info >= (3, 12):
137-
shutil.rmtree(dir, onexc=partial(rmtree_errorhandler, onexc=onexc))
146+
# See https://docs.python.org/3.12/whatsnew/3.12.html#shutil.
147+
shutil.rmtree(dir, onexc=handler)
138148
else:
139-
shutil.rmtree(dir, onerror=partial(rmtree_errorhandler, onexc=onexc))
149+
shutil.rmtree(dir, onerror=handler)
140150

141151

142152
def _onerror_ignore(*_args: Any) -> None:
@@ -148,11 +158,11 @@ def _onerror_reraise(*_args: Any) -> None:
148158

149159

150160
def rmtree_errorhandler(
151-
func: Callable[..., Any],
152-
path: str,
161+
func: FunctionType,
162+
path: Path,
153163
exc_info: Union[ExcInfo, BaseException],
154164
*,
155-
onexc: Callable[..., Any] = _onerror_reraise,
165+
onexc: OnExc = _onerror_reraise,
156166
) -> None:
157167
"""
158168
`rmtree` error handler to 'force' a file remove (i.e. like `rm -f`).
@@ -183,6 +193,8 @@ def rmtree_errorhandler(
183193
except OSError:
184194
pass
185195

196+
if not isinstance(exc_info, BaseException):
197+
_, exc_info, _ = exc_info
186198
onexc(func, path, exc_info)
187199

188200

src/pip/_internal/utils/temp_dir.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
import tempfile
66
import traceback
77
from contextlib import ExitStack, contextmanager
8+
from pathlib import Path
9+
from types import FunctionType
810
from typing import (
911
Any,
10-
Callable,
1112
Dict,
1213
Generator,
1314
List,
1415
Optional,
15-
Tuple,
16-
Type,
1716
TypeVar,
1817
Union,
1918
)
@@ -188,22 +187,24 @@ def cleanup(self) -> None:
188187
errors: List[BaseException] = []
189188

190189
def onerror(
191-
func: Callable[[str], Any],
192-
path: str,
193-
exc_info: Tuple[Type[BaseException], BaseException, Any],
190+
func: FunctionType,
191+
path: Path,
192+
exc_val: BaseException,
194193
) -> None:
195194
"""Log a warning for a `rmtree` error and continue"""
196-
exc_val = "\n".join(traceback.format_exception_only(*exc_info[:2]))
197-
exc_val = exc_val.rstrip() # remove trailing new line
195+
formatted_exc = "\n".join(
196+
traceback.format_exception_only(type(exc_val), exc_val)
197+
)
198+
formatted_exc = formatted_exc.rstrip() # remove trailing new line
198199
if func in (os.unlink, os.remove, os.rmdir):
199200
logger.debug(
200201
"Failed to remove a temporary file '%s' due to %s.\n",
201202
path,
202-
exc_val,
203+
formatted_exc,
203204
)
204205
else:
205-
logger.debug("%s failed with %s.", func.__qualname__, exc_val)
206-
errors.append(exc_info[1])
206+
logger.debug("%s failed with %s.", func.__qualname__, formatted_exc)
207+
errors.append(exc_val)
207208

208209
if self.ignore_cleanup_errors:
209210
try:

0 commit comments

Comments
 (0)