Skip to content

Commit 8cdfee1

Browse files
Fidget-Spinnerakulakovserhiy-storchaka
authored
bpo-43153: Don't mask PermissionError with NotADirectoryError during tempdirectory cleanup (GH-29940)
Co-authored-by: andrei kulakov <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 5aa317e commit 8cdfee1

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

Lib/tempfile.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import io as _io
4242
import os as _os
4343
import shutil as _shutil
44+
import stat as _stat
4445
import errno as _errno
4546
from random import Random as _Random
4647
import sys as _sys
@@ -889,8 +890,31 @@ def resetperms(path):
889890

890891
try:
891892
_os.unlink(path)
892-
# PermissionError is raised on FreeBSD for directories
893-
except (IsADirectoryError, PermissionError):
893+
except IsADirectoryError:
894+
cls._rmtree(path, ignore_errors=ignore_errors)
895+
except PermissionError:
896+
# The PermissionError handler was originally added for
897+
# FreeBSD in directories, but it seems that it is raised
898+
# on Windows too.
899+
# bpo-43153: Calling _rmtree again may
900+
# raise NotADirectoryError and mask the PermissionError.
901+
# So we must re-raise the current PermissionError if
902+
# path is not a directory.
903+
try:
904+
st = _os.lstat(path)
905+
except OSError:
906+
if ignore_errors:
907+
return
908+
raise
909+
if (_stat.S_ISLNK(st.st_mode) or
910+
not _stat.S_ISDIR(st.st_mode) or
911+
(hasattr(st, 'st_file_attributes') and
912+
st.st_file_attributes & _stat.FILE_ATTRIBUTE_REPARSE_POINT and
913+
st.st_reparse_tag == _stat.IO_REPARSE_TAG_MOUNT_POINT)
914+
):
915+
if ignore_errors:
916+
return
917+
raise
894918
cls._rmtree(path, ignore_errors=ignore_errors)
895919
except FileNotFoundError:
896920
pass

Lib/test/test_tempfile.py

+11
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,17 @@ def test_explicit_cleanup_ignore_errors(self):
16411641
temp_path.exists(),
16421642
f"TemporaryDirectory {temp_path!s} exists after cleanup")
16431643

1644+
@unittest.skipUnless(os.name == "nt", "Only on Windows.")
1645+
def test_explicit_cleanup_correct_error(self):
1646+
with tempfile.TemporaryDirectory() as working_dir:
1647+
temp_dir = self.do_create(dir=working_dir)
1648+
with open(os.path.join(temp_dir.name, "example.txt"), 'wb'):
1649+
# Previously raised NotADirectoryError on some OSes
1650+
# (e.g. Windows). See bpo-43153.
1651+
with self.assertRaises(PermissionError):
1652+
temp_dir.cleanup()
1653+
1654+
16441655
@os_helper.skip_unless_symlink
16451656
def test_cleanup_with_symlink_to_a_directory(self):
16461657
# cleanup() should not follow symlinks to directories (issue #12464)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
On Windows, ``tempfile.TemporaryDirectory`` previously masked a
2+
``PermissionError`` with ``NotADirectoryError`` during directory cleanup. It
3+
now correctly raises ``PermissionError`` if errors are not ignored. Patch by
4+
Andrei Kulakov and Ken Jin.

0 commit comments

Comments
 (0)