Commit 9eb0c0e7 authored by Serhiy Storchaka's avatar Serhiy Storchaka

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.
parent 49f2ccf8
...@@ -205,9 +205,14 @@ def _get_default_tempdir(): ...@@ -205,9 +205,14 @@ def _get_default_tempdir():
_os.unlink(filename) _os.unlink(filename)
return dir return dir
except (OSError, IOError) as e: except (OSError, IOError) as e:
if e.args[0] != _errno.EEXIST: if e.args[0] == _errno.EEXIST:
continue
if (_os.name == 'nt' and e.args[0] == _errno.EACCES and
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
# On windows, when a directory with the chosen name already
# exists, EACCES error code is returned instead of EEXIST.
continue
break # no point trying more names in this directory break # no point trying more names in this directory
pass
raise IOError, (_errno.ENOENT, raise IOError, (_errno.ENOENT,
("No usable temporary directory found in %s" % dirlist)) ("No usable temporary directory found in %s" % dirlist))
...@@ -242,7 +247,8 @@ def _mkstemp_inner(dir, pre, suf, flags): ...@@ -242,7 +247,8 @@ def _mkstemp_inner(dir, pre, suf, flags):
except OSError, e: except OSError, e:
if e.errno == _errno.EEXIST: if e.errno == _errno.EEXIST:
continue # try again continue # try again
if _os.name == 'nt' and e.errno == _errno.EACCES: if (_os.name == 'nt' and e.errno == _errno.EACCES and
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
# On windows, when a directory with the chosen name already # On windows, when a directory with the chosen name already
# exists, EACCES error code is returned instead of EEXIST. # exists, EACCES error code is returned instead of EEXIST.
continue continue
...@@ -335,6 +341,11 @@ def mkdtemp(suffix="", prefix=template, dir=None): ...@@ -335,6 +341,11 @@ def mkdtemp(suffix="", prefix=template, dir=None):
except OSError, e: except OSError, e:
if e.errno == _errno.EEXIST: if e.errno == _errno.EEXIST:
continue # try again continue # try again
if (_os.name == 'nt' and e.errno == _errno.EACCES and
_os.path.isdir(dir) and _os.access(dir, _os.W_OK)):
# On windows, when a directory with the chosen name already
# exists, EACCES error code is returned instead of EEXIST.
continue
raise raise
raise IOError, (_errno.EEXIST, "No usable temporary directory name found") raise IOError, (_errno.EEXIST, "No usable temporary directory name found")
......
...@@ -287,7 +287,42 @@ def _mock_candidate_names(*names): ...@@ -287,7 +287,42 @@ def _mock_candidate_names(*names):
lambda: iter(names)) lambda: iter(names))
class test__mkstemp_inner(TC): 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(OSError) as cm:
self.make_temp()
self.assertIn(cm.exception.errno, (errno.EPERM, errno.EACCES))
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(OSError) as cm:
self.make_temp()
self.assertEqual(cm.exception.errno, errno.ENOENT)
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(OSError) as cm:
self.make_temp()
self.assertIn(cm.exception.errno, (errno.ENOTDIR, errno.ENOENT))
class test__mkstemp_inner(TestBadTempdir, TC):
"""Test the internal function _mkstemp_inner.""" """Test the internal function _mkstemp_inner."""
class mkstemped: class mkstemped:
...@@ -400,7 +435,7 @@ class test__mkstemp_inner(TC): ...@@ -400,7 +435,7 @@ class test__mkstemp_inner(TC):
self.do_create(bin=0).write("blat\n") self.do_create(bin=0).write("blat\n")
# XXX should test that the file really is a text file # XXX should test that the file really is a text file
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,
'', '',
...@@ -411,11 +446,11 @@ class test__mkstemp_inner(TC): ...@@ -411,11 +446,11 @@ class test__mkstemp_inner(TC):
# 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'))
...@@ -427,7 +462,7 @@ class test__mkstemp_inner(TC): ...@@ -427,7 +462,7 @@ class test__mkstemp_inner(TC):
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'))
...@@ -542,9 +577,12 @@ class test_mkstemp(TC): ...@@ -542,9 +577,12 @@ class test_mkstemp(TC):
test_classes.append(test_mkstemp) test_classes.append(test_mkstemp)
class test_mkdtemp(TC): class test_mkdtemp(TestBadTempdir, TC):
"""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()
......
...@@ -15,6 +15,11 @@ Core and Builtins ...@@ -15,6 +15,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 #6598: Increased time precision and random number range in - Issue #6598: Increased time precision and random number range in
email.utils.make_msgid() to strengthen the uniqueness of the message ID. email.utils.make_msgid() to strengthen the uniqueness of the message ID.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment