-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor/unify/extract
shutil.rmtree
callbacks (and avoid repetitio…
…n) (#4682)
- Loading branch information
Showing
8 changed files
with
90 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
"""Convenience layer on top of stdlib's shutil and os""" | ||
|
||
import os | ||
import stat | ||
from typing import Callable, TypeVar | ||
|
||
from .compat import py311 | ||
|
||
from distutils import log | ||
|
||
try: | ||
from os import chmod # pyright: ignore[reportAssignmentType] | ||
# Losing type-safety w/ pyright, but that's ok | ||
except ImportError: # pragma: no cover | ||
# Jython compatibility | ||
def chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy reuses the imported definition anyway | ||
pass | ||
|
||
|
||
_T = TypeVar("_T") | ||
|
||
|
||
def attempt_chmod_verbose(path, mode): | ||
log.debug("changing mode of %s to %o", path, mode) | ||
try: | ||
chmod(path, mode) | ||
except OSError as e: # pragma: no cover | ||
log.debug("chmod failed: %s", e) | ||
|
||
|
||
# Must match shutil._OnExcCallback | ||
def _auto_chmod( | ||
func: Callable[..., _T], arg: str, exc: BaseException | ||
) -> _T: # pragma: no cover | ||
"""shutils onexc callback to automatically call chmod for certain functions.""" | ||
# Only retry for scenarios known to have an issue | ||
if func in [os.unlink, os.remove] and os.name == 'nt': | ||
attempt_chmod_verbose(arg, stat.S_IWRITE) | ||
return func(arg) | ||
raise exc | ||
|
||
|
||
def rmtree(path, ignore_errors=False, onexc=_auto_chmod): | ||
""" | ||
Similar to ``shutil.rmtree`` but automatically executes ``chmod`` | ||
for well know Windows failure scenarios. | ||
""" | ||
return py311.shutil_rmtree(path, ignore_errors, onexc) | ||
|
||
|
||
def rmdir(path, **opts): | ||
if os.path.isdir(path): | ||
rmtree(path, **opts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import stat | ||
import sys | ||
from unittest.mock import Mock | ||
|
||
from setuptools import _shutil | ||
|
||
|
||
def test_rmtree_readonly(monkeypatch, tmp_path): | ||
"""Verify onerr works as expected""" | ||
|
||
tmp_dir = tmp_path / "with_readonly" | ||
tmp_dir.mkdir() | ||
some_file = tmp_dir.joinpath("file.txt") | ||
some_file.touch() | ||
some_file.chmod(stat.S_IREAD) | ||
|
||
expected_count = 1 if sys.platform.startswith("win") else 0 | ||
chmod_fn = Mock(wraps=_shutil.attempt_chmod_verbose) | ||
monkeypatch.setattr(_shutil, "attempt_chmod_verbose", chmod_fn) | ||
|
||
_shutil.rmtree(tmp_dir) | ||
assert chmod_fn.call_count == expected_count | ||
assert not tmp_dir.is_dir() |