(original) (raw)

changeset: 96165:1134198e23bd parent: 96162:f3dbeca1e30d parent: 96164:3a387854d106 user: Serhiy Storchaka storchaka@gmail.com date: Wed May 20 00:14:00 2015 +0300 files: Lib/tempfile.py Lib/test/test_tempfile.py Misc/NEWS description: 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. diff -r f3dbeca1e30d -r 1134198e23bd Lib/tempfile.py --- a/Lib/tempfile.py Tue May 19 21:06:04 2015 +0200 +++ b/Lib/tempfile.py Wed May 20 00:14:00 2015 +0300 @@ -166,6 +166,13 @@ 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 @@ 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 @@ 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 -r f3dbeca1e30d -r 1134198e23bd Lib/test/test_tempfile.py --- a/Lib/test/test_tempfile.py Tue May 19 21:06:04 2015 +0200 +++ b/Lib/test/test_tempfile.py Wed May 20 00:14:00 2015 +0300 @@ -275,7 +275,39 @@ 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 @@ 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 @@ # 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 @@ 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 @@ 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 -r f3dbeca1e30d -r 1134198e23bd Misc/NEWS --- a/Misc/NEWS Tue May 19 21:06:04 2015 +0200 +++ b/Misc/NEWS Wed May 20 00:14:00 2015 +0300 @@ -52,6 +52,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 #23780: Improved error message in os.path.join() with single argument. - Issue #6598: Increased time precision and random number range in /storchaka@gmail.com