From f8ffd490848cc036ad332da3dba0e487c04d2eea Mon Sep 17 00:00:00 2001 From: barneygale Date: Fri, 25 Oct 2024 19:03:25 +0100 Subject: [PATCH] GH-125413: `pathlib.Path.copy()`: get common metadata keys only once Improve `pathlib._abc.PathBase.copy()` (which provides `Path.copy()`) by fetching operands' supported metadata keys up-front, rather than once for each path in the tree. This prepares the way for using `os.DirEntry` objects in `copy()`. --- Lib/pathlib/_abc.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 11c8018b28f26b..bb115cbb99bc14 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -837,16 +837,6 @@ def _write_metadata(self, metadata, *, follow_symlinks=True): """ raise UnsupportedOperation(self._unsupported_msg('_write_metadata()')) - def _copy_metadata(self, target, *, follow_symlinks=True): - """ - Copies metadata (permissions, timestamps, etc) from this path to target. - """ - # Metadata types supported by both source and target. - keys = self._readable_metadata & target._writable_metadata - if keys: - metadata = self._read_metadata(keys, follow_symlinks=follow_symlinks) - target._write_metadata(metadata, follow_symlinks=follow_symlinks) - def _copy_file(self, target): """ Copy the contents of this file to the given target. @@ -872,24 +862,32 @@ def copy(self, target, *, follow_symlinks=True, dirs_exist_ok=False, if not isinstance(target, PathBase): target = self.with_segments(target) self._ensure_distinct_path(target) + if preserve_metadata: + # Metadata types supported by both source and target. + metadata_keys = self._readable_metadata & target._writable_metadata + else: + metadata_keys = None stack = [(self, target)] while stack: src, dst = stack.pop() if not follow_symlinks and src.is_symlink(): dst._symlink_to_target_of(src) - if preserve_metadata: - src._copy_metadata(dst, follow_symlinks=False) + if metadata_keys: + metadata = src._read_metadata(metadata_keys, follow_symlinks=False) + dst._write_metadata(metadata, follow_symlinks=False) elif src.is_dir(): children = src.iterdir() dst.mkdir(exist_ok=dirs_exist_ok) stack.extend((child, dst.joinpath(child.name)) for child in children) - if preserve_metadata: - src._copy_metadata(dst) + if metadata_keys: + metadata = src._read_metadata(metadata_keys) + dst._write_metadata(metadata) else: src._copy_file(dst) - if preserve_metadata: - src._copy_metadata(dst) + if metadata_keys: + metadata = src._read_metadata(metadata_keys) + dst._write_metadata(metadata) return target def copy_into(self, target_dir, *, follow_symlinks=True,