From f5e946bfe827dd5f7b5f69c7563d3b32d4c39672 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 30 Nov 2024 19:24:33 +0000 Subject: [PATCH 01/10] Change the default value of `PurePathBase.parser` from `ParserBase()` to `posixpath`. As a result, user subclasses of `PurePathBase` and `PathBase` use POSIX path syntax by default, which is very often desirable. --- Lib/pathlib/_abc.py | 2 +- Lib/test/test_pathlib/test_pathlib_abc.py | 40 ++--------------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 2b314b6c9a16bf..16493fb630ff83 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -121,7 +121,7 @@ class PurePathBase: # work from occurring when `resolve()` calls `stat()` or `readlink()`. '_resolving', ) - parser = ParserBase() + parser = posixpath _globber = PathGlobber def __init__(self, *args): diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index af94ac039808f0..3cc07194a9d78a 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -63,37 +63,6 @@ def test_unsupported_operation(self): class PurePathBaseTest(unittest.TestCase): cls = PurePathBase - def test_unsupported_operation_pure(self): - p = self.cls('foo') - e = UnsupportedOperation - with self.assertRaises(e): - p.drive - with self.assertRaises(e): - p.root - with self.assertRaises(e): - p.anchor - with self.assertRaises(e): - p.parts - with self.assertRaises(e): - p.parent - with self.assertRaises(e): - p.parents - with self.assertRaises(e): - p.name - with self.assertRaises(e): - p.stem - with self.assertRaises(e): - p.suffix - with self.assertRaises(e): - p.suffixes - self.assertRaises(e, p.with_name, 'bar') - self.assertRaises(e, p.with_stem, 'bar') - self.assertRaises(e, p.with_suffix, '.txt') - self.assertRaises(e, p.relative_to, '') - self.assertRaises(e, p.is_relative_to, '') - self.assertRaises(e, p.is_absolute) - self.assertRaises(e, p.match, '*') - def test_magic_methods(self): P = self.cls self.assertFalse(hasattr(P, '__fspath__')) @@ -108,12 +77,11 @@ def test_magic_methods(self): self.assertIs(P.__ge__, object.__ge__) def test_parser(self): - self.assertIsInstance(self.cls.parser, ParserBase) + self.assertIs(self.cls.parser, posixpath) class DummyPurePath(PurePathBase): __slots__ = () - parser = posixpath def __eq__(self, other): if not isinstance(other, DummyPurePath): @@ -1367,10 +1335,9 @@ def test_unsupported_operation(self): self.assertRaises(e, p.write_bytes, b'foo') self.assertRaises(e, p.write_text, 'foo') self.assertRaises(e, p.iterdir) - self.assertRaises(e, p.glob, '*') - self.assertRaises(e, p.rglob, '*') + self.assertRaises(e, lambda: list(p.glob('*'))) + self.assertRaises(e, lambda: list(p.rglob('*'))) self.assertRaises(e, lambda: list(p.walk())) - self.assertRaises(e, p.absolute) self.assertRaises(e, p.expanduser) self.assertRaises(e, p.readlink) self.assertRaises(e, p.symlink_to, 'foo') @@ -1442,7 +1409,6 @@ class DummyPath(PathBase): memory. """ __slots__ = () - parser = posixpath _files = {} _directories = {} From 9febad0f5d7874ed76dee3c9660e02dd841aefd5 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 30 Nov 2024 19:29:08 +0000 Subject: [PATCH 02/10] Move `pathlib._abc.ParserBase` to `pathlib._types.Parser`, and convert it to a runtime-checkable protocol. --- Lib/pathlib/_abc.py | 53 --------------------- Lib/pathlib/_types.py | 23 +++++++++ Lib/test/test_pathlib/test_pathlib_abc.py | 18 +------ Lib/test/test_pathlib/test_pathlib_types.py | 15 ++++++ 4 files changed, 39 insertions(+), 70 deletions(-) create mode 100644 Lib/pathlib/_types.py create mode 100644 Lib/test/test_pathlib/test_pathlib_types.py diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 16493fb630ff83..0d1d9982ab2c4b 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -34,59 +34,6 @@ def _is_case_sensitive(parser): return parser.normcase('Aa') == 'Aa' - -class ParserBase: - """Base class for path parsers, which do low-level path manipulation. - - Path parsers provide a subset of the os.path API, specifically those - functions needed to provide PurePathBase functionality. Each PurePathBase - subclass references its path parser via a 'parser' class attribute. - - Every method in this base class raises an UnsupportedOperation exception. - """ - - @classmethod - def _unsupported_msg(cls, attribute): - return f"{cls.__name__}.{attribute} is unsupported" - - @property - def sep(self): - """The character used to separate path components.""" - raise UnsupportedOperation(self._unsupported_msg('sep')) - - def join(self, path, *paths): - """Join path segments.""" - raise UnsupportedOperation(self._unsupported_msg('join()')) - - def split(self, path): - """Split the path into a pair (head, tail), where *head* is everything - before the final path separator, and *tail* is everything after. - Either part may be empty. - """ - raise UnsupportedOperation(self._unsupported_msg('split()')) - - def splitdrive(self, path): - """Split the path into a 2-item tuple (drive, tail), where *drive* is - a device name or mount point, and *tail* is everything after the - drive. Either part may be empty.""" - raise UnsupportedOperation(self._unsupported_msg('splitdrive()')) - - def splitext(self, path): - """Split the path into a pair (root, ext), where *ext* is empty or - begins with a period and contains at most one period, - and *root* is everything before the extension.""" - raise UnsupportedOperation(self._unsupported_msg('splitext()')) - - def normcase(self, path): - """Normalize the case of the path.""" - raise UnsupportedOperation(self._unsupported_msg('normcase()')) - - def isabs(self, path): - """Returns whether the path is absolute, i.e. unaffected by the - current directory or drive.""" - raise UnsupportedOperation(self._unsupported_msg('isabs()')) - - class PathGlobber(_GlobberBase): """ Class providing shell-style globbing for path objects. diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py new file mode 100644 index 00000000000000..bd522705b2d5da --- /dev/null +++ b/Lib/pathlib/_types.py @@ -0,0 +1,23 @@ +""" +Protocols for supporting classes in pathlib. +""" + +from typing import Protocol, runtime_checkable + + +@runtime_checkable +class Parser(Protocol): + """Protocol for path parsers, which do low-level path manipulation. + + Path parsers provide a subset of the os.path API, specifically those + functions needed to provide PurePathBase functionality. Each PurePathBase + subclass references its path parser via a 'parser' class attribute. + """ + + sep: str + def join(self, path: str, *paths: str) -> str: ... + def split(self, path: str) -> (str, str): ... + def splitdrive(self, path: str) -> (str, str): ... + def splitext(self, path: str) -> (str, str): ... + def normcase(self, path: str) -> str: ... + def isabs(self, path: str) -> bool: ... diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 3cc07194a9d78a..b7a6f38114f357 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -6,7 +6,7 @@ import stat import unittest -from pathlib._abc import UnsupportedOperation, ParserBase, PurePathBase, PathBase +from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase import posixpath from test.support import is_wasi @@ -39,22 +39,6 @@ def test_is_notimplemented(self): self.assertTrue(issubclass(UnsupportedOperation, NotImplementedError)) self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError)) - -class ParserBaseTest(unittest.TestCase): - cls = ParserBase - - def test_unsupported_operation(self): - m = self.cls() - e = UnsupportedOperation - with self.assertRaises(e): - m.sep - self.assertRaises(e, m.join, 'foo') - self.assertRaises(e, m.split, 'foo') - self.assertRaises(e, m.splitdrive, 'foo') - self.assertRaises(e, m.splitext, 'foo') - self.assertRaises(e, m.normcase, 'foo') - self.assertRaises(e, m.isabs, 'foo') - # # Tests for the pure classes. # diff --git a/Lib/test/test_pathlib/test_pathlib_types.py b/Lib/test/test_pathlib/test_pathlib_types.py new file mode 100644 index 00000000000000..67a793ea93c035 --- /dev/null +++ b/Lib/test/test_pathlib/test_pathlib_types.py @@ -0,0 +1,15 @@ +import ntpath +import posixpath +import unittest + +from pathlib._types import Parser + + +class ParserTest(unittest.TestCase): + def test_path_modules(self): + self.assertTrue(isinstance(posixpath, Parser)) + self.assertTrue(isinstance(ntpath, Parser)) + + +if __name__ == "__main__": + unittest.main() From 6e07a3c4d50b5cbb63ebb86a9aff6e238b38c95f Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 30 Nov 2024 19:42:59 +0000 Subject: [PATCH 03/10] Add `pathlib._types.DirEntry`, which is a protocol for directory entries. Objects of this type provide a subset of the `os.DirEntry` API, specifically those methods and attributes needed to implement `glob()` and `walk()`. --- Lib/pathlib/_types.py | 16 ++++++++++++++++ Lib/test/test_pathlib/test_pathlib_types.py | 20 +++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index bd522705b2d5da..bbc989be06f8de 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -21,3 +21,19 @@ def splitdrive(self, path: str) -> (str, str): ... def splitext(self, path: str) -> (str, str): ... def normcase(self, path: str) -> str: ... def isabs(self, path: str) -> bool: ... + + +@runtime_checkable +class DirEntry(Protocol): + """Protocol for directory entries, which store cached information about + directory children. + + Directory entries provide a subset of the os.DirEntry API, specifically + those methods and attributes needed to provide PathBase functionality + like glob() and walk(). Directory entry objects are generated by + PathBase.scandir(). + """ + + name: str + def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... + def is_symlink(self) -> bool: ... diff --git a/Lib/test/test_pathlib/test_pathlib_types.py b/Lib/test/test_pathlib/test_pathlib_types.py index 67a793ea93c035..4d168b15af7787 100644 --- a/Lib/test/test_pathlib/test_pathlib_types.py +++ b/Lib/test/test_pathlib/test_pathlib_types.py @@ -1,8 +1,12 @@ import ntpath +import os import posixpath import unittest -from pathlib._types import Parser +from pathlib._abc import PathBase +from pathlib._types import Parser, DirEntry + +from test.support.os_helper import TESTFN class ParserTest(unittest.TestCase): @@ -11,5 +15,19 @@ def test_path_modules(self): self.assertTrue(isinstance(ntpath, Parser)) +class DirEntryTest(unittest.TestCase): + def test_os_direntry(self): + os.mkdir(TESTFN) + try: + with os.scandir() as entries: + entry = next(entries) + self.assertTrue(isinstance(entry, DirEntry)) + finally: + os.rmdir(TESTFN) + + def test_pathbase(self): + self.assertTrue(isinstance(PathBase(), DirEntry)) + + if __name__ == "__main__": unittest.main() From 2ada5d11725046e4dddcc33c9d5578e76c2af0f1 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 30 Nov 2024 19:57:01 +0000 Subject: [PATCH 04/10] Add `pathlib._types.StatResult`, which is a protocol for stat() results. Objects of this type provide a small subset of the `os.stat_result` API, specifically attributes for the file type, permissions and location/offset. --- Lib/pathlib/_types.py | 14 +++++++++++++- Lib/test/test_pathlib/test_pathlib_types.py | 7 ++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index bbc989be06f8de..5624aa637cda1c 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -1,7 +1,6 @@ """ Protocols for supporting classes in pathlib. """ - from typing import Protocol, runtime_checkable @@ -37,3 +36,16 @@ class DirEntry(Protocol): name: str def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... def is_symlink(self) -> bool: ... + + +@runtime_checkable +class StatResult(Protocol): + """Protocol for stat results, which store low-level information about + files. + + Stat results provide a subset of the os.stat_result API, specifically + attributes for the file type, permissions and offset.""" + + st_mode: int + st_ino: int + st_dev: int diff --git a/Lib/test/test_pathlib/test_pathlib_types.py b/Lib/test/test_pathlib/test_pathlib_types.py index 4d168b15af7787..7a8598610a37d7 100644 --- a/Lib/test/test_pathlib/test_pathlib_types.py +++ b/Lib/test/test_pathlib/test_pathlib_types.py @@ -4,7 +4,7 @@ import unittest from pathlib._abc import PathBase -from pathlib._types import Parser, DirEntry +from pathlib._types import Parser, DirEntry, StatResult from test.support.os_helper import TESTFN @@ -29,5 +29,10 @@ def test_pathbase(self): self.assertTrue(isinstance(PathBase(), DirEntry)) +class StatResultTest(unittest.TestCase): + def test_os_stat_result(self): + self.assertTrue(isinstance(os.stat('.'), StatResult)) + + if __name__ == "__main__": unittest.main() From 0d0140af4fcab9d7411437af853cd7e82c1ed6f3 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 30 Nov 2024 20:39:33 +0000 Subject: [PATCH 05/10] Revise tests --- Lib/test/test_pathlib/test_pathlib_abc.py | 10 +++++- Lib/test/test_pathlib/test_pathlib_types.py | 38 --------------------- 2 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 Lib/test/test_pathlib/test_pathlib_types.py diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index b7a6f38114f357..205ab239f176e1 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -7,6 +7,7 @@ import unittest from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase +from pathlib._types import Parser, DirEntry, StatResult import posixpath from test.support import is_wasi @@ -96,6 +97,9 @@ def setUp(self): self.sep = self.parser.sep self.altsep = self.parser.altsep + def test_parser(self): + self.assertIsInstance(self.cls.parser, Parser) + def test_constructor_common(self): P = self.cls p = P('a') @@ -2169,7 +2173,7 @@ def test_scandir(self): with p.scandir() as entries: for entry in entries: child = p / entry.name - self.assertIsNotNone(entry) + self.assertIsInstance(entry, DirEntry) self.assertEqual(entry.name, child.name) self.assertEqual(entry.is_symlink(), child.is_symlink()) @@ -2591,6 +2595,10 @@ def test_stat(self): statA = self.cls(self.base).joinpath('fileA').stat() statB = self.cls(self.base).joinpath('dirB', 'fileB').stat() statC = self.cls(self.base).joinpath('dirC').stat() + # all instances of StatResult + self.assertIsInstance(statA, StatResult) + self.assertIsInstance(statB, StatResult) + self.assertIsInstance(statC, StatResult) # st_mode: files are the same, directory differs. self.assertIsInstance(statA.st_mode, int) self.assertEqual(statA.st_mode, statB.st_mode) diff --git a/Lib/test/test_pathlib/test_pathlib_types.py b/Lib/test/test_pathlib/test_pathlib_types.py deleted file mode 100644 index 7a8598610a37d7..00000000000000 --- a/Lib/test/test_pathlib/test_pathlib_types.py +++ /dev/null @@ -1,38 +0,0 @@ -import ntpath -import os -import posixpath -import unittest - -from pathlib._abc import PathBase -from pathlib._types import Parser, DirEntry, StatResult - -from test.support.os_helper import TESTFN - - -class ParserTest(unittest.TestCase): - def test_path_modules(self): - self.assertTrue(isinstance(posixpath, Parser)) - self.assertTrue(isinstance(ntpath, Parser)) - - -class DirEntryTest(unittest.TestCase): - def test_os_direntry(self): - os.mkdir(TESTFN) - try: - with os.scandir() as entries: - entry = next(entries) - self.assertTrue(isinstance(entry, DirEntry)) - finally: - os.rmdir(TESTFN) - - def test_pathbase(self): - self.assertTrue(isinstance(PathBase(), DirEntry)) - - -class StatResultTest(unittest.TestCase): - def test_os_stat_result(self): - self.assertTrue(isinstance(os.stat('.'), StatResult)) - - -if __name__ == "__main__": - unittest.main() From 4d51683d8ccae388b2fddb46979cab77dd70317f Mon Sep 17 00:00:00 2001 From: barneygale Date: Sun, 1 Dec 2024 23:50:25 +0000 Subject: [PATCH 06/10] Docstring tweaks --- Lib/pathlib/_abc.py | 2 +- Lib/pathlib/_local.py | 2 +- Lib/pathlib/_types.py | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 0d1d9982ab2c4b..60d7be7ce36542 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -580,7 +580,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None): return f.write(data) def scandir(self): - """Yield os.DirEntry objects of the directory contents. + """Yield DirEntry objects of the directory contents. The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index b5d9dc49f58463..214999507afd86 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -635,7 +635,7 @@ def _filter_trailing_slash(self, paths): yield path_str def scandir(self): - """Yield os.DirEntry objects of the directory contents. + """Yield DirEntry objects of the directory contents. The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index 5624aa637cda1c..db1f1dba4584b1 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -24,13 +24,16 @@ def isabs(self, path: str) -> bool: ... @runtime_checkable class DirEntry(Protocol): - """Protocol for directory entries, which store cached information about - directory children. + """Protocol for directory entries, which store information about directory + children. Directory entries provide a subset of the os.DirEntry API, specifically those methods and attributes needed to provide PathBase functionality like glob() and walk(). Directory entry objects are generated by PathBase.scandir(). + + Path and PathBase implement this protocol, but their is_*() methods always + fetch up-to-date information. """ name: str From 59201c3709494eca2621fc01f5cff24b36896121 Mon Sep 17 00:00:00 2001 From: Barney Gale Date: Tue, 3 Dec 2024 20:06:15 +0000 Subject: [PATCH 07/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/pathlib/_types.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index db1f1dba4584b1..bc478bf3994c46 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -15,9 +15,9 @@ class Parser(Protocol): sep: str def join(self, path: str, *paths: str) -> str: ... - def split(self, path: str) -> (str, str): ... - def splitdrive(self, path: str) -> (str, str): ... - def splitext(self, path: str) -> (str, str): ... + def split(self, path: str) -> tuple[str, str]: ... + def splitdrive(self, path: str) -> tuple[str, str]: ... + def splitext(self, path: str) -> tuple[str, str]: ... def normcase(self, path: str) -> str: ... def isabs(self, path: str) -> bool: ... @@ -47,7 +47,8 @@ class StatResult(Protocol): files. Stat results provide a subset of the os.stat_result API, specifically - attributes for the file type, permissions and offset.""" + attributes for the file type, permissions and offset. + """ st_mode: int st_ino: int From 2fa207c1ea502f71be03f3acadb62d1cd2e8bc5a Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 3 Dec 2024 20:11:20 +0000 Subject: [PATCH 08/10] Add DirEntry.is_file() --- Lib/pathlib/_types.py | 1 + Lib/test/test_pathlib/test_pathlib_abc.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index bc478bf3994c46..d05ce45597b3fd 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -37,6 +37,7 @@ class DirEntry(Protocol): """ name: str + def is_file(self, *, follow_symlinks: bool = True) -> bool: ... def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... def is_symlink(self) -> bool: ... diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 205ab239f176e1..f9c484f01e5526 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -1387,8 +1387,11 @@ def __init__(self, name, is_symlink, is_dir): def is_symlink(self): return self._is_symlink + def is_file(self, *, follow_symlinks=True): + return (follow_symlinks or not self._is_symlink) and not self._is_dir + def is_dir(self, *, follow_symlinks=True): - return self._is_dir and (follow_symlinks or not self._is_symlink) + return (follow_symlinks or not self._is_symlink) and self._is_dir class DummyPath(PathBase): From 137875ce2f881f679b99bf3260a34dbd3ca814c0 Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 3 Dec 2024 20:42:56 +0000 Subject: [PATCH 09/10] Another docstring tweak --- Lib/pathlib/_types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index d05ce45597b3fd..438186c5d8908c 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -32,6 +32,9 @@ class DirEntry(Protocol): like glob() and walk(). Directory entry objects are generated by PathBase.scandir(). + Methods may return cached information for performance reasons, though this + is not required. + Path and PathBase implement this protocol, but their is_*() methods always fetch up-to-date information. """ From ee23e0e189cd0b250acbe83610920b00a51a2d60 Mon Sep 17 00:00:00 2001 From: barneygale Date: Sat, 7 Dec 2024 02:11:38 +0000 Subject: [PATCH 10/10] Remove DirEntry and StatResult for now. --- Lib/pathlib/_abc.py | 2 +- Lib/pathlib/_local.py | 2 +- Lib/pathlib/_types.py | 37 ----------------------- Lib/test/test_pathlib/test_pathlib_abc.py | 8 ++--- 4 files changed, 4 insertions(+), 45 deletions(-) diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py index 78c1a0017316b7..c5ee5ab1ef34d3 100644 --- a/Lib/pathlib/_abc.py +++ b/Lib/pathlib/_abc.py @@ -569,7 +569,7 @@ def write_text(self, data, encoding=None, errors=None, newline=None): return f.write(data) def _scandir(self): - """Yield DirEntry objects of the directory contents. + """Yield os.DirEntry-like objects of the directory contents. The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 652bfeafe9c008..250bc12956f5bc 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -636,7 +636,7 @@ def _filter_trailing_slash(self, paths): yield path_str def _scandir(self): - """Yield DirEntry objects of the directory contents. + """Yield os.DirEntry-like objects of the directory contents. The children are yielded in arbitrary order, and the special entries '.' and '..' are not included. diff --git a/Lib/pathlib/_types.py b/Lib/pathlib/_types.py index 438186c5d8908c..60df94d0b46049 100644 --- a/Lib/pathlib/_types.py +++ b/Lib/pathlib/_types.py @@ -20,40 +20,3 @@ def splitdrive(self, path: str) -> tuple[str, str]: ... def splitext(self, path: str) -> tuple[str, str]: ... def normcase(self, path: str) -> str: ... def isabs(self, path: str) -> bool: ... - - -@runtime_checkable -class DirEntry(Protocol): - """Protocol for directory entries, which store information about directory - children. - - Directory entries provide a subset of the os.DirEntry API, specifically - those methods and attributes needed to provide PathBase functionality - like glob() and walk(). Directory entry objects are generated by - PathBase.scandir(). - - Methods may return cached information for performance reasons, though this - is not required. - - Path and PathBase implement this protocol, but their is_*() methods always - fetch up-to-date information. - """ - - name: str - def is_file(self, *, follow_symlinks: bool = True) -> bool: ... - def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... - def is_symlink(self) -> bool: ... - - -@runtime_checkable -class StatResult(Protocol): - """Protocol for stat results, which store low-level information about - files. - - Stat results provide a subset of the os.stat_result API, specifically - attributes for the file type, permissions and offset. - """ - - st_mode: int - st_ino: int - st_dev: int diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index ad2f483d6b6508..411bdc503325db 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -6,7 +6,7 @@ import unittest from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase -from pathlib._types import Parser, DirEntry, StatResult +from pathlib._types import Parser import posixpath from test.support.os_helper import TESTFN @@ -1920,7 +1920,7 @@ def test_scandir(self): with p._scandir() as entries: for entry in entries: child = p / entry.name - self.assertIsInstance(entry, DirEntry) + self.assertIsNotNone(entry) self.assertEqual(entry.name, child.name) self.assertEqual(entry.is_symlink(), child.is_symlink()) @@ -2057,10 +2057,6 @@ def test_stat(self): statA = self.cls(self.base).joinpath('fileA').stat() statB = self.cls(self.base).joinpath('dirB', 'fileB').stat() statC = self.cls(self.base).joinpath('dirC').stat() - # all instances of StatResult - self.assertIsInstance(statA, StatResult) - self.assertIsInstance(statB, StatResult) - self.assertIsInstance(statC, StatResult) # st_mode: files are the same, directory differs. self.assertIsInstance(statA.st_mode, int) self.assertEqual(statA.st_mode, statB.st_mode)