]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.8] bpo-38688, shutil.copytree: consume iterator and create list of entries to...
authorBruno P. Kinoshita <kinow@users.noreply.github.com>
Wed, 27 Nov 2019 04:49:37 +0000 (17:49 +1300)
committerGiampaolo Rodola <g.rodola@gmail.com>
Wed, 27 Nov 2019 04:49:37 +0000 (12:49 +0800)
(cherry picked from commit 9bbcbc9f6dfe1368fe7330b117707f828e6a2c18)

Co-authored-by: Bruno P. Kinoshita <kinow@users.noreply.github.com>
Lib/shutil.py
Lib/test/test_shutil.py
Misc/NEWS.d/next/Library/2019-11-22-10-45-03.bpo-38668.iKx23z.rst [new file with mode: 0644]

index 1e89256cc3444370a1421990e0962a4f984b2a32..755ce392e6d9ae09f8f8a311f12b6414159687b2 100644 (file)
@@ -442,7 +442,7 @@ def ignore_patterns(*patterns):
 def _copytree(entries, src, dst, symlinks, ignore, copy_function,
               ignore_dangling_symlinks, dirs_exist_ok=False):
     if ignore is not None:
-        ignored_names = ignore(src, set(os.listdir(src)))
+        ignored_names = ignore(src, {x.name for x in entries})
     else:
         ignored_names = set()
 
@@ -543,11 +543,12 @@ def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
 
     """
     sys.audit("shutil.copytree", src, dst)
-    with os.scandir(src) as entries:
-        return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
-                         ignore=ignore, copy_function=copy_function,
-                         ignore_dangling_symlinks=ignore_dangling_symlinks,
-                         dirs_exist_ok=dirs_exist_ok)
+    with os.scandir(src) as itr:
+        entries = list(itr)
+    return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
+                     ignore=ignore, copy_function=copy_function,
+                     ignore_dangling_symlinks=ignore_dangling_symlinks,
+                     dirs_exist_ok=dirs_exist_ok)
 
 if hasattr(os.stat_result, 'st_file_attributes'):
     # Special handling for directory junctions to make them behave like
index b98e7dc798abe79017202edc9b5131cb09caaca2..286e333a8aa9cdfe8a9265bcf727fe76a3aea452 100644 (file)
@@ -1605,6 +1605,18 @@ class TestShutil(unittest.TestCase):
         rv = shutil.copytree(src_dir, dst_dir)
         self.assertEqual(['foo'], os.listdir(rv))
 
+    def test_copytree_subdirectory(self):
+        # copytree where dst is a subdirectory of src, see Issue 38688
+        base_dir = self.mkdtemp()
+        self.addCleanup(shutil.rmtree, base_dir, ignore_errors=True)
+        src_dir = os.path.join(base_dir, "t", "pg")
+        dst_dir = os.path.join(src_dir, "somevendor", "1.0")
+        os.makedirs(src_dir)
+        src = os.path.join(src_dir, 'pol')
+        write_file(src, 'pol')
+        rv = shutil.copytree(src_dir, dst_dir)
+        self.assertEqual(['pol'], os.listdir(rv))
+
 
 class TestWhich(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Library/2019-11-22-10-45-03.bpo-38668.iKx23z.rst b/Misc/NEWS.d/next/Library/2019-11-22-10-45-03.bpo-38668.iKx23z.rst
new file mode 100644 (file)
index 0000000..28b82ab
--- /dev/null
@@ -0,0 +1,5 @@
+Calling func:`shutil.copytree` to copy a directory tree from one directory
+to another subdirectory resulted in an endless loop and a RecursionError. A
+fix was added to consume an iterator and create the list of the entries to
+be copied, avoiding the recursion for newly created directories. Patch by
+Bruno P. Kinoshita.