Skip to content

Commit

Permalink
Sync from upstream.
Browse files Browse the repository at this point in the history
  • Loading branch information
barneygale committed Jan 13, 2024
1 parent 10653be commit 4f4e1f1
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 24 deletions.
2 changes: 1 addition & 1 deletion cpython
Submodule cpython updated 160 files
61 changes: 38 additions & 23 deletions pathlib_abc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
"""
Abstract base classes for rich path objects.
This module is published as a PyPI package called "pathlib-abc".
This module is also a *PRIVATE* part of the Python standard library, where
it's developed alongside pathlib. If it finds success and maturity as a PyPI
package, it could become a public part of the standard library.
Two base classes are defined here -- PurePathBase and PathBase -- that
resemble pathlib's PurePath and Path respectively.
"""

import functools
import abc
import pathlib
ntpath = object()
from . import _posixpath as posixpath
from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
Expand Down Expand Up @@ -57,9 +69,6 @@ def _compile_pattern(pat, sep, case_sensitive):

flags = 0 if case_sensitive else re.IGNORECASE
regex = glob.translate(pat, recursive=True, include_hidden=True, seps=sep)
# The string representation of an empty path is a single dot ('.'). Empty
# paths shouldn't match wildcards, so we consume it with an atomic group.
regex = r'(?=(?P<empty>\.\Z|))(?P=empty)' + regex
return re.compile(regex, flags=flags).match


Expand Down Expand Up @@ -167,10 +176,15 @@ def with_segments(self, *pathsegments):
"""
return type(self)(*pathsegments)

@property
def _raw_path(self):
"""The joined but unnormalized path."""
return self.pathmod.join(*self._raw_paths)

def __str__(self):
"""Return the string representation of the path, suitable for
passing to system calls."""
return self.pathmod.join(*self._raw_paths)
return self._raw_path

def as_posix(self):
"""Return the string representation of the path with forward (/)
Expand All @@ -180,23 +194,23 @@ def as_posix(self):
@property
def drive(self):
"""The drive prefix (letter or UNC path), if any."""
return self.pathmod.splitdrive(str(self))[0]
return self.pathmod.splitdrive(self._raw_path)[0]

@property
def root(self):
"""The root of the path, if any."""
return self.pathmod.splitroot(str(self))[1]
return self.pathmod.splitroot(self._raw_path)[1]

@property
def anchor(self):
"""The concatenation of the drive and root, or ''."""
drive, root, _ = self.pathmod.splitroot(str(self))
drive, root, _ = self.pathmod.splitroot(self._raw_path)
return drive + root

@property
def name(self):
"""The final path component, if any."""
return self.pathmod.basename(str(self))
return self.pathmod.basename(self._raw_path)

@property
def suffix(self):
Expand Down Expand Up @@ -240,7 +254,7 @@ def with_name(self, name):
dirname = self.pathmod.dirname
if dirname(name):
raise ValueError(f"Invalid name {name!r}")
return self.with_segments(dirname(str(self)), name)
return self.with_segments(dirname(self._raw_path), name)

def with_stem(self, stem):
"""Return a new path with the stem changed."""
Expand Down Expand Up @@ -270,18 +284,20 @@ def relative_to(self, other, *, walk_up=False):
other = self.with_segments(other)
anchor0, parts0 = self._stack
anchor1, parts1 = other._stack
if isinstance(anchor0, str) != isinstance(anchor1, str):
raise TypeError(f"{self._raw_path!r} and {other._raw_path!r} have different types")
if anchor0 != anchor1:
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
raise ValueError(f"{self._raw_path!r} and {other._raw_path!r} have different anchors")
while parts0 and parts1 and parts0[-1] == parts1[-1]:
parts0.pop()
parts1.pop()
for part in parts1:
if not part or part == '.':
pass
elif not walk_up:
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
raise ValueError(f"{self._raw_path!r} is not in the subpath of {other._raw_path!r}")
elif part == '..':
raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
raise ValueError(f"'..' segment in {other._raw_path!r} cannot be walked")
else:
parts0.append('..')
return self.with_segments('', *reversed(parts0))
Expand All @@ -293,6 +309,8 @@ def is_relative_to(self, other):
other = self.with_segments(other)
anchor0, parts0 = self._stack
anchor1, parts1 = other._stack
if isinstance(anchor0, str) != isinstance(anchor1, str):
raise TypeError(f"{self._raw_path!r} and {other._raw_path!r} have different types")
if anchor0 != anchor1:
return False
while parts0 and parts1 and parts0[-1] == parts1[-1]:
Expand Down Expand Up @@ -340,7 +358,7 @@ def _stack(self):
*parts* is a reversed list of parts following the anchor.
"""
split = self.pathmod.split
path = str(self)
path = self._raw_path
parent, name = split(path)
names = []
while path != parent:
Expand All @@ -352,7 +370,7 @@ def _stack(self):
@property
def parent(self):
"""The logical parent of the path."""
path = str(self)
path = self._raw_path
parent = self.pathmod.dirname(path)
if path != parent:
parent = self.with_segments(parent)
Expand All @@ -364,7 +382,7 @@ def parent(self):
def parents(self):
"""A sequence of this path's logical parents."""
dirname = self.pathmod.dirname
path = str(self)
path = self._raw_path
parent = dirname(path)
parents = []
while path != parent:
Expand All @@ -376,17 +394,14 @@ def parents(self):
def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
a drive)."""
if self.pathmod is ntpath:
# ntpath.isabs() is defective - see GH-44626.
return bool(self.drive and self.root)
elif self.pathmod is posixpath:
if self.pathmod is posixpath:
# Optimization: work with raw paths on POSIX.
for path in self._raw_paths:
if path.startswith('/'):
return True
return False
else:
return self.pathmod.isabs(str(self))
return self.pathmod.isabs(self._raw_path)

def is_reserved(self):
"""Return True if the path contains one of the special names reserved
Expand Down Expand Up @@ -901,7 +916,7 @@ def resolve(self, strict=False):
# encountered during resolution.
link_count += 1
if link_count >= self._max_symlinks:
raise OSError(ELOOP, "Too many symbolic links in path", str(self))
raise OSError(ELOOP, "Too many symbolic links in path", self._raw_path)
target_root, target_parts = path.readlink()._stack
# If the symlink target is absolute (like '/etc/hosts'), set the current
# path to its uppermost parent (like '/').
Expand All @@ -915,7 +930,7 @@ def resolve(self, strict=False):
parts.extend(target_parts)
continue
elif parts and not S_ISDIR(st.st_mode):
raise NotADirectoryError(ENOTDIR, "Not a directory", str(self))
raise NotADirectoryError(ENOTDIR, "Not a directory", self._raw_path)
except OSError:
if strict:
raise
Expand Down

0 comments on commit 4f4e1f1

Please sign in to comment.