mirror of https://github.com/python/cpython
bpo-43153: Don't mask `PermissionError` with `NotADirectoryError` during tempdirectory cleanup (GH-29940)
Co-authored-by: andrei kulakov <andrei.avk@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
5aa317e4ca
commit
8cdfee1bb9
|
@ -41,6 +41,7 @@ import warnings as _warnings
|
||||||
import io as _io
|
import io as _io
|
||||||
import os as _os
|
import os as _os
|
||||||
import shutil as _shutil
|
import shutil as _shutil
|
||||||
|
import stat as _stat
|
||||||
import errno as _errno
|
import errno as _errno
|
||||||
from random import Random as _Random
|
from random import Random as _Random
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
@ -889,8 +890,31 @@ class TemporaryDirectory:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_os.unlink(path)
|
_os.unlink(path)
|
||||||
# PermissionError is raised on FreeBSD for directories
|
except IsADirectoryError:
|
||||||
except (IsADirectoryError, PermissionError):
|
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)
|
cls._rmtree(path, ignore_errors=ignore_errors)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1641,6 +1641,17 @@ class TestTemporaryDirectory(BaseTestCase):
|
||||||
temp_path.exists(),
|
temp_path.exists(),
|
||||||
f"TemporaryDirectory {temp_path!s} exists after cleanup")
|
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
|
@os_helper.skip_unless_symlink
|
||||||
def test_cleanup_with_symlink_to_a_directory(self):
|
def test_cleanup_with_symlink_to_a_directory(self):
|
||||||
# cleanup() should not follow symlinks to directories (issue #12464)
|
# cleanup() should not follow symlinks to directories (issue #12464)
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue