Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1105,11 +1105,7 @@ def copy(self, target, **kwargs):
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):
Expand Down
32 changes: 8 additions & 24 deletions Lib/pathlib/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@
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:
Expand Down Expand Up @@ -95,7 +93,7 @@ def __str__(self):
@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):
Expand Down Expand Up @@ -169,7 +167,7 @@ def with_suffix(self, suffix):
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))
Expand Down Expand Up @@ -221,11 +219,9 @@ def full_match(self, pattern):
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


Expand Down Expand Up @@ -282,9 +278,7 @@ def glob(self, pattern, *, recurse_symlinks=True):
"""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:
Expand Down Expand Up @@ -338,14 +332,8 @@ def copy(self, target, **kwargs):
"""
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):
Expand All @@ -355,11 +343,7 @@ def copy_into(self, target_dir, **kwargs):
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):
Expand Down
Loading