From 5e0ab5a11cc4f2313e83ab371ee4456e55005180 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 16:52:38 +0100 Subject: [PATCH 1/8] Document full public importlib.metadata API --- Doc/library/importlib.metadata.rst | 123 ++++++++++++++++++++--------- Lib/importlib/metadata/__init__.py | 1 + 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 9e08e5aa989cf7..ab2db5846509fc 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -291,7 +291,7 @@ Distribution files .. function:: files(distribution_name) Return the full set of files contained within the named - distribution package. + distribution package as :class:`PackagePath` instances. Raises :exc:`PackageNotFoundError` if the named distribution package is not installed in the current Python environment. @@ -304,12 +304,22 @@ Distribution files A :class:`pathlib.PurePath` derived object with additional ``dist``, ``size``, and ``hash`` properties corresponding to the distribution - package's installation metadata for that file. + package's installation metadata for that file, also: + + .. method:: locate() + + If possible, return the concrete :class:`SimplePath` allowing to access data, + or raise a :exc:`NotImplementedError` otherwise. + +.. class:: SimplePath + + A protocol representing a minimal subset of :class:`pathlib.Path` that allows to + check if it ``exists()``, to traverse using ``joinpath()`` and ``parent``, + and to retrieve data using ``read_text()`` and ``read_bytes()``. The :func:`!files` function takes a `Distribution Package `_ -name and returns all of the files installed by this distribution. Each file is reported -as a :class:`PackagePath` instance. For example:: +name and returns all of the files installed by this distribution. For example:: >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP >>> util # doctest: +SKIP @@ -402,6 +412,18 @@ function is not reliable with such installs. Distributions ============= +While the module level API described above is the most common and convenient usage, +you can get all of that information from the :class:`!Distribution` class. +:class:`!Distribution` is an abstract object that represents the metadata for +a Python `Distribution Package `_. +You can get the concrete :class:`!Distribution` subclass instance for an installed +distribution package by calling the :func:`distribution` function:: + + >>> from importlib.metadata import distribution # doctest: +SKIP + >>> dist = distribution('wheel') # doctest: +SKIP + >>> type(dist) # doctest: +SKIP + + .. function:: distribution(distribution_name) Return a :class:`Distribution` instance describing the named @@ -410,6 +432,14 @@ Distributions Raises :exc:`PackageNotFoundError` if the named distribution package is not installed in the current Python environment. +Thus, an alternative way to get e.g. the version number is through the +:attr:`!Distribution.version` attribute:: + + >>> dist.version # doctest: +SKIP + '0.32.3' + +The same applies for :func:`entry_points` and :func:`files`. + .. class:: Distribution Details of an installed distribution package. @@ -418,53 +448,70 @@ Distributions equal, even if they relate to the same installed distribution and accordingly have the same attributes. - .. method:: discover(cls, *, context=None, **kwargs) + .. staticmethod:: at(path) + .. classmethod:: from_name(name) + + Return a :class:`!Distribution` instance at the given path or + with the given name. - Returns an iterable of :class:`Distribution` instances for all packages. + .. classmethod:: discover(*, context: Context | None, **kwargs) + + Returns an iterable of :class:`Distribution` instances for all packages + (see distribution-discovery_). The optional argument *context* is a :class:`DistributionFinder.Context` instance, used to modify the search for distributions. Alternatively, *kwargs* may contain keyword arguments for constructing a new :class:`!DistributionFinder.Context`. + .. attribute:: metadata + .. attribute:: name + .. attribute:: requires + .. attribute:: version -While the module level API described above is the most common and convenient usage, -you can get all of that information from the :class:`!Distribution` class. -:class:`!Distribution` is an abstract object that represents the metadata for -a Python `Distribution Package `_. -You can get the concrete :class:`!Distribution` subclass instance for an installed -distribution package by calling the :func:`distribution` function:: + There are all kinds of additional metadata available on :class:`!Distribution` + instances as a :class:`PackageMetadata` instance:: - >>> from importlib.metadata import distribution # doctest: +SKIP - >>> dist = distribution('wheel') # doctest: +SKIP - >>> type(dist) # doctest: +SKIP - + >>> dist.metadata['Requires-Python'] # doctest: +SKIP + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + >>> dist.metadata['License'] # doctest: +SKIP + 'MIT' -Thus, an alternative way to get the version number is through the -:class:`!Distribution` instance:: + The full set of available metadata is not described here. + See the PyPA `Core metadata specification `_ for additional details. - >>> dist.version # doctest: +SKIP - '0.32.3' + .. attribute:: origin -There are all kinds of additional metadata available on :class:`!Distribution` -instances:: + For editable packages, an ``origin`` property may present :pep:`610` + metadata (for non-editable packages, ``origin`` is :const:`None`):: - >>> dist.metadata['Requires-Python'] # doctest: +SKIP - '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> dist.metadata['License'] # doctest: +SKIP - 'MIT' + >>> dist.origin.url + 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl' -For editable packages, an ``origin`` property may present :pep:`610` -metadata:: + The ``origin`` object follows the `Direct URL Data Structure + `_. - >>> dist.origin.url - 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl' + .. versionadded:: 3.13 -The full set of available metadata is not described here. -See the PyPA `Core metadata specification `_ for additional details. + .. attribute:: entry_points + + The :class:`!EntryPoints` provided by this distribution package. + + .. attribute:: files + + A sequence of :class:`!PackagePath`\s contained in this distribution package. + Like :func:`files`, this returns :const:`None` if there are no records. + + .. method:: locate_file(path) + + Like :meth:`!PackagePath.locate`, return a :class:`SimplePath` for the given path. + Takes a :class:`os.PathLike` or a :class:`str`. + + .. method:: read_text(filename) + + A shortcut for ``distribution.locate_file(filename).read_text()``. -.. versionadded:: 3.13 - The ``.origin`` property was added. +.. _distribution-discovery: Distribution Discovery ====================== @@ -575,8 +622,8 @@ consumer. In practice, to support finding distribution package metadata in locations other than the file system, subclass -``Distribution`` and implement the abstract methods. Then from -a custom finder, return instances of this derived ``Distribution`` in the +:class:`!Distribution` and implement the abstract methods. Then from +a custom finder, return instances of this derived :class:`!Distribution` in the ``find_distributions()`` method. Example @@ -653,8 +700,8 @@ packages served by the ``DatabaseImporter``, assuming that the ``.entry_points`` attributes. The ``DatabaseDistribution`` may also provide other metadata files, like -``RECORD`` (required for ``Distribution.files``) or override the -implementation of ``Distribution.files``. See the source for more inspiration. +``RECORD`` (required for :attr:`!Distribution.files`) or override the +implementation of :attr:`!Distribution.files`. See the source for more inspiration. .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py index b010bb8525e5cc..1c3b74f49a5832 100644 --- a/Lib/importlib/metadata/__init__.py +++ b/Lib/importlib/metadata/__init__.py @@ -41,6 +41,7 @@ 'DistributionFinder', 'PackageMetadata', 'PackageNotFoundError', + 'PackagePath', 'SimplePath', 'distribution', 'distributions', From cc5a37ee9134c13d7523777cf7c9bf9769a736e2 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 16:57:13 +0100 Subject: [PATCH 2/8] some more narrative --- Doc/library/importlib.metadata.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index ab2db5846509fc..a4fc3873b7661b 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -502,6 +502,8 @@ The same applies for :func:`entry_points` and :func:`files`. A sequence of :class:`!PackagePath`\s contained in this distribution package. Like :func:`files`, this returns :const:`None` if there are no records. + The following two abstract methods need to be implemented when implementing-custom-providers_: + .. method:: locate_file(path) Like :meth:`!PackagePath.locate`, return a :class:`SimplePath` for the given path. From e806bc97f8a2567c43b5f5304fcd931ede343b52 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 16:59:44 +0100 Subject: [PATCH 3/8] signature convention --- Doc/library/importlib.metadata.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index a4fc3873b7661b..0a0dc213dbde19 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -454,7 +454,7 @@ The same applies for :func:`entry_points` and :func:`files`. Return a :class:`!Distribution` instance at the given path or with the given name. - .. classmethod:: discover(*, context: Context | None, **kwargs) + .. classmethod:: discover(*, context=None, **kwargs) Returns an iterable of :class:`Distribution` instances for all packages (see distribution-discovery_). From b4675a7b293e700158a8b25fbcab970b019f283e Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 16:04:21 +0000 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst diff --git a/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst b/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst new file mode 100644 index 00000000000000..ab3f9d8e535430 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst @@ -0,0 +1 @@ +Document rest of full public :class:`importlib.metadata.Distribution` API. Also add the (already documented) :class:`~importlib.metadata.PackagePath` to `__all__`. From 21a7a59673d30927afaa1aa66f1f1bed2d460692 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 17:08:31 +0100 Subject: [PATCH 5/8] ffix blurb syntax --- .../2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst b/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst index ab3f9d8e535430..d29bde5ca690c6 100644 --- a/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst +++ b/Misc/NEWS.d/next/Documentation/2026-01-06-16-04-08.gh-issue-110937.SyO5lk.rst @@ -1 +1 @@ -Document rest of full public :class:`importlib.metadata.Distribution` API. Also add the (already documented) :class:`~importlib.metadata.PackagePath` to `__all__`. +Document rest of full public :class:`importlib.metadata.Distribution` API. Also add the (already documented) :class:`~importlib.metadata.PackagePath` to ``__all__``. From e99c7fb009beace9685978317a786c4a2dc7c074 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 17:30:07 +0100 Subject: [PATCH 6/8] add versionadded for name --- Doc/library/importlib.metadata.rst | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 0a0dc213dbde19..bb8ba43df3bd2e 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -465,9 +465,7 @@ The same applies for :func:`entry_points` and :func:`files`. :class:`!DistributionFinder.Context`. .. attribute:: metadata - .. attribute:: name - .. attribute:: requires - .. attribute:: version + :type: PackageMetadata There are all kinds of additional metadata available on :class:`!Distribution` instances as a :class:`PackageMetadata` instance:: @@ -480,6 +478,19 @@ The same applies for :func:`entry_points` and :func:`files`. The full set of available metadata is not described here. See the PyPA `Core metadata specification `_ for additional details. + .. attribute:: name + :type: str + .. attribute:: requires + :type: list[str] + .. attribute:: version + :type: str + + A few metadata fields are also available as shortcut properties. + + .. versionadded:: 3.10 + + The ``name`` shortcut was added. + .. attribute:: origin For editable packages, an ``origin`` property may present :pep:`610` @@ -494,12 +505,14 @@ The same applies for :func:`entry_points` and :func:`files`. .. versionadded:: 3.13 .. attribute:: entry_points + :type: EntryPoints - The :class:`!EntryPoints` provided by this distribution package. + The entry points provided by this distribution package. .. attribute:: files + :type: list[PackagePath] | None - A sequence of :class:`!PackagePath`\s contained in this distribution package. + All files contained in this distribution package. Like :func:`files`, this returns :const:`None` if there are no records. The following two abstract methods need to be implemented when implementing-custom-providers_: From 26edf32f69b7ef60aa6528d68613ff95ba5cd638 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 17:46:17 +0100 Subject: [PATCH 7/8] It is actually possible --- Doc/library/importlib.metadata.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index bb8ba43df3bd2e..c6e718ffdeca97 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -125,8 +125,8 @@ Entry points :meth:`!select` method for comparison to the attributes of the individual entry point definitions. - Note: it is not currently possible to query for entry points based on - their :attr:`!EntryPoint.dist` attribute (as different :class:`!Distribution` + Note: to query for entry points based on :attr:`!EntryPoint.dist` attribute, + use :meth:`!Distribution.entry_points` instead (as different :class:`!Distribution` instances do not currently compare equal, even if they have the same attributes) .. class:: EntryPoints From 561328d5b7de60df41af629e894227af1e297116 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Tue, 6 Jan 2026 17:51:37 +0100 Subject: [PATCH 8/8] link where it makes sense --- Doc/library/importlib.metadata.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index c6e718ffdeca97..feff05f77e6465 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -126,7 +126,7 @@ Entry points the individual entry point definitions. Note: to query for entry points based on :attr:`!EntryPoint.dist` attribute, - use :meth:`!Distribution.entry_points` instead (as different :class:`!Distribution` + use :meth:`Distribution.entry_points` instead (as different :class:`Distribution` instances do not currently compare equal, even if they have the same attributes) .. class:: EntryPoints @@ -413,7 +413,7 @@ Distributions ============= While the module level API described above is the most common and convenient usage, -you can get all of that information from the :class:`!Distribution` class. +you can get all of that information from the :class:`Distribution` class. :class:`!Distribution` is an abstract object that represents the metadata for a Python `Distribution Package `_. You can get the concrete :class:`!Distribution` subclass instance for an installed @@ -433,7 +433,7 @@ distribution package by calling the :func:`distribution` function:: package is not installed in the current Python environment. Thus, an alternative way to get e.g. the version number is through the -:attr:`!Distribution.version` attribute:: +:attr:`Distribution.version` attribute:: >>> dist.version # doctest: +SKIP '0.32.3' @@ -456,7 +456,7 @@ The same applies for :func:`entry_points` and :func:`files`. .. classmethod:: discover(*, context=None, **kwargs) - Returns an iterable of :class:`Distribution` instances for all packages + Returns an iterable of :class:`!Distribution` instances for all packages (see distribution-discovery_). The optional argument *context* is a :class:`DistributionFinder.Context`