Skip to content

Commit 23920a0

Browse files
miss-islingtonFidget-Spinnerakulakovserhiy-storchaka
authored
[3.11] bpo-43153: Don't mask PermissionError with NotADirectoryError during tempdirectory cleanup (GH-29940) (GH-112754)
(cherry picked from commit 8cdfee1) Co-authored-by: Ken Jin <[email protected]> Co-authored-by: andrei kulakov <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 80c314c commit 23920a0

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
@@ -876,8 +877,31 @@ def resetperms(path):
876877

877878
try:
878879
_os.unlink(path)
879-
# PermissionError is raised on FreeBSD for directories
880-
except (IsADirectoryError, PermissionError):
880+
except IsADirectoryError:
881+
cls._rmtree(path, ignore_errors=ignore_errors)
882+
except PermissionError:
883+
# The PermissionError handler was originally added for
884+
# FreeBSD in directories, but it seems that it is raised
885+
# on Windows too.
886+
# bpo-43153: Calling _rmtree again may
887+
# raise NotADirectoryError and mask the PermissionError.
888+
# So we must re-raise the current PermissionError if
889+
# path is not a directory.
890+
try:
891+
st = _os.lstat(path)
892+
except OSError:
893+
if ignore_errors:
894+
return
895+
raise
896+
if (_stat.S_ISLNK(st.st_mode) or
897+
not _stat.S_ISDIR(st.st_mode) or
898+
(hasattr(st, 'st_file_attributes') and
899+
st.st_file_attributes & _stat.FILE_ATTRIBUTE_REPARSE_POINT and
900+
st.st_reparse_tag == _stat.IO_REPARSE_TAG_MOUNT_POINT)
901+
):
902+
if ignore_errors:
903+
return
904+
raise
881905
cls._rmtree(path, ignore_errors=ignore_errors)
882906
except FileNotFoundError:
883907
pass

Lib/test/test_tempfile.py

+11
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,17 @@ def test_explict_cleanup_ignore_errors(self):
15331533
temp_path.exists(),
15341534
f"TemporaryDirectory {temp_path!s} exists after cleanup")
15351535

1536+
@unittest.skipUnless(os.name == "nt", "Only on Windows.")
1537+
def test_explicit_cleanup_correct_error(self):
1538+
with tempfile.TemporaryDirectory() as working_dir:
1539+
temp_dir = self.do_create(dir=working_dir)
1540+
with open(os.path.join(temp_dir.name, "example.txt"), 'wb'):
1541+
# Previously raised NotADirectoryError on some OSes
1542+
# (e.g. Windows). See bpo-43153.
1543+
with self.assertRaises(PermissionError):
1544+
temp_dir.cleanup()
1545+
1546+
15361547
@os_helper.skip_unless_symlink
15371548
def test_cleanup_with_symlink_to_a_directory(self):
15381549
# 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)