Skip to content

Commit

Permalink
Revert "pythonGH-100502: Add pathlib.PurePath.pathmod attribute (py…
Browse files Browse the repository at this point in the history
…thonGH-106533)"

This reverts commit c6c5665.
  • Loading branch information
barneygale committed Dec 19, 2023
1 parent 2f0ec7f commit cfa2e96
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 46 deletions.
7 changes: 0 additions & 7 deletions Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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::
Expand Down
22 changes: 11 additions & 11 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand All @@ -129,34 +129,34 @@ 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):
# Cached parts with normalized case, for comparisons.
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

Expand Down Expand Up @@ -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__ = ()


Expand All @@ -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__ = ()


Expand Down Expand Up @@ -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)

Expand Down
40 changes: 20 additions & 20 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 '?.':
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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())
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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] == '**':
Expand All @@ -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
Expand All @@ -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):
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_pathlib/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_pathlib/test_pathlib_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -96,15 +96,15 @@ 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')
self.assertNotEqual(p, q)

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')
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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.

This comment has been minimized.

Copy link
@merwok

merwok Dec 20, 2023

Interesting question here: should we add a changelog entry for something added and removed in alpha phase?
The changelog for people using the beta or final releases will contain two entries with a null result.
Maybe there’s a way to inform alpha users but remove the notes from the final document.

0 comments on commit cfa2e96

Please sign in to comment.