Skip to content

Commit

Permalink
pythonGH-112361: Speed up pathlib by removing some temporary objects. (
Browse files Browse the repository at this point in the history
…python#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()`
  • Loading branch information
barneygale authored Nov 25, 2023
1 parent 6b961b8 commit 19a1fc1
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 22 deletions.
32 changes: 12 additions & 20 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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
Expand Down Expand Up @@ -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('')
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Speed up a small handful of :mod:`pathlib` methods by removing some
temporary objects.

0 comments on commit 19a1fc1

Please sign in to comment.