From 5a945256f9f02d8d39c5cfd2a23c4043271e07fb Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 7 Aug 2024 03:18:12 +0100 Subject: [PATCH] Return the target as a Path object. --- Doc/library/pathlib.rst | 3 +- Lib/pathlib/_abc.py | 23 ++++++------ Lib/test/test_pathlib/test_pathlib_abc.py | 45 +++++++++++++++-------- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 6bf4b1fae9016d..bf3fefb628d852 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1542,7 +1542,8 @@ Copying, renaming and deleting .. method:: Path.copy(target, *, follow_symlinks=True, dirs_exist_ok=False, \ preserve_metadata=False, ignore=None, on_error=None) - Copy this file or directory tree to the given *target*. + Copy this file or directory tree to the given *target*, and return a new + :class:`!Path` instance pointing to *target*. If the source is a file, the target will be replaced if it is an existing file. If the source is a symlink and *follow_symlinks* is true (the diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 8ea6aab84a8baf..ca91e1c30fdb9d 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -852,26 +852,27 @@ def on_error(err): raise err stack = [(self, target)] while stack: - source, target = stack.pop() + src, dst = stack.pop() try: - if not follow_symlinks and source.is_symlink(): - target._symlink_to_target_of(source) + if not follow_symlinks and src.is_symlink(): + dst._symlink_to_target_of(src) if preserve_metadata: - source._copy_metadata(target, follow_symlinks=False) - elif source.is_dir(): - children = source.iterdir() - target.mkdir(exist_ok=dirs_exist_ok) + src._copy_metadata(dst, follow_symlinks=False) + elif src.is_dir(): + children = src.iterdir() + dst.mkdir(exist_ok=dirs_exist_ok) for child in children: if not (ignore and ignore(child)): - stack.append((child, target.joinpath(child.name))) + stack.append((child, dst.joinpath(child.name))) if preserve_metadata: - source._copy_metadata(target) + src._copy_metadata(dst) else: - source._copy_data(target) + src._copy_data(dst) if preserve_metadata: - source._copy_metadata(target) + src._copy_metadata(dst) except OSError as err: on_error(err) + return target def rename(self, target): """ diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 4cad9864c22b79..629a1d4bdeb4de 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1731,7 +1731,8 @@ def test_copy_file(self): base = self.cls(self.base) source = base / 'fileA' target = base / 'copyA' - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertEqual(source.read_text(), target.read_text()) @@ -1740,7 +1741,8 @@ def test_copy_symlink_follow_symlinks_true(self): base = self.cls(self.base) source = base / 'linkA' target = base / 'copyA' - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertFalse(target.is_symlink()) self.assertEqual(source.read_text(), target.read_text()) @@ -1750,7 +1752,8 @@ def test_copy_symlink_follow_symlinks_false(self): base = self.cls(self.base) source = base / 'linkA' target = base / 'copyA' - source.copy(target, follow_symlinks=False) + result = source.copy(target, follow_symlinks=False) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertTrue(target.is_symlink()) self.assertEqual(source.readlink(), target.readlink()) @@ -1760,7 +1763,8 @@ def test_copy_directory_symlink_follow_symlinks_false(self): base = self.cls(self.base) source = base / 'linkB' target = base / 'copyA' - source.copy(target, follow_symlinks=False) + result = source.copy(target, follow_symlinks=False) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertTrue(target.is_symlink()) self.assertEqual(source.readlink(), target.readlink()) @@ -1769,7 +1773,8 @@ def test_copy_file_to_existing_file(self): base = self.cls(self.base) source = base / 'fileA' target = base / 'dirB' / 'fileB' - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertEqual(source.read_text(), target.read_text()) @@ -1786,7 +1791,8 @@ def test_copy_file_to_existing_symlink(self): source = base / 'dirB' / 'fileB' target = base / 'linkA' real_target = base / 'fileA' - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertTrue(target.is_symlink()) self.assertTrue(real_target.exists()) @@ -1799,7 +1805,8 @@ def test_copy_file_to_existing_symlink_follow_symlinks_false(self): source = base / 'dirB' / 'fileB' target = base / 'linkA' real_target = base / 'fileA' - source.copy(target, follow_symlinks=False) + result = source.copy(target, follow_symlinks=False) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertTrue(target.is_symlink()) self.assertTrue(real_target.exists()) @@ -1811,7 +1818,8 @@ def test_copy_file_empty(self): source = base / 'empty' target = base / 'copyA' source.write_bytes(b'') - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.exists()) self.assertEqual(target.read_bytes(), b'') @@ -1819,7 +1827,8 @@ def test_copy_dir_simple(self): base = self.cls(self.base) source = base / 'dirC' target = base / 'copyC' - source.copy(target) + result = source.copy(target) + self.assertEqual(result, target) self.assertTrue(target.is_dir()) self.assertTrue(target.joinpath('dirD').is_dir()) self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) @@ -1845,7 +1854,8 @@ def ordered_walk(path): # Perform the copy target = base / 'copyC' - source.copy(target, follow_symlinks=follow_symlinks) + result = source.copy(target, follow_symlinks=follow_symlinks) + self.assertEqual(result, target) # Compare the source and target trees source_walk = ordered_walk(source) @@ -1888,7 +1898,8 @@ def test_copy_dir_to_existing_directory_dirs_exist_ok(self): target = base / 'copyC' target.mkdir() target.joinpath('dirD').mkdir() - source.copy(target, dirs_exist_ok=True) + result = source.copy(target, dirs_exist_ok=True) + self.assertEqual(result, target) self.assertTrue(target.is_dir()) self.assertTrue(target.joinpath('dirD').is_dir()) self.assertTrue(target.joinpath('dirD', 'fileD').is_file()) @@ -1903,7 +1914,8 @@ def test_copy_missing_on_error(self): source = base / 'foo' target = base / 'copyA' errors = [] - source.copy(target, on_error=errors.append) + result = source.copy(target, on_error=errors.append) + self.assertEqual(result, target) self.assertEqual(len(errors), 1) self.assertIsInstance(errors[0], FileNotFoundError) @@ -1915,7 +1927,8 @@ def test_copy_dir_ignore_false(self): def ignore_false(path): ignores.append(path) return False - source.copy(target, ignore=ignore_false) + result = source.copy(target, ignore=ignore_false) + self.assertEqual(result, target) self.assertEqual(set(ignores), { source / 'dirD', source / 'dirD' / 'fileD', @@ -1939,7 +1952,8 @@ def test_copy_dir_ignore_true(self): def ignore_true(path): ignores.append(path) return True - source.copy(target, ignore=ignore_true) + result = source.copy(target, ignore=ignore_true) + self.assertEqual(result, target) self.assertEqual(set(ignores), { source / 'dirD', source / 'fileC', @@ -1962,7 +1976,8 @@ def test_copy_dangling_symlink(self): self.assertRaises(FileNotFoundError, source.copy, target) target2 = base / 'target2' - source.copy(target2, follow_symlinks=False) + result = source.copy(target2, follow_symlinks=False) + self.assertEqual(result, target2) self.assertTrue(target2.joinpath('link').is_symlink()) self.assertEqual(target2.joinpath('link').readlink(), self.cls('nonexistent'))