8000 GH-127456: pathlib ABCs: add protocol for path parser by barneygale · Pull Request #127494 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

GH-127456: pathlib ABCs: add protocol for path parser #127494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move pathlib._abc.ParserBase to pathlib._types.Parser, and conver…
…t it

to a runtime-checkable protocol.
  • Loading branch information
barneygale committed Nov 30, 2024
commit 9febad0f5d7874ed76dee3c9660e02dd841aefd5
53 changes: 0 additions & 53 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions Lib/pathlib/_types.py
Original file line number Diff line number Diff line change
@@ -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: ...
18 changes: 1 addition & 17 deletions Lib/test/test_pathlib/test_pathlib_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
#
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_pathlib/test_pathlib_types.py
Original file line number Diff line number Diff line change
@@ -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()
0