Skip to content

Commit ae7d9cf

Browse files
frenzymadnesskwi-dkserhiy-storchaka
authored andcommitted
Combines Two fixes for tempfile.TemporaryDirectory: python@e9b51c0 python@e9b51c0 Co-authored-by: Søren Løvborg <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 529b0ff commit ae7d9cf

File tree

2 files changed

+77
-9
lines changed

2 files changed

+77
-9
lines changed

Lib/tempfile.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,23 @@ def _resetperms(path):
313313
_dont_follow_symlinks(_os.chmod, path, 0o700)
314314

315315

316+
def _dont_follow_symlinks(func, path, *args):
317+
# Pass follow_symlinks=False, unless not supported on this platform.
318+
if func in _os.supports_follow_symlinks:
319+
func(path, *args, follow_symlinks=False)
320+
elif _os.name == 'nt' or not _os.path.islink(path):
321+
func(path, *args)
322+
323+
324+
def _resetperms(path):
325+
try:
326+
chflags = _os.chflags
327+
except AttributeError:
328+
pass
329+
else:
330+
_dont_follow_symlinks(chflags, path, 0)
331+
_dont_follow_symlinks(_os.chmod, path, 0o700)
332+
316333
# User visible interfaces.
317334

318335
def gettempprefix():

Lib/test/test_tempfile.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,19 +1299,25 @@ def __exit__(self, *exc_info):
12991299
class TestTemporaryDirectory(BaseTestCase):
13001300
"""Test TemporaryDirectory()."""
13011301

1302-
def do_create(self, dir=None, pre="", suf="", recurse=1):
1302+
def do_create(self, dir=None, pre="", suf="", recurse=1, dirs=1, files=1):
13031303
if dir is None:
13041304
dir = tempfile.gettempdir()
13051305
tmp = tempfile.TemporaryDirectory(dir=dir, prefix=pre, suffix=suf)
13061306
self.nameCheck(tmp.name, dir, pre, suf)
1307-
# Create a subdirectory and some files
1308-
if recurse:
1309-
d1 = self.do_create(tmp.name, pre, suf, recurse-1)
1310-
d1.name = None
1311-
with open(os.path.join(tmp.name, "test.txt"), "wb") as f:
1312-
f.write(b"Hello world!")
1307+
self.do_create2(tmp.name, recurse, dirs, files)
13131308
return tmp
13141309

1310+
def do_create2(self, path, recurse=1, dirs=1, files=1):
1311+
# Create subdirectories and some files
1312+
if recurse:
1313+
for i in range(dirs):
1314+
name = os.path.join(path, "dir%d" % i)
1315+
os.mkdir(name)
1316+
self.do_create2(name, recurse-1, dirs, files)
1317+
for i in range(files):
1318+
with open(os.path.join(path, "test%d.txt" % i), "wb") as f:
1319+
f.write(b"Hello world!")
1320+
13151321
def test_mkdtemp_failure(self):
13161322
# Check no additional exception if mkdtemp fails
13171323
# Previously would raise AttributeError instead
@@ -1351,7 +1357,7 @@ def test_cleanup_with_symlink_to_a_directory(self):
13511357
"TemporaryDirectory %s exists after cleanup" % d1.name)
13521358
self.assertTrue(os.path.exists(d2.name),
13531359
"Directory pointed to by a symlink was deleted")
1354-
self.assertEqual(os.listdir(d2.name), ['test.txt'],
1360+
self.assertEqual(os.listdir(d2.name), ['test0.txt'],
13551361
"Contents of the directory pointed to by a symlink "
13561362
"were deleted")
13571363
d2.cleanup()
@@ -1483,7 +1489,7 @@ def test_del_on_shutdown(self):
14831489
14841490
tmp2 = os.path.join(tmp.name, 'test_dir')
14851491
os.mkdir(tmp2)
1486-
with open(os.path.join(tmp2, "test.txt"), "w") as f:
1492+
with open(os.path.join(tmp2, "test0.txt"), "w") as f:
14871493
f.write("Hello world!")
14881494
14891495
{mod}.tmp = tmp
@@ -1551,6 +1557,51 @@ def test_context_manager(self):
15511557
self.assertEqual(name, d.name)
15521558
self.assertFalse(os.path.exists(name))
15531559

1560+
def test_modes(self):
1561+
for mode in range(8):
1562+
mode <<= 6
1563+
with self.subTest(mode=format(mode, '03o')):
1564+
d = self.do_create(recurse=3, dirs=2, files=2)
1565+
with d:
1566+
# Change files and directories mode recursively.
1567+
for root, dirs, files in os.walk(d.name, topdown=False):
1568+
for name in files:
1569+
os.chmod(os.path.join(root, name), mode)
1570+
os.chmod(root, mode)
1571+
d.cleanup()
1572+
self.assertFalse(os.path.exists(d.name))
1573+
1574+
def check_flags(self, flags):
1575+
# skip the test if these flags are not supported (ex: FreeBSD 13)
1576+
filename = support.TESTFN
1577+
try:
1578+
open(filename, "w").close()
1579+
try:
1580+
os.chflags(filename, flags)
1581+
except OSError as exc:
1582+
# "OSError: [Errno 45] Operation not supported"
1583+
self.skipTest(f"chflags() doesn't support flags "
1584+
f"{flags:#b}: {exc}")
1585+
else:
1586+
os.chflags(filename, 0)
1587+
finally:
1588+
support.unlink(filename)
1589+
1590+
@unittest.skipUnless(hasattr(os, 'chflags'), 'requires os.lchflags')
1591+
def test_flags(self):
1592+
flags = stat.UF_IMMUTABLE | stat.UF_NOUNLINK
1593+
self.check_flags(flags)
1594+
1595+
d = self.do_create(recurse=3, dirs=2, files=2)
1596+
with d:
1597+
# Change files and directories flags recursively.
1598+
for root, dirs, files in os.walk(d.name, topdown=False):
1599+
for name in files:
1600+
os.chflags(os.path.join(root, name), flags)
1601+
os.chflags(root, flags)
1602+
d.cleanup()
1603+
self.assertFalse(os.path.exists(d.name))
1604+
15541605

15551606
if __name__ == "__main__":
15561607
unittest.main()

0 commit comments

Comments
 (0)