GH-114575: Rename PurePath.pathmod to PurePath.parser (#116513)

And rename the private base class from `PathModuleBase` to `ParserBase`.
This commit is contained in:
Barney Gale 2024-03-31 19:14:48 +01:00 committed by GitHub
parent bfc57d43d8
commit 752e18389e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 123 additions and 119 deletions

View file

@ -303,10 +303,10 @@ Methods and properties
Pure paths provide the following methods and properties:
.. attribute:: PurePath.pathmod
.. attribute:: PurePath.parser
The implementation of the :mod:`os.path` module used for low-level path
operations: either :mod:`posixpath` or :mod:`ntpath`.
parsing and joining: either :mod:`posixpath` or :mod:`ntpath`.
.. versionadded:: 3.13

View file

@ -524,6 +524,10 @@ pathlib
shell-style wildcards, including the recursive wildcard "``**``".
(Contributed by Barney Gale in :gh:`73435`.)
* Add :attr:`pathlib.PurePath.parser` class attribute that stores the
implementation of :mod:`os.path` used for low-level path parsing and
joining: either ``posixpath`` or ``ntpath``.
* Add *follow_symlinks* keyword-only argument to :meth:`pathlib.Path.glob`,
:meth:`~pathlib.Path.rglob`, :meth:`~pathlib.Path.is_file`,
:meth:`~pathlib.Path.is_dir`, :meth:`~pathlib.Path.owner`,

View file

@ -110,7 +110,7 @@ class PurePath(_abc.PurePathBase):
# path. It's set when `__hash__()` is called for the first time.
'_hash',
)
pathmod = os.path
parser = os.path
def __new__(cls, *args, **kwargs):
"""Construct a PurePath from one or several strings and or existing
@ -126,7 +126,7 @@ class PurePath(_abc.PurePathBase):
paths = []
for arg in args:
if isinstance(arg, PurePath):
if arg.pathmod is ntpath and self.pathmod is posixpath:
if arg.parser is ntpath and self.parser is posixpath:
# GH-103631: Convert separators for backwards compatibility.
paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
else:
@ -187,7 +187,7 @@ class PurePath(_abc.PurePathBase):
try:
return self._str_normcase_cached
except AttributeError:
if _abc._is_case_sensitive(self.pathmod):
if _abc._is_case_sensitive(self.parser):
self._str_normcase_cached = str(self)
else:
self._str_normcase_cached = str(self).lower()
@ -203,7 +203,7 @@ class PurePath(_abc.PurePathBase):
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.parser is other.parser
@property
def _parts_normcase(self):
@ -211,26 +211,26 @@ class PurePath(_abc.PurePathBase):
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.parser.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.parser is not other.parser:
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.parser is not other.parser:
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.parser is not other.parser:
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.parser is not other.parser:
return NotImplemented
return self._parts_normcase >= other._parts_normcase
@ -247,10 +247,10 @@ class PurePath(_abc.PurePathBase):
@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.parser.sep.join(tail)
elif tail and cls.parser.splitdrive(tail[0])[0]:
tail = ['.'] + tail
return cls.pathmod.sep.join(tail)
return cls.parser.sep.join(tail)
def _from_parsed_parts(self, drv, root, tail):
path_str = self._format_parsed_parts(drv, root, tail)
@ -265,11 +265,11 @@ class PurePath(_abc.PurePathBase):
def _parse_path(cls, path):
if not path:
return '', '', []
sep = cls.pathmod.sep
altsep = cls.pathmod.altsep
sep = cls.parser.sep
altsep = cls.parser.altsep
if altsep:
path = path.replace(altsep, sep)
drv, root, rel = cls.pathmod.splitroot(path)
drv, root, rel = cls.parser.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 '?.':
@ -290,7 +290,7 @@ class PurePath(_abc.PurePathBase):
elif len(paths) == 1:
path = paths[0]
else:
path = self.pathmod.join(*paths)
path = self.parser.join(*paths)
return path
@property
@ -360,8 +360,8 @@ class PurePath(_abc.PurePathBase):
def with_name(self, name):
"""Return a new path with the file name changed."""
m = self.pathmod
if not name or m.sep in name or (m.altsep and m.altsep in name) or name == '.':
p = self.parser
if not name or p.sep in name or (p.altsep and p.altsep in name) or name == '.':
raise ValueError(f"Invalid name {name!r}")
tail = self._tail.copy()
if not tail:
@ -413,13 +413,13 @@ class PurePath(_abc.PurePathBase):
def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
a drive)."""
if self.pathmod is posixpath:
if self.parser is posixpath:
# Optimization: work with raw paths on POSIX.
for path in self._raw_paths:
if path.startswith('/'):
return True
return False
return self.pathmod.isabs(self)
return self.parser.isabs(self)
def is_reserved(self):
"""Return True if the path contains one of the special names reserved
@ -428,8 +428,8 @@ class PurePath(_abc.PurePathBase):
"for removal in Python 3.15. Use os.path.isreserved() to "
"detect reserved paths on Windows.")
warnings.warn(msg, DeprecationWarning, stacklevel=2)
if self.pathmod is ntpath:
return self.pathmod.isreserved(self)
if self.parser is ntpath:
return self.parser.isreserved(self)
return False
def as_uri(self):
@ -462,7 +462,7 @@ class PurePath(_abc.PurePathBase):
raise NotImplementedError("Non-relative patterns are unsupported")
elif not parts:
raise ValueError("Unacceptable pattern: {!r}".format(pattern))
elif pattern[-1] in (self.pathmod.sep, self.pathmod.altsep):
elif pattern[-1] in (self.parser.sep, self.parser.altsep):
# GH-65238: pathlib doesn't preserve trailing slash. Add it back.
parts.append('')
parts.reverse()
@ -487,7 +487,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
parser = posixpath
__slots__ = ()
@ -497,7 +497,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
parser = ntpath
__slots__ = ()
@ -607,7 +607,7 @@ class Path(_abc.PathBase, PurePath):
path_str = str(self)
tail = self._tail
if tail:
path_str = f'{path_str}{self.pathmod.sep}{name}'
path_str = f'{path_str}{self.parser.sep}{name}'
elif path_str != '.':
path_str = f'{path_str}{name}'
else:
@ -675,7 +675,7 @@ class Path(_abc.PathBase, PurePath):
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.parser.sep)
tail.extend(self._tail)
return self._from_parsed_parts(drive, root, tail)

View file

@ -37,8 +37,8 @@ def _ignore_error(exception):
@functools.cache
def _is_case_sensitive(pathmod):
return pathmod.normcase('Aa') == 'Aa'
def _is_case_sensitive(parser):
return parser.normcase('Aa') == 'Aa'
#
# Globbing helpers
@ -156,12 +156,12 @@ class UnsupportedOperation(NotImplementedError):
pass
class PathModuleBase:
"""Base class for path modules, which do low-level path manipulation.
class ParserBase:
"""Base class for path parsers, which do low-level path manipulation.
Path modules provide a subset of the os.path API, specifically those
Path parsers provide a subset of the os.path API, specifically those
functions needed to provide PurePathBase functionality. Each PurePathBase
subclass references its path module via a 'pathmod' class attribute.
subclass references its path parser via a 'parser' class attribute.
Every method in this base class raises an UnsupportedOperation exception.
"""
@ -221,10 +221,10 @@ class PurePathBase:
# work from occurring when `resolve()` calls `stat()` or `readlink()`.
'_resolving',
)
pathmod = PathModuleBase()
parser = ParserBase()
def __init__(self, path, *paths):
self._raw_path = self.pathmod.join(path, *paths) if paths else path
self._raw_path = self.parser.join(path, *paths) if paths else path
if not isinstance(self._raw_path, str):
raise TypeError(
f"path should be a str, not {type(self._raw_path).__name__!r}")
@ -245,17 +245,17 @@ class PurePathBase:
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.parser.sep, '/')
@property
def drive(self):
"""The drive prefix (letter or UNC path), if any."""
return self.pathmod.splitdrive(self.anchor)[0]
return self.parser.splitdrive(self.anchor)[0]
@property
def root(self):
"""The root of the path, if any."""
return self.pathmod.splitdrive(self.anchor)[1]
return self.parser.splitdrive(self.anchor)[1]
@property
def anchor(self):
@ -265,7 +265,7 @@ class PurePathBase:
@property
def name(self):
"""The final path component, if any."""
return self.pathmod.split(self._raw_path)[1]
return self.parser.split(self._raw_path)[1]
@property
def suffix(self):
@ -306,7 +306,7 @@ class PurePathBase:
def with_name(self, name):
"""Return a new path with the file name changed."""
split = self.pathmod.split
split = self.parser.split
if split(name)[0]:
raise ValueError(f"Invalid name {name!r}")
return self.with_segments(split(self._raw_path)[0], name)
@ -419,7 +419,7 @@ class PurePathBase:
uppermost parent of the path (equivalent to path.parents[-1]), and
*parts* is a reversed list of parts following the anchor.
"""
split = self.pathmod.split
split = self.parser.split
path = self._raw_path
parent, name = split(path)
names = []
@ -433,7 +433,7 @@ class PurePathBase:
def parent(self):
"""The logical parent of the path."""
path = self._raw_path
parent = self.pathmod.split(path)[0]
parent = self.parser.split(path)[0]
if path != parent:
parent = self.with_segments(parent)
parent._resolving = self._resolving
@ -443,7 +443,7 @@ class PurePathBase:
@property
def parents(self):
"""A sequence of this path's logical parents."""
split = self.pathmod.split
split = self.parser.split
path = self._raw_path
parent = split(path)[0]
parents = []
@ -456,7 +456,7 @@ class PurePathBase:
def is_absolute(self):
"""True if the path is absolute (has both a root and, if applicable,
a drive)."""
return self.pathmod.isabs(self._raw_path)
return self.parser.isabs(self._raw_path)
@property
def _pattern_stack(self):
@ -481,8 +481,8 @@ class PurePathBase:
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.parser)
sep = path_pattern.parser.sep
path_parts = self.parts[::-1]
pattern_parts = path_pattern.parts[::-1]
if not pattern_parts:
@ -505,8 +505,8 @@ class PurePathBase:
if not isinstance(pattern, PurePathBase):
pattern = self.with_segments(pattern)
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self.pathmod)
match = _compile_pattern(pattern._pattern_str, pattern.pathmod.sep, case_sensitive)
case_sensitive = _is_case_sensitive(self.parser)
match = _compile_pattern(pattern._pattern_str, pattern.parser.sep, case_sensitive)
return match(self._pattern_str) is not None
@ -797,12 +797,12 @@ class PathBase(PurePathBase):
pattern = self.with_segments(pattern)
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.parser)
stack = pattern._pattern_stack
specials = ('', '.', '..')
deduplicate_paths = False
sep = self.pathmod.sep
sep = self.parser.sep
paths = iter([self] if self.is_dir() else [])
while stack:
part = stack.pop()
@ -973,7 +973,7 @@ class PathBase(PurePathBase):
continue
path_tail.append(part)
if querying and part != '..':
path = self.with_segments(path_root + self.pathmod.sep.join(path_tail))
path = self.with_segments(path_root + self.parser.sep.join(path_tail))
path._resolving = True
try:
st = path.stat(follow_symlinks=False)
@ -1002,7 +1002,7 @@ class PathBase(PurePathBase):
raise
else:
querying = False
return self.with_segments(path_root + self.pathmod.sep.join(path_tail))
return self.with_segments(path_root + self.parser.sep.join(path_tail))
def symlink_to(self, target, target_is_directory=False):
"""

View file

@ -65,7 +65,7 @@ class PurePathTest(test_pathlib_abc.DummyPurePathTest):
p = self.cls('a')
self.assertIs(type(p), expected)
def test_concrete_pathmod(self):
def test_concrete_parser(self):
if self.cls is pathlib.PurePosixPath:
expected = posixpath
elif self.cls is pathlib.PureWindowsPath:
@ -73,19 +73,19 @@ class PurePathTest(test_pathlib_abc.DummyPurePathTest):
else:
expected = os.path
p = self.cls('a')
self.assertIs(p.pathmod, expected)
self.assertIs(p.parser, expected)
def test_different_pathmods_unequal(self):
def test_different_parsers_unequal(self):
p = self.cls('a')
if p.pathmod is posixpath:
if p.parser is posixpath:
q = pathlib.PureWindowsPath('a')
else:
q = pathlib.PurePosixPath('a')
self.assertNotEqual(p, q)
def test_different_pathmods_unordered(self):
def test_different_parsers_unordered(self):
p = self.cls('a')
if p.pathmod is posixpath:
if p.parser is posixpath:
q = pathlib.PureWindowsPath('a')
else:
q = pathlib.PurePosixPath('a')
@ -108,16 +108,16 @@ class PurePathTest(test_pathlib_abc.DummyPurePathTest):
self.assertEqual(P(P('./a:b')), P('./a:b'))
def _check_parse_path(self, raw_path, *expected):
sep = self.pathmod.sep
sep = self.parser.sep
actual = self.cls._parse_path(raw_path.replace('/', sep))
self.assertEqual(actual, expected)
if altsep := self.pathmod.altsep:
if altsep := self.parser.altsep:
actual = self.cls._parse_path(raw_path.replace('/', altsep))
self.assertEqual(actual, expected)
def test_parse_path_common(self):
check = self._check_parse_path
sep = self.pathmod.sep
sep = self.parser.sep
check('', '', '', [])
check('a', '', '', ['a'])
check('a/', '', '', ['a'])
@ -523,10 +523,10 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
def setUp(self):
super().setUp()
os.chmod(self.pathmod.join(self.base, 'dirE'), 0)
os.chmod(self.parser.join(self.base, 'dirE'), 0)
def tearDown(self):
os.chmod(self.pathmod.join(self.base, 'dirE'), 0o777)
os.chmod(self.parser.join(self.base, 'dirE'), 0o777)
os_helper.rmtree(self.base)
def tempdir(self):
@ -541,8 +541,8 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
path_names = {name for name in dir(pathlib._abc.PathBase) if name[0] != '_'}
self.assertEqual(our_names, path_names)
for attr_name in our_names:
if attr_name == 'pathmod':
# On Windows, Path.pathmod is ntpath, but PathBase.pathmod is
if attr_name == 'parser':
# On Windows, Path.parser is ntpath, but PathBase.parser is
# posixpath, and so their docstrings differ.
continue
our_attr = getattr(self.cls, attr_name)
@ -557,9 +557,9 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
p = self.cls('a')
self.assertIs(type(p), expected)
def test_unsupported_pathmod(self):
if self.cls.pathmod is os.path:
self.skipTest("path flavour is supported")
def test_unsupported_parser(self):
if self.cls.parser is os.path:
self.skipTest("path parser is supported")
else:
self.assertRaises(pathlib.UnsupportedOperation, self.cls)
@ -809,7 +809,7 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
self.assertTrue(target.exists())
# Linking to a str of a relative path.
link2 = P / 'dirA' / 'fileAAA'
target2 = self.pathmod.join(TESTFN, 'fileA')
target2 = self.parser.join(TESTFN, 'fileA')
link2.hardlink_to(target2)
self.assertEqual(os.stat(target2).st_size, size)
self.assertTrue(link2.exists())
@ -834,7 +834,7 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
self.assertEqual(q.stat().st_size, size)
self.assertFileNotFound(p.stat)
# Renaming to a str of a relative path.
r = self.pathmod.join(TESTFN, 'fileAAA')
r = self.parser.join(TESTFN, 'fileAAA')
renamed_q = q.rename(r)
self.assertEqual(renamed_q, self.cls(r))
self.assertEqual(os.stat(r).st_size, size)
@ -851,7 +851,7 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
self.assertEqual(q.stat().st_size, size)
self.assertFileNotFound(p.stat)
# Replacing another (existing) path.
r = self.pathmod.join(TESTFN, 'dirB', 'fileB')
r = self.parser.join(TESTFN, 'dirB', 'fileB')
replaced_q = q.replace(r)
self.assertEqual(replaced_q, self.cls(r))
self.assertEqual(os.stat(r).st_size, size)
@ -1060,9 +1060,9 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
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.parser, 'isjunction'):
self.assertEqual(P.is_junction(), P.parser.isjunction.return_value)
P.parser.isjunction.assert_called_once_with(P)
@unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
@unittest.skipIf(sys.platform == "vxworks",
@ -1294,12 +1294,12 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
p = self.cls(self.base)
with (p / 'new_file').open('wb'):
pass
st = os.stat(self.pathmod.join(self.base, 'new_file'))
st = os.stat(self.parser.join(self.base, 'new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
os.umask(0o022)
with (p / 'other_new_file').open('wb'):
pass
st = os.stat(self.pathmod.join(self.base, 'other_new_file'))
st = os.stat(self.parser.join(self.base, 'other_new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
@needs_posix
@ -1322,14 +1322,14 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest):
self.addCleanup(os.umask, old_mask)
p = self.cls(self.base)
(p / 'new_file').touch()
st = os.stat(self.pathmod.join(self.base, 'new_file'))
st = os.stat(self.parser.join(self.base, 'new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
os.umask(0o022)
(p / 'other_new_file').touch()
st = os.stat(self.pathmod.join(self.base, 'other_new_file'))
st = os.stat(self.parser.join(self.base, 'other_new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
(p / 'masked_new_file').touch(mode=0o750)
st = os.stat(self.pathmod.join(self.base, 'masked_new_file'))
st = os.stat(self.parser.join(self.base, 'masked_new_file'))
self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
@unittest.skipUnless(hasattr(pwd, 'getpwall'),

View file

@ -5,7 +5,7 @@ import errno
import stat
import unittest
from pathlib._abc import UnsupportedOperation, PathModuleBase, PurePathBase, PathBase
from pathlib._abc import UnsupportedOperation, ParserBase, PurePathBase, PathBase
import posixpath
from test.support.os_helper import TESTFN
@ -38,8 +38,8 @@ class UnsupportedOperationTest(unittest.TestCase):
self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError))
class PathModuleBaseTest(unittest.TestCase):
cls = PathModuleBase
class ParserBaseTest(unittest.TestCase):
cls = ParserBase
def test_unsupported_operation(self):
m = self.cls()
@ -109,13 +109,13 @@ class PurePathBaseTest(unittest.TestCase):
self.assertIs(P.__gt__, object.__gt__)
self.assertIs(P.__ge__, object.__ge__)
def test_pathmod(self):
self.assertIsInstance(self.cls.pathmod, PathModuleBase)
def test_parser(self):
self.assertIsInstance(self.cls.parser, ParserBase)
class DummyPurePath(PurePathBase):
__slots__ = ()
pathmod = posixpath
parser = posixpath
def __eq__(self, other):
if not isinstance(other, DummyPurePath):
@ -137,14 +137,14 @@ class DummyPurePathTest(unittest.TestCase):
def setUp(self):
name = self.id().split('.')[-1]
if name in _tests_needing_posix and self.cls.pathmod is not posixpath:
if name in _tests_needing_posix and self.cls.parser is not posixpath:
self.skipTest('requires POSIX-flavoured path class')
if name in _tests_needing_windows and self.cls.pathmod is posixpath:
if name in _tests_needing_windows and self.cls.parser is posixpath:
self.skipTest('requires Windows-flavoured path class')
p = self.cls('a')
self.pathmod = p.pathmod
self.sep = self.pathmod.sep
self.altsep = self.pathmod.altsep
self.parser = p.parser
self.sep = self.parser.sep
self.altsep = self.parser.altsep
def test_constructor_common(self):
P = self.cls
@ -1411,7 +1411,7 @@ class DummyPath(PathBase):
memory.
"""
__slots__ = ()
pathmod = posixpath
parser = posixpath
_files = {}
_directories = {}
@ -1530,7 +1530,7 @@ class DummyPathTest(DummyPurePathTest):
name = self.id().split('.')[-1]
if name in _tests_needing_symlinks and not self.can_symlink:
self.skipTest('requires symlinks')
pathmod = self.cls.pathmod
parser = self.cls.parser
p = self.cls(self.base)
p.mkdir(parents=True)
p.joinpath('dirA').mkdir()
@ -1552,8 +1552,8 @@ class DummyPathTest(DummyPurePathTest):
p.joinpath('linkA').symlink_to('fileA')
p.joinpath('brokenLink').symlink_to('non-existing')
p.joinpath('linkB').symlink_to('dirB')
p.joinpath('dirA', 'linkC').symlink_to(pathmod.join('..', 'dirB'))
p.joinpath('dirB', 'linkD').symlink_to(pathmod.join('..', 'dirB'))
p.joinpath('dirA', 'linkC').symlink_to(parser.join('..', 'dirB'))
p.joinpath('dirB', 'linkD').symlink_to(parser.join('..', 'dirB'))
p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop')
def tearDown(self):
@ -1573,13 +1573,13 @@ class DummyPathTest(DummyPurePathTest):
self.assertEqual(cm.exception.errno, errno.ENOENT)
def assertEqualNormCase(self, path_a, path_b):
normcase = self.pathmod.normcase
normcase = self.parser.normcase
self.assertEqual(normcase(path_a), normcase(path_b))
def test_samefile(self):
pathmod = self.pathmod
fileA_path = pathmod.join(self.base, 'fileA')
fileB_path = pathmod.join(self.base, 'dirB', 'fileB')
parser = self.parser
fileA_path = parser.join(self.base, 'fileA')
fileB_path = parser.join(self.base, 'dirB', 'fileB')
p = self.cls(fileA_path)
pp = self.cls(fileA_path)
q = self.cls(fileB_path)
@ -1588,7 +1588,7 @@ class DummyPathTest(DummyPurePathTest):
self.assertFalse(p.samefile(fileB_path))
self.assertFalse(p.samefile(q))
# Test the non-existent file case
non_existent = pathmod.join(self.base, 'foo')
non_existent = parser.join(self.base, 'foo')
r = self.cls(non_existent)
self.assertRaises(FileNotFoundError, p.samefile, r)
self.assertRaises(FileNotFoundError, p.samefile, non_existent)
@ -2050,15 +2050,15 @@ class DummyPathTest(DummyPurePathTest):
p.resolve(strict=True)
self.assertEqual(cm.exception.errno, errno.ENOENT)
# Non-strict
pathmod = self.pathmod
parser = self.parser
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(self.base, 'foo'))
parser.join(self.base, 'foo'))
p = P(self.base, 'foo', 'in', 'spam')
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(self.base, 'foo', 'in', 'spam'))
parser.join(self.base, 'foo', 'in', 'spam'))
p = P(self.base, '..', 'foo', 'in', 'spam')
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(pathmod.dirname(self.base), 'foo', 'in', 'spam'))
parser.join(parser.dirname(self.base), 'foo', 'in', 'spam'))
# These are all relative symlinks.
p = P(self.base, 'dirB', 'fileB')
self._check_resolve_relative(p, p)
@ -2073,7 +2073,7 @@ class DummyPathTest(DummyPurePathTest):
self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB', 'foo', 'in',
'spam'), False)
p = P(self.base, 'dirA', 'linkC', '..', 'foo', 'in', 'spam')
if self.cls.pathmod is not posixpath:
if self.cls.parser is not posixpath:
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
# resolves to 'dirA' without resolving linkY first.
self._check_resolve_relative(p, P(self.base, 'dirA', 'foo', 'in',
@ -2085,7 +2085,7 @@ class DummyPathTest(DummyPurePathTest):
# Now create absolute symlinks.
d = self.tempdir()
P(self.base, 'dirA', 'linkX').symlink_to(d)
P(self.base, str(d), 'linkY').symlink_to(self.pathmod.join(self.base, 'dirB'))
P(self.base, str(d), 'linkY').symlink_to(self.parser.join(self.base, 'dirB'))
p = P(self.base, 'dirA', 'linkX', 'linkY', 'fileB')
self._check_resolve_absolute(p, P(self.base, 'dirB', 'fileB'))
# Non-strict
@ -2093,7 +2093,7 @@ class DummyPathTest(DummyPurePathTest):
self._check_resolve_relative(p, P(self.base, 'dirB', 'foo', 'in', 'spam'),
False)
p = P(self.base, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam')
if self.cls.pathmod is not posixpath:
if self.cls.parser is not posixpath:
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
# resolves to 'dirA' without resolving linkY first.
self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False)
@ -2105,11 +2105,11 @@ class DummyPathTest(DummyPurePathTest):
@needs_symlinks
def test_resolve_dot(self):
# See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
pathmod = self.pathmod
parser = self.parser
p = self.cls(self.base)
p.joinpath('0').symlink_to('.', target_is_directory=True)
p.joinpath('1').symlink_to(pathmod.join('0', '0'), target_is_directory=True)
p.joinpath('2').symlink_to(pathmod.join('1', '1'), target_is_directory=True)
p.joinpath('1').symlink_to(parser.join('0', '0'), target_is_directory=True)
p.joinpath('2').symlink_to(parser.join('1', '1'), target_is_directory=True)
q = p / '2'
self.assertEqual(q.resolve(strict=True), p)
r = q / '3' / '4'
@ -2137,11 +2137,11 @@ class DummyPathTest(DummyPurePathTest):
p = self.cls(self.base, 'linkZ', 'foo')
self.assertEqual(p.resolve(strict=False), p)
# Loops with absolute symlinks.
self.cls(self.base, 'linkU').symlink_to(self.pathmod.join(self.base, 'linkU/inside'))
self.cls(self.base, 'linkU').symlink_to(self.parser.join(self.base, 'linkU/inside'))
self._check_symlink_loop(self.base, 'linkU')
self.cls(self.base, 'linkV').symlink_to(self.pathmod.join(self.base, 'linkV'))
self.cls(self.base, 'linkV').symlink_to(self.parser.join(self.base, 'linkV'))
self._check_symlink_loop(self.base, 'linkV')
self.cls(self.base, 'linkW').symlink_to(self.pathmod.join(self.base, 'linkW/../linkW'))
self.cls(self.base, 'linkW').symlink_to(self.parser.join(self.base, 'linkW/../linkW'))
self._check_symlink_loop(self.base, 'linkW')
# Non-strict
q = self.cls(self.base, 'linkW', 'foo')
@ -2313,11 +2313,11 @@ class DummyPathTest(DummyPurePathTest):
def _check_complex_symlinks(self, link0_target):
# Test solving a non-looping chain of symlinks (issue #19887).
pathmod = self.pathmod
parser = self.parser
P = self.cls(self.base)
P.joinpath('link1').symlink_to(pathmod.join('link0', 'link0'), target_is_directory=True)
P.joinpath('link2').symlink_to(pathmod.join('link1', 'link1'), target_is_directory=True)
P.joinpath('link3').symlink_to(pathmod.join('link2', 'link2'), target_is_directory=True)
P.joinpath('link1').symlink_to(parser.join('link0', 'link0'), target_is_directory=True)
P.joinpath('link2').symlink_to(parser.join('link1', 'link1'), target_is_directory=True)
P.joinpath('link3').symlink_to(parser.join('link2', 'link2'), target_is_directory=True)
P.joinpath('link0').symlink_to(link0_target, target_is_directory=True)
# Resolve absolute paths.
@ -2367,7 +2367,7 @@ class DummyPathTest(DummyPurePathTest):
@needs_symlinks
def test_complex_symlinks_relative_dot_dot(self):
self._check_complex_symlinks(self.pathmod.join('dirA', '..'))
self._check_complex_symlinks(self.parser.join('dirA', '..'))
def setUpWalk(self):
# Build: