Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 492f027

Browse files
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.
2 parents 873e0df + 5d6b7b1 commit 492f027

3 files changed

Lines changed: 63 additions & 7 deletions

File tree

Lib/tempfile.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ def _get_default_tempdir():
166166
return dir
167167
except FileExistsError:
168168
pass
169+
except PermissionError:
170+
# This exception is thrown when a directory with the chosen name
171+
# already exists on windows.
172+
if (_os.name == 'nt' and _os.path.isdir(dir) and
173+
_os.access(dir, _os.W_OK)):
174+
continue
175+
break # no point trying more names in this directory
169176
except OSError:
170177
break # no point trying more names in this directory
171178
raise FileNotFoundError(_errno.ENOENT,
@@ -204,7 +211,8 @@ def _mkstemp_inner(dir, pre, suf, flags):
204211
except PermissionError:
205212
# This exception is thrown when a directory with the chosen name
206213
# already exists on windows.
207-
if _os.name == 'nt':
214+
if (_os.name == 'nt' and _os.path.isdir(dir) and
215+
_os.access(dir, _os.W_OK)):
208216
continue
209217
else:
210218
raise
@@ -296,6 +304,14 @@ def mkdtemp(suffix="", prefix=template, dir=None):
296304
return file
297305
except FileExistsError:
298306
continue # try again
307+
except PermissionError:
308+
# This exception is thrown when a directory with the chosen name
309+
# already exists on windows.
310+
if (_os.name == 'nt' and _os.path.isdir(dir) and
311+
_os.access(dir, _os.W_OK)):
312+
continue
313+
else:
314+
raise
299315

300316
raise FileExistsError(_errno.EEXIST,
301317
"No usable temporary directory name found")

Lib/test/test_tempfile.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,39 @@ def _mock_candidate_names(*names):
275275
lambda: iter(names))
276276

277277

278-
class TestMkstempInner(BaseTestCase):
278+
class TestBadTempdir:
279+
280+
def test_read_only_directory(self):
281+
with _inside_empty_temp_dir():
282+
oldmode = mode = os.stat(tempfile.tempdir).st_mode
283+
mode &= ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
284+
os.chmod(tempfile.tempdir, mode)
285+
try:
286+
if os.access(tempfile.tempdir, os.W_OK):
287+
self.skipTest("can't set the directory read-only")
288+
with self.assertRaises(PermissionError):
289+
self.make_temp()
290+
self.assertEqual(os.listdir(tempfile.tempdir), [])
291+
finally:
292+
os.chmod(tempfile.tempdir, oldmode)
293+
294+
def test_nonexisting_directory(self):
295+
with _inside_empty_temp_dir():
296+
tempdir = os.path.join(tempfile.tempdir, 'nonexistent')
297+
with support.swap_attr(tempfile, 'tempdir', tempdir):
298+
with self.assertRaises(FileNotFoundError):
299+
self.make_temp()
300+
301+
def test_non_directory(self):
302+
with _inside_empty_temp_dir():
303+
tempdir = os.path.join(tempfile.tempdir, 'file')
304+
open(tempdir, 'wb').close()
305+
with support.swap_attr(tempfile, 'tempdir', tempdir):
306+
with self.assertRaises((NotADirectoryError, FileNotFoundError)):
307+
self.make_temp()
308+
309+
310+
class TestMkstempInner(TestBadTempdir, BaseTestCase):
279311
"""Test the internal function _mkstemp_inner."""
280312

281313
class mkstemped:
@@ -390,7 +422,7 @@ def test_textmode(self):
390422
os.lseek(f.fd, 0, os.SEEK_SET)
391423
self.assertEqual(os.read(f.fd, 20), b"blat")
392424

393-
def default_mkstemp_inner(self):
425+
def make_temp(self):
394426
return tempfile._mkstemp_inner(tempfile.gettempdir(),
395427
tempfile.template,
396428
'',
@@ -401,11 +433,11 @@ def test_collision_with_existing_file(self):
401433
# the chosen name already exists
402434
with _inside_empty_temp_dir(), \
403435
_mock_candidate_names('aaa', 'aaa', 'bbb'):
404-
(fd1, name1) = self.default_mkstemp_inner()
436+
(fd1, name1) = self.make_temp()
405437
os.close(fd1)
406438
self.assertTrue(name1.endswith('aaa'))
407439

408-
(fd2, name2) = self.default_mkstemp_inner()
440+
(fd2, name2) = self.make_temp()
409441
os.close(fd2)
410442
self.assertTrue(name2.endswith('bbb'))
411443

@@ -417,7 +449,7 @@ def test_collision_with_existing_directory(self):
417449
dir = tempfile.mkdtemp()
418450
self.assertTrue(dir.endswith('aaa'))
419451

420-
(fd, name) = self.default_mkstemp_inner()
452+
(fd, name) = self.make_temp()
421453
os.close(fd)
422454
self.assertTrue(name.endswith('bbb'))
423455

@@ -529,9 +561,12 @@ def test_choose_directory(self):
529561
os.rmdir(dir)
530562

531563

532-
class TestMkdtemp(BaseTestCase):
564+
class TestMkdtemp(TestBadTempdir, BaseTestCase):
533565
"""Test mkdtemp()."""
534566

567+
def make_temp(self):
568+
return tempfile.mkdtemp()
569+
535570
def do_create(self, dir=None, pre="", suf=""):
536571
if dir is None:
537572
dir = tempfile.gettempdir()

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ Core and Builtins
5252
Library
5353
-------
5454

55+
- Issue #22107: tempfile.gettempdir() and tempfile.mkdtemp() now try again
56+
when a directory with the chosen name already exists on Windows as well as
57+
on Unix. tempfile.mkstemp() now fails early if parent directory is not
58+
valid (not exists or is a file) on Windows.
59+
5560
- Issue #23780: Improved error message in os.path.join() with single argument.
5661

5762
- Issue #6598: Increased time precision and random number range in

0 commit comments

Comments
 (0)