aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/pathlib/_abc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/pathlib/_abc.py')
-rw-r--r--Lib/pathlib/_abc.py397
1 files changed, 0 insertions, 397 deletions
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
deleted file mode 100644
index d9fb018d75f..00000000000
--- 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)