Skip to content

Commit

Permalink
pythonGH-113528: Deoptimise pathlib._abc.PurePathBase.parent[s]
Browse files Browse the repository at this point in the history
Replace use of `_from_parsed_parts()` with `with_segments()`, and move
assignments to `_drv`, `_root`, _tail_cached` and `_str` slots into
`PurePath`.
  • Loading branch information
barneygale committed Dec 28, 2023
1 parent 1b19d73 commit 1bc7df7
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 41 deletions.
49 changes: 49 additions & 0 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ntpath
import os
import posixpath
from _collections_abc import Sequence

try:
import pwd
Expand All @@ -29,6 +30,35 @@
]


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


Expand Down Expand Up @@ -164,6 +194,25 @@ def __ge__(self, other):
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
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)

def as_uri(self):
"""Return the path as a URI."""
if not self.is_absolute():
Expand Down
54 changes: 13 additions & 41 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import posixpath
import sys
import warnings
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
Expand Down Expand Up @@ -140,35 +139,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.
Expand Down Expand Up @@ -457,21 +427,23 @@ def __rtruediv__(self, key):
@property
def parent(self):
"""The logical parent of the path."""
drv = self.drive
root = self.root
tail = self._tail
if not tail:
return self
path = self._from_parsed_parts(drv, root, tail[:-1])
path._resolving = self._resolving
return path
path, name = self.pathmod.split(str(self))
if name and name != '.':
path = self.with_segments(path)
path._resolving = self._resolving
return path
return self

@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)
split = self.pathmod.split
paths = []
path, name = split(str(self))
while name and name != '.':
paths.append(self.with_segments(path))
path, name = split(path)
return tuple(paths)

def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
Expand Down

0 comments on commit 1bc7df7

Please sign in to comment.