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

View File

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