https://github.com/python/cpython/commit/633942e348da2c26e6c0eaa9c70a467c5f3b2b78 commit: 633942e348da2c26e6c0eaa9c70a467c5f3b2b78 branch: main author: Barney Gale <barney.g...@gmail.com> committer: barneygale <barney.g...@gmail.com> date: 2025-03-14T20:04:07Z summary:
GH-130614: pathlib ABCs: delete vestigial `test_pathlib_abc` module (#131215) Remove the `test.test_pathlib.test_pathlib_abc` test module, which was hollowed out in previous commits. Its few remaining tests are most relevant to `PurePath` and `Path`, so we move them into `test_pathlib`. files: D Lib/test/test_pathlib/test_pathlib_abc.py M Lib/test/test_pathlib/test_pathlib.py diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 51814ae970dab0..36fb62e53e14ed 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -20,9 +20,6 @@ from test.support import infinite_recursion from test.support import os_helper from test.support.os_helper import TESTFN, FakePath -from test.test_pathlib import test_pathlib_abc -from test.test_pathlib.test_pathlib_abc import needs_posix, needs_windows - try: import fcntl except ImportError: @@ -56,7 +53,20 @@ def new_test(self): return new_test +_tests_needing_posix = set() +_tests_needing_windows = set() _tests_needing_symlinks = set() + +def needs_posix(fn): + """Decorator that marks a test as requiring a POSIX-flavoured path class.""" + _tests_needing_posix.add(fn.__name__) + return fn + +def needs_windows(fn): + """Decorator that marks a test as requiring a Windows-flavoured path class.""" + _tests_needing_windows.add(fn.__name__) + return fn + def needs_symlinks(fn): """Decorator that marks a test as requiring a path class that supports symlinks.""" _tests_needing_symlinks.add(fn.__name__) @@ -74,7 +84,7 @@ def test_is_notimplemented(self): # Tests for the pure classes. # -class PurePathTest(test_pathlib_abc.JoinablePathTest): +class PurePathTest(unittest.TestCase): cls = pathlib.PurePath # Make sure any symbolic links in the base test path are resolved. @@ -96,6 +106,72 @@ class PurePathTest(test_pathlib_abc.JoinablePathTest): ], } + def setUp(self): + name = self.id().split('.')[-1] + 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.parser is posixpath: + self.skipTest('requires Windows-flavoured path class') + p = self.cls('a') + self.parser = p.parser + self.sep = self.parser.sep + self.altsep = self.parser.altsep + + def _check_str_subclass(self, *args): + # Issue #21127: it should be possible to construct a PurePath object + # from a str subclass instance, and it then gets converted to + # a pure str object. + class StrSubclass(str): + pass + P = self.cls + p = P(*(StrSubclass(x) for x in args)) + self.assertEqual(p, P(*args)) + for part in p.parts: + self.assertIs(type(part), str) + + def test_str_subclass_common(self): + self._check_str_subclass('') + self._check_str_subclass('.') + self._check_str_subclass('a') + self._check_str_subclass('a/b.txt') + self._check_str_subclass('/a/b.txt') + + @needs_windows + def test_str_subclass_windows(self): + self._check_str_subclass('.\\a:b') + self._check_str_subclass('c:') + self._check_str_subclass('c:a') + self._check_str_subclass('c:a\\b.txt') + self._check_str_subclass('c:\\') + self._check_str_subclass('c:\\a') + self._check_str_subclass('c:\\a\\b.txt') + self._check_str_subclass('\\\\some\\share') + self._check_str_subclass('\\\\some\\share\\a') + self._check_str_subclass('\\\\some\\share\\a\\b.txt') + + def _check_str(self, expected, args): + p = self.cls(*args) + self.assertEqual(str(p), expected.replace('/', self.sep)) + + def test_str_common(self): + # Canonicalized paths roundtrip. + for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): + self._check_str(pathstr, (pathstr,)) + # Other tests for str() are in test_equivalences(). + + @needs_windows + def test_str_windows(self): + p = self.cls('a/b/c') + self.assertEqual(str(p), 'a\\b\\c') + p = self.cls('c:/a/b/c') + self.assertEqual(str(p), 'c:\\a\\b\\c') + p = self.cls('//a/b') + self.assertEqual(str(p), '\\\\a\\b\\') + p = self.cls('//a/b/c') + self.assertEqual(str(p), '\\\\a\\b\\c') + p = self.cls('//a/b/c/d') + self.assertEqual(str(p), '\\\\a\\b\\c\\d') + def test_concrete_class(self): if self.cls is pathlib.PurePath: expected = pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath @@ -1032,7 +1108,7 @@ class cls(pathlib.PurePath): # Tests for the concrete classes. # -class PathTest(test_pathlib_abc.RWPathTest, PurePathTest): +class PathTest(PurePathTest): """Tests for the FS-accessing functionalities of the Path classes.""" cls = pathlib.Path can_symlink = os_helper.can_symlink() @@ -1042,8 +1118,6 @@ def setUp(self): if name in _tests_needing_symlinks and not self.can_symlink: self.skipTest('requires symlinks') super().setUp() - - def createTestHierarchy(self): os.mkdir(self.base) os.mkdir(os.path.join(self.base, 'dirA')) os.mkdir(os.path.join(self.base, 'dirB')) @@ -1082,6 +1156,15 @@ def tearDown(self): os.chmod(os.path.join(self.base, 'dirE'), 0o777) os_helper.rmtree(self.base) + def assertFileNotFound(self, func, *args, **kwargs): + with self.assertRaises(FileNotFoundError) as cm: + func(*args, **kwargs) + self.assertEqual(cm.exception.errno, errno.ENOENT) + + def assertEqualNormCase(self, path_a, path_b): + normcase = self.parser.normcase + self.assertEqual(normcase(path_a), normcase(path_b)) + def tempdir(self): d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD', dir=os.getcwd())) @@ -2747,6 +2830,24 @@ def test_iterdir_symlink(self): expected = { P(self.base, 'linkB', q) for q in ['fileB', 'linkD'] } self.assertEqual(paths, expected) + @needs_posix + def test_glob_posix(self): + P = self.cls + p = P(self.base) + q = p / "FILEa" + given = set(p.glob("FILEa")) + expect = {q} if q.info.exists() else set() + self.assertEqual(given, expect) + self.assertEqual(set(p.glob("FILEa*")), set()) + + @needs_windows + def test_glob_windows(self): + P = self.cls + p = P(self.base) + self.assertEqual(set(p.glob("FILEa")), { P(self.base, "fileA") }) + self.assertEqual(set(p.glob("*a\\")), { P(self.base, "dirA/") }) + self.assertEqual(set(p.glob("F*a")), { P(self.base, "fileA") }) + def test_glob_empty_pattern(self): p = self.cls('') with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'): @@ -3335,7 +3436,7 @@ def test_group_windows(self): P('c:/').group() -class PathWalkTest(test_pathlib_abc.ReadablePathWalkTest): +class PathWalkTest(unittest.TestCase): cls = pathlib.Path base = PathTest.base can_symlink = PathTest.can_symlink @@ -3344,9 +3445,13 @@ def setUp(self): name = self.id().split('.')[-1] if name in _tests_needing_symlinks and not self.can_symlink: self.skipTest('requires symlinks') - super().setUp() + self.walk_path = self.cls(self.base, "TEST1") + self.sub1_path = self.walk_path / "SUB1" + self.sub11_path = self.sub1_path / "SUB11" + self.sub2_path = self.walk_path / "SUB2" + self.link_path = self.sub2_path / "link" + self.sub2_tree = (self.sub2_path, [], ["tmp3"]) - def createTestHierarchy(self): # Build: # TESTFN/ # TEST1/ a file kid and two directory kids diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py deleted file mode 100644 index b6e0e36eb87e25..00000000000000 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ /dev/null @@ -1,385 +0,0 @@ -import io -import os -import errno -import unittest - -from pathlib.types import _JoinablePath, _ReadablePath, _WritablePath -import posixpath - -from test.support.os_helper import TESTFN - - -_tests_needing_posix = set() -_tests_needing_windows = set() - - -def needs_posix(fn): - """Decorator that marks a test as requiring a POSIX-flavoured path class.""" - _tests_needing_posix.add(fn.__name__) - return fn - -def needs_windows(fn): - """Decorator that marks a test as requiring a Windows-flavoured path class.""" - _tests_needing_windows.add(fn.__name__) - return fn - - -# -# Tests for the pure classes. -# - - -class DummyJoinablePath(_JoinablePath): - __slots__ = ('_segments',) - - parser = posixpath - - def __init__(self, *segments): - self._segments = segments - - def __str__(self): - if self._segments: - return self.parser.join(*self._segments) - return '' - - def __eq__(self, other): - if not isinstance(other, DummyJoinablePath): - return NotImplemented - return str(self) == str(other) - - def __hash__(self): - return hash(str(self)) - - def __repr__(self): - return "{}({!r})".format(self.__class__.__name__, str(self)) - - def with_segments(self, *pathsegments): - return type(self)(*pathsegments) - - -class JoinablePathTest(unittest.TestCase): - cls = DummyJoinablePath - - # Use a base path that's unrelated to any real filesystem path. - base = f'/this/path/kills/fascists/{TESTFN}' - - def setUp(self): - name = self.id().split('.')[-1] - 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.parser is posixpath: - self.skipTest('requires Windows-flavoured path class') - p = self.cls('a') - self.parser = p.parser - self.sep = self.parser.sep - self.altsep = self.parser.altsep - - def _check_str_subclass(self, *args): - # Issue #21127: it should be possible to construct a PurePath object - # from a str subclass instance, and it then gets converted to - # a pure str object. - class StrSubclass(str): - pass - P = self.cls - p = P(*(StrSubclass(x) for x in args)) - self.assertEqual(p, P(*args)) - for part in p.parts: - self.assertIs(type(part), str) - - def test_str_subclass_common(self): - self._check_str_subclass('') - self._check_str_subclass('.') - self._check_str_subclass('a') - self._check_str_subclass('a/b.txt') - self._check_str_subclass('/a/b.txt') - - @needs_windows - def test_str_subclass_windows(self): - self._check_str_subclass('.\\a:b') - self._check_str_subclass('c:') - self._check_str_subclass('c:a') - self._check_str_subclass('c:a\\b.txt') - self._check_str_subclass('c:\\') - self._check_str_subclass('c:\\a') - self._check_str_subclass('c:\\a\\b.txt') - self._check_str_subclass('\\\\some\\share') - self._check_str_subclass('\\\\some\\share\\a') - self._check_str_subclass('\\\\some\\share\\a\\b.txt') - - def _check_str(self, expected, args): - p = self.cls(*args) - self.assertEqual(str(p), expected.replace('/', self.sep)) - - def test_str_common(self): - # Canonicalized paths roundtrip. - for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'): - self._check_str(pathstr, (pathstr,)) - # Other tests for str() are in test_equivalences(). - - @needs_windows - def test_str_windows(self): - p = self.cls('a/b/c') - self.assertEqual(str(p), 'a\\b\\c') - p = self.cls('c:/a/b/c') - self.assertEqual(str(p), 'c:\\a\\b\\c') - p = self.cls('//a/b') - self.assertEqual(str(p), '\\\\a\\b\\') - p = self.cls('//a/b/c') - self.assertEqual(str(p), '\\\\a\\b\\c') - p = self.cls('//a/b/c/d') - self.assertEqual(str(p), '\\\\a\\b\\c\\d') - - -# -# Tests for the virtual classes. -# - - -class DummyWritablePathIO(io.BytesIO): - """ - Used by DummyWritablePath to implement `__open_wb__()` - """ - - def __init__(self, files, path): - super().__init__() - self.files = files - self.path = path - - def close(self): - self.files[self.path] = self.getvalue() - super().close() - - -class DummyReadablePathInfo: - __slots__ = ('_is_dir', '_is_file') - - def __init__(self, is_dir, is_file): - self._is_dir = is_dir - self._is_file = is_file - - def exists(self, *, follow_symlinks=True): - return self._is_dir or self._is_file - - def is_dir(self, *, follow_symlinks=True): - return self._is_dir - - def is_file(self, *, follow_symlinks=True): - return self._is_file - - def is_symlink(self): - return False - - -class DummyReadablePath(_ReadablePath, DummyJoinablePath): - """ - Simple implementation of DummyReadablePath that keeps files and - directories in memory. - """ - __slots__ = ('_info') - - _files = {} - _directories = {} - parser = posixpath - - def __init__(self, *segments): - super().__init__(*segments) - self._info = None - - @property - def info(self): - if self._info is None: - path_str = str(self) - self._info = DummyReadablePathInfo( - is_dir=path_str.rstrip('/') in self._directories, - is_file=path_str in self._files) - return self._info - - def __open_rb__(self, buffering=-1): - path = str(self) - if path in self._directories: - raise IsADirectoryError(errno.EISDIR, "Is a directory", path) - elif path not in self._files: - raise FileNotFoundError(errno.ENOENT, "File not found", path) - return io.BytesIO(self._files[path]) - - def iterdir(self): - path = str(self).rstrip('/') - if path in self._files: - raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path) - elif path in self._directories: - return iter([self / name for name in self._directories[path]]) - else: - raise FileNotFoundError(errno.ENOENT, "File not found", path) - - def readlink(self): - raise NotImplementedError - - -class DummyWritablePath(_WritablePath, DummyJoinablePath): - __slots__ = () - - def __open_wb__(self, buffering=-1): - path = str(self) - if path in self._directories: - raise IsADirectoryError(errno.EISDIR, "Is a directory", path) - parent, name = posixpath.split(path) - if parent not in self._directories: - raise FileNotFoundError(errno.ENOENT, "File not found", parent) - self._files[path] = b'' - self._directories[parent].add(name) - return DummyWritablePathIO(self._files, path) - - def mkdir(self): - path = str(self) - parent = str(self.parent) - if path in self._directories: - raise FileExistsError(errno.EEXIST, "File exists", path) - try: - if self.name: - self._directories[parent].add(self.name) - self._directories[path] = set() - except KeyError: - raise FileNotFoundError(errno.ENOENT, "File not found", parent) from None - - def symlink_to(self, target, target_is_directory=False): - raise NotImplementedError - - -class ReadablePathTest(JoinablePathTest): - """Tests for ReadablePathTest methods that use stat(), open() and iterdir().""" - - cls = DummyReadablePath - can_symlink = False - - # (self.base) - # | - # |-- brokenLink -> non-existing - # |-- dirA - # | `-- linkC -> ../dirB - # |-- dirB - # | |-- fileB - # | `-- linkD -> ../dirB - # |-- dirC - # | |-- dirD - # | | `-- fileD - # | `-- fileC - # | `-- novel.txt - # |-- dirE # No permissions - # |-- fileA - # |-- linkA -> fileA - # |-- linkB -> dirB - # `-- brokenLinkLoop -> brokenLinkLoop - # - - def setUp(self): - super().setUp() - self.createTestHierarchy() - - def createTestHierarchy(self): - cls = self.cls - cls._files = { - f'{self.base}/fileA': b'this is file A\n', - f'{self.base}/dirB/fileB': b'this is file B\n', - f'{self.base}/dirC/fileC': b'this is file C\n', - f'{self.base}/dirC/dirD/fileD': b'this is file D\n', - f'{self.base}/dirC/novel.txt': b'this is a novel\n', - } - cls._directories = { - f'{self.base}': {'fileA', 'dirA', 'dirB', 'dirC', 'dirE'}, - f'{self.base}/dirA': set(), - f'{self.base}/dirB': {'fileB'}, - f'{self.base}/dirC': {'fileC', 'dirD', 'novel.txt'}, - f'{self.base}/dirC/dirD': {'fileD'}, - f'{self.base}/dirE': set(), - } - - def tearDown(self): - cls = self.cls - cls._files.clear() - cls._directories.clear() - - def tempdir(self): - path = self.cls(self.base).with_name('tmp-dirD') - path.mkdir() - return path - - def assertFileNotFound(self, func, *args, **kwargs): - with self.assertRaises(FileNotFoundError) as cm: - func(*args, **kwargs) - self.assertEqual(cm.exception.errno, errno.ENOENT) - - def assertEqualNormCase(self, path_a, path_b): - normcase = self.parser.normcase - self.assertEqual(normcase(path_a), normcase(path_b)) - - @needs_posix - def test_glob_posix(self): - P = self.cls - p = P(self.base) - q = p / "FILEa" - given = set(p.glob("FILEa")) - expect = {q} if q.info.exists() else set() - self.assertEqual(given, expect) - self.assertEqual(set(p.glob("FILEa*")), set()) - - @needs_windows - def test_glob_windows(self): - P = self.cls - p = P(self.base) - self.assertEqual(set(p.glob("FILEa")), { P(self.base, "fileA") }) - self.assertEqual(set(p.glob("*a\\")), { P(self.base, "dirA/") }) - self.assertEqual(set(p.glob("F*a")), { P(self.base, "fileA") }) - - -class WritablePathTest(JoinablePathTest): - cls = DummyWritablePath - - -class DummyRWPath(DummyWritablePath, DummyReadablePath): - __slots__ = () - - -class RWPathTest(WritablePathTest, ReadablePathTest): - cls = DummyRWPath - can_symlink = False - - -class ReadablePathWalkTest(unittest.TestCase): - cls = DummyReadablePath - base = ReadablePathTest.base - can_symlink = False - - def setUp(self): - self.walk_path = self.cls(self.base, "TEST1") - self.sub1_path = self.walk_path / "SUB1" - self.sub11_path = self.sub1_path / "SUB11" - self.sub2_path = self.walk_path / "SUB2" - self.link_path = self.sub2_path / "link" - self.sub2_tree = (self.sub2_path, [], ["tmp3"]) - self.createTestHierarchy() - - def createTestHierarchy(self): - cls = self.cls - cls._files = { - f'{self.base}/TEST1/tmp1': b'this is tmp1\n', - f'{self.base}/TEST1/SUB1/tmp2': b'this is tmp2\n', - f'{self.base}/TEST1/SUB2/tmp3': b'this is tmp3\n', - f'{self.base}/TEST2/tmp4': b'this is tmp4\n', - } - cls._directories = { - f'{self.base}': {'TEST1', 'TEST2'}, - f'{self.base}/TEST1': {'SUB1', 'SUB2', 'tmp1'}, - f'{self.base}/TEST1/SUB1': {'SUB11', 'tmp2'}, - f'{self.base}/TEST1/SUB1/SUB11': set(), - f'{self.base}/TEST1/SUB2': {'tmp3'}, - f'{self.base}/TEST2': {'tmp4'}, - } - - def tearDown(self): - cls = self.cls - cls._files.clear() - cls._directories.clear() - - -if __name__ == "__main__": - unittest.main() _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com