Issue #22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again
when a directory with the chosen name already exists on Windows as well as on Unix. tempfile.mkstemp() now fails early if parent directory is not valid (not exists or is a file) on Windows.
This commit is contained in:
commit
492f027793
|
@ -166,6 +166,13 @@ def _get_default_tempdir():
|
||||||
return dir
|
return dir
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
pass
|
pass
|
||||||
|
except PermissionError:
|
||||||
|
# This exception is thrown when a directory with the chosen name
|
||||||
|
# already exists on windows.
|
||||||
|
if (_os.name == 'nt' and _os.path.isdir(dir) and
|
||||||
|
_os.access(dir, _os.W_OK)):
|
||||||
|
continue
|
||||||
|
break # no point trying more names in this directory
|
||||||
except OSError:
|
except OSError:
|
||||||
break # no point trying more names in this directory
|
break # no point trying more names in this directory
|
||||||
raise FileNotFoundError(_errno.ENOENT,
|
raise FileNotFoundError(_errno.ENOENT,
|
||||||
|
@ -204,7 +211,8 @@ def _mkstemp_inner(dir, pre, suf, flags):
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
# This exception is thrown when a directory with the chosen name
|
# This exception is thrown when a directory with the chosen name
|
||||||
# already exists on windows.
|
# already exists on windows.
|
||||||
if _os.name == 'nt':
|
if (_os.name == 'nt' and _os.path.isdir(dir) and
|
||||||
|
_os.access(dir, _os.W_OK)):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
@ -296,6 +304,14 @@ def mkdtemp(suffix="", prefix=template, dir=None):
|
||||||
return file
|
return file
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
continue # try again
|
continue # try again
|
||||||
|
except PermissionError:
|
||||||
|
# This exception is thrown when a directory with the chosen name
|
||||||
|
# already exists on windows.
|
||||||
|
if (_os.name == 'nt' and _os.path.isdir(dir) and
|
||||||
|
_os.access(dir, _os.W_OK)):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
raise FileExistsError(_errno.EEXIST,
|
raise FileExistsError(_errno.EEXIST,
|
||||||
"No usable temporary directory name found")
|
"No usable temporary directory name found")
|
||||||
|
|
|
@ -275,7 +275,39 @@ def _mock_candidate_names(*names):
|
||||||
lambda: iter(names))
|
lambda: iter(names))
|
||||||
|
|
||||||
|
|
||||||
class TestMkstempInner(BaseTestCase):
|
class TestBadTempdir:
|
||||||
|
|
||||||
|
def test_read_only_directory(self):
|
||||||
|
with _inside_empty_temp_dir():
|
||||||
|
oldmode = mode = os.stat(tempfile.tempdir).st_mode
|
||||||
|
mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
|
||||||
|
os.chmod(tempfile.tempdir, mode)
|
||||||
|
try:
|
||||||
|
if os.access(tempfile.tempdir, os.W_OK):
|
||||||
|
self.skipTest("can't set the directory read-only")
|
||||||
|
with self.assertRaises(PermissionError):
|
||||||
|
self.make_temp()
|
||||||
|
self.assertEqual(os.listdir(tempfile.tempdir), [])
|
||||||
|
finally:
|
||||||
|
os.chmod(tempfile.tempdir, oldmode)
|
||||||
|
|
||||||
|
def test_nonexisting_directory(self):
|
||||||
|
with _inside_empty_temp_dir():
|
||||||
|
tempdir = os.path.join(tempfile.tempdir, 'nonexistent')
|
||||||
|
with support.swap_attr(tempfile, 'tempdir', tempdir):
|
||||||
|
with self.assertRaises(FileNotFoundError):
|
||||||
|
self.make_temp()
|
||||||
|
|
||||||
|
def test_non_directory(self):
|
||||||
|
with _inside_empty_temp_dir():
|
||||||
|
tempdir = os.path.join(tempfile.tempdir, 'file')
|
||||||
|
open(tempdir, 'wb').close()
|
||||||
|
with support.swap_attr(tempfile, 'tempdir', tempdir):
|
||||||
|
with self.assertRaises((NotADirectoryError, FileNotFoundError)):
|
||||||
|
self.make_temp()
|
||||||
|
|
||||||
|
|
||||||
|
class TestMkstempInner(TestBadTempdir, BaseTestCase):
|
||||||
"""Test the internal function _mkstemp_inner."""
|
"""Test the internal function _mkstemp_inner."""
|
||||||
|
|
||||||
class mkstemped:
|
class mkstemped:
|
||||||
|
@ -390,7 +422,7 @@ class TestMkstempInner(BaseTestCase):
|
||||||
os.lseek(f.fd, 0, os.SEEK_SET)
|
os.lseek(f.fd, 0, os.SEEK_SET)
|
||||||
self.assertEqual(os.read(f.fd, 20), b"blat")
|
self.assertEqual(os.read(f.fd, 20), b"blat")
|
||||||
|
|
||||||
def default_mkstemp_inner(self):
|
def make_temp(self):
|
||||||
return tempfile._mkstemp_inner(tempfile.gettempdir(),
|
return tempfile._mkstemp_inner(tempfile.gettempdir(),
|
||||||
tempfile.template,
|
tempfile.template,
|
||||||
'',
|
'',
|
||||||
|
@ -401,11 +433,11 @@ class TestMkstempInner(BaseTestCase):
|
||||||
# the chosen name already exists
|
# the chosen name already exists
|
||||||
with _inside_empty_temp_dir(), \
|
with _inside_empty_temp_dir(), \
|
||||||
_mock_candidate_names('aaa', 'aaa', 'bbb'):
|
_mock_candidate_names('aaa', 'aaa', 'bbb'):
|
||||||
(fd1, name1) = self.default_mkstemp_inner()
|
(fd1, name1) = self.make_temp()
|
||||||
os.close(fd1)
|
os.close(fd1)
|
||||||
self.assertTrue(name1.endswith('aaa'))
|
self.assertTrue(name1.endswith('aaa'))
|
||||||
|
|
||||||
(fd2, name2) = self.default_mkstemp_inner()
|
(fd2, name2) = self.make_temp()
|
||||||
os.close(fd2)
|
os.close(fd2)
|
||||||
self.assertTrue(name2.endswith('bbb'))
|
self.assertTrue(name2.endswith('bbb'))
|
||||||
|
|
||||||
|
@ -417,7 +449,7 @@ class TestMkstempInner(BaseTestCase):
|
||||||
dir = tempfile.mkdtemp()
|
dir = tempfile.mkdtemp()
|
||||||
self.assertTrue(dir.endswith('aaa'))
|
self.assertTrue(dir.endswith('aaa'))
|
||||||
|
|
||||||
(fd, name) = self.default_mkstemp_inner()
|
(fd, name) = self.make_temp()
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
self.assertTrue(name.endswith('bbb'))
|
self.assertTrue(name.endswith('bbb'))
|
||||||
|
|
||||||
|
@ -529,9 +561,12 @@ class TestMkstemp(BaseTestCase):
|
||||||
os.rmdir(dir)
|
os.rmdir(dir)
|
||||||
|
|
||||||
|
|
||||||
class TestMkdtemp(BaseTestCase):
|
class TestMkdtemp(TestBadTempdir, BaseTestCase):
|
||||||
"""Test mkdtemp()."""
|
"""Test mkdtemp()."""
|
||||||
|
|
||||||
|
def make_temp(self):
|
||||||
|
return tempfile.mkdtemp()
|
||||||
|
|
||||||
def do_create(self, dir=None, pre="", suf=""):
|
def do_create(self, dir=None, pre="", suf=""):
|
||||||
if dir is None:
|
if dir is None:
|
||||||
dir = tempfile.gettempdir()
|
dir = tempfile.gettempdir()
|
||||||
|
|
|
@ -52,6 +52,11 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again
|
||||||
|
when a directory with the chosen name already exists on Windows as well as
|
||||||
|
on Unix. tempfile.mkstemp() now fails early if parent directory is not
|
||||||
|
valid (not exists or is a file) on Windows.
|
||||||
|
|
||||||
- Issue #23780: Improved error message in os.path.join() with single argument.
|
- Issue #23780: Improved error message in os.path.join() with single argument.
|
||||||
|
|
||||||
- Issue #6598: Increased time precision and random number range in
|
- Issue #6598: Increased time precision and random number range in
|
||||||
|
|
Loading…
Reference in New Issue