]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-43153: Don't mask `PermissionError` with `NotADirectoryError` during tempdirector...
authorKen Jin <kenjin@python.org>
Tue, 5 Dec 2023 13:30:59 +0000 (21:30 +0800)
committerGitHub <noreply@github.com>
Tue, 5 Dec 2023 13:30:59 +0000 (15:30 +0200)
Co-authored-by: andrei kulakov <andrei.avk@gmail.com>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/tempfile.py
Lib/test/test_tempfile.py
Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst [new file with mode: 0644]

index 2b4f43132471285e975ac3f2b93fe81539b346a1..55403ad1faf46d875fa0b32090a28407eefa7798 100644 (file)
@@ -41,6 +41,7 @@ import warnings as _warnings
 import io as _io
 import os as _os
 import shutil as _shutil
+import stat as _stat
 import errno as _errno
 from random import Random as _Random
 import sys as _sys
@@ -889,8 +890,31 @@ class TemporaryDirectory:
 
                     try:
                         _os.unlink(path)
-                    # PermissionError is raised on FreeBSD for directories
-                    except (IsADirectoryError, PermissionError):
+                    except IsADirectoryError:
+                        cls._rmtree(path, ignore_errors=ignore_errors)
+                    except PermissionError:
+                        # The PermissionError handler was originally added for
+                        # FreeBSD in directories, but it seems that it is raised
+                        # on Windows too.
+                        # bpo-43153: Calling _rmtree again may
+                        # raise NotADirectoryError and mask the PermissionError.
+                        # So we must re-raise the current PermissionError if
+                        # path is not a directory.
+                        try:
+                            st = _os.lstat(path)
+                        except OSError:
+                            if ignore_errors:
+                                return
+                            raise
+                        if (_stat.S_ISLNK(st.st_mode) or
+                            not _stat.S_ISDIR(st.st_mode) or
+                            (hasattr(st, 'st_file_attributes') and
+                             st.st_file_attributes & _stat.FILE_ATTRIBUTE_REPARSE_POINT and
+                             st.st_reparse_tag == _stat.IO_REPARSE_TAG_MOUNT_POINT)
+                        ):
+                            if ignore_errors:
+                                return
+                            raise
                         cls._rmtree(path, ignore_errors=ignore_errors)
                 except FileNotFoundError:
                     pass
index 1673507e2f7c91f2c3ea5feadb782b441ff75291..f4aef887799ed4a2c2468cc615d2fe2e0289fe54 100644 (file)
@@ -1641,6 +1641,17 @@ class TestTemporaryDirectory(BaseTestCase):
                 temp_path.exists(),
                 f"TemporaryDirectory {temp_path!s} exists after cleanup")
 
+    @unittest.skipUnless(os.name == "nt", "Only on Windows.")
+    def test_explicit_cleanup_correct_error(self):
+        with tempfile.TemporaryDirectory() as working_dir:
+            temp_dir = self.do_create(dir=working_dir)
+            with open(os.path.join(temp_dir.name, "example.txt"), 'wb'):
+                # Previously raised NotADirectoryError on some OSes
+                # (e.g. Windows). See bpo-43153.
+                with self.assertRaises(PermissionError):
+                    temp_dir.cleanup()
+
+
     @os_helper.skip_unless_symlink
     def test_cleanup_with_symlink_to_a_directory(self):
         # cleanup() should not follow symlinks to directories (issue #12464)
diff --git a/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst b/Misc/NEWS.d/next/Library/2021-12-06-22-10-53.bpo-43153.J7mjSy.rst
new file mode 100644 (file)
index 0000000..7800e0a
--- /dev/null
@@ -0,0 +1,4 @@
+On Windows, ``tempfile.TemporaryDirectory`` previously masked a
+``PermissionError`` with ``NotADirectoryError`` during directory cleanup. It
+now correctly raises ``PermissionError`` if errors are not ignored. Patch by
+Andrei Kulakov and Ken Jin.