Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge bugfixes from distutils #4737

Merged
merged 8 commits into from
Nov 7, 2024
1 change: 1 addition & 0 deletions newsfragments/+f0b61194.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Merge with pypa/distutils@251797602, including fix for dirutil.mkpath handling in pypa/distutils#304.
11 changes: 3 additions & 8 deletions setuptools/_distutils/dir_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ def wrap(self, func):
def wrapper(path, *args, **kwargs):
if path.absolute() in self:
return
result = func(path, *args, **kwargs)
self.add(path.absolute())
return func(path, *args, **kwargs)
return result

return wrapper

Expand All @@ -44,29 +45,23 @@ def wrapper(path, *args, **kwargs):

@functools.singledispatch
@wrapper
def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False):
def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False) -> None:
"""Create a directory and any missing ancestor directories.

If the directory already exists (or if 'name' is the empty string, which
means the current directory, which of course exists), then do nothing.
Raise DistutilsFileError if unable to create some directory along the way
(eg. some sub-path exists, but is a file rather than a directory).
If 'verbose' is true, log the directory created.
Return the list of directories actually created.
"""
if verbose and not name.is_dir():
log.info("creating %s", name)

ancestry = itertools.chain((name,), name.parents)
missing = (path for path in ancestry if not path.is_dir())

try:
dry_run or name.mkdir(mode=mode, parents=True, exist_ok=True)
except OSError as exc:
raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}")

return list(map(str, missing))


@mkpath.register
def _(name: str, *args, **kwargs):
Expand Down
22 changes: 22 additions & 0 deletions setuptools/_distutils/tests/test_dir_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for distutils.dir_util."""

import os
import pathlib
import stat
import unittest.mock as mock
from distutils import dir_util, errors
Expand Down Expand Up @@ -110,3 +111,24 @@ def test_copy_tree_exception_in_listdir(self):
):
src = self.tempdirs[-1]
dir_util.copy_tree(src, None)

def test_mkpath_exception_uncached(self, monkeypatch, tmp_path):
"""
Caching should not remember failed attempts.

pypa/distutils#304
"""

class FailPath(pathlib.Path):
def mkdir(self, *args, **kwargs):
raise OSError("Failed to create directory")

target = tmp_path / 'foodir'

with pytest.raises(errors.DistutilsFileError):
mkpath(FailPath(target))

assert not target.exists()

mkpath(target)
assert target.exists()
Loading