GH-112361: Speed up pathlib by removing some temporary objects. (#112362)

Construct only one new list object (using `list.copy()`) when creating a
new path object with a modified tail. This slightly speeds up
`with_name()` and `with_suffix()`
This commit is contained in:
Barney Gale 2023-11-25 17:19:38 +00:00 committed by GitHub
parent 6b961b8cea
commit 19a1fc1b3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 14 additions and 22 deletions

View file

@ -400,13 +400,14 @@ def stem(self):
def with_name(self, name):
"""Return a new path with the file name changed."""
if not self.name:
raise ValueError("%r has an empty name" % (self,))
m = self.pathmod
if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.':
raise ValueError("Invalid name %r" % (name))
return self._from_parsed_parts(self.drive, self.root,
self._tail[:-1] + [name])
raise ValueError(f"Invalid name {name!r}")
tail = self._tail.copy()
if not tail:
raise ValueError(f"{self!r} has an empty name")
tail[-1] = name
return self._from_parsed_parts(self.drive, self.root, tail)
def with_stem(self, stem):
"""Return a new path with the stem changed."""
@ -417,21 +418,12 @@ def with_suffix(self, suffix):
has no suffix, add given suffix. If the given suffix is an empty
string, remove the suffix from the path.
"""
m = self.pathmod
if m.sep in suffix or m.altsep and m.altsep in suffix:
raise ValueError("Invalid suffix %r" % (suffix,))
if suffix and not suffix.startswith('.') or suffix == '.':
raise ValueError("Invalid suffix %r" % (suffix))
name = self.name
if not name:
raise ValueError("%r has an empty name" % (self,))
old_suffix = self.suffix
if not old_suffix:
name = name + suffix
if not suffix:
return self.with_name(self.stem)
elif suffix.startswith('.') and len(suffix) > 1:
return self.with_name(self.stem + suffix)
else:
name = name[:-len(old_suffix)] + suffix
return self._from_parsed_parts(self.drive, self.root,
self._tail[:-1] + [name])
raise ValueError(f"Invalid suffix {suffix!r}")
def relative_to(self, other, /, *_deprecated, walk_up=False):
"""Return the relative path to another path identified by the passed
@ -1029,7 +1021,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks):
elif not path_pattern._tail:
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
pattern_parts = list(path_pattern._tail)
pattern_parts = path_pattern._tail.copy()
if pattern[-1] in (self.pathmod.sep, self.pathmod.altsep):
# GH-65238: pathlib doesn't preserve trailing slash. Add it back.
pattern_parts.append('')

View file

@ -575,8 +575,6 @@ def test_with_suffix_common(self):
self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
self.assertRaises(ValueError, P('a/b').with_suffix, './.d')
self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
self.assertRaises(ValueError, P('a/b').with_suffix,
(self.pathmod.sep, 'd'))
def test_relative_to_common(self):
P = self.cls

View file

@ -0,0 +1,2 @@
Speed up a small handful of :mod:`pathlib` methods by removing some
temporary objects.