Skip to content

GH-112855: Speed up pathlib.PurePath pickling #112856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
4 changes: 1 addition & 3 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ def __init__(self, *args):
super().__init__(*paths)

def __reduce__(self):
# Using the parts tuple helps share interned path parts
# when pickling related paths.
return (self.__class__, self.parts)
return (self.__class__, tuple(self._raw_paths))

def __fspath__(self):
return str(self)
Expand Down
3 changes: 1 addition & 2 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ def _parse_path(cls, path):
elif len(drv_parts) == 6:
# e.g. //?/unc/server/share
root = sep
parsed = [sys.intern(str(x)) for x in rel.split(sep) if x and x != '.']
parsed = [x for x in rel.split(sep) if x and x != '.']
return drv, root, parsed

def _load_parts(self):
Expand Down Expand Up @@ -531,7 +531,6 @@ def match(self, path_pattern, *, case_sensitive=None):
return match(str(self)) is not None



class PathBase(PurePathBase):
"""Base class for concrete path objects.

Expand Down
37 changes: 13 additions & 24 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,14 +720,19 @@ def test_div_nested(self):

def test_pickling_common(self):
P = self.cls
p = P('/a/b')
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
dumped = pickle.dumps(p, proto)
pp = pickle.loads(dumped)
self.assertIs(pp.__class__, p.__class__)
self.assertEqual(pp, p)
self.assertEqual(hash(pp), hash(p))
self.assertEqual(str(pp), str(p))
paths = [
P('a'), P('a', 'b'), P('a/b'), P('a', 'b', 'c'), P('a/b/c'),
P('/'), P('/a', 'b'), P('/a/b'), P('/a', 'b', 'c'), P('/a/b/c'),
]
for p in paths:
with self.subTest(path=p):
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
dumped = pickle.dumps(p, proto)
pp = pickle.loads(dumped)
self.assertIs(pp.__class__, p.__class__)
self.assertEqual(pp, p)
self.assertEqual(hash(pp), hash(p))
self.assertEqual(str(pp), str(p))

def test_fspath_common(self):
P = self.cls
Expand Down Expand Up @@ -2564,22 +2569,6 @@ def test_is_char_device_false(self):
self.assertIs((P / 'fileA\udfff').is_char_device(), False)
self.assertIs((P / 'fileA\x00').is_char_device(), False)

def test_pickling_common(self):
p = self.cls(BASE, 'fileA')
for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
dumped = pickle.dumps(p, proto)
pp = pickle.loads(dumped)
self.assertEqual(pp.stat(), p.stat())

def test_parts_interning(self):
P = self.cls
p = P('/usr/bin/foo')
q = P('/usr/local/bin')
# 'usr'
self.assertIs(p.parts[1], q.parts[1])
# 'bin'
self.assertIs(p.parts[2], q.parts[3])

def _check_complex_symlinks(self, link0_target):
if not self.can_symlink:
self.skipTest("symlinks required")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Speed up pickling of :class:`pathlib.PurePath` objects. Patch by Barney
Gale.