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 sys
import warnings
from _collections_abc import Sequence
try:
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
@ -95,7 +125,6 @@ class PurePath(_abc.PurePathBase):
paths.append(path)
# Avoid calling super().__init__, as an optimisation
self._raw_paths = paths
self._resolving = False
def __reduce__(self):
# Using the parts tuple helps share interned path parts
@ -166,6 +195,23 @@ class PurePath(_abc.PurePathBase):
return NotImplemented
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
def name(self):
"""The final path component, if any."""

View file

@ -2,7 +2,6 @@ import functools
import ntpath
import posixpath
import sys
from _collections_abc import Sequence
from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
from itertools import chain
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
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:
"""Base class for pure path objects.
@ -442,21 +412,26 @@ class PurePathBase:
@property
def parent(self):
"""The logical parent of the path."""
drv = self.drive
root = self.root
tail = self._tail
if not tail:
path = str(self)
parent = self.pathmod.dirname(path)
if path != parent:
parent = self.with_segments(parent)
parent._resolving = self._resolving
return parent
return self
path = self._from_parsed_parts(drv, root, tail[:-1])
path._resolving = self._resolving
return path
@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)
dirname = self.pathmod.dirname
path = str(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):
"""True if the path is absolute (has both a root and, if applicable,