diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 33e963c944fc5f..115e120572d9f1 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -192,7 +192,7 @@ def full_match(self, pattern, *, case_sensitive=None): Return True if this path matches the given glob-style pattern. The pattern is matched against the entire path. """ - if not isinstance(pattern, JoinablePath): + if not hasattr(pattern, 'with_segments'): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = self.parser.normcase('Aa') == 'Aa' @@ -286,7 +286,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): """Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern. """ - if not isinstance(pattern, JoinablePath): + if not hasattr(pattern, 'with_segments'): pattern = self.with_segments(pattern) anchor, parts = _explode_path(pattern) if anchor: @@ -348,7 +348,7 @@ def copy(self, target, follow_symlinks=True, dirs_exist_ok=False, """ Recursively copy this file or directory tree to the given destination. """ - if not hasattr(target, '_copy_writer'): + if not hasattr(target, 'with_segments'): target = self.with_segments(target) # Delegate to the target path's CopyWriter object. @@ -366,7 +366,7 @@ def copy_into(self, target_dir, *, follow_symlinks=True, name = self.name if not name: raise ValueError(f"{self!r} has an empty name") - elif hasattr(target_dir, '_copy_writer'): + elif hasattr(target_dir, 'with_segments'): target = target_dir / name else: target = self.with_segments(target_dir, name) diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index bd1e871ebcfad7..9b2738652e9754 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -475,7 +475,7 @@ def relative_to(self, other, *, walk_up=False): The *walk_up* parameter controls whether `..` may be used to resolve the path. """ - if not isinstance(other, PurePath): + if not hasattr(other, 'with_segments'): other = self.with_segments(other) for step, path in enumerate(chain([other], other.parents)): if path == self or path in self.parents: @@ -492,7 +492,7 @@ def relative_to(self, other, *, walk_up=False): def is_relative_to(self, other): """Return True if the path is relative to another path or False. """ - if not isinstance(other, PurePath): + if not hasattr(other, 'with_segments'): other = self.with_segments(other) return other == self or other in self.parents @@ -545,7 +545,7 @@ def full_match(self, pattern, *, case_sensitive=None): Return True if this path matches the given glob-style pattern. The pattern is matched against the entire path. """ - if not isinstance(pattern, PurePath): + if not hasattr(pattern, 'with_segments'): pattern = self.with_segments(pattern) if case_sensitive is None: case_sensitive = self.parser is posixpath @@ -564,7 +564,7 @@ def match(self, path_pattern, *, case_sensitive=None): is matched. The recursive wildcard '**' is *not* supported by this method. """ - if not isinstance(path_pattern, PurePath): + if not hasattr(path_pattern, 'with_segments'): path_pattern = self.with_segments(path_pattern) if case_sensitive is None: case_sensitive = self.parser is posixpath @@ -1064,7 +1064,9 @@ def rename(self, target): Returns the new Path instance pointing to the target path. """ os.rename(self, target) - return self.with_segments(target) + if not hasattr(target, 'with_segments'): + target = self.with_segments(target) + return target def replace(self, target): """ @@ -1077,7 +1079,9 @@ def replace(self, target): Returns the new Path instance pointing to the target path. """ os.replace(self, target) - return self.with_segments(target) + if not hasattr(target, 'with_segments'): + target = self.with_segments(target) + return target _copy_writer = property(LocalCopyWriter) @@ -1086,7 +1090,7 @@ def copy(self, target, follow_symlinks=True, dirs_exist_ok=False, """ Recursively copy this file or directory tree to the given destination. """ - if not hasattr(target, '_copy_writer'): + if not hasattr(target, 'with_segments'): target = self.with_segments(target) # Delegate to the target path's CopyWriter object. @@ -1104,7 +1108,7 @@ def copy_into(self, target_dir, *, follow_symlinks=True, name = self.name if not name: raise ValueError(f"{self!r} has an empty name") - elif hasattr(target_dir, '_copy_writer'): + elif hasattr(target_dir, 'with_segments'): target = target_dir / name else: target = self.with_segments(target_dir, name) @@ -1118,16 +1122,13 @@ def move(self, target): """ # Use os.replace() if the target is os.PathLike and on the same FS. try: - target_str = os.fspath(target) + target = self.with_segments(target) except TypeError: pass else: - if not hasattr(target, '_copy_writer'): - target = self.with_segments(target_str) ensure_different_files(self, target) try: - os.replace(self, target_str) - return target + return self.replace(target) except OSError as err: if err.errno != EXDEV: raise @@ -1143,7 +1144,7 @@ def move_into(self, target_dir): name = self.name if not name: raise ValueError(f"{self!r} has an empty name") - elif hasattr(target_dir, '_copy_writer'): + elif hasattr(target_dir, 'with_segments'): target = target_dir / name else: target = self.with_segments(target_dir, name) diff --git a/Misc/NEWS.d/next/Library/2025-02-16-18-13-40.gh-issue-128520.iZtOMz.rst b/Misc/NEWS.d/next/Library/2025-02-16-18-13-40.gh-issue-128520.iZtOMz.rst new file mode 100644 index 00000000000000..15de99e8bbdbcd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-16-18-13-40.gh-issue-128520.iZtOMz.rst @@ -0,0 +1,5 @@ +Apply type conversion consistently in :class:`pathlib.PurePath` and +:class:`~pathlib.Path` methods can accept a path object as an argument, such +as :meth:`~pathlib.PurePath.match` and :meth:`~pathlib.Path.rename`. The +argument is now converted to path object if it lacks a +:meth:`~pathlib.PurePath.with_segments` attribute, and not otherwise.