summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2015-05-19 21:14:00 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2015-05-19 21:14:00 (GMT)
commit492f0277933fd5714762a99ec99fd7f97d99866a (patch)
tree85451c0d2ed74f4b10266dd2d71205ffb5fc9a87
parent873e0df946e53af525b515df7773ff72884a8c3b (diff)
parent5d6b7b1cb7943255b8682ea3663ce2c0da500e96 (diff)
downloadcpython-492f0277933fd5714762a99ec99fd7f97d99866a.zip
cpython-492f0277933fd5714762a99ec99fd7f97d99866a.tar.gz
cpython-492f0277933fd5714762a99ec99fd7f97d99866a.tar.bz2
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.
-rw-r--r--Lib/tempfile.py18
-rw-r--r--Lib/test/test_tempfile.py47
-rw-r--r--Misc/NEWS5
3 files changed, 63 insertions, 7 deletions
diff --git a/Lib/tempfile.py b/Lib/tempfile.py
index 81c289a..226c6e6 100644
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -166,6 +166,13 @@ def _get_default_tempdir():
return dir
except FileExistsError:
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:
break # no point trying more names in this directory
raise FileNotFoundError(_errno.ENOENT,
@@ -204,7 +211,8 @@ def _mkstemp_inner(dir, pre, suf, flags):
except PermissionError:
# This exception is thrown when a directory with the chosen name
# 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
else:
raise
@@ -296,6 +304,14 @@ def mkdtemp(suffix="", prefix=template, dir=None):
return file
except FileExistsError:
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,
"No usable temporary directory name found")
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index 9b7df44..b81a842 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -275,7 +275,39 @@ def _mock_candidate_names(*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."""
class mkstemped:
@@ -390,7 +422,7 @@ class TestMkstempInner(BaseTestCase):
os.lseek(f.fd, 0, os.SEEK_SET)
self.assertEqual(os.read(f.fd, 20), b"blat")
- def default_mkstemp_inner(self):
+ def make_temp(self):
return tempfile._mkstemp_inner(tempfile.gettempdir(),
tempfile.template,
'',
@@ -401,11 +433,11 @@ class TestMkstempInner(BaseTestCase):
# the chosen name already exists
with _inside_empty_temp_dir(), \
_mock_candidate_names('aaa', 'aaa', 'bbb'):
- (fd1, name1) = self.default_mkstemp_inner()
+ (fd1, name1) = self.make_temp()
os.close(fd1)
self.assertTrue(name1.endswith('aaa'))
- (fd2, name2) = self.default_mkstemp_inner()
+ (fd2, name2) = self.make_temp()
os.close(fd2)
self.assertTrue(name2.endswith('bbb'))
@@ -417,7 +449,7 @@ class TestMkstempInner(BaseTestCase):
dir = tempfile.mkdtemp()
self.assertTrue(dir.endswith('aaa'))
- (fd, name) = self.default_mkstemp_inner()
+ (fd, name) = self.make_temp()
os.close(fd)
self.assertTrue(name.endswith('bbb'))
@@ -529,9 +561,12 @@ class TestMkstemp(BaseTestCase):
os.rmdir(dir)
-class TestMkdtemp(BaseTestCase):
+class TestMkdtemp(TestBadTempdir, BaseTestCase):
"""Test mkdtemp()."""
+ def make_temp(self):
+ return tempfile.mkdtemp()
+
def do_create(self, dir=None, pre="", suf=""):
if dir is None:
dir = tempfile.gettempdir()
diff --git a/Misc/NEWS b/Misc/NEWS
index 63e4329..71b35c3 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -52,6 +52,11 @@ Core and Builtins
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 #6598: Increased time precision and random number range in