https://github.com/python/cpython/commit/45c2ef48ca8be4d5fe6fe0373961e04da813475b
commit: 45c2ef48ca8be4d5fe6fe0373961e04da813475b
branch: main
author: Barney Gale <barney.g...@gmail.com>
committer: barneygale <barney.g...@gmail.com>
date: 2025-03-13T21:56:59Z
summary:

GH-130614: pathlib ABCs: parametrize test suite for path copying (#131168)

Test copying from `Path` and `ReadableZipPath` (types of `_ReadablePath`)
to `Path` and `WritableZipPath` (types of `_WritablePath`).

files:
A Lib/test/test_pathlib/test_copy.py
M Lib/pathlib/_os.py
M Lib/test/test_pathlib/test_pathlib_abc.py

diff --git a/Lib/pathlib/_os.py b/Lib/pathlib/_os.py
index c8cb4be548d045..121b6d656a8f53 100644
--- a/Lib/pathlib/_os.py
+++ b/Lib/pathlib/_os.py
@@ -248,7 +248,7 @@ def copy_file(source, target, follow_symlinks=True, 
preserve_metadata=False):
     """
     info = source.info
     if not follow_symlinks and info.is_symlink():
-        target.symlink_to(source.readlink(), info.is_dir())
+        target.symlink_to(str(source.readlink()), info.is_dir())
         if preserve_metadata:
             target._write_info(info, follow_symlinks=False)
     elif info.is_dir():
diff --git a/Lib/test/test_pathlib/test_copy.py 
b/Lib/test/test_pathlib/test_copy.py
new file mode 100644
index 00000000000000..698a0a7b75070f
--- /dev/null
+++ b/Lib/test/test_pathlib/test_copy.py
@@ -0,0 +1,172 @@
+"""
+Tests for copying from pathlib.types._ReadablePath to _WritablePath.
+"""
+
+import contextlib
+import unittest
+
+from pathlib import Path
+
+from test.test_pathlib.support.local_path import LocalPathGround, 
WritableLocalPath
+from test.test_pathlib.support.zip_path import ZipPathGround, ReadableZipPath, 
WritableZipPath
+
+
+class CopyTestBase:
+    def setUp(self):
+        self.source_root = self.source_ground.setup()
+        self.source_ground.create_hierarchy(self.source_root)
+        self.target_root = self.target_ground.setup(local_suffix="_target")
+
+    def tearDown(self):
+        self.source_ground.teardown(self.source_root)
+        self.target_ground.teardown(self.target_root)
+
+    def test_copy_file(self):
+        source = self.source_root / 'fileA'
+        target = self.target_root / 'copyA'
+        result = source.copy(target)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isfile(target))
+        self.assertEqual(self.source_ground.readbytes(source),
+                         self.target_ground.readbytes(result))
+
+    def test_copy_file_empty(self):
+        source = self.source_root / 'empty'
+        target = self.target_root / 'copyA'
+        self.source_ground.create_file(source, b'')
+        result = source.copy(target)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isfile(target))
+        self.assertEqual(self.target_ground.readbytes(result), b'')
+
+    def test_copy_file_to_existing_file(self):
+        source = self.source_root / 'fileA'
+        target = self.target_root / 'copyA'
+        self.target_ground.create_file(target, b'this is a copy\n')
+        with contextlib.ExitStack() as stack:
+            if isinstance(target, WritableZipPath):
+                stack.enter_context(self.assertWarns(UserWarning))
+            result = source.copy(target)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isfile(target))
+        self.assertEqual(self.source_ground.readbytes(source),
+                         self.target_ground.readbytes(result))
+
+    def test_copy_file_to_directory(self):
+        if not isinstance(self.target_root, WritableLocalPath):
+            self.skipTest('needs local target')
+        source = self.source_root / 'fileA'
+        target = self.target_root / 'copyA'
+        self.target_ground.create_dir(target)
+        self.assertRaises(OSError, source.copy, target)
+
+    def test_copy_file_to_itself(self):
+        source = self.source_root / 'fileA'
+        self.assertRaises(OSError, source.copy, source)
+        self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
+
+    def test_copy_dir(self):
+        source = self.source_root / 'dirC'
+        target = self.target_root / 'copyC'
+        result = source.copy(target)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isdir(target))
+        self.assertTrue(self.target_ground.isfile(target / 'fileC'))
+        self.assertEqual(self.target_ground.readtext(target / 'fileC'), 'this 
is file C\n')
+        self.assertTrue(self.target_ground.isdir(target / 'dirD'))
+        self.assertTrue(self.target_ground.isfile(target / 'dirD' / 'fileD'))
+        self.assertEqual(self.target_ground.readtext(target / 'dirD' / 
'fileD'), 'this is file D\n')
+
+    def test_copy_dir_follow_symlinks_true(self):
+        if not self.source_ground.can_symlink:
+            self.skipTest('needs symlink support on source')
+        source = self.source_root / 'dirC'
+        target = self.target_root / 'copyC'
+        self.source_ground.create_symlink(source / 'linkC', 'fileC')
+        self.source_ground.create_symlink(source / 'linkD', 'dirD')
+        result = source.copy(target)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isdir(target))
+        self.assertFalse(self.target_ground.islink(target / 'linkC'))
+        self.assertTrue(self.target_ground.isfile(target / 'linkC'))
+        self.assertEqual(self.target_ground.readtext(target / 'linkC'), 'this 
is file C\n')
+        self.assertFalse(self.target_ground.islink(target / 'linkD'))
+        self.assertTrue(self.target_ground.isdir(target / 'linkD'))
+        self.assertTrue(self.target_ground.isfile(target / 'linkD' / 'fileD'))
+        self.assertEqual(self.target_ground.readtext(target / 'linkD' / 
'fileD'), 'this is file D\n')
+
+    def test_copy_dir_follow_symlinks_false(self):
+        if not self.source_ground.can_symlink:
+            self.skipTest('needs symlink support on source')
+        if not self.target_ground.can_symlink:
+            self.skipTest('needs symlink support on target')
+        source = self.source_root / 'dirC'
+        target = self.target_root / 'copyC'
+        self.source_ground.create_symlink(source / 'linkC', 'fileC')
+        self.source_ground.create_symlink(source / 'linkD', 'dirD')
+        result = source.copy(target, follow_symlinks=False)
+        self.assertEqual(result, target)
+        self.assertTrue(self.target_ground.isdir(target))
+        self.assertTrue(self.target_ground.islink(target / 'linkC'))
+        self.assertEqual(self.target_ground.readlink(target / 'linkC'), 
'fileC')
+        self.assertTrue(self.target_ground.islink(target / 'linkD'))
+        self.assertEqual(self.target_ground.readlink(target / 'linkD'), 'dirD')
+
+    def test_copy_dir_to_existing_directory(self):
+        if not isinstance(self.target_root, WritableLocalPath):
+            self.skipTest('needs local target')
+        source = self.source_root / 'dirC'
+        target = self.target_root / 'copyC'
+        self.target_ground.create_dir(target)
+        self.assertRaises(FileExistsError, source.copy, target)
+
+    def test_copy_dir_to_itself(self):
+        source = self.source_root / 'dirC'
+        self.assertRaises(OSError, source.copy, source)
+        self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
+
+    def test_copy_dir_into_itself(self):
+        source = self.source_root / 'dirC'
+        target = self.source_root / 'dirC' / 'dirD' / 'copyC'
+        self.assertRaises(OSError, source.copy, target)
+        self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
+
+    def test_copy_into(self):
+        source = self.source_root / 'fileA'
+        target_dir = self.target_root / 'dirA'
+        self.target_ground.create_dir(target_dir)
+        result = source.copy_into(target_dir)
+        self.assertEqual(result, target_dir / 'fileA')
+        self.assertTrue(self.target_ground.isfile(result))
+        self.assertEqual(self.source_ground.readbytes(source),
+                         self.target_ground.readbytes(result))
+
+    def test_copy_into_empty_name(self):
+        source = self.source_root.with_segments()
+        target_dir = self.target_root / 'dirA'
+        self.target_ground.create_dir(target_dir)
+        self.assertRaises(ValueError, source.copy_into, target_dir)
+
+
+class ZipToZipPathCopyTest(CopyTestBase, unittest.TestCase):
+    source_ground = ZipPathGround(ReadableZipPath)
+    target_ground = ZipPathGround(WritableZipPath)
+
+
+class ZipToLocalPathCopyTest(CopyTestBase, unittest.TestCase):
+    source_ground = ZipPathGround(ReadableZipPath)
+    target_ground = LocalPathGround(Path)
+
+
+class LocalToZipPathCopyTest(CopyTestBase, unittest.TestCase):
+    source_ground = LocalPathGround(Path)
+    target_ground = ZipPathGround(WritableZipPath)
+
+
+class LocalToLocalPathCopyTest(CopyTestBase, unittest.TestCase):
+    source_ground = LocalPathGround(Path)
+    target_ground = LocalPathGround(Path)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py 
b/Lib/test/test_pathlib/test_pathlib_abc.py
index 08dad4d28b8eb3..b6e0e36eb87e25 100644
--- a/Lib/test/test_pathlib/test_pathlib_abc.py
+++ b/Lib/test/test_pathlib/test_pathlib_abc.py
@@ -343,144 +343,6 @@ class RWPathTest(WritablePathTest, ReadablePathTest):
     cls = DummyRWPath
     can_symlink = False
 
-    def test_copy_file(self):
-        base = self.cls(self.base)
-        source = base / 'fileA'
-        target = base / 'copyA'
-        result = source.copy(target)
-        self.assertEqual(result, target)
-        self.assertTrue(result.info.exists())
-        self.assertEqual(source.read_text(), result.read_text())
-
-    def test_copy_file_to_existing_file(self):
-        base = self.cls(self.base)
-        source = base / 'fileA'
-        target = base / 'dirB' / 'fileB'
-        result = source.copy(target)
-        self.assertEqual(result, target)
-        self.assertTrue(result.info.exists())
-        self.assertEqual(source.read_text(), result.read_text())
-
-    def test_copy_file_to_existing_directory(self):
-        base = self.cls(self.base)
-        source = base / 'fileA'
-        target = base / 'dirA'
-        self.assertRaises(OSError, source.copy, target)
-
-    def test_copy_file_empty(self):
-        base = self.cls(self.base)
-        source = base / 'empty'
-        target = base / 'copyA'
-        source.write_bytes(b'')
-        result = source.copy(target)
-        self.assertEqual(result, target)
-        self.assertTrue(result.info.exists())
-        self.assertEqual(result.read_bytes(), b'')
-
-    def test_copy_file_to_itself(self):
-        base = self.cls(self.base)
-        source = base / 'empty'
-        source.write_bytes(b'')
-        self.assertRaises(OSError, source.copy, source)
-        self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
-
-    def test_copy_dir_simple(self):
-        base = self.cls(self.base)
-        source = base / 'dirC'
-        target = base / 'copyC'
-        result = source.copy(target)
-        self.assertEqual(result, target)
-        self.assertTrue(result.info.is_dir())
-        self.assertTrue(result.joinpath('dirD').info.is_dir())
-        self.assertTrue(result.joinpath('dirD', 'fileD').info.is_file())
-        self.assertEqual(result.joinpath('dirD', 'fileD').read_text(),
-                         "this is file D\n")
-        self.assertTrue(result.joinpath('fileC').info.is_file())
-        self.assertTrue(result.joinpath('fileC').read_text(),
-                        "this is file C\n")
-
-    def test_copy_dir_complex(self, follow_symlinks=True):
-        def ordered_walk(path):
-            for dirpath, dirnames, filenames in 
path.walk(follow_symlinks=follow_symlinks):
-                dirnames.sort()
-                filenames.sort()
-                yield dirpath, dirnames, filenames
-        base = self.cls(self.base)
-        source = base / 'dirC'
-
-        if self.can_symlink:
-            # Add some symlinks
-            source.joinpath('linkC').symlink_to('fileC')
-            source.joinpath('linkD').symlink_to('dirD', 
target_is_directory=True)
-
-        # Perform the copy
-        target = base / 'copyC'
-        result = source.copy(target, follow_symlinks=follow_symlinks)
-        self.assertEqual(result, target)
-
-        # Compare the source and target trees
-        source_walk = ordered_walk(source)
-        target_walk = ordered_walk(result)
-        for source_item, target_item in zip(source_walk, target_walk, 
strict=True):
-            self.assertEqual(source_item[0].parts[len(source.parts):],
-                             target_item[0].parts[len(target.parts):])  # 
dirpath
-            self.assertEqual(source_item[1], target_item[1])  # dirnames
-            self.assertEqual(source_item[2], target_item[2])  # filenames
-            # Compare files and symlinks
-            for filename in source_item[2]:
-                source_file = source_item[0].joinpath(filename)
-                target_file = target_item[0].joinpath(filename)
-                if follow_symlinks or not source_file.info.is_symlink():
-                    # Regular file.
-                    self.assertEqual(source_file.read_bytes(), 
target_file.read_bytes())
-                elif source_file.info.is_dir():
-                    # Symlink to directory.
-                    self.assertTrue(target_file.info.is_dir())
-                    self.assertEqual(source_file.readlink(), 
target_file.readlink())
-                else:
-                    # Symlink to file.
-                    self.assertEqual(source_file.read_bytes(), 
target_file.read_bytes())
-                    self.assertEqual(source_file.readlink(), 
target_file.readlink())
-
-    def test_copy_dir_complex_follow_symlinks_false(self):
-        self.test_copy_dir_complex(follow_symlinks=False)
-
-    def test_copy_dir_to_existing_directory(self):
-        base = self.cls(self.base)
-        source = base / 'dirC'
-        target = base / 'copyC'
-        target.mkdir()
-        target.joinpath('dirD').mkdir()
-        self.assertRaises(FileExistsError, source.copy, target)
-
-    def test_copy_dir_to_itself(self):
-        base = self.cls(self.base)
-        source = base / 'dirC'
-        self.assertRaises(OSError, source.copy, source)
-        self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
-
-    def test_copy_dir_into_itself(self):
-        base = self.cls(self.base)
-        source = base / 'dirC'
-        target = base / 'dirC' / 'dirD' / 'copyC'
-        self.assertRaises(OSError, source.copy, target)
-        self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
-        self.assertFalse(target.info.exists())
-
-    def test_copy_into(self):
-        base = self.cls(self.base)
-        source = base / 'fileA'
-        target_dir = base / 'dirA'
-        result = source.copy_into(target_dir)
-        self.assertEqual(result, target_dir / 'fileA')
-        self.assertTrue(result.info.exists())
-        self.assertEqual(source.read_text(), result.read_text())
-
-    def test_copy_into_empty_name(self):
-        source = self.cls('')
-        target_dir = self.base
-        self.assertRaises(ValueError, source.copy_into, target_dir)
-
 
 class ReadablePathWalkTest(unittest.TestCase):
     cls = DummyReadablePath

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to