From 237e2cff00cca49db47bcb7ea13683a4d9ad1ea5 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Fri, 22 Dec 2023 15:11:16 +0000 Subject: [PATCH] GH-110109: Fix misleading `pathlib._abc.PurePathBase` repr (#113376) `PurePathBase.__repr__()` produces a string like `MyPath('/foo')`. This repr is incorrect/misleading when a subclass's `__init__()` method is customized, which I expect to be the very common. This commit moves the `__repr__()` method to `PurePath`, leaving `PurePathBase` with the default `object` repr. No user-facing changes because the `pathlib._abc` module remains private. --- Lib/pathlib/__init__.py | 3 +++ Lib/pathlib/_abc.py | 3 --- Lib/test/test_pathlib/test_pathlib.py | 12 ++++++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 13 +------------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index b020d2db350..4a8bee60122 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -99,6 +99,9 @@ class PurePath(_abc.PurePathBase): # when pickling related paths. return (self.__class__, self.parts) + def __repr__(self): + return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + def __fspath__(self): return str(self) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 4808d0e61f7..fc4b6443448 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -282,9 +282,6 @@ class PurePathBase: slashes.""" return str(self).replace(self.pathmod.sep, '/') - def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, self.as_posix()) - @property def drive(self): """The drive prefix (letter or UNC path), if any.""" diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 00cfdd37e56..d55ccd9e255 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -69,6 +69,18 @@ class PurePathTest(test_pathlib_abc.DummyPurePathTest): self.assertEqual(hash(pp), hash(p)) self.assertEqual(str(pp), str(p)) + def test_repr_common(self): + for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): + with self.subTest(pathstr=pathstr): + p = self.cls(pathstr) + clsname = p.__class__.__name__ + r = repr(p) + # The repr() is in the form ClassName("forward-slashes path"). + self.assertTrue(r.startswith(clsname + '('), r) + self.assertTrue(r.endswith(')'), r) + inner = r[len(clsname) + 1 : -1] + self.assertEqual(eval(inner), p.as_posix()) + def test_fspath_common(self): P = self.cls p = P('a/b') diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index a272973d9c1..6748def91a1 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -31,6 +31,7 @@ class PurePathBaseTest(unittest.TestCase): self.assertFalse(hasattr(P, '__fspath__')) self.assertFalse(hasattr(P, '__bytes__')) self.assertIs(P.__reduce__, object.__reduce__) + self.assertIs(P.__repr__, object.__repr__) self.assertIs(P.__hash__, object.__hash__) self.assertIs(P.__eq__, object.__eq__) self.assertIs(P.__lt__, object.__lt__) @@ -227,18 +228,6 @@ class DummyPurePathTest(unittest.TestCase): self.assertEqual(P(pathstr).as_posix(), pathstr) # Other tests for as_posix() are in test_equivalences(). - def test_repr_common(self): - for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): - with self.subTest(pathstr=pathstr): - p = self.cls(pathstr) - clsname = p.__class__.__name__ - r = repr(p) - # The repr() is in the form ClassName("forward-slashes path"). - self.assertTrue(r.startswith(clsname + '('), r) - self.assertTrue(r.endswith(')'), r) - inner = r[len(clsname) + 1 : -1] - self.assertEqual(eval(inner), p.as_posix()) - def test_eq_common(self): P = self.cls self.assertEqual(P('a/b'), P('a/b'))