From a3b2cad45e614baee60aa21ac3f7494a2f6e9a83 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 4 Oct 2023 00:26:28 +0100 Subject: [PATCH 1/3] GH-110109: Make pathlib extensible Add `PurePathBase` and `PathBase` abstract base classes. `PathBase` can be subclassed to implement *virtual paths*: objects representing paths on virtual filesystems, like archive files or remote storage systems. --- Doc/library/pathlib.rst | 102 ++++++++++++++++++ Doc/whatsnew/3.13.rst | 6 ++ ...-10-04-00-20-00.gh-issue-110109.RAaIgG.rst | 4 + 3 files changed, 112 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-10-04-00-20-00.gh-issue-110109.RAaIgG.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 8ee89a003a339a..418226be4fcb6d 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1591,6 +1591,108 @@ call fails (for example because the path doesn't exist). .. versionchanged:: 3.10 The *newline* parameter was added. + +Virtual paths +------------- + +.. class:: PurePathBase(*path_segments) + + Abstract base class for path objects without I/O support. + + The :class:`PurePath` class is a subclass of this class. + + Unlike :class:`PurePath`, this class does not define any of the following + magic methods: + :meth:`~os.PathLike.__fspath__`, :meth:`__bytes__`, + :meth:`__reduce__`, :meth:`__hash__`, :meth:`__eq__`, + :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, :meth:`__ge__` + + .. versionadded:: 3.13 + + +.. class:: PathBase(*path_segments) + + Abstract base class for path objects with I/O support. + + This class provides abstract implementations for methods that derived + classes can override selectively; the default implementations raise + :exc:`UnsupportedOperation`. + + The :class:`Path` class is a subclass of this class that operates on the + local filesystem. User-defined subclasses may operate on other kinds of + filesystem, such as archive files or remote storage; instances of these + classes are *virtual paths*. + + Like its superclass :class:`PurePathBase` (and unlike :class:`PurePath` and + :class:`Path`), this class does not define any of the following magic methods: + :meth:`~os.PathLike.__fspath__`, :meth:`__bytes__`, + :meth:`__reduce__`, :meth:`__hash__`, :meth:`__eq__`, + :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, :meth:`__ge__`. + In all other respects, its interface is identical to :class:`Path`. + + The default implementations of the most basic methods, like :meth:`stat` + and ``iterdir()``, directly raise :exc:`UnsupportedOperation`. Other + methods like ``is_dir()`` and ``glob()`` call through to the basic methods. + The following table summarises their relationships: + + .. list-table:: + :header-rows: 1 + + * - Basic Methods + - Mixin Methods + * - :meth:`stat` + - ``lstat()``, ``exists()``, ``samefile()``, ``is_dir()``, + ``is_file()``, ``is_mount()``, ``is_symlink()``, + ``is_block_device()``, ``is_char_device()``, ``is_fifo()``, + ``is_socket()``, :meth:`resolve` + * - ``open()`` + - ``read_text()``, ``write_text()``, + ``read_bytes()``, ``write_bytes()`` + * - ``iterdir()`` and ``stat()`` + - ``glob()``, ``rglob()``, ``walk()`` + * - ``absolute()`` + - ``cwd()`` + * - ``expanduser()`` + - ``home()`` + * - ``chmod()`` + - ``lchmod()`` + * - ``symlink_to()``, ``hardlink_to()``, ``mkdir()``, ``touch()``, + ``rename()``, ``replace()``, ``unlink()``, ``rmdir()``, + ``owner()``, ``group()``, ``from_uri()``, ``as_uri()`` + :meth:`is_junction` + - No mixin methods + + These methods are described in the :ref:`concrete paths ` + documentation. That said, the following methods deserve special attention: + + .. method:: stat(*, follow_symlinks=True) + + FIXME: how do we describe the return value?? + + .. method:: resolve(strict=False) + + Resolves symlinks and eliminates ``..`` path components. If supported, + make the path absolute. + + The default implementation of this method first calls + ``absolute()``, but suppresses any resulting :exc:`UnsupportedOperation` + exception; this allows paths to be resolved on filesystems that lack + a notion of a working directory. It calls ``stat()`` on each ancestor + path, and ``readlink()`` when a stat result indicates a symlink. + :exc:`OSError` is raised if more than 40 symlinks are encountered while + resolving a path; this is taken to indicate a loop. + + .. method:: is_junction() + + Returns ``True`` if the path points to a junction. + + The default implementation of this method returns ``False`` rather than + raising :exc:`UnsupportedOperation`, because junctions are almost never + available in virtual filesystems. + + .. versionadded:: 3.13 + + Correspondence to tools in the :mod:`os` module ----------------------------------------------- diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 785deea1c1e48f..b7c74d4880ac13 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -180,6 +180,12 @@ os pathlib ------- +* Add :class:`pathlib.PurePathBase` and :class:`~pathlib.PathBase` abstract + base classes. Users can subclass :class:`~pathlib.PathBase` to implement + *virtual paths*: objects representing paths in archive files, on remote + storage systems, and so forth. + (Contributed by Barney Gale in :gh:`110109`.) + * Add :exc:`pathlib.UnsupportedOperation`, which is raised instead of :exc:`NotImplementedError` when a path operation isn't supported. (Contributed by Barney Gale in :gh:`89812`.) diff --git a/Misc/NEWS.d/next/Library/2023-10-04-00-20-00.gh-issue-110109.RAaIgG.rst b/Misc/NEWS.d/next/Library/2023-10-04-00-20-00.gh-issue-110109.RAaIgG.rst new file mode 100644 index 00000000000000..32a027a4bcadbd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-04-00-20-00.gh-issue-110109.RAaIgG.rst @@ -0,0 +1,4 @@ +Add :class:`pathlib.PurePathBase` and :class:`~pathlib.PathBase` abstract +base classes. Users can subclass :class:`pathlib.PathBase` to implement +*virtual paths*: objects that represent paths in archive files, remote +storage systems, disc images, etc. From 4c1814d06b44b061b1de6e312388e65db7f514ba Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 4 Oct 2023 00:45:48 +0100 Subject: [PATCH 2/3] Fix links to magic methods --- Doc/library/pathlib.rst | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 418226be4fcb6d..b262f65ec6c195 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1603,9 +1603,11 @@ Virtual paths Unlike :class:`PurePath`, this class does not define any of the following magic methods: - :meth:`~os.PathLike.__fspath__`, :meth:`__bytes__`, - :meth:`__reduce__`, :meth:`__hash__`, :meth:`__eq__`, - :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, :meth:`__ge__` + :meth:`~os.PathLike.__fspath__`, :meth:`~object.__bytes__`, + :meth:`~object.__reduce__`, + :meth:`~object.__hash__`, :meth:`~object.__eq__`, + :meth:`~object.__lt__`, :meth:`~object.__le__`, + :meth:`~object.__gt__`, :meth:`~object.__ge__`. .. versionadded:: 3.13 @@ -1625,9 +1627,11 @@ Virtual paths Like its superclass :class:`PurePathBase` (and unlike :class:`PurePath` and :class:`Path`), this class does not define any of the following magic methods: - :meth:`~os.PathLike.__fspath__`, :meth:`__bytes__`, - :meth:`__reduce__`, :meth:`__hash__`, :meth:`__eq__`, - :meth:`__lt__`, :meth:`__le__`, :meth:`__gt__`, :meth:`__ge__`. + :meth:`~os.PathLike.__fspath__`, :meth:`~object.__bytes__`, + :meth:`~object.__reduce__`, + :meth:`~object.__hash__`, :meth:`~object.__eq__`, + :meth:`~object.__lt__`, :meth:`~object.__le__`, + :meth:`~object.__gt__`, :meth:`~object.__ge__`. In all other respects, its interface is identical to :class:`Path`. The default implementations of the most basic methods, like :meth:`stat` From 9775428bf0b72aa0800e3d87463cbf3710ea2c81 Mon Sep 17 00:00:00 2001 From: barneygale Date: Wed, 4 Oct 2023 01:21:41 +0100 Subject: [PATCH 3/3] Docs tweaks, simplify language --- Doc/library/pathlib.rst | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index b262f65ec6c195..246dcba19edb6e 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -1601,7 +1601,7 @@ Virtual paths The :class:`PurePath` class is a subclass of this class. - Unlike :class:`PurePath`, this class does not define any of the following + Unlike :class:`PurePath`, this class does not define any of these magic methods: :meth:`~os.PathLike.__fspath__`, :meth:`~object.__bytes__`, :meth:`~object.__reduce__`, @@ -1626,52 +1626,58 @@ Virtual paths classes are *virtual paths*. Like its superclass :class:`PurePathBase` (and unlike :class:`PurePath` and - :class:`Path`), this class does not define any of the following magic methods: + :class:`Path`), this class does not define these magic methods: :meth:`~os.PathLike.__fspath__`, :meth:`~object.__bytes__`, :meth:`~object.__reduce__`, :meth:`~object.__hash__`, :meth:`~object.__eq__`, :meth:`~object.__lt__`, :meth:`~object.__le__`, :meth:`~object.__gt__`, :meth:`~object.__ge__`. - In all other respects, its interface is identical to :class:`Path`. + In all other respects, its interface is the same as :class:`Path`. - The default implementations of the most basic methods, like :meth:`stat` + The default implementations of the most basic methods, like ``stat()`` and ``iterdir()``, directly raise :exc:`UnsupportedOperation`. Other methods like ``is_dir()`` and ``glob()`` call through to the basic methods. - The following table summarises their relationships: + The following table shows their relationships: .. list-table:: :header-rows: 1 * - Basic Methods - Mixin Methods - * - :meth:`stat` - - ``lstat()``, ``exists()``, ``samefile()``, ``is_dir()``, - ``is_file()``, ``is_mount()``, ``is_symlink()``, + * - ``stat()`` + - ``lstat()``, ``exists()``, ``resolve()``, ``samefile()``, + ``is_dir()``, ``is_file()``, ``is_mount()``, ``is_symlink()``, ``is_block_device()``, ``is_char_device()``, ``is_fifo()``, - ``is_socket()``, :meth:`resolve` + ``is_socket()`` * - ``open()`` - ``read_text()``, ``write_text()``, ``read_bytes()``, ``write_bytes()`` * - ``iterdir()`` and ``stat()`` - ``glob()``, ``rglob()``, ``walk()`` - * - ``absolute()`` - - ``cwd()`` - * - ``expanduser()`` - - ``home()`` * - ``chmod()`` - ``lchmod()`` + * - ``expanduser()`` + - ``home()`` + * - ``absolute()`` + - ``cwd()`` * - ``symlink_to()``, ``hardlink_to()``, ``mkdir()``, ``touch()``, ``rename()``, ``replace()``, ``unlink()``, ``rmdir()``, - ``owner()``, ``group()``, ``from_uri()``, ``as_uri()`` - :meth:`is_junction` + ``owner()``, ``group()``, ``from_uri()``, ``as_uri()``, + ``is_junction()`` - No mixin methods - These methods are described in the :ref:`concrete paths ` - documentation. That said, the following methods deserve special attention: + These methods are described in the documentation for + :ref:`concrete paths `. That said, some methods deserve + special attention: .. method:: stat(*, follow_symlinks=True) - FIXME: how do we describe the return value?? + Returns information about the path. + + The default implementation of this method immediately raises + :exc:`UnsupportedOperation`. Subclasses that override this method should + return an object that resembles a :class:`~os.stat_result`; it should at + least have ``st_mode``, ``st_dev`` and ``st_ino`` attributes. .. method:: resolve(strict=False)