GH-130614: pathlib ABCs: improve support for receiving path metadata (#131259)

In the private pathlib ABCs, replace `_WritablePath._write_info()` with
`_WritablePath._copy_from()`. This provides the target path object with
more control over the copying process, including support for querying and
setting metadata *before* the path is created.

Adjust `_ReadablePath.copy()` so that it forwards its keyword arguments to
`_WritablePath._copy_from()` of the target path object. This allows us to
remove the unimplemented *preserve_metadata* argument in the ABC method,
making it a `Path` exclusive.
This commit is contained in:
Barney Gale 2025-03-16 06:11:20 +00:00 committed by GitHub
parent e82c2ca2a5
commit 563ab5cefe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 90 additions and 62 deletions

View file

@ -102,16 +102,16 @@ else:
if _winapi and hasattr(_winapi, 'CopyFile2'):
def _copyfile2(source, target):
def copyfile2(source, target):
"""
Copy from one file to another using CopyFile2 (Windows only).
"""
_winapi.CopyFile2(source, target, 0)
else:
_copyfile2 = None
copyfile2 = None
def _copyfileobj(source_f, target_f):
def copyfileobj(source_f, target_f):
"""
Copy data from file-like object source_f to file-like object target_f.
"""
@ -242,42 +242,6 @@ def ensure_different_files(source, target):
raise err
def copy_file(source, target, follow_symlinks=True, preserve_metadata=False):
"""
Recursively copy the given source ReadablePath to the given target WritablePath.
"""
info = source.info
if not follow_symlinks and info.is_symlink():
target.symlink_to(str(source.readlink()), info.is_dir())
if preserve_metadata:
target._write_info(info, follow_symlinks=False)
elif info.is_dir():
children = source.iterdir()
target.mkdir()
for src in children:
dst = target.joinpath(src.name)
copy_file(src, dst, follow_symlinks, preserve_metadata)
if preserve_metadata:
target._write_info(info)
else:
if _copyfile2:
# Use fast OS routine for local file copying where available.
try:
source_p = os.fspath(source)
target_p = os.fspath(target)
except TypeError:
pass
else:
_copyfile2(source_p, target_p)
return
ensure_different_files(source, target)
with magic_open(source, 'rb') as source_f:
with magic_open(target, 'wb') as target_f:
_copyfileobj(source_f, target_f)
if preserve_metadata:
target._write_info(info)
def copy_info(info, target, follow_symlinks=True):
"""Copy metadata from the given PathInfo to the given local path."""
copy_times_ns = (