GH-73991: Prune pathlib.Path.copy() and copy_into() arguments (#123337)

Remove *ignore* and *on_error* arguments from `pathlib.Path.copy[_into]()`,
because these arguments are under-designed. Specifically:

- *ignore* is appropriated from `shutil.copytree()`, but it's not clear
  how it should apply when the user copies a non-directory. We've changed
  the callback signature from the `shutil` version, but I'm not confident
  the new signature is as good as it can be.
- *on_error* is a generalisation of `shutil.copytree()`'s error handling,
  which is to accumulate exceptions and raise a single `shutil.Error` at
  the end. It's not obvious which solution is better.

Additionally, this arguments may be challenging to implement in future user
subclasses of `PathBase`, which might utilise a native recursive copying
method.
This commit is contained in:
Barney Gale 2024-08-26 17:05:34 +01:00 committed by GitHub
parent 033d537cd4
commit 7bd6ebf696
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 21 additions and 108 deletions

View file

@ -1984,14 +1984,6 @@ def test_copy_dir_to_itself(self):
self.assertRaises(OSError, source.copy, source)
self.assertRaises(OSError, source.copy, source, follow_symlinks=False)
def test_copy_dir_to_itself_on_error(self):
base = self.cls(self.base)
source = base / 'dirC'
errors = []
source.copy(source, on_error=errors.append)
self.assertEqual(len(errors), 1)
self.assertIsInstance(errors[0], OSError)
def test_copy_dir_into_itself(self):
base = self.cls(self.base)
source = base / 'dirC'
@ -2000,61 +1992,6 @@ def test_copy_dir_into_itself(self):
self.assertRaises(OSError, source.copy, target, follow_symlinks=False)
self.assertFalse(target.exists())
def test_copy_missing_on_error(self):
base = self.cls(self.base)
source = base / 'foo'
target = base / 'copyA'
errors = []
result = source.copy(target, on_error=errors.append)
self.assertEqual(result, target)
self.assertEqual(len(errors), 1)
self.assertIsInstance(errors[0], FileNotFoundError)
def test_copy_dir_ignore_false(self):
base = self.cls(self.base)
source = base / 'dirC'
target = base / 'copyC'
ignores = []
def ignore_false(path):
ignores.append(path)
return False
result = source.copy(target, ignore=ignore_false)
self.assertEqual(result, target)
self.assertEqual(set(ignores), {
source / 'dirD',
source / 'dirD' / 'fileD',
source / 'fileC',
source / 'novel.txt',
})
self.assertTrue(target.is_dir())
self.assertTrue(target.joinpath('dirD').is_dir())
self.assertTrue(target.joinpath('dirD', 'fileD').is_file())
self.assertEqual(target.joinpath('dirD', 'fileD').read_text(),
"this is file D\n")
self.assertTrue(target.joinpath('fileC').is_file())
self.assertTrue(target.joinpath('fileC').read_text(),
"this is file C\n")
def test_copy_dir_ignore_true(self):
base = self.cls(self.base)
source = base / 'dirC'
target = base / 'copyC'
ignores = []
def ignore_true(path):
ignores.append(path)
return True
result = source.copy(target, ignore=ignore_true)
self.assertEqual(result, target)
self.assertEqual(set(ignores), {
source / 'dirD',
source / 'fileC',
source / 'novel.txt',
})
self.assertTrue(target.is_dir())
self.assertFalse(target.joinpath('dirD').exists())
self.assertFalse(target.joinpath('fileC').exists())
self.assertFalse(target.joinpath('novel.txt').exists())
@needs_symlinks
def test_copy_dangling_symlink(self):
base = self.cls(self.base)