From cfa2e96553bbcecf35e7cb89e57f207411736540 Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 19 Dec 2023 20:31:39 +0000 Subject: [PATCH] Revert "GH-100502: Add `pathlib.PurePath.pathmod` attribute (GH-106533)" This reverts commit c6c5665ee0c0a5ddc96da255c9a62daa332c32b3. --- Doc/library/pathlib.rst | 7 ---- Lib/pathlib/__init__.py | 22 +++++----- Lib/pathlib/_abc.py | 40 +++++++++---------- Lib/test/test_pathlib/test_pathlib.py | 8 ++-- Lib/test/test_pathlib/test_pathlib_abc.py | 8 ++-- ...-12-19-20-52-53.gh-issue-100502.kkxuRa.rst | 3 ++ 6 files changed, 42 insertions(+), 46 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-19-20-52-53.gh-issue-100502.kkxuRa.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 60791725c2323d..5ddc3cc4e25765 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -303,13 +303,6 @@ Methods and properties Pure paths provide the following methods and properties: -.. attribute:: PurePath.pathmod - - The implementation of the :mod:`os.path` module used for low-level path - operations: either :mod:`posixpath` or :mod:`ntpath`. - - .. versionadded:: 3.13 - .. attribute:: PurePath.drive A string representing the drive letter or name, if any:: diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py index b020d2db350da8..a0ce43499a491b 100644 --- a/Lib/pathlib/__init__.py +++ b/Lib/pathlib/__init__.py @@ -74,7 +74,7 @@ def __init__(self, *args): paths = [] for arg in args: if isinstance(arg, PurePath): - if arg.pathmod is ntpath and self.pathmod is posixpath: + if arg._pathmod is ntpath and self._pathmod is posixpath: # GH-103631: Convert separators for backwards compatibility. paths.extend(path.replace('\\', '/') for path in arg._raw_paths) else: @@ -113,7 +113,7 @@ def _str_normcase(self): try: return self._str_normcase_cached except AttributeError: - if _abc._is_case_sensitive(self.pathmod): + if _abc._is_case_sensitive(self._pathmod): self._str_normcase_cached = str(self) else: self._str_normcase_cached = str(self).lower() @@ -129,7 +129,7 @@ def __hash__(self): def __eq__(self, other): if not isinstance(other, PurePath): return NotImplemented - return self._str_normcase == other._str_normcase and self.pathmod is other.pathmod + return self._str_normcase == other._str_normcase and self._pathmod is other._pathmod @property def _parts_normcase(self): @@ -137,26 +137,26 @@ def _parts_normcase(self): try: return self._parts_normcase_cached except AttributeError: - self._parts_normcase_cached = self._str_normcase.split(self.pathmod.sep) + self._parts_normcase_cached = self._str_normcase.split(self._pathmod.sep) return self._parts_normcase_cached def __lt__(self, other): - if not isinstance(other, PurePath) or self.pathmod is not other.pathmod: + if not isinstance(other, PurePath) or self._pathmod is not other._pathmod: return NotImplemented return self._parts_normcase < other._parts_normcase def __le__(self, other): - if not isinstance(other, PurePath) or self.pathmod is not other.pathmod: + if not isinstance(other, PurePath) or self._pathmod is not other._pathmod: return NotImplemented return self._parts_normcase <= other._parts_normcase def __gt__(self, other): - if not isinstance(other, PurePath) or self.pathmod is not other.pathmod: + if not isinstance(other, PurePath) or self._pathmod is not other._pathmod: return NotImplemented return self._parts_normcase > other._parts_normcase def __ge__(self, other): - if not isinstance(other, PurePath) or self.pathmod is not other.pathmod: + if not isinstance(other, PurePath) or self._pathmod is not other._pathmod: return NotImplemented return self._parts_normcase >= other._parts_normcase @@ -193,7 +193,7 @@ class PurePosixPath(PurePath): On a POSIX system, instantiating a PurePath should return this object. However, you can also instantiate it directly on any system. """ - pathmod = posixpath + _pathmod = posixpath __slots__ = () @@ -203,7 +203,7 @@ class PureWindowsPath(PurePath): On a Windows system, instantiating a PurePath should return this object. However, you can also instantiate it directly on any system. """ - pathmod = ntpath + _pathmod = ntpath __slots__ = () @@ -305,7 +305,7 @@ def absolute(self): drive, root, rel = os.path.splitroot(cwd) if not rel: return self._from_parsed_parts(drive, root, self._tail) - tail = rel.split(self.pathmod.sep) + tail = rel.split(self._pathmod.sep) tail.extend(self._tail) return self._from_parsed_parts(drive, root, tail) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 4808d0e61f7038..b608a1a95c9549 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -204,7 +204,7 @@ class PurePathBase: # work from occurring when `resolve()` calls `stat()` or `readlink()`. '_resolving', ) - pathmod = os.path + _pathmod = os.path def __init__(self, *paths): self._raw_paths = paths @@ -221,11 +221,11 @@ def with_segments(self, *pathsegments): def _parse_path(cls, path): if not path: return '', '', [] - sep = cls.pathmod.sep - altsep = cls.pathmod.altsep + sep = cls._pathmod.sep + altsep = cls._pathmod.altsep if altsep: path = path.replace(altsep, sep) - drv, root, rel = cls.pathmod.splitroot(path) + drv, root, rel = cls._pathmod.splitroot(path) if not root and drv.startswith(sep) and not drv.endswith(sep): drv_parts = drv.split(sep) if len(drv_parts) == 4 and drv_parts[2] not in '?.': @@ -244,7 +244,7 @@ def _load_parts(self): elif len(paths) == 1: path = paths[0] else: - path = self.pathmod.join(*paths) + path = self._pathmod.join(*paths) drv, root, tail = self._parse_path(path) self._drv = drv self._root = root @@ -262,10 +262,10 @@ def _from_parsed_parts(self, drv, root, tail): @classmethod def _format_parsed_parts(cls, drv, root, tail): if drv or root: - return drv + root + cls.pathmod.sep.join(tail) - elif tail and cls.pathmod.splitdrive(tail[0])[0]: + return drv + root + cls._pathmod.sep.join(tail) + elif tail and cls._pathmod.splitdrive(tail[0])[0]: tail = ['.'] + tail - return cls.pathmod.sep.join(tail) + return cls._pathmod.sep.join(tail) def __str__(self): """Return the string representation of the path, suitable for @@ -280,7 +280,7 @@ def __str__(self): def as_posix(self): """Return the string representation of the path with forward (/) slashes.""" - return str(self).replace(self.pathmod.sep, '/') + return str(self).replace(self._pathmod.sep, '/') def __repr__(self): return "{}({!r})".format(self.__class__.__name__, self.as_posix()) @@ -364,7 +364,7 @@ def stem(self): def with_name(self, name): """Return a new path with the file name changed.""" - m = self.pathmod + m = self._pathmod if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.': raise ValueError(f"Invalid name {name!r}") tail = self._tail.copy() @@ -483,22 +483,22 @@ def parents(self): def is_absolute(self): """True if the path is absolute (has both a root and, if applicable, a drive).""" - if self.pathmod is ntpath: + if self._pathmod is ntpath: # ntpath.isabs() is defective - see GH-44626. return bool(self.drive and self.root) - elif self.pathmod is posixpath: + elif self._pathmod is posixpath: # Optimization: work with raw paths on POSIX. for path in self._raw_paths: if path.startswith('/'): return True return False else: - return self.pathmod.isabs(str(self)) + return self._pathmod.isabs(str(self)) def is_reserved(self): """Return True if the path contains one of the special names reserved by the system, if any.""" - if self.pathmod is posixpath or not self._tail: + if self._pathmod is posixpath or not self._tail: return False # NOTE: the rules for reserved names seem somewhat complicated @@ -518,8 +518,8 @@ def match(self, path_pattern, *, case_sensitive=None): if not isinstance(path_pattern, PurePathBase): path_pattern = self.with_segments(path_pattern) if case_sensitive is None: - case_sensitive = _is_case_sensitive(self.pathmod) - sep = path_pattern.pathmod.sep + case_sensitive = _is_case_sensitive(self._pathmod) + sep = path_pattern._pathmod.sep pattern_str = str(path_pattern) if path_pattern.drive or path_pattern.root: pass @@ -801,7 +801,7 @@ def _make_child_relpath(self, name): path_str = str(self) tail = self._tail if tail: - path_str = f'{path_str}{self.pathmod.sep}{name}' + path_str = f'{path_str}{self._pathmod.sep}{name}' elif path_str != '.': path_str = f'{path_str}{name}' else: @@ -836,7 +836,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): raise ValueError("Unacceptable pattern: {!r}".format(pattern)) 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. pattern_parts.append('') if pattern_parts[-1] == '**': @@ -850,7 +850,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): if case_sensitive is None: # TODO: evaluate case-sensitivity of each directory in _select_children(). - case_sensitive = _is_case_sensitive(self.pathmod) + case_sensitive = _is_case_sensitive(self._pathmod) # If symlinks are handled consistently, and the pattern does not # contain '..' components, then we can use a 'walk-and-match' strategy @@ -861,7 +861,7 @@ def _glob(self, pattern, case_sensitive, follow_symlinks): # do not perform any filesystem access, which can be much faster! filter_paths = follow_symlinks is not None and '..' not in pattern_parts deduplicate_paths = False - sep = self.pathmod.sep + sep = self._pathmod.sep paths = iter([self] if self.is_dir() else []) part_idx = 0 while part_idx < len(pattern_parts): diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 00cfdd37e568a6..d07d65b07e8638 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -953,7 +953,7 @@ def test_concrete_class(self): self.assertIs(type(p), expected) def test_unsupported_pathmod(self): - if self.cls.pathmod is os.path: + if self.cls._pathmod is os.path: self.skipTest("path flavour is supported") else: self.assertRaises(pathlib.UnsupportedOperation, self.cls) @@ -1456,9 +1456,9 @@ def test_symlink_to_unsupported(self): def test_is_junction(self): P = self.cls(self.base) - with mock.patch.object(P.pathmod, 'isjunction'): - self.assertEqual(P.is_junction(), P.pathmod.isjunction.return_value) - P.pathmod.isjunction.assert_called_once_with(P) + with mock.patch.object(P._pathmod, 'isjunction'): + self.assertEqual(P.is_junction(), P._pathmod.isjunction.return_value) + P._pathmod.isjunction.assert_called_once_with(P) @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") @unittest.skipIf(sys.platform == "vxworks", diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index a272973d9c1d61..e21188dc0a19dd 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -73,7 +73,7 @@ class DummyPurePathTest(unittest.TestCase): def setUp(self): p = self.cls('a') - self.pathmod = p.pathmod + self.pathmod = p._pathmod self.sep = self.pathmod.sep self.altsep = self.pathmod.altsep @@ -96,7 +96,7 @@ def test_concrete_class(self): def test_different_pathmods_unequal(self): p = self.cls('a') - if p.pathmod is posixpath: + if p._pathmod is posixpath: q = pathlib.PureWindowsPath('a') else: q = pathlib.PurePosixPath('a') @@ -104,7 +104,7 @@ def test_different_pathmods_unequal(self): def test_different_pathmods_unordered(self): p = self.cls('a') - if p.pathmod is posixpath: + if p._pathmod is posixpath: q = pathlib.PureWindowsPath('a') else: q = pathlib.PurePosixPath('a') @@ -874,7 +874,7 @@ class DummyPathTest(DummyPurePathTest): def setUp(self): super().setUp() - pathmod = self.cls.pathmod + pathmod = self.cls._pathmod p = self.cls(self.base) p.mkdir(parents=True) p.joinpath('dirA').mkdir() diff --git a/Misc/NEWS.d/next/Library/2023-12-19-20-52-53.gh-issue-100502.kkxuRa.rst b/Misc/NEWS.d/next/Library/2023-12-19-20-52-53.gh-issue-100502.kkxuRa.rst new file mode 100644 index 00000000000000..0dfee5101c4667 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-19-20-52-53.gh-issue-100502.kkxuRa.rst @@ -0,0 +1,3 @@ +Remove ``pathlib.PurePath.pathmod`` class attribute, which was added earlier +in Python 3.13 development. This attribute may not be the best way to +publicly represent differences in path flavours.