GH-130608: Remove dirs_exist_ok argument from pathlib.Path.copy() (#130610)

This feature isn't sufficiently motivated.
This commit is contained in:
Barney Gale 2025-02-28 19:29:20 +00:00 committed by GitHub
parent fdcbc29f26
commit b545450961
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 13 additions and 40 deletions

View file

@ -1571,8 +1571,7 @@ Creating files and directories
Copying, moving and deleting Copying, moving and deleting
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. method:: Path.copy(target, *, follow_symlinks=True, dirs_exist_ok=False, \ .. method:: Path.copy(target, *, follow_symlinks=True, preserve_metadata=False)
preserve_metadata=False)
Copy this file or directory tree to the given *target*, and return a new Copy this file or directory tree to the given *target*, and return a new
:class:`!Path` instance pointing to *target*. :class:`!Path` instance pointing to *target*.
@ -1582,12 +1581,6 @@ Copying, moving and deleting
default), the symlink's target is copied. Otherwise, the symlink is default), the symlink's target is copied. Otherwise, the symlink is
recreated at the destination. recreated at the destination.
If the source is a directory and *dirs_exist_ok* is false (the default), a
:exc:`FileExistsError` is raised if the target is an existing directory.
If *dirs_exists_ok* is true, the copying operation will overwrite
existing files within the destination tree with corresponding files
from the source tree.
If *preserve_metadata* is false (the default), only directory structures If *preserve_metadata* is false (the default), only directory structures
and file data are guaranteed to be copied. Set *preserve_metadata* to true and file data are guaranteed to be copied. Set *preserve_metadata* to true
to ensure that file and directory permissions, flags, last access and to ensure that file and directory permissions, flags, last access and
@ -1604,7 +1597,7 @@ Copying, moving and deleting
.. method:: Path.copy_into(target_dir, *, follow_symlinks=True, \ .. method:: Path.copy_into(target_dir, *, follow_symlinks=True, \
dirs_exist_ok=False, preserve_metadata=False) preserve_metadata=False)
Copy this file or directory tree into the given *target_dir*, which should Copy this file or directory tree into the given *target_dir*, which should
be an existing directory. Other arguments are handled identically to be an existing directory. Other arguments are handled identically to

View file

@ -340,19 +340,18 @@ class ReadablePath(JoinablePath):
""" """
raise NotImplementedError raise NotImplementedError
def copy(self, target, follow_symlinks=True, dirs_exist_ok=False, def copy(self, target, follow_symlinks=True, preserve_metadata=False):
preserve_metadata=False):
""" """
Recursively copy this file or directory tree to the given destination. Recursively copy this file or directory tree to the given destination.
""" """
if not hasattr(target, 'with_segments'): if not hasattr(target, 'with_segments'):
target = self.with_segments(target) target = self.with_segments(target)
ensure_distinct_paths(self, target) ensure_distinct_paths(self, target)
copy_file(self, target, follow_symlinks, dirs_exist_ok, preserve_metadata) copy_file(self, target, follow_symlinks, preserve_metadata)
return target.joinpath() # Empty join to ensure fresh metadata. return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, *, follow_symlinks=True, def copy_into(self, target_dir, *, follow_symlinks=True,
dirs_exist_ok=False, preserve_metadata=False): preserve_metadata=False):
""" """
Copy this file or directory tree into the given existing directory. Copy this file or directory tree into the given existing directory.
""" """
@ -364,7 +363,6 @@ class ReadablePath(JoinablePath):
else: else:
target = self.with_segments(target_dir, name) target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks, return self.copy(target, follow_symlinks=follow_symlinks,
dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata) preserve_metadata=preserve_metadata)

View file

@ -1093,19 +1093,18 @@ class Path(PurePath):
target = self.with_segments(target) target = self.with_segments(target)
return target return target
def copy(self, target, follow_symlinks=True, dirs_exist_ok=False, def copy(self, target, follow_symlinks=True, preserve_metadata=False):
preserve_metadata=False):
""" """
Recursively copy this file or directory tree to the given destination. Recursively copy this file or directory tree to the given destination.
""" """
if not hasattr(target, 'with_segments'): if not hasattr(target, 'with_segments'):
target = self.with_segments(target) target = self.with_segments(target)
ensure_distinct_paths(self, target) ensure_distinct_paths(self, target)
copy_file(self, target, follow_symlinks, dirs_exist_ok, preserve_metadata) copy_file(self, target, follow_symlinks, preserve_metadata)
return target.joinpath() # Empty join to ensure fresh metadata. return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, *, follow_symlinks=True, def copy_into(self, target_dir, *, follow_symlinks=True,
dirs_exist_ok=False, preserve_metadata=False): preserve_metadata=False):
""" """
Copy this file or directory tree into the given existing directory. Copy this file or directory tree into the given existing directory.
""" """
@ -1117,7 +1116,6 @@ class Path(PurePath):
else: else:
target = self.with_segments(target_dir, name) target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks, return self.copy(target, follow_symlinks=follow_symlinks,
dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata) preserve_metadata=preserve_metadata)
def move(self, target): def move(self, target):

View file

@ -242,8 +242,7 @@ def ensure_different_files(source, target):
raise err raise err
def copy_file(source, target, follow_symlinks=True, dirs_exist_ok=False, def copy_file(source, target, follow_symlinks=True, preserve_metadata=False):
preserve_metadata=False):
""" """
Recursively copy the given source ReadablePath to the given target WritablePath. Recursively copy the given source ReadablePath to the given target WritablePath.
""" """
@ -254,10 +253,10 @@ def copy_file(source, target, follow_symlinks=True, dirs_exist_ok=False,
target._write_info(info, follow_symlinks=False) target._write_info(info, follow_symlinks=False)
elif info.is_dir(): elif info.is_dir():
children = source.iterdir() children = source.iterdir()
target.mkdir(exist_ok=dirs_exist_ok) target.mkdir()
for src in children: for src in children:
dst = target.joinpath(src.name) dst = target.joinpath(src.name)
copy_file(src, dst, follow_symlinks, dirs_exist_ok, preserve_metadata) copy_file(src, dst, follow_symlinks, preserve_metadata)
if preserve_metadata: if preserve_metadata:
target._write_info(info) target._write_info(info)
else: else:

View file

@ -1495,23 +1495,6 @@ class RWPathTest(WritablePathTest, ReadablePathTest):
target.joinpath('dirD').mkdir() target.joinpath('dirD').mkdir()
self.assertRaises(FileExistsError, source.copy, target) self.assertRaises(FileExistsError, source.copy, target)
def test_copy_dir_to_existing_directory_dirs_exist_ok(self):
base = self.cls(self.base)
source = base / 'dirC'
target = base / 'copyC'
target.mkdir()
target.joinpath('dirD').mkdir()
result = source.copy(target, dirs_exist_ok=True)
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_to_itself(self): def test_copy_dir_to_itself(self):
base = self.cls(self.base) base = self.cls(self.base)
source = base / 'dirC' source = base / 'dirC'

View file

@ -0,0 +1,2 @@
Remove *dirs_exist_ok* argument from :meth:`pathlib.Path.copy` and
:meth:`~pathlib.Path.copy_into`. These methods are new in Python 3.14.