GH-113528: Deoptimise pathlib._abc.PurePathBase.parent (#113530)

Replace use of `_from_parsed_parts()` with `with_segments()`, and move
assignments to `_drv`, `_root`, _tail_cached` and `_str` slots into
`PurePath`.
This commit is contained in:
Barney Gale 2024-01-06 21:17:51 +00:00 committed by GitHub
parent 1e914ad89d
commit 37bd893a22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 42 deletions

View file

@ -11,6 +11,7 @@ import os
import posixpath import posixpath
import sys import sys
import warnings import warnings
from _collections_abc import Sequence
try: try:
import pwd import pwd
@ -31,6 +32,35 @@ __all__ = [
] ]
class _PathParents(Sequence):
"""This object provides sequence-like access to the logical ancestors
of a path. Don't try to construct it yourself."""
__slots__ = ('_path', '_drv', '_root', '_tail')
def __init__(self, path):
self._path = path
self._drv = path.drive
self._root = path.root
self._tail = path._tail
def __len__(self):
return len(self._tail)
def __getitem__(self, idx):
if isinstance(idx, slice):
return tuple(self[i] for i in range(*idx.indices(len(self))))
if idx >= len(self) or idx < -len(self):
raise IndexError(idx)
if idx < 0:
idx += len(self)
return self._path._from_parsed_parts(self._drv, self._root,
self._tail[:-idx - 1])
def __repr__(self):
return "<{}.parents>".format(type(self._path).__name__)
UnsupportedOperation = _abc.UnsupportedOperation UnsupportedOperation = _abc.UnsupportedOperation
@ -95,7 +125,6 @@ class PurePath(_abc.PurePathBase):
paths.append(path) paths.append(path)
# Avoid calling super().__init__, as an optimisation # Avoid calling super().__init__, as an optimisation
self._raw_paths = paths self._raw_paths = paths
self._resolving = False
def __reduce__(self): def __reduce__(self):
# Using the parts tuple helps share interned path parts # Using the parts tuple helps share interned path parts
@ -166,6 +195,23 @@ class PurePath(_abc.PurePathBase):
return NotImplemented return NotImplemented
return self._parts_normcase >= other._parts_normcase return self._parts_normcase >= other._parts_normcase
@property
def parent(self):
"""The logical parent of the path."""
drv = self.drive
root = self.root
tail = self._tail
if not tail:
return self
return self._from_parsed_parts(drv, root, tail[:-1])
@property
def parents(self):
"""A sequence of this path's logical parents."""
# The value of this property should not be cached on the path object,
# as doing so would introduce a reference cycle.
return _PathParents(self)
@property @property
def name(self): def name(self):
"""The final path component, if any.""" """The final path component, if any."""

View file

@ -2,7 +2,6 @@ import functools
import ntpath import ntpath
import posixpath import posixpath
import sys import sys
from _collections_abc import Sequence
from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
from itertools import chain from itertools import chain
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
@ -138,35 +137,6 @@ class UnsupportedOperation(NotImplementedError):
pass pass
class _PathParents(Sequence):
"""This object provides sequence-like access to the logical ancestors
of a path. Don't try to construct it yourself."""
__slots__ = ('_path', '_drv', '_root', '_tail')
def __init__(self, path):
self._path = path
self._drv = path.drive
self._root = path.root
self._tail = path._tail
def __len__(self):
return len(self._tail)
def __getitem__(self, idx):
if isinstance(idx, slice):
return tuple(self[i] for i in range(*idx.indices(len(self))))
if idx >= len(self) or idx < -len(self):
raise IndexError(idx)
if idx < 0:
idx += len(self)
return self._path._from_parsed_parts(self._drv, self._root,
self._tail[:-idx - 1])
def __repr__(self):
return "<{}.parents>".format(type(self._path).__name__)
class PurePathBase: class PurePathBase:
"""Base class for pure path objects. """Base class for pure path objects.
@ -442,21 +412,26 @@ class PurePathBase:
@property @property
def parent(self): def parent(self):
"""The logical parent of the path.""" """The logical parent of the path."""
drv = self.drive path = str(self)
root = self.root parent = self.pathmod.dirname(path)
tail = self._tail if path != parent:
if not tail: parent = self.with_segments(parent)
return self parent._resolving = self._resolving
path = self._from_parsed_parts(drv, root, tail[:-1]) return parent
path._resolving = self._resolving return self
return path
@property @property
def parents(self): def parents(self):
"""A sequence of this path's logical parents.""" """A sequence of this path's logical parents."""
# The value of this property should not be cached on the path object, dirname = self.pathmod.dirname
# as doing so would introduce a reference cycle. path = str(self)
return _PathParents(self) parent = dirname(path)
parents = []
while path != parent:
parents.append(self.with_segments(parent))
path = parent
parent = dirname(path)
return tuple(parents)
def is_absolute(self): def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable, """True if the path is absolute (has both a root and, if applicable,