GH-128520: pathlib ABCs: tighten up argument types (#131621)

In `JoinablePath.full_match()` and `ReadablePath.glob()`, accept a `str`
pattern argument rather than `JoinablePath | str`.

In `ReadablePath.copy()` and `copy_into()`, accept a `WritablePath` target
argument rather than `WritablePath | str`.
This commit is contained in:
Barney Gale 2025-03-24 15:39:08 +00:00 committed by GitHub
parent d2d886215c
commit d372472896
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 9 additions and 29 deletions

View file

@ -1105,11 +1105,7 @@ class Path(PurePath):
if not hasattr(target, 'with_segments'):
target = self.with_segments(target)
ensure_distinct_paths(self, target)
try:
copy_to_target = target._copy_from
except AttributeError:
raise TypeError(f"Target path is not writable: {target!r}") from None
copy_to_target(self, **kwargs)
target._copy_from(self, **kwargs)
return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, **kwargs):

View file

@ -17,14 +17,12 @@ from pathlib import PurePath, Path
from typing import Optional, Protocol, runtime_checkable
def _explode_path(path):
def _explode_path(path, split):
"""
Split the path into a 2-tuple (anchor, parts), where *anchor* is the
uppermost parent of the path (equivalent to path.parents[-1]), and
*parts* is a reversed list of parts following the anchor.
"""
split = path.parser.split
path = str(path)
parent, name = split(path)
names = []
while path != parent:
@ -95,7 +93,7 @@ class _JoinablePath(ABC):
@property
def anchor(self):
"""The concatenation of the drive and root, or ''."""
return _explode_path(self)[0]
return _explode_path(str(self), self.parser.split)[0]
@property
def name(self):
@ -169,7 +167,7 @@ class _JoinablePath(ABC):
def parts(self):
"""An object providing sequence-like access to the
components in the filesystem path."""
anchor, parts = _explode_path(self)
anchor, parts = _explode_path(str(self), self.parser.split)
if anchor:
parts.append(anchor)
return tuple(reversed(parts))
@ -221,11 +219,9 @@ class _JoinablePath(ABC):
Return True if this path matches the given glob-style pattern. The
pattern is matched against the entire path.
"""
if not hasattr(pattern, 'with_segments'):
pattern = self.with_segments(pattern)
case_sensitive = self.parser.normcase('Aa') == 'Aa'
globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True)
match = globber.compile(str(pattern), altsep=pattern.parser.altsep)
globber = _PathGlobber(self.parser.sep, case_sensitive, recursive=True)
match = globber.compile(pattern, altsep=self.parser.altsep)
return match(str(self)) is not None
@ -282,9 +278,7 @@ class _ReadablePath(_JoinablePath):
"""Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern.
"""
if not hasattr(pattern, 'with_segments'):
pattern = self.with_segments(pattern)
anchor, parts = _explode_path(pattern)
anchor, parts = _explode_path(pattern, self.parser.split)
if anchor:
raise NotImplementedError("Non-relative patterns are unsupported")
elif not parts:
@ -338,14 +332,8 @@ class _ReadablePath(_JoinablePath):
"""
Recursively copy this file or directory tree to the given destination.
"""
if not hasattr(target, 'with_segments'):
target = self.with_segments(target)
ensure_distinct_paths(self, target)
try:
copy_to_target = target._copy_from
except AttributeError:
raise TypeError(f"Target path is not writable: {target!r}") from None
copy_to_target(self, **kwargs)
target._copy_from(self, **kwargs)
return target.joinpath() # Empty join to ensure fresh metadata.
def copy_into(self, target_dir, **kwargs):
@ -355,11 +343,7 @@ class _ReadablePath(_JoinablePath):
name = self.name
if not name:
raise ValueError(f"{self!r} has an empty name")
elif hasattr(target_dir, 'with_segments'):
target = target_dir / name
else:
target = self.with_segments(target_dir, name)
return self.copy(target, **kwargs)
return self.copy(target_dir / name, **kwargs)
class _WritablePath(_JoinablePath):