Visitar URL original
GH-101362: Optimise pathlib by deferring path normalisation by barneygale · Pull Request #101560 · python/cpython · GitHub
Skip to content
Closed
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Undo addition of __init__() and change to _from_parsed_parts()
  • Loading branch information
barneygale committed Mar 6, 2023
commit cc2c7111d73b3b33008fe3d848d99655a249bd76
78 changes: 41 additions & 37 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,19 +256,37 @@ class PurePath(object):
)
_flavour = os.path

def __new__(cls, *args, **kwargs):
def __new__(cls, *args):
"""Construct a PurePath from one or several strings and or existing
PurePath objects. The strings and path objects are combined so as
to yield a canonicalized path, which is incorporated into the
new PurePath object.
"""
if cls is PurePath:
cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
return super().__new__(cls)
return cls._from_parts(args)

def __reduce__(self):
return (self.__class__, (self._fspath,))

@classmethod
def _join_parts(cls, parts):
if not parts:
return ''
elif len(parts) == 1:
path = os.fspath(parts[0])
else:
path = cls._flavour.join(*parts)
if isinstance(path, str):
# Force-cast str subclasses to str (issue #21127)
path = str(path)
else:
raise TypeError(
"argument should be a str or an os.PathLike "
"object where __fspath__ returns a str, "
f"not {type(path).__name__!r}")
return path

@classmethod
def _parse_path(cls, path):
if not path:
Expand All @@ -285,22 +303,11 @@ def _parse_path(cls, path):
parsed = [sys.intern(x) for x in unfiltered_parsed if x and x != '.']
return drv, root, parsed

def __init__(self, *args):
if args:
if len(args) == 1:
path = os.fspath(args[0])
else:
path = self._flavour.join(*args)
if isinstance(path, str):
# Force-cast str subclasses to str (issue #21127)
self._fspath = str(path) or '.'
else:
raise TypeError(
"argument should be a str or an os.PathLike "
"object where __fspath__ returns a str, "
f"not {type(path).__name__!r}")
else:
self._fspath = '.'
@classmethod
def _from_parts(cls, args):
self = object.__new__(cls)
self._fspath = cls._join_parts(args)
return self

def _load_parts(self):
drv, root, parts = self._parse_path(self._fspath)
Expand All @@ -310,9 +317,7 @@ def _load_parts(self):

@classmethod
def _from_parsed_parts(cls, drv, root, parts):
path = cls._format_parsed_parts(drv, root, parts)
self = cls(path)
self._str = path
self = object.__new__(cls)
self._drv = drv
self._root = root
self._parts_cached = parts
Expand All @@ -322,10 +327,8 @@ def _from_parsed_parts(cls, drv, root, parts):
def _format_parsed_parts(cls, drv, root, parts):
if drv or root:
return drv + root + cls._flavour.sep.join(parts[1:])
elif parts:
return cls._flavour.sep.join(parts)
else:
return '.'
return cls._flavour.sep.join(parts)

def __str__(self):
"""Return the string representation of the path, suitable for
Expand All @@ -334,11 +337,15 @@ def __str__(self):
return self._str
except AttributeError:
self._str = self._format_parsed_parts(self.drive, self.root,
self._parts)
self._parts) or '.'
return self._str

def __fspath__(self):
return self._fspath
try:
return self._fspath or '.'
except AttributeError:
self._fspath = str(self)
return self._fspath

def as_posix(self):
"""Return the string representation of the path with forward (/)
Expand Down Expand Up @@ -584,7 +591,7 @@ def joinpath(self, *args):
paths) or a totally different path (if one of the arguments is
anchored).
"""
return type(self)(self, *args)
return self._from_parts((self,) + args)

def __truediv__(self, key):
try:
Expand All @@ -594,7 +601,7 @@ def __truediv__(self, key):

def __rtruediv__(self, key):
try:
return type(self)(key, self)
return self._from_parts([key] + self._parts)
except TypeError:
return NotImplemented

Expand Down Expand Up @@ -695,17 +702,14 @@ class Path(PurePath):
"""
__slots__ = ()

def __init__(self, *args, **kwargs):
def __new__(cls, *args, **kwargs):
if kwargs:
msg = ("support for supplying keyword arguments to pathlib.PurePath "
"is deprecated and scheduled for removal in Python {remove}")
warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14))
super().__init__(*args)

def __new__(cls, *args, **kwargs):
if cls is Path:
cls = WindowsPath if os.name == 'nt' else PosixPath
return super().__new__(cls)
return cls._from_parts(args)

def _make_child_relpath(self, part):
# This is an optimization used for dir walking. `part` must be
Expand Down Expand Up @@ -818,7 +822,7 @@ def absolute(self):
cwd = self._flavour.abspath(self.drive)
else:
cwd = os.getcwd()
return type(self)(cwd, *self._parts)
return self._from_parts([cwd] + self._parts)

def resolve(self, strict=False):
"""
Expand All @@ -836,7 +840,7 @@ def check_eloop(e):
except OSError as e:
check_eloop(e)
raise
p = type(self)(s)
p = self._from_parts((s,))

# In non-strict mode, realpath() doesn't raise on symlink loops.
# Ensure we get an exception by calling stat()
Expand Down Expand Up @@ -926,7 +930,7 @@ def readlink(self):
"""
if not hasattr(os, "readlink"):
raise NotImplementedError("os.readlink() not available on this system")
return type(self)(os.readlink(self))
return self._from_parts((os.readlink(self),))

def touch(self, mode=0o666, exist_ok=True):
"""
Expand Down Expand Up @@ -1200,7 +1204,7 @@ def expanduser(self):
homedir = self._flavour.expanduser(self._parts[0])
if homedir[:1] == "~":
raise RuntimeError("Could not determine home directory.")
return type(self)(homedir, *self._parts[1:])
return self._from_parts([homedir] + self._parts[1:])

return self

Expand Down