From 5ff81da6d3a8eb01fc5500fd1c9eaa6543286301 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Mon, 7 Nov 2022 03:45:16 -0800 Subject: bpo-38523: ignore_dangling_symlinks does not apply recursively (GH-22937) --- Lib/shutil.py | 5 +++-- Lib/test/test_shutil.py | 19 +++++++++++++------ .../Library/2020-10-23-22-20-52.bpo-38523.CrkxLh.rst | 2 ++ 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-10-23-22-20-52.bpo-38523.CrkxLh.rst diff --git a/Lib/shutil.py b/Lib/shutil.py index ac1dd53..f5687e3 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -490,12 +490,13 @@ def _copytree(entries, src, dst, symlinks, ignore, copy_function, # otherwise let the copy occur. copy2 will raise an error if srcentry.is_dir(): copytree(srcobj, dstname, symlinks, ignore, - copy_function, dirs_exist_ok=dirs_exist_ok) + copy_function, ignore_dangling_symlinks, + dirs_exist_ok) else: copy_function(srcobj, dstname) elif srcentry.is_dir(): copytree(srcobj, dstname, symlinks, ignore, copy_function, - dirs_exist_ok=dirs_exist_ok) + ignore_dangling_symlinks, dirs_exist_ok) else: # Will raise a SpecialFileError for unsupported file types copy_function(srcobj, dstname) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 6789fe4..8fe6221 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -752,18 +752,25 @@ class TestCopyTree(BaseTest, unittest.TestCase): @os_helper.skip_unless_symlink def test_copytree_dangling_symlinks(self): - # a dangling symlink raises an error at the end src_dir = self.mkdtemp() + valid_file = os.path.join(src_dir, 'test.txt') + write_file(valid_file, 'abc') + dir_a = os.path.join(src_dir, 'dir_a') + os.mkdir(dir_a) + for d in src_dir, dir_a: + os.symlink('IDONTEXIST', os.path.join(d, 'broken')) + os.symlink(valid_file, os.path.join(d, 'valid')) + + # A dangling symlink should raise an error. dst_dir = os.path.join(self.mkdtemp(), 'destination') - os.symlink('IDONTEXIST', os.path.join(src_dir, 'test.txt')) - os.mkdir(os.path.join(src_dir, 'test_dir')) - write_file((src_dir, 'test_dir', 'test.txt'), '456') self.assertRaises(Error, shutil.copytree, src_dir, dst_dir) - # a dangling symlink is ignored with the proper flag + # Dangling symlinks should be ignored with the proper flag. dst_dir = os.path.join(self.mkdtemp(), 'destination2') shutil.copytree(src_dir, dst_dir, ignore_dangling_symlinks=True) - self.assertNotIn('test.txt', os.listdir(dst_dir)) + for root, dirs, files in os.walk(dst_dir): + self.assertNotIn('broken', files) + self.assertIn('valid', files) # a dangling symlink is copied if symlinks=True dst_dir = os.path.join(self.mkdtemp(), 'destination3') diff --git a/Misc/NEWS.d/next/Library/2020-10-23-22-20-52.bpo-38523.CrkxLh.rst b/Misc/NEWS.d/next/Library/2020-10-23-22-20-52.bpo-38523.CrkxLh.rst new file mode 100644 index 0000000..3810e29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-23-22-20-52.bpo-38523.CrkxLh.rst @@ -0,0 +1,2 @@ +:func:`shutil.copytree` now applies the *ignore_dangling_symlinks* argument +recursively. -- cgit v0.12