diff --git a/src/contextsafe/diagnostics.py b/src/contextsafe/diagnostics.py index dd36cc8..8096e79 100644 --- a/src/contextsafe/diagnostics.py +++ b/src/contextsafe/diagnostics.py @@ -34,6 +34,7 @@ from __future__ import annotations +import errno import os import platform import sys @@ -231,9 +232,13 @@ def remove_cleanup(plan: CleanupPlan) -> tuple[int, int]: # deleting the thing we just declined to delete. try: target.rmdir() - except OSError: - retained += 1 - continue + except OSError as exc: + if exc.errno in (errno.ENOTEMPTY, errno.EEXIST): + retained += 1 + continue + raise ContextSafeError( + "cleanup_io_error", "$", "a workspace entry could not be removed" + ) from exc else: try: target.unlink() diff --git a/tests/test_diagnostics.py b/tests/test_diagnostics.py index 864ce30..8dada83 100644 --- a/tests/test_diagnostics.py +++ b/tests/test_diagnostics.py @@ -16,6 +16,7 @@ from __future__ import annotations +import errno import json import os import unicodedata @@ -549,6 +550,21 @@ def refuse(self: Path, *args: Any, **kwargs: Any) -> None: assert excinfo.value.code == "cleanup_io_error" +def test_removal_reports_a_directory_it_cannot_delete_for_non_empty_error( + monkeypatch: pytest.MonkeyPatch, populated_workspace: Path +) -> None: + """A directory rmdir failure from permission/IO error raises cleanup_io_error.""" + + def refuse(self: Path, *args: Any, **kwargs: Any) -> None: + raise PermissionError(errno.EACCES, "Permission denied") + + plan = enumerate_cleanup(populated_workspace) + monkeypatch.setattr(Path, "rmdir", refuse) + with pytest.raises(ContextSafeError) as excinfo: + remove_cleanup(plan) + assert excinfo.value.code == "cleanup_io_error" + + def test_an_unreadable_log_is_reported_not_overwritten( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: