summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/pathlib/__init__.py507
-rw-r--r--Lib/pathlib/_abc.py (renamed from Lib/pathlib.py)514
-rw-r--r--Lib/test/test_pathlib.py8
3 files changed, 518 insertions, 511 deletions
diff --git a/Lib/pathlib/__init__.py b/Lib/pathlib/__init__.py
new file mode 100644
index 0000000..f4668ab
--- /dev/null
+++ b/Lib/pathlib/__init__.py
@@ -0,0 +1,507 @@
+"""Object-oriented filesystem paths.
+
+This module provides classes to represent abstract paths and concrete
+paths with operations that have semantics appropriate for different
+operating systems.
+"""
+
+import io
+import ntpath
+import os
+import posixpath
+
+try:
+ import pwd
+except ImportError:
+ pwd = None
+try:
+ import grp
+except ImportError:
+ grp = None
+
+from . import _abc
+
+
+__all__ = [
+ "UnsupportedOperation",
+ "PurePath", "PurePosixPath", "PureWindowsPath",
+ "Path", "PosixPath", "WindowsPath",
+ ]
+
+
+UnsupportedOperation = _abc.UnsupportedOperation
+
+
+class PurePath(_abc.PurePathBase):
+ """Base class for manipulating paths without I/O.
+
+ PurePath represents a filesystem path and offers operations which
+ don't imply any actual filesystem I/O. Depending on your system,
+ instantiating a PurePath will return either a PurePosixPath or a
+ PureWindowsPath object. You can also instantiate either of these classes
+ directly, regardless of your system.
+ """
+
+ __slots__ = (
+ # The `_str_normcase_cached` slot stores the string path with
+ # normalized case. It is set when the `_str_normcase` property is
+ # accessed for the first time. It's used to implement `__eq__()`
+ # `__hash__()`, and `_parts_normcase`
+ '_str_normcase_cached',
+
+ # The `_parts_normcase_cached` slot stores the case-normalized
+ # string path after splitting on path separators. It's set when the
+ # `_parts_normcase` property is accessed for the first time. It's used
+ # to implement comparison methods like `__lt__()`.
+ '_parts_normcase_cached',
+
+ # The `_hash` slot stores the hash of the case-normalized string
+ # path. It's set when `__hash__()` is called for the first time.
+ '_hash',
+ )
+
+ def __new__(cls, *args, **kwargs):
+ """Construct a PurePath from one or several strings and or existing
+ PurePath objects. The strings and path objects are combined so as
+ to yield a canonicalized path, which is incorporated into the
+ new PurePath object.
+ """
+ if cls is PurePath:
+ cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
+ return object.__new__(cls)
+
+ def __init__(self, *args):
+ paths = []
+ for arg in args:
+ if isinstance(arg, PurePath):
+ if arg.pathmod is ntpath and self.pathmod is posixpath:
+ # GH-103631: Convert separators for backwards compatibility.
+ paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
+ else:
+ paths.extend(arg._raw_paths)
+ else:
+ try:
+ path = os.fspath(arg)
+ except TypeError:
+ path = arg
+ if not isinstance(path, str):
+ raise TypeError(
+ "argument should be a str or an os.PathLike "
+ "object where __fspath__ returns a str, "
+ f"not {type(path).__name__!r}")
+ paths.append(path)
+ super().__init__(*paths)
+
+ def __reduce__(self):
+ # Using the parts tuple helps share interned path parts
+ # when pickling related paths.
+ return (self.__class__, self.parts)
+
+ def __fspath__(self):
+ return str(self)
+
+ def __bytes__(self):
+ """Return the bytes representation of the path. This is only
+ recommended to use under Unix."""
+ return os.fsencode(self)
+
+ @property
+ def _str_normcase(self):
+ # String with normalized case, for hashing and equality checks
+ try:
+ return self._str_normcase_cached
+ except AttributeError:
+ if _abc._is_case_sensitive(self.pathmod):
+ self._str_normcase_cached = str(self)
+ else:
+ self._str_normcase_cached = str(self).lower()
+ return self._str_normcase_cached
+
+ def __hash__(self):
+ try:
+ return self._hash
+ except AttributeError:
+ self._hash = hash(self._str_normcase)
+ return self._hash
+
+ def __eq__(self, other):
+ if not isinstance(other, PurePath):
+ return NotImplemented
+ return self._str_normcase == other._str_normcase and self.pathmod is other.pathmod
+
+ @property
+ def _parts_normcase(self):
+ # Cached parts with normalized case, for comparisons.
+ try:
+ return self._parts_normcase_cached
+ except AttributeError:
+ self._parts_normcase_cached = self._str_normcase.split(self.pathmod.sep)
+ return self._parts_normcase_cached
+
+ def __lt__(self, other):
+ if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
+ return NotImplemented
+ return self._parts_normcase < other._parts_normcase
+
+ def __le__(self, other):
+ if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
+ return NotImplemented
+ return self._parts_normcase <= other._parts_normcase
+
+ def __gt__(self, other):
+ if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
+ return NotImplemented
+ return self._parts_normcase > other._parts_normcase
+
+ def __ge__(self, other):
+ if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
+ return NotImplemented
+ return self._parts_normcase >= other._parts_normcase
+
+ def as_uri(self):
+ """Return the path as a URI."""
+ if not self.is_absolute():
+ raise ValueError("relative path can't be expressed as a file URI")
+
+ drive = self.drive
+ if len(drive) == 2 and drive[1] == ':':
+ # It's a path on a local drive => 'file:///c:/a/b'
+ prefix = 'file:///' + drive
+ path = self.as_posix()[2:]
+ elif drive:
+ # It's a path on a network drive => 'file://host/share/a/b'
+ prefix = 'file:'
+ path = self.as_posix()
+ else:
+ # It's a posix path => 'file:///etc/hosts'
+ prefix = 'file://'
+ path = str(self)
+ from urllib.parse import quote_from_bytes
+ return prefix + quote_from_bytes(os.fsencode(path))
+
+
+# Subclassing os.PathLike makes isinstance() checks slower,
+# which in turn makes Path construction slower. Register instead!
+os.PathLike.register(PurePath)
+
+
+class PurePosixPath(PurePath):
+ """PurePath subclass for non-Windows systems.
+
+ On a POSIX system, instantiating a PurePath should return this object.
+ However, you can also instantiate it directly on any system.
+ """
+ pathmod = posixpath
+ __slots__ = ()
+
+
+class PureWindowsPath(PurePath):
+ """PurePath subclass for Windows systems.
+
+ On a Windows system, instantiating a PurePath should return this object.
+ However, you can also instantiate it directly on any system.
+ """
+ pathmod = ntpath
+ __slots__ = ()
+
+
+class Path(_abc.PathBase, PurePath):
+ """PurePath subclass that can make system calls.
+
+ Path represents a filesystem path but unlike PurePath, also offers
+ methods to do system calls on path objects. Depending on your system,
+ instantiating a Path will return either a PosixPath or a WindowsPath
+ object. You can also instantiate a PosixPath or WindowsPath directly,
+ but cannot instantiate a WindowsPath on a POSIX system or vice versa.
+ """
+ __slots__ = ()
+ as_uri = PurePath.as_uri
+
+ @classmethod
+ def _unsupported(cls, method_name):
+ msg = f"{cls.__name__}.{method_name}() is unsupported on this system"
+ raise UnsupportedOperation(msg)
+
+ def __init__(self, *args, **kwargs):
+ if kwargs:
+ import warnings
+ msg = ("support for supplying keyword arguments to pathlib.PurePath "
+ "is deprecated and scheduled for removal in Python {remove}")
+ warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14))
+ super().__init__(*args)
+
+ def __new__(cls, *args, **kwargs):
+ if cls is Path:
+ cls = WindowsPath if os.name == 'nt' else PosixPath
+ return object.__new__(cls)
+
+ def stat(self, *, follow_symlinks=True):
+ """
+ Return the result of the stat() system call on this path, like
+ os.stat() does.
+ """
+ return os.stat(self, follow_symlinks=follow_symlinks)
+
+ def is_mount(self):
+ """
+ Check if this path is a mount point
+ """
+ return os.path.ismount(self)
+
+ def is_junction(self):
+ """
+ Whether this path is a junction.
+ """
+ return os.path.isjunction(self)
+
+ def open(self, mode='r', buffering=-1, encoding=None,
+ errors=None, newline=None):
+ """
+ Open the file pointed by this path and return a file object, as
+ the built-in open() function does.
+ """
+ if "b" not in mode:
+ encoding = io.text_encoding(encoding)
+ return io.open(self, mode, buffering, encoding, errors, newline)
+
+ 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.
+ """
+ return (self._make_child_relpath(name) for name in os.listdir(self))
+
+ def _scandir(self):
+ return os.scandir(self)
+
+ def absolute(self):
+ """Return an absolute version of this path
+ No normalization or symlink resolution is performed.
+
+ Use resolve() to resolve symlinks and remove '..' segments.
+ """
+ if self.is_absolute():
+ return self
+ if self.root:
+ drive = os.path.splitroot(os.getcwd())[0]
+ return self._from_parsed_parts(drive, self.root, self._tail)
+ if self.drive:
+ # There is a CWD on each drive-letter drive.
+ cwd = os.path.abspath(self.drive)
+ else:
+ cwd = os.getcwd()
+ if not self._tail:
+ # Fast path for "empty" paths, e.g. Path("."), Path("") or Path().
+ # We pass only one argument to with_segments() to avoid the cost
+ # of joining, and we exploit the fact that getcwd() returns a
+ # fully-normalized string by storing it in _str. This is used to
+ # implement Path.cwd().
+ result = self.with_segments(cwd)
+ result._str = cwd
+ return result
+ drive, root, rel = os.path.splitroot(cwd)
+ if not rel:
+ return self._from_parsed_parts(drive, root, self._tail)
+ tail = rel.split(self.pathmod.sep)
+ tail.extend(self._tail)
+ return self._from_parsed_parts(drive, root, tail)
+
+ def resolve(self, strict=False):
+ """
+ Make the path absolute, resolving all symlinks on the way and also
+ normalizing it.
+ """
+
+ return self.with_segments(os.path.realpath(self, strict=strict))
+
+ if pwd:
+ def owner(self, *, follow_symlinks=True):
+ """
+ Return the login name of the file owner.
+ """
+ uid = self.stat(follow_symlinks=follow_symlinks).st_uid
+ return pwd.getpwuid(uid).pw_name
+
+ if grp:
+ def group(self, *, follow_symlinks=True):
+ """
+ Return the group name of the file gid.
+ """
+ gid = self.stat(follow_symlinks=follow_symlinks).st_gid
+ return grp.getgrgid(gid).gr_name
+
+ if hasattr(os, "readlink"):
+ def readlink(self):
+ """
+ Return the path to which the symbolic link points.
+ """
+ return self.with_segments(os.readlink(self))
+
+ def touch(self, mode=0o666, exist_ok=True):
+ """
+ Create this file with the given access mode, if it doesn't exist.
+ """
+
+ if exist_ok:
+ # First try to bump modification time
+ # Implementation note: GNU touch uses the UTIME_NOW option of
+ # the utimensat() / futimens() functions.
+ try:
+ os.utime(self, None)
+ except OSError:
+ # Avoid exception chaining
+ pass
+ else:
+ return
+ flags = os.O_CREAT | os.O_WRONLY
+ if not exist_ok:
+ flags |= os.O_EXCL
+ fd = os.open(self, flags, mode)
+ os.close(fd)
+
+ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
+ """
+ Create a new directory at this given path.
+ """
+ try:
+ os.mkdir(self, mode)
+ except FileNotFoundError:
+ if not parents or self.parent == self:
+ raise
+ self.parent.mkdir(parents=True, exist_ok=True)
+ self.mkdir(mode, parents=False, exist_ok=exist_ok)
+ except OSError:
+ # Cannot rely on checking for EEXIST, since the operating system
+ # could give priority to other errors like EACCES or EROFS
+ if not exist_ok or not self.is_dir():
+ raise
+
+ def chmod(self, mode, *, follow_symlinks=True):
+ """
+ Change the permissions of the path, like os.chmod().
+ """
+ os.chmod(self, mode, follow_symlinks=follow_symlinks)
+
+ def unlink(self, missing_ok=False):
+ """
+ Remove this file or link.
+ If the path is a directory, use rmdir() instead.
+ """
+ try:
+ os.unlink(self)
+ except FileNotFoundError:
+ if not missing_ok:
+ raise
+
+ def rmdir(self):
+ """
+ Remove this directory. The directory must be empty.
+ """
+ os.rmdir(self)
+
+ def rename(self, target):
+ """
+ Rename this path to the target path.
+
+ The target path may be absolute or relative. Relative paths are
+ interpreted relative to the current working directory, *not* the
+ directory of the Path object.
+
+ Returns the new Path instance pointing to the target path.
+ """
+ os.rename(self, target)
+ return self.with_segments(target)
+
+ def replace(self, target):
+ """
+ Rename this path to the target path, overwriting if that path exists.
+
+ The target path may be absolute or relative. Relative paths are
+ interpreted relative to the current working directory, *not* the
+ directory of the Path object.
+
+ Returns the new Path instance pointing to the target path.
+ """
+ os.replace(self, target)
+ return self.with_segments(target)
+
+ if hasattr(os, "symlink"):
+ 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.
+ """
+ os.symlink(target, self, target_is_directory)
+
+ if hasattr(os, "link"):
+ def hardlink_to(self, target):
+ """
+ Make this path a hard link pointing to the same file as *target*.
+
+ Note the order of arguments (self, target) is the reverse of os.link's.
+ """
+ os.link(target, self)
+
+ def expanduser(self):
+ """ Return a new path with expanded ~ and ~user constructs
+ (as returned by os.path.expanduser)
+ """
+ if (not (self.drive or self.root) and
+ self._tail and self._tail[0][:1] == '~'):
+ homedir = os.path.expanduser(self._tail[0])
+ if homedir[:1] == "~":
+ raise RuntimeError("Could not determine home directory.")
+ drv, root, tail = self._parse_path(homedir)
+ return self._from_parsed_parts(drv, root, tail + self._tail[1:])
+
+ return self
+
+ @classmethod
+ def from_uri(cls, uri):
+ """Return a new path from the given 'file' URI."""
+ if not uri.startswith('file:'):
+ raise ValueError(f"URI does not start with 'file:': {uri!r}")
+ path = uri[5:]
+ if path[:3] == '///':
+ # Remove empty authority
+ path = path[2:]
+ elif path[:12] == '//localhost/':
+ # Remove 'localhost' authority
+ path = path[11:]
+ if path[:3] == '///' or (path[:1] == '/' and path[2:3] in ':|'):
+ # Remove slash before DOS device/UNC path
+ path = path[1:]
+ if path[1:2] == '|':
+ # Replace bar with colon in DOS drive
+ path = path[:1] + ':' + path[2:]
+ from urllib.parse import unquote_to_bytes
+ path = cls(os.fsdecode(unquote_to_bytes(path)))
+ if not path.is_absolute():
+ raise ValueError(f"URI is not absolute: {uri!r}")
+ return path
+
+
+class PosixPath(Path, PurePosixPath):
+ """Path subclass for non-Windows systems.
+
+ On a POSIX system, instantiating a Path should return this object.
+ """
+ __slots__ = ()
+
+ if os.name == 'nt':
+ def __new__(cls, *args, **kwargs):
+ raise UnsupportedOperation(
+ f"cannot instantiate {cls.__name__!r} on your system")
+
+class WindowsPath(Path, PureWindowsPath):
+ """Path subclass for Windows systems.
+
+ On a Windows system, instantiating a Path should return this object.
+ """
+ __slots__ = ()
+
+ if os.name != 'nt':
+ def __new__(cls, *args, **kwargs):
+ raise UnsupportedOperation(
+ f"cannot instantiate {cls.__name__!r} on your system")
diff --git a/Lib/pathlib.py b/Lib/pathlib/_abc.py
index 87d1f6b..4808d0e 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib/_abc.py
@@ -1,10 +1,3 @@
-"""Object-oriented filesystem paths.
-
-This module provides classes to represent abstract paths and concrete
-paths with operations that have semantics appropriate for different
-operating systems.
-"""
-
import functools
import io
import ntpath
@@ -17,27 +10,11 @@ from errno import ENOENT, ENOTDIR, EBADF, ELOOP, EINVAL
from itertools import chain
from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
-try:
- import pwd
-except ImportError:
- pwd = None
-try:
- import grp
-except ImportError:
- grp = None
-
-
-__all__ = [
- "UnsupportedOperation",
- "PurePath", "PurePosixPath", "PureWindowsPath",
- "Path", "PosixPath", "WindowsPath",
- ]
-
#
# Internals
#
-# Maximum number of symlinks to follow in _PathBase.resolve()
+# Maximum number of symlinks to follow in PathBase.resolve()
_MAX_SYMLINKS = 40
# Reference for Windows paths can be found at
@@ -158,10 +135,6 @@ def _select_unique(paths):
yielded.clear()
-#
-# Public API
-#
-
class UnsupportedOperation(NotImplementedError):
"""An exception that is raised when an unsupported operation is called on
a path object.
@@ -198,7 +171,7 @@ class _PathParents(Sequence):
return "<{}.parents>".format(type(self._path).__name__)
-class _PurePathBase:
+class PurePathBase:
"""Base class for pure path objects.
This class *does not* provide several magic methods that are defined in
@@ -227,7 +200,7 @@ class _PurePathBase:
'_str',
# The '_resolving' slot stores a boolean indicating whether the path
- # is being processed by `_PathBase.resolve()`. This prevents duplicate
+ # is being processed by `PathBase.resolve()`. This prevents duplicate
# work from occurring when `resolve()` calls `stat()` or `readlink()`.
'_resolving',
)
@@ -431,7 +404,7 @@ class _PurePathBase:
warnings._deprecated("pathlib.PurePath.relative_to(*args)", msg,
remove=(3, 14))
other = self.with_segments(other, *_deprecated)
- elif not isinstance(other, _PurePathBase):
+ elif not isinstance(other, PurePathBase):
other = self.with_segments(other)
for step, path in enumerate(chain([other], other.parents)):
if path == self or path in self.parents:
@@ -455,7 +428,7 @@ class _PurePathBase:
warnings._deprecated("pathlib.PurePath.is_relative_to(*args)",
msg, remove=(3, 14))
other = self.with_segments(other, *_deprecated)
- elif not isinstance(other, _PurePathBase):
+ elif not isinstance(other, PurePathBase):
other = self.with_segments(other)
return other == self or other in self.parents
@@ -542,7 +515,7 @@ class _PurePathBase:
"""
Return True if this path matches the given pattern.
"""
- if not isinstance(path_pattern, _PurePathBase):
+ if not isinstance(path_pattern, PurePathBase):
path_pattern = self.with_segments(path_pattern)
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self.pathmod)
@@ -558,183 +531,8 @@ class _PurePathBase:
return match(str(self)) is not None
-class PurePath(_PurePathBase):
- """Base class for manipulating paths without I/O.
-
- PurePath represents a filesystem path and offers operations which
- don't imply any actual filesystem I/O. Depending on your system,
- instantiating a PurePath will return either a PurePosixPath or a
- PureWindowsPath object. You can also instantiate either of these classes
- directly, regardless of your system.
- """
-
- __slots__ = (
- # The `_str_normcase_cached` slot stores the string path with
- # normalized case. It is set when the `_str_normcase` property is
- # accessed for the first time. It's used to implement `__eq__()`
- # `__hash__()`, and `_parts_normcase`
- '_str_normcase_cached',
-
- # The `_parts_normcase_cached` slot stores the case-normalized
- # string path after splitting on path separators. It's set when the
- # `_parts_normcase` property is accessed for the first time. It's used
- # to implement comparison methods like `__lt__()`.
- '_parts_normcase_cached',
-
- # The `_hash` slot stores the hash of the case-normalized string
- # path. It's set when `__hash__()` is called for the first time.
- '_hash',
- )
-
- def __new__(cls, *args, **kwargs):
- """Construct a PurePath from one or several strings and or existing
- PurePath objects. The strings and path objects are combined so as
- to yield a canonicalized path, which is incorporated into the
- new PurePath object.
- """
- if cls is PurePath:
- cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
- return object.__new__(cls)
-
- def __init__(self, *args):
- paths = []
- for arg in args:
- if isinstance(arg, PurePath):
- if arg.pathmod is ntpath and self.pathmod is posixpath:
- # GH-103631: Convert separators for backwards compatibility.
- paths.extend(path.replace('\\', '/') for path in arg._raw_paths)
- else:
- paths.extend(arg._raw_paths)
- else:
- try:
- path = os.fspath(arg)
- except TypeError:
- path = arg
- if not isinstance(path, str):
- raise TypeError(
- "argument should be a str or an os.PathLike "
- "object where __fspath__ returns a str, "
- f"not {type(path).__name__!r}")
- paths.append(path)
- super().__init__(*paths)
-
- def __reduce__(self):
- # Using the parts tuple helps share interned path parts
- # when pickling related paths.
- return (self.__class__, self.parts)
-
- def __fspath__(self):
- return str(self)
-
- def __bytes__(self):
- """Return the bytes representation of the path. This is only
- recommended to use under Unix."""
- return os.fsencode(self)
-
- @property
- def _str_normcase(self):
- # String with normalized case, for hashing and equality checks
- try:
- return self._str_normcase_cached
- except AttributeError:
- if _is_case_sensitive(self.pathmod):
- self._str_normcase_cached = str(self)
- else:
- self._str_normcase_cached = str(self).lower()
- return self._str_normcase_cached
-
- def __hash__(self):
- try:
- return self._hash
- except AttributeError:
- self._hash = hash(self._str_normcase)
- return self._hash
-
- def __eq__(self, other):
- if not isinstance(other, PurePath):
- return NotImplemented
- return self._str_normcase == other._str_normcase and self.pathmod is other.pathmod
-
- @property
- def _parts_normcase(self):
- # Cached parts with normalized case, for comparisons.
- try:
- return self._parts_normcase_cached
- except AttributeError:
- self._parts_normcase_cached = self._str_normcase.split(self.pathmod.sep)
- return self._parts_normcase_cached
-
- def __lt__(self, other):
- if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
- return NotImplemented
- return self._parts_normcase < other._parts_normcase
-
- def __le__(self, other):
- if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
- return NotImplemented
- return self._parts_normcase <= other._parts_normcase
-
- def __gt__(self, other):
- if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
- return NotImplemented
- return self._parts_normcase > other._parts_normcase
-
- def __ge__(self, other):
- if not isinstance(other, PurePath) or self.pathmod is not other.pathmod:
- return NotImplemented
- return self._parts_normcase >= other._parts_normcase
-
- def as_uri(self):
- """Return the path as a URI."""
- if not self.is_absolute():
- raise ValueError("relative path can't be expressed as a file URI")
-
- drive = self.drive
- if len(drive) == 2 and drive[1] == ':':
- # It's a path on a local drive => 'file:///c:/a/b'
- prefix = 'file:///' + drive
- path = self.as_posix()[2:]
- elif drive:
- # It's a path on a network drive => 'file://host/share/a/b'
- prefix = 'file:'
- path = self.as_posix()
- else:
- # It's a posix path => 'file:///etc/hosts'
- prefix = 'file://'
- path = str(self)
- from urllib.parse import quote_from_bytes
- return prefix + quote_from_bytes(os.fsencode(path))
-
-
-# Subclassing os.PathLike makes isinstance() checks slower,
-# which in turn makes Path construction slower. Register instead!
-os.PathLike.register(PurePath)
-
-
-class PurePosixPath(PurePath):
- """PurePath subclass for non-Windows systems.
-
- On a POSIX system, instantiating a PurePath should return this object.
- However, you can also instantiate it directly on any system.
- """
- pathmod = posixpath
- __slots__ = ()
-
-
-class PureWindowsPath(PurePath):
- """PurePath subclass for Windows systems.
-
- On a Windows system, instantiating a PurePath should return this object.
- However, you can also instantiate it directly on any system.
- """
- pathmod = ntpath
- __slots__ = ()
-
-
-# Filesystem-accessing classes
-
-class _PathBase(_PurePathBase):
+class PathBase(PurePathBase):
"""Base class for concrete path objects.
This class provides dummy implementations for many methods that derived
@@ -752,8 +550,6 @@ class _PathBase(_PurePathBase):
@classmethod
def _unsupported(cls, method_name):
msg = f"{cls.__name__}.{method_name}() is unsupported"
- if issubclass(cls, Path):
- msg += " on this system"
raise UnsupportedOperation(msg)
def stat(self, *, follow_symlinks=True):
@@ -1352,299 +1148,3 @@ class _PathBase(_PurePathBase):
def as_uri(self):
"""Return the path as a URI."""
self._unsupported("as_uri")
-
-
-class Path(_PathBase, PurePath):
- """PurePath subclass that can make system calls.
-
- Path represents a filesystem path but unlike PurePath, also offers
- methods to do system calls on path objects. Depending on your system,
- instantiating a Path will return either a PosixPath or a WindowsPath
- object. You can also instantiate a PosixPath or WindowsPath directly,
- but cannot instantiate a WindowsPath on a POSIX system or vice versa.
- """
- __slots__ = ()
- as_uri = PurePath.as_uri
-
- def __init__(self, *args, **kwargs):
- if kwargs:
- msg = ("support for supplying keyword arguments to pathlib.PurePath "
- "is deprecated and scheduled for removal in Python {remove}")
- warnings._deprecated("pathlib.PurePath(**kwargs)", msg, remove=(3, 14))
- super().__init__(*args)
-
- def __new__(cls, *args, **kwargs):
- if cls is Path:
- cls = WindowsPath if os.name == 'nt' else PosixPath
- return object.__new__(cls)
-
- def stat(self, *, follow_symlinks=True):
- """
- Return the result of the stat() system call on this path, like
- os.stat() does.
- """
- return os.stat(self, follow_symlinks=follow_symlinks)
-
- def is_mount(self):
- """
- Check if this path is a mount point
- """
- return os.path.ismount(self)
-
- def is_junction(self):
- """
- Whether this path is a junction.
- """
- return os.path.isjunction(self)
-
- def open(self, mode='r', buffering=-1, encoding=None,
- errors=None, newline=None):
- """
- Open the file pointed by this path and return a file object, as
- the built-in open() function does.
- """
- if "b" not in mode:
- encoding = io.text_encoding(encoding)
- return io.open(self, mode, buffering, encoding, errors, newline)
-
- 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.
- """
- return (self._make_child_relpath(name) for name in os.listdir(self))
-
- def _scandir(self):
- return os.scandir(self)
-
- def absolute(self):
- """Return an absolute version of this path
- No normalization or symlink resolution is performed.
-
- Use resolve() to resolve symlinks and remove '..' segments.
- """
- if self.is_absolute():
- return self
- if self.root:
- drive = os.path.splitroot(os.getcwd())[0]
- return self._from_parsed_parts(drive, self.root, self._tail)
- if self.drive:
- # There is a CWD on each drive-letter drive.
- cwd = os.path.abspath(self.drive)
- else:
- cwd = os.getcwd()
- if not self._tail:
- # Fast path for "empty" paths, e.g. Path("."), Path("") or Path().
- # We pass only one argument to with_segments() to avoid the cost
- # of joining, and we exploit the fact that getcwd() returns a
- # fully-normalized string by storing it in _str. This is used to
- # implement Path.cwd().
- result = self.with_segments(cwd)
- result._str = cwd
- return result
- drive, root, rel = os.path.splitroot(cwd)
- if not rel:
- return self._from_parsed_parts(drive, root, self._tail)
- tail = rel.split(self.pathmod.sep)
- tail.extend(self._tail)
- return self._from_parsed_parts(drive, root, tail)
-
- def resolve(self, strict=False):
- """
- Make the path absolute, resolving all symlinks on the way and also
- normalizing it.
- """
-
- return self.with_segments(os.path.realpath(self, strict=strict))
-
- if pwd:
- def owner(self, *, follow_symlinks=True):
- """
- Return the login name of the file owner.
- """
- uid = self.stat(follow_symlinks=follow_symlinks).st_uid
- return pwd.getpwuid(uid).pw_name
-
- if grp:
- def group(self, *, follow_symlinks=True):
- """
- Return the group name of the file gid.
- """
- gid = self.stat(follow_symlinks=follow_symlinks).st_gid
- return grp.getgrgid(gid).gr_name
-
- if hasattr(os, "readlink"):
- def readlink(self):
- """
- Return the path to which the symbolic link points.
- """
- return self.with_segments(os.readlink(self))
-
- def touch(self, mode=0o666, exist_ok=True):
- """
- Create this file with the given access mode, if it doesn't exist.
- """
-
- if exist_ok:
- # First try to bump modification time
- # Implementation note: GNU touch uses the UTIME_NOW option of
- # the utimensat() / futimens() functions.
- try:
- os.utime(self, None)
- except OSError:
- # Avoid exception chaining
- pass
- else:
- return
- flags = os.O_CREAT | os.O_WRONLY
- if not exist_ok:
- flags |= os.O_EXCL
- fd = os.open(self, flags, mode)
- os.close(fd)
-
- def mkdir(self, mode=0o777, parents=False, exist_ok=False):
- """
- Create a new directory at this given path.
- """
- try:
- os.mkdir(self, mode)
- except FileNotFoundError:
- if not parents or self.parent == self:
- raise
- self.parent.mkdir(parents=True, exist_ok=True)
- self.mkdir(mode, parents=False, exist_ok=exist_ok)
- except OSError:
- # Cannot rely on checking for EEXIST, since the operating system
- # could give priority to other errors like EACCES or EROFS
- if not exist_ok or not self.is_dir():
- raise
-
- def chmod(self, mode, *, follow_symlinks=True):
- """
- Change the permissions of the path, like os.chmod().
- """
- os.chmod(self, mode, follow_symlinks=follow_symlinks)
-
- def unlink(self, missing_ok=False):
- """
- Remove this file or link.
- If the path is a directory, use rmdir() instead.
- """
- try:
- os.unlink(self)
- except FileNotFoundError:
- if not missing_ok:
- raise
-
- def rmdir(self):
- """
- Remove this directory. The directory must be empty.
- """
- os.rmdir(self)
-
- def rename(self, target):
- """
- Rename this path to the target path.
-
- The target path may be absolute or relative. Relative paths are
- interpreted relative to the current working directory, *not* the
- directory of the Path object.
-
- Returns the new Path instance pointing to the target path.
- """
- os.rename(self, target)
- return self.with_segments(target)
-
- def replace(self, target):
- """
- Rename this path to the target path, overwriting if that path exists.
-
- The target path may be absolute or relative. Relative paths are
- interpreted relative to the current working directory, *not* the
- directory of the Path object.
-
- Returns the new Path instance pointing to the target path.
- """
- os.replace(self, target)
- return self.with_segments(target)
-
- if hasattr(os, "symlink"):
- 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.
- """
- os.symlink(target, self, target_is_directory)
-
- if hasattr(os, "link"):
- def hardlink_to(self, target):
- """
- Make this path a hard link pointing to the same file as *target*.
-
- Note the order of arguments (self, target) is the reverse of os.link's.
- """
- os.link(target, self)
-
- def expanduser(self):
- """ Return a new path with expanded ~ and ~user constructs
- (as returned by os.path.expanduser)
- """
- if (not (self.drive or self.root) and
- self._tail and self._tail[0][:1] == '~'):
- homedir = os.path.expanduser(self._tail[0])
- if homedir[:1] == "~":
- raise RuntimeError("Could not determine home directory.")
- drv, root, tail = self._parse_path(homedir)
- return self._from_parsed_parts(drv, root, tail + self._tail[1:])
-
- return self
-
- @classmethod
- def from_uri(cls, uri):
- """Return a new path from the given 'file' URI."""
- if not uri.startswith('file:'):
- raise ValueError(f"URI does not start with 'file:': {uri!r}")
- path = uri[5:]
- if path[:3] == '///':
- # Remove empty authority
- path = path[2:]
- elif path[:12] == '//localhost/':
- # Remove 'localhost' authority
- path = path[11:]
- if path[:3] == '///' or (path[:1] == '/' and path[2:3] in ':|'):
- # Remove slash before DOS device/UNC path
- path = path[1:]
- if path[1:2] == '|':
- # Replace bar with colon in DOS drive
- path = path[:1] + ':' + path[2:]
- from urllib.parse import unquote_to_bytes
- path = cls(os.fsdecode(unquote_to_bytes(path)))
- if not path.is_absolute():
- raise ValueError(f"URI is not absolute: {uri!r}")
- return path
-
-
-class PosixPath(Path, PurePosixPath):
- """Path subclass for non-Windows systems.
-
- On a POSIX system, instantiating a Path should return this object.
- """
- __slots__ = ()
-
- if os.name == 'nt':
- def __new__(cls, *args, **kwargs):
- raise UnsupportedOperation(
- f"cannot instantiate {cls.__name__!r} on your system")
-
-class WindowsPath(Path, PureWindowsPath):
- """Path subclass for Windows systems.
-
- On a Windows system, instantiating a Path should return this object.
- """
- __slots__ = ()
-
- if os.name != 'nt':
- def __new__(cls, *args, **kwargs):
- raise UnsupportedOperation(
- f"cannot instantiate {cls.__name__!r} on your system")
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index d35516a..a1acba4 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -51,7 +51,7 @@ if hasattr(os, 'geteuid'):
class PurePathBaseTest(unittest.TestCase):
- cls = pathlib._PurePathBase
+ cls = pathlib._abc.PurePathBase
def test_magic_methods(self):
P = self.cls
@@ -66,7 +66,7 @@ class PurePathBaseTest(unittest.TestCase):
self.assertIs(P.__ge__, object.__ge__)
-class DummyPurePath(pathlib._PurePathBase):
+class DummyPurePath(pathlib._abc.PurePathBase):
def __eq__(self, other):
if not isinstance(other, DummyPurePath):
return NotImplemented
@@ -1586,7 +1586,7 @@ class PurePathSubclassTest(PurePathTest):
#
class PathBaseTest(PurePathBaseTest):
- cls = pathlib._PathBase
+ cls = pathlib._abc.PathBase
def test_unsupported_operation(self):
P = self.cls
@@ -1667,7 +1667,7 @@ class DummyPathIO(io.BytesIO):
super().close()
-class DummyPath(pathlib._PathBase):
+class DummyPath(pathlib._abc.PathBase):
"""
Simple implementation of PathBase that keeps files and directories in
memory.