https://github.com/python/cpython/commit/d0eb01c9de9a455df36f885506314d8d00645c9f commit: d0eb01c9de9a455df36f885506314d8d00645c9f branch: main author: Barney Gale <barney.g...@gmail.com> committer: barneygale <barney.g...@gmail.com> date: 2025-03-03T17:56:57Z summary:
GH-128520: Merge `pathlib._abc` into `pathlib.types` (#130747) There used to be a meaningful distinction between these modules: `pathlib` imported `pathlib._abc` but not `pathlib.types`. This is no longer the case (neither module is imported), so we move the ABCs as follows: - `pathlib._abc.JoinablePath` --> `pathlib.types._JoinablePath` - `pathlib._abc.ReadablePath` --> `pathlib.types._ReadablePath` - `pathlib._abc.WritablePath` --> `pathlib.types._WritablePath` files: D Lib/pathlib/_abc.py M Lib/pathlib/types.py M Lib/test/test_pathlib/test_pathlib.py M Lib/test/test_pathlib/test_pathlib_abc.py diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py deleted file mode 100644 index d9fb018d75f538..00000000000000 --- a/Lib/pathlib/_abc.py +++ /dev/null @@ -1,397 +0,0 @@ -""" -Abstract base classes for rich path objects. - -This module is published as a PyPI package called "pathlib-abc". - -This module is also a *PRIVATE* part of the Python standard library, where -it's developed alongside pathlib. If it finds success and maturity as a PyPI -package, it could become a public part of the standard library. - -Three base classes are defined here -- JoinablePath, ReadablePath and -WritablePath. -""" - -from abc import ABC, abstractmethod -from glob import _PathGlobber, _no_recurse_symlinks -from pathlib import PurePath, Path -from pathlib._os import magic_open, ensure_distinct_paths, copy_file - - -def _explode_path(path): - """ - Split the path into a 2-tuple (anchor, parts), where *anchor* is the - uppermost parent of the path (equivalent to path.parents[-1]), and - *parts* is a reversed list of parts following the anchor. - """ - split = path.parser.split - path = str(path) - parent, name = split(path) - names = [] - while path != parent: - names.append(name) - path = parent - parent, name = split(path) - return path, names - - -class JoinablePath(ABC): - """Abstract base class for pure path objects. - - This class *does not* provide several magic methods that are defined in - its implementation PurePath. They are: __init__, __fspath__, __bytes__, - __reduce__, __hash__, __eq__, __lt__, __le__, __gt__, __ge__. - """ - __slots__ = () - - @property - @abstractmethod - def parser(self): - """Implementation of pathlib._types.Parser used for low-level path - parsing and manipulation. - """ - raise NotImplementedError - - @abstractmethod - def with_segments(self, *pathsegments): - """Construct a new path object from any number of path-like objects. - Subclasses may override this method to customize how new path objects - are created from methods like `iterdir()`. - """ - raise NotImplementedError - - @abstractmethod - def __str__(self): - """Return the string representation of the path, suitable for - passing to system calls.""" - raise NotImplementedError - - @property - def anchor(self): - """The concatenation of the drive and root, or ''.""" - return _explode_path(self)[0] - - @property - def name(self): - """The final path component, if any.""" - return self.parser.split(str(self))[1] - - @property - def suffix(self): - """ - The final component's last suffix, if any. - - This includes the leading period. For example: '.txt' - """ - return self.parser.splitext(self.name)[1] - - @property - def suffixes(self): - """ - A list of the final component's suffixes, if any. - - These include the leading periods. For example: ['.tar', '.gz'] - """ - split = self.parser.splitext - stem, suffix = split(self.name) - suffixes = [] - while suffix: - suffixes.append(suffix) - stem, suffix = split(stem) - return suffixes[::-1] - - @property - def stem(self): - """The final path component, minus its last suffix.""" - return self.parser.splitext(self.name)[0] - - def with_name(self, name): - """Return a new path with the file name changed.""" - split = self.parser.split - if split(name)[0]: - raise ValueError(f"Invalid name {name!r}") - return self.with_segments(split(str(self))[0], name) - - def with_stem(self, stem): - """Return a new path with the stem changed.""" - suffix = self.suffix - if not suffix: - return self.with_name(stem) - elif not stem: - # If the suffix is non-empty, we can't make the stem empty. - raise ValueError(f"{self!r} has a non-empty suffix") - else: - return self.with_name(stem + suffix) - - def with_suffix(self, suffix): - """Return a new path with the file suffix changed. If the path - has no suffix, add given suffix. If the given suffix is an empty - string, remove the suffix from the path. - """ - stem = self.stem - if not stem: - # If the stem is empty, we can't make the suffix non-empty. - raise ValueError(f"{self!r} has an empty name") - elif suffix and not suffix.startswith('.'): - raise ValueError(f"Invalid suffix {suffix!r}") - else: - return self.with_name(stem + suffix) - - @property - def parts(self): - """An object providing sequence-like access to the - components in the filesystem path.""" - anchor, parts = _explode_path(self) - if anchor: - parts.append(anchor) - return tuple(reversed(parts)) - - def joinpath(self, *pathsegments): - """Combine this path with one or several arguments, and return a - new path representing either a subpath (if all arguments are relative - paths) or a totally different path (if one of the arguments is - anchored). - """ - return self.with_segments(str(self), *pathsegments) - - def __truediv__(self, key): - try: - return self.with_segments(str(self), key) - except TypeError: - return NotImplemented - - def __rtruediv__(self, key): - try: - return self.with_segments(key, str(self)) - except TypeError: - return NotImplemented - - @property - def parent(self): - """The logical parent of the path.""" - path = str(self) - parent = self.parser.split(path)[0] - if path != parent: - return self.with_segments(parent) - return self - - @property - def parents(self): - """A sequence of this path's logical parents.""" - split = self.parser.split - path = str(self) - parent = split(path)[0] - parents = [] - while path != parent: - parents.append(self.with_segments(parent)) - path = parent - parent = split(path)[0] - return tuple(parents) - - def full_match(self, pattern, *, case_sensitive=None): - """ - Return True if this path matches the given glob-style pattern. The - pattern is matched against the entire path. - """ - if not hasattr(pattern, 'with_segments'): - pattern = self.with_segments(pattern) - if case_sensitive is None: - case_sensitive = self.parser.normcase('Aa') == 'Aa' - globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) - match = globber.compile(str(pattern)) - return match(str(self)) is not None - - -class ReadablePath(JoinablePath): - """Abstract base class for readable path objects. - - The Path class implements this ABC for local filesystem paths. Users may - create subclasses to implement readable virtual filesystem paths, such as - paths in archive files or on remote storage systems. - """ - __slots__ = () - - @property - @abstractmethod - def info(self): - """ - A PathInfo object that exposes the file type and other file attributes - of this path. - """ - raise NotImplementedError - - @abstractmethod - def __open_rb__(self, buffering=-1): - """ - Open the file pointed to by this path for reading in binary mode and - return a file object, like open(mode='rb'). - """ - raise NotImplementedError - - def read_bytes(self): - """ - Open the file in bytes mode, read it, and close the file. - """ - with magic_open(self, mode='rb', buffering=0) as f: - return f.read() - - def read_text(self, encoding=None, errors=None, newline=None): - """ - Open the file in text mode, read it, and close the file. - """ - with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: - return f.read() - - @abstractmethod - def iterdir(self): - """Yield path objects of the directory contents. - - The children are yielded in arbitrary order, and the - special entries '.' and '..' are not included. - """ - raise NotImplementedError - - def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): - """Iterate over this subtree and yield all existing files (of any - kind, including directories) matching the given relative pattern. - """ - if not hasattr(pattern, 'with_segments'): - pattern = self.with_segments(pattern) - anchor, parts = _explode_path(pattern) - if anchor: - raise NotImplementedError("Non-relative patterns are unsupported") - case_sensitive_default = self.parser.normcase('Aa') == 'Aa' - if case_sensitive is None: - case_sensitive = case_sensitive_default - case_pedantic = False - else: - case_pedantic = case_sensitive_default != case_sensitive - recursive = True if recurse_symlinks else _no_recurse_symlinks - globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) - select = globber.selector(parts) - return select(self.joinpath('')) - - def walk(self, top_down=True, on_error=None, follow_symlinks=False): - """Walk the directory tree from this directory, similar to os.walk().""" - paths = [self] - while paths: - path = paths.pop() - if isinstance(path, tuple): - yield path - continue - dirnames = [] - filenames = [] - if not top_down: - paths.append((path, dirnames, filenames)) - try: - for child in path.iterdir(): - if child.info.is_dir(follow_symlinks=follow_symlinks): - if not top_down: - paths.append(child) - dirnames.append(child.name) - else: - filenames.append(child.name) - except OSError as error: - if on_error is not None: - on_error(error) - if not top_down: - while not isinstance(paths.pop(), tuple): - pass - continue - if top_down: - yield path, dirnames, filenames - paths += [path.joinpath(d) for d in reversed(dirnames)] - - @abstractmethod - def readlink(self): - """ - Return the path to which the symbolic link points. - """ - raise NotImplementedError - - def copy(self, target, follow_symlinks=True, preserve_metadata=False): - """ - Recursively copy this file or directory tree to the given destination. - """ - if not hasattr(target, 'with_segments'): - target = self.with_segments(target) - ensure_distinct_paths(self, target) - copy_file(self, target, follow_symlinks, preserve_metadata) - return target.joinpath() # Empty join to ensure fresh metadata. - - def copy_into(self, target_dir, *, follow_symlinks=True, - preserve_metadata=False): - """ - Copy this file or directory tree into the given existing directory. - """ - name = self.name - if not name: - raise ValueError(f"{self!r} has an empty name") - elif hasattr(target_dir, 'with_segments'): - target = target_dir / name - else: - target = self.with_segments(target_dir, name) - return self.copy(target, follow_symlinks=follow_symlinks, - preserve_metadata=preserve_metadata) - - -class WritablePath(JoinablePath): - """Abstract base class for writable path objects. - - The Path class implements this ABC for local filesystem paths. Users may - create subclasses to implement writable virtual filesystem paths, such as - paths in archive files or on remote storage systems. - """ - __slots__ = () - - @abstractmethod - def symlink_to(self, target, target_is_directory=False): - """ - Make this path a symlink pointing to the target path. - Note the order of arguments (link, target) is the reverse of os.symlink. - """ - raise NotImplementedError - - @abstractmethod - def mkdir(self): - """ - Create a new directory at this given path. - """ - raise NotImplementedError - - @abstractmethod - def __open_wb__(self, buffering=-1): - """ - Open the file pointed to by this path for writing in binary mode and - return a file object, like open(mode='wb'). - """ - raise NotImplementedError - - def write_bytes(self, data): - """ - Open the file in bytes mode, write to it, and close the file. - """ - # type-check for the buffer interface before truncating the file - view = memoryview(data) - with magic_open(self, mode='wb') as f: - return f.write(view) - - def write_text(self, data, encoding=None, errors=None, newline=None): - """ - Open the file in text mode, write to it, and close the file. - """ - if not isinstance(data, str): - raise TypeError('data must be str, not %s' % - data.__class__.__name__) - with magic_open(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f: - return f.write(data) - - def _write_info(self, info, follow_symlinks=True): - """ - Write the given PathInfo to this path. - """ - pass - - -JoinablePath.register(PurePath) -ReadablePath.register(Path) -WritablePath.register(Path) diff --git a/Lib/pathlib/types.py b/Lib/pathlib/types.py index b781264796bf67..cfdbc89644a24a 100644 --- a/Lib/pathlib/types.py +++ b/Lib/pathlib/types.py @@ -1,9 +1,39 @@ """ Protocols for supporting classes in pathlib. """ + +# This module also provides abstract base classes for rich path objects. +# These ABCs are a *private* part of the Python standard library, but they're +# made available as a PyPI package called "pathlib-abc". It's possible they'll +# become an official part of the standard library in future. +# +# Three ABCs are provided -- _JoinablePath, _ReadablePath and _WritablePath + + +from abc import ABC, abstractmethod +from glob import _PathGlobber, _no_recurse_symlinks +from pathlib import PurePath, Path +from pathlib._os import magic_open, ensure_distinct_paths, copy_file from typing import Protocol, runtime_checkable +def _explode_path(path): + """ + Split the path into a 2-tuple (anchor, parts), where *anchor* is the + uppermost parent of the path (equivalent to path.parents[-1]), and + *parts* is a reversed list of parts following the anchor. + """ + split = path.parser.split + path = str(path) + parent, name = split(path) + names = [] + while path != parent: + names.append(name) + path = parent + parent, name = split(path) + return path, names + + @runtime_checkable class _PathParser(Protocol): """Protocol for path parsers, which do low-level path manipulation. @@ -28,3 +58,366 @@ def exists(self, *, follow_symlinks: bool = True) -> bool: ... def is_dir(self, *, follow_symlinks: bool = True) -> bool: ... def is_file(self, *, follow_symlinks: bool = True) -> bool: ... def is_symlink(self) -> bool: ... + + +class _JoinablePath(ABC): + """Abstract base class for pure path objects. + + This class *does not* provide several magic methods that are defined in + its implementation PurePath. They are: __init__, __fspath__, __bytes__, + __reduce__, __hash__, __eq__, __lt__, __le__, __gt__, __ge__. + """ + __slots__ = () + + @property + @abstractmethod + def parser(self): + """Implementation of pathlib._types.Parser used for low-level path + parsing and manipulation. + """ + raise NotImplementedError + + @abstractmethod + def with_segments(self, *pathsegments): + """Construct a new path object from any number of path-like objects. + Subclasses may override this method to customize how new path objects + are created from methods like `iterdir()`. + """ + raise NotImplementedError + + @abstractmethod + def __str__(self): + """Return the string representation of the path, suitable for + passing to system calls.""" + raise NotImplementedError + + @property + def anchor(self): + """The concatenation of the drive and root, or ''.""" + return _explode_path(self)[0] + + @property + def name(self): + """The final path component, if any.""" + return self.parser.split(str(self))[1] + + @property + def suffix(self): + """ + The final component's last suffix, if any. + + This includes the leading period. For example: '.txt' + """ + return self.parser.splitext(self.name)[1] + + @property + def suffixes(self): + """ + A list of the final component's suffixes, if any. + + These include the leading periods. For example: ['.tar', '.gz'] + """ + split = self.parser.splitext + stem, suffix = split(self.name) + suffixes = [] + while suffix: + suffixes.append(suffix) + stem, suffix = split(stem) + return suffixes[::-1] + + @property + def stem(self): + """The final path component, minus its last suffix.""" + return self.parser.splitext(self.name)[0] + + def with_name(self, name): + """Return a new path with the file name changed.""" + split = self.parser.split + if split(name)[0]: + raise ValueError(f"Invalid name {name!r}") + return self.with_segments(split(str(self))[0], name) + + def with_stem(self, stem): + """Return a new path with the stem changed.""" + suffix = self.suffix + if not suffix: + return self.with_name(stem) + elif not stem: + # If the suffix is non-empty, we can't make the stem empty. + raise ValueError(f"{self!r} has a non-empty suffix") + else: + return self.with_name(stem + suffix) + + def with_suffix(self, suffix): + """Return a new path with the file suffix changed. If the path + has no suffix, add given suffix. If the given suffix is an empty + string, remove the suffix from the path. + """ + stem = self.stem + if not stem: + # If the stem is empty, we can't make the suffix non-empty. + raise ValueError(f"{self!r} has an empty name") + elif suffix and not suffix.startswith('.'): + raise ValueError(f"Invalid suffix {suffix!r}") + else: + return self.with_name(stem + suffix) + + @property + def parts(self): + """An object providing sequence-like access to the + components in the filesystem path.""" + anchor, parts = _explode_path(self) + if anchor: + parts.append(anchor) + return tuple(reversed(parts)) + + def joinpath(self, *pathsegments): + """Combine this path with one or several arguments, and return a + new path representing either a subpath (if all arguments are relative + paths) or a totally different path (if one of the arguments is + anchored). + """ + return self.with_segments(str(self), *pathsegments) + + def __truediv__(self, key): + try: + return self.with_segments(str(self), key) + except TypeError: + return NotImplemented + + def __rtruediv__(self, key): + try: + return self.with_segments(key, str(self)) + except TypeError: + return NotImplemented + + @property + def parent(self): + """The logical parent of the path.""" + path = str(self) + parent = self.parser.split(path)[0] + if path != parent: + return self.with_segments(parent) + return self + + @property + def parents(self): + """A sequence of this path's logical parents.""" + split = self.parser.split + path = str(self) + parent = split(path)[0] + parents = [] + while path != parent: + parents.append(self.with_segments(parent)) + path = parent + parent = split(path)[0] + return tuple(parents) + + def full_match(self, pattern, *, case_sensitive=None): + """ + Return True if this path matches the given glob-style pattern. The + pattern is matched against the entire path. + """ + if not hasattr(pattern, 'with_segments'): + pattern = self.with_segments(pattern) + if case_sensitive is None: + case_sensitive = self.parser.normcase('Aa') == 'Aa' + globber = _PathGlobber(pattern.parser.sep, case_sensitive, recursive=True) + match = globber.compile(str(pattern)) + return match(str(self)) is not None + + +class _ReadablePath(_JoinablePath): + """Abstract base class for readable path objects. + + The Path class implements this ABC for local filesystem paths. Users may + create subclasses to implement readable virtual filesystem paths, such as + paths in archive files or on remote storage systems. + """ + __slots__ = () + + @property + @abstractmethod + def info(self): + """ + A PathInfo object that exposes the file type and other file attributes + of this path. + """ + raise NotImplementedError + + @abstractmethod + def __open_rb__(self, buffering=-1): + """ + Open the file pointed to by this path for reading in binary mode and + return a file object, like open(mode='rb'). + """ + raise NotImplementedError + + def read_bytes(self): + """ + Open the file in bytes mode, read it, and close the file. + """ + with magic_open(self, mode='rb', buffering=0) as f: + return f.read() + + def read_text(self, encoding=None, errors=None, newline=None): + """ + Open the file in text mode, read it, and close the file. + """ + with magic_open(self, mode='r', encoding=encoding, errors=errors, newline=newline) as f: + return f.read() + + @abstractmethod + def iterdir(self): + """Yield path objects of the directory contents. + + The children are yielded in arbitrary order, and the + special entries '.' and '..' are not included. + """ + raise NotImplementedError + + def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True): + """Iterate over this subtree and yield all existing files (of any + kind, including directories) matching the given relative pattern. + """ + if not hasattr(pattern, 'with_segments'): + pattern = self.with_segments(pattern) + anchor, parts = _explode_path(pattern) + if anchor: + raise NotImplementedError("Non-relative patterns are unsupported") + case_sensitive_default = self.parser.normcase('Aa') == 'Aa' + if case_sensitive is None: + case_sensitive = case_sensitive_default + case_pedantic = False + else: + case_pedantic = case_sensitive_default != case_sensitive + recursive = True if recurse_symlinks else _no_recurse_symlinks + globber = _PathGlobber(self.parser.sep, case_sensitive, case_pedantic, recursive) + select = globber.selector(parts) + return select(self.joinpath('')) + + def walk(self, top_down=True, on_error=None, follow_symlinks=False): + """Walk the directory tree from this directory, similar to os.walk().""" + paths = [self] + while paths: + path = paths.pop() + if isinstance(path, tuple): + yield path + continue + dirnames = [] + filenames = [] + if not top_down: + paths.append((path, dirnames, filenames)) + try: + for child in path.iterdir(): + if child.info.is_dir(follow_symlinks=follow_symlinks): + if not top_down: + paths.append(child) + dirnames.append(child.name) + else: + filenames.append(child.name) + except OSError as error: + if on_error is not None: + on_error(error) + if not top_down: + while not isinstance(paths.pop(), tuple): + pass + continue + if top_down: + yield path, dirnames, filenames + paths += [path.joinpath(d) for d in reversed(dirnames)] + + @abstractmethod + def readlink(self): + """ + Return the path to which the symbolic link points. + """ + raise NotImplementedError + + def copy(self, target, follow_symlinks=True, preserve_metadata=False): + """ + Recursively copy this file or directory tree to the given destination. + """ + if not hasattr(target, 'with_segments'): + target = self.with_segments(target) + ensure_distinct_paths(self, target) + copy_file(self, target, follow_symlinks, preserve_metadata) + return target.joinpath() # Empty join to ensure fresh metadata. + + def copy_into(self, target_dir, *, follow_symlinks=True, + preserve_metadata=False): + """ + Copy this file or directory tree into the given existing directory. + """ + name = self.name + if not name: + raise ValueError(f"{self!r} has an empty name") + elif hasattr(target_dir, 'with_segments'): + target = target_dir / name + else: + target = self.with_segments(target_dir, name) + return self.copy(target, follow_symlinks=follow_symlinks, + preserve_metadata=preserve_metadata) + + +class _WritablePath(_JoinablePath): + """Abstract base class for writable path objects. + + The Path class implements this ABC for local filesystem paths. Users may + create subclasses to implement writable virtual filesystem paths, such as + paths in archive files or on remote storage systems. + """ + __slots__ = () + + @abstractmethod + def symlink_to(self, target, target_is_directory=False): + """ + Make this path a symlink pointing to the target path. + Note the order of arguments (link, target) is the reverse of os.symlink. + """ + raise NotImplementedError + + @abstractmethod + def mkdir(self): + """ + Create a new directory at this given path. + """ + raise NotImplementedError + + @abstractmethod + def __open_wb__(self, buffering=-1): + """ + Open the file pointed to by this path for writing in binary mode and + return a file object, like open(mode='wb'). + """ + raise NotImplementedError + + def write_bytes(self, data): + """ + Open the file in bytes mode, write to it, and close the file. + """ + # type-check for the buffer interface before truncating the file + view = memoryview(data) + with magic_open(self, mode='wb') as f: + return f.write(view) + + def write_text(self, data, encoding=None, errors=None, newline=None): + """ + Open the file in text mode, write to it, and close the file. + """ + if not isinstance(data, str): + raise TypeError('data must be str, not %s' % + data.__class__.__name__) + with magic_open(self, mode='w', encoding=encoding, errors=errors, newline=newline) as f: + return f.write(data) + + def _write_info(self, info, follow_symlinks=True): + """ + Write the given PathInfo to this path. + """ + pass + + +_JoinablePath.register(PurePath) +_ReadablePath.register(Path) +_WritablePath.register(Path) diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index 4bb62daecb6356..830bfa4ca78138 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -1059,14 +1059,14 @@ def tempdir(self): return d def test_matches_writablepath_docstrings(self): - path_names = {name for name in dir(pathlib._abc.WritablePath) if name[0] != '_'} + path_names = {name for name in dir(pathlib.types._WritablePath) if name[0] != '_'} for attr_name in path_names: if attr_name == 'parser': # On Windows, Path.parser is ntpath, but WritablePath.parser is # posixpath, and so their docstrings differ. continue our_attr = getattr(self.cls, attr_name) - path_attr = getattr(pathlib._abc.WritablePath, attr_name) + path_attr = getattr(pathlib.types._WritablePath, attr_name) self.assertEqual(our_attr.__doc__, path_attr.__doc__) def test_concrete_class(self): diff --git a/Lib/test/test_pathlib/test_pathlib_abc.py b/Lib/test/test_pathlib/test_pathlib_abc.py index 1c1797ff04f653..dea16e6351265f 100644 --- a/Lib/test/test_pathlib/test_pathlib_abc.py +++ b/Lib/test/test_pathlib/test_pathlib_abc.py @@ -4,8 +4,8 @@ import errno import unittest -from pathlib._abc import JoinablePath, ReadablePath, WritablePath, magic_open -from pathlib.types import _PathParser, PathInfo +from pathlib._os import magic_open +from pathlib.types import _PathParser, PathInfo, _JoinablePath, _ReadablePath, _WritablePath import posixpath from test.support.os_helper import TESTFN @@ -31,7 +31,7 @@ def needs_windows(fn): # -class DummyJoinablePath(JoinablePath): +class DummyJoinablePath(_JoinablePath): __slots__ = ('_segments',) parser = posixpath @@ -78,7 +78,7 @@ def setUp(self): def test_is_joinable(self): p = self.cls(self.base) - self.assertIsInstance(p, JoinablePath) + self.assertIsInstance(p, _JoinablePath) def test_parser(self): self.assertIsInstance(self.cls.parser, _PathParser) @@ -855,7 +855,7 @@ def is_symlink(self): return False -class DummyReadablePath(ReadablePath, DummyJoinablePath): +class DummyReadablePath(_ReadablePath, DummyJoinablePath): """ Simple implementation of DummyReadablePath that keeps files and directories in memory. @@ -900,7 +900,7 @@ def readlink(self): raise NotImplementedError -class DummyWritablePath(WritablePath, DummyJoinablePath): +class DummyWritablePath(_WritablePath, DummyJoinablePath): __slots__ = () def __open_wb__(self, buffering=-1): @@ -999,7 +999,7 @@ def assertEqualNormCase(self, path_a, path_b): def test_is_readable(self): p = self.cls(self.base) - self.assertIsInstance(p, ReadablePath) + self.assertIsInstance(p, _ReadablePath) def test_magic_open(self): p = self.cls(self.base) @@ -1130,7 +1130,7 @@ def test_info_exists_caching(self): q = p / 'myfile' self.assertFalse(q.info.exists()) self.assertFalse(q.info.exists(follow_symlinks=False)) - if isinstance(self.cls, WritablePath): + if isinstance(self.cls, _WritablePath): q.write_text('hullo') self.assertFalse(q.info.exists()) self.assertFalse(q.info.exists(follow_symlinks=False)) @@ -1162,7 +1162,7 @@ def test_info_is_dir_caching(self): q = p / 'mydir' self.assertFalse(q.info.is_dir()) self.assertFalse(q.info.is_dir(follow_symlinks=False)) - if isinstance(self.cls, WritablePath): + if isinstance(self.cls, _WritablePath): q.mkdir() self.assertFalse(q.info.is_dir()) self.assertFalse(q.info.is_dir(follow_symlinks=False)) @@ -1194,7 +1194,7 @@ def test_info_is_file_caching(self): q = p / 'myfile' self.assertFalse(q.info.is_file()) self.assertFalse(q.info.is_file(follow_symlinks=False)) - if isinstance(self.cls, WritablePath): + if isinstance(self.cls, _WritablePath): q.write_text('hullo') self.assertFalse(q.info.is_file()) self.assertFalse(q.info.is_file(follow_symlinks=False)) @@ -1220,7 +1220,7 @@ class WritablePathTest(JoinablePathTest): def test_is_writable(self): p = self.cls(self.base) - self.assertIsInstance(p, WritablePath) + self.assertIsInstance(p, _WritablePath) class DummyRWPath(DummyWritablePath, DummyReadablePath): _______________________________________________ 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