[Python-checkins] cpython (2.7): Issue #22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again

serhiy.storchaka python-checkins at python.org
Tue May 19 23:14:43 CEST 2015


https://hg.python.org/cpython/rev/63f0ae6e218a
changeset:   96163:63f0ae6e218a
branch:      2.7
parent:      96151:6969bac411fa
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Wed May 20 00:10:56 2015 +0300
summary:
  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.

files:
  Lib/tempfile.py           |  19 ++++++++--
  Lib/test/test_tempfile.py |  50 +++++++++++++++++++++++---
  Misc/NEWS                 |   5 ++
  3 files changed, 64 insertions(+), 10 deletions(-)


diff --git a/Lib/tempfile.py b/Lib/tempfile.py
--- a/Lib/tempfile.py
+++ b/Lib/tempfile.py
@@ -205,9 +205,14 @@
                     _os.unlink(filename)
                 return dir
             except (OSError, IOError) as e:
-                if e.args[0] != _errno.EEXIST:
-                    break # no point trying more names in this directory
-                pass
+                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
     raise IOError, (_errno.ENOENT,
                     ("No usable temporary directory found in %s" % dirlist))
 
@@ -242,7 +247,8 @@
         except OSError, e:
             if e.errno == _errno.EEXIST:
                 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
                 # exists, EACCES error code is returned instead of EEXIST.
                 continue
@@ -335,6 +341,11 @@
         except OSError, e:
             if e.errno == _errno.EEXIST:
                 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 IOError, (_errno.EEXIST, "No usable temporary directory name found")
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -287,7 +287,42 @@
                              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."""
 
     class mkstemped:
@@ -400,7 +435,7 @@
         self.do_create(bin=0).write("blat\n")
         # 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(),
                                        tempfile.template,
                                        '',
@@ -411,11 +446,11 @@
         # 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'))
 
@@ -427,7 +462,7 @@
             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'))
 
@@ -542,9 +577,12 @@
 test_classes.append(test_mkstemp)
 
 
-class test_mkdtemp(TC):
+class test_mkdtemp(TestBadTempdir, TC):
     """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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,11 @@
 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
   email.utils.make_msgid() to strengthen the uniqueness of the message ID.
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list