Add pathlib._abc.PathModuleBase (#113893)

Path modules 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.

This commit adds a new `PathModuleBase` class, which provides abstract
methods that unconditionally raise `UnsupportedOperation`. An instance of
this class is assigned to `PurePathBase.pathmod`, replacing `posixpath`.
As a result, `PurePathBase` is no longer POSIX-y by default, and
all its methods raise `UnsupportedOperation` courtesy of `pathmod`.

Users who subclass `PurePathBase` or `PathBase` should choose the path
syntax by setting `pathmod` to `posixpath`, `ntpath`, `os.path`, or their
own subclass of `PathModuleBase`, as circumstances demand.
This commit is contained in:
Barney Gale 2024-01-14 21:49:53 +00:00 committed by GitHub
parent c2808431b3
commit ca6cf56330
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 182 additions and 59 deletions

View file

@ -5,7 +5,7 @@ import errno
import stat
import unittest
from pathlib._abc import UnsupportedOperation, PurePathBase, PathBase
from pathlib._abc import UnsupportedOperation, PathModuleBase, PurePathBase, PathBase
import posixpath
from test.support.os_helper import TESTFN
@ -17,6 +17,20 @@ class UnsupportedOperationTest(unittest.TestCase):
self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError))
class PathModuleBaseTest(unittest.TestCase):
cls = PathModuleBase
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.splitroot, 'foo')
self.assertRaises(e, m.normcase, 'foo')
self.assertRaises(e, m.isabs, 'foo')
#
# Tests for the pure classes.
#
@ -25,6 +39,42 @@ class UnsupportedOperationTest(unittest.TestCase):
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
with self.assertRaises(e):
p / 'bar'
with self.assertRaises(e):
'bar' / p
self.assertRaises(e, p.joinpath, 'bar')
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__'))
@ -39,11 +89,12 @@ class PurePathBaseTest(unittest.TestCase):
self.assertIs(P.__ge__, object.__ge__)
def test_pathmod(self):
self.assertIs(self.cls.pathmod, posixpath)
self.assertIsInstance(self.cls.pathmod, PathModuleBase)
class DummyPurePath(PurePathBase):
__slots__ = ()
pathmod = posixpath
def __eq__(self, other):
if not isinstance(other, DummyPurePath):
@ -669,6 +720,7 @@ class DummyPath(PathBase):
memory.
"""
__slots__ = ()
pathmod = posixpath
_files = {}
_directories = {}