Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/+removedirs-missing.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``Path.removedirs_p`` now tolerates missing directories, including repeated cleanup of an already removed tree.
3 changes: 2 additions & 1 deletion path/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,8 @@ def removedirs(self) -> Self:
def removedirs_p(self) -> Self:
"""Like :meth:`removedirs`, but does not raise an exception if the
directory is not empty or does not exist."""
with contextlib.suppress(FileExistsError, DirectoryNotEmpty):
# 2026-10-11: Repeated cleanup must also tolerate missing directories.
with contextlib.suppress(FileNotFoundError, FileExistsError, DirectoryNotEmpty):
with DirectoryNotEmpty.translate():
self.removedirs()
return self
Expand Down
24 changes: 24 additions & 0 deletions tests/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,30 @@ def test_removedirs_p(self, tmpdir):
# TODO: shouldn't sub get removed?
# assert not (dir / 'sub').is_dir()

@pytest.mark.parametrize('nested', [False, True])
def test_removedirs_p_missing(self, tmpdir, nested):
# 2026-10-11: Cleanup is harmless when the directory or its parent is absent.
directory = Path(tmpdir) / 'missing'
if nested:
directory /= 'child'
assert directory.removedirs_p() is directory
assert not directory.exists()

def test_removedirs_p_repeated(self, tmpdir):
# 2026-10-11: Removing an empty tree twice must remain idempotent.
(Path(tmpdir) / 'keep').touch()
directory = (Path(tmpdir) / 'parent' / 'child').makedirs()
assert directory.removedirs_p() is directory
assert directory.removedirs_p() is directory
assert not directory.parent.exists()

def test_removedirs_p_file(self, tmpdir):
# 2026-10-11: Existing files are still an error rather than absent directories.
file = (Path(tmpdir) / 'file').touch()
with pytest.raises(OSError):
file.removedirs_p()
assert file.is_file()

@pytest.mark.skipif("not hasattr(Path, 'group')")
def test_group(self, tmpdir):
file = Path(tmpdir).joinpath('file').touch()
Expand Down