2019-05-24 20:59:01 -03:00
.. _using:
2019-12-29 13:26:35 -04:00
=================================
Using :mod: `!importlib.metadata`
=================================
2019-05-24 20:59:01 -03:00
2021-04-14 21:56:21 -03:00
.. module :: importlib.metadata
:synopsis: The implementation of the importlib metadata.
2021-04-16 20:13:38 -03:00
.. versionadded :: 3.8
2021-07-12 20:56:40 -03:00
.. versionchanged :: 3.10
`` importlib.metadata `` is no longer provisional.
2021-04-16 20:13:38 -03:00
2021-10-30 12:12:47 -03:00
**Source code:** :source: `Lib/importlib/metadata/__init__.py`
2021-04-14 21:56:21 -03:00
2022-06-25 22:04:28 -03:00
`` importlib.metadata `` is a library that provides access to installed
package metadata, such as its entry points or its
top-level name. Built in part on Python's import system, this library
2019-05-24 20:59:01 -03:00
intends to replace similar functionality in the `entry point
API`_ and `metadata API`_ of `` pkg_resources `` . Along with
2022-06-25 22:04:28 -03:00
:mod: `importlib.resources` ,
this package can eliminate the need to use the older and less efficient
2019-05-24 20:59:01 -03:00
`` pkg_resources `` package.
By "installed package" we generally mean a third-party package installed into
Python's `` site-packages `` directory via tools such as `pip
<https://pypi.org/project/pip/> `_. Specifically,
it means a package with either a discoverable `` dist-info `` or `` egg-info ``
2019-12-29 13:26:35 -04:00
directory, and metadata defined by :pep: `566` or its older specifications.
2019-05-24 20:59:01 -03:00
By default, package metadata can live on the file system or in zip archives on
2019-12-29 13:26:35 -04:00
:data: `sys.path` . Through an extension mechanism, the metadata can live almost
2019-05-24 20:59:01 -03:00
anywhere.
2022-06-25 22:04:28 -03:00
.. seealso ::
https://importlib-metadata.readthedocs.io/
The documentation for `` importlib_metadata `` , which supplies a
backport of `` importlib.metadata `` .
2019-05-24 20:59:01 -03:00
Overview
========
Let's say you wanted to get the version string for a package you've installed
using `` pip `` . We start by creating a virtual environment and installing
2019-05-26 11:30:52 -03:00
something into it:
2019-05-24 20:59:01 -03:00
2019-05-26 11:30:52 -03:00
.. code-block :: shell-session
2019-05-24 20:59:01 -03:00
$ python3 -m venv example
$ source example/bin/activate
2022-04-09 15:37:01 -03:00
(example) $ python -m pip install wheel
2019-05-24 20:59:01 -03:00
2019-05-26 11:30:52 -03:00
You can get the version string for `` wheel `` by running the following:
2019-05-24 20:59:01 -03:00
2019-05-26 11:30:52 -03:00
.. code-block :: pycon
2019-05-24 20:59:01 -03:00
(example) $ python
>>> from importlib.metadata import version # doctest: +SKIP
>>> version('wheel') # doctest: +SKIP
'0.32.3'
2022-06-25 22:04:28 -03:00
You can also get a collection of entry points selectable by properties of the EntryPoint (typically 'group' or 'name'), such as
2019-05-24 20:59:01 -03:00
`` console_scripts `` , `` distutils.commands `` and others. Each group contains a
2022-06-25 22:04:28 -03:00
collection of :ref: `EntryPoint <entry-points>` objects.
2019-05-24 20:59:01 -03:00
You can get the :ref: `metadata for a distribution <metadata>` ::
>>> list(metadata('wheel')) # doctest: +SKIP
['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']
You can also get a :ref: `distribution's version number <version>` , list its
:ref: `constituent files <files>` , and get a list of the distribution's
:ref: `requirements` .
Functional API
==============
This package provides the following functionality via its public API.
.. _entry-points:
Entry points
------------
2021-03-13 12:31:45 -04:00
The `` entry_points() `` function returns a collection of entry points.
Entry points are represented by `` EntryPoint `` instances;
2019-05-24 20:59:01 -03:00
each `` EntryPoint `` has a `` .name `` , `` .group `` , and `` .value `` attributes and
2020-06-05 17:34:16 -03:00
a `` .load() `` method to resolve the value. There are also `` .module `` ,
`` .attr `` , and `` .extras `` attributes for getting the components of the
2021-03-14 23:20:49 -03:00
`` .value `` attribute.
Query all entry points::
2019-05-24 20:59:01 -03:00
>>> eps = entry_points() # doctest: +SKIP
2021-03-14 23:20:49 -03:00
The `` entry_points() `` function returns an `` EntryPoints `` object,
2022-06-25 22:04:28 -03:00
a collection of all `` EntryPoint `` objects with `` names `` and `` groups ``
2021-03-14 23:20:49 -03:00
attributes for convenience::
2021-03-13 12:31:45 -04:00
>>> sorted(eps.groups) # doctest: +SKIP
2019-05-24 20:59:01 -03:00
['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
2021-03-14 23:20:49 -03:00
`` EntryPoints `` has a `` select `` method to select entry points
matching specific properties. Select entry points in the
`` console_scripts `` group::
2021-03-13 12:31:45 -04:00
>>> scripts = eps.select(group='console_scripts') # doctest: +SKIP
2021-03-14 23:20:49 -03:00
Equivalently, since `` entry_points `` passes keyword arguments
through to select::
>>> scripts = entry_points(group='console_scripts') # doctest: +SKIP
Pick out a specific script named "wheel" (found in the wheel project)::
2021-03-13 12:31:45 -04:00
>>> 'wheel' in scripts.names # doctest: +SKIP
True
>>> wheel = scripts['wheel'] # doctest: +SKIP
2021-03-14 23:20:49 -03:00
Equivalently, query for that entry point during selection::
>>> (wheel,) = entry_points(group='console_scripts', name='wheel') # doctest: +SKIP
>>> (wheel,) = entry_points().select(group='console_scripts', name='wheel') # doctest: +SKIP
Inspect the resolved entry point::
2019-05-24 20:59:01 -03:00
>>> wheel # doctest: +SKIP
EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
2020-06-05 17:34:16 -03:00
>>> wheel.module # doctest: +SKIP
'wheel.cli'
>>> wheel.attr # doctest: +SKIP
'main'
>>> wheel.extras # doctest: +SKIP
[]
2019-05-24 20:59:01 -03:00
>>> main = wheel.load() # doctest: +SKIP
>>> main # doctest: +SKIP
<function main at 0x103528488>
The `` group `` and `` name `` are arbitrary values defined by the package author
and usually a client will wish to resolve all entry points for a particular
group. Read `the setuptools docs
2022-06-21 15:55:18 -03:00
<https://setuptools.pypa.io/en/latest/userguide/entry_point.html> `_
2020-06-05 17:34:16 -03:00
for more information on entry points, their definition, and usage.
2019-05-24 20:59:01 -03:00
2021-03-14 23:20:49 -03:00
*Compatibility Note*
The "selectable" entry points were introduced in `` importlib_metadata ``
3.6 and Python 3.10. Prior to those changes, `` entry_points `` accepted
no parameters and always returned a dictionary of entry points, keyed
by group. For compatibility, if no parameters are passed to entry_points,
a `` SelectableGroups `` object is returned, implementing that dict
interface. In the future, calling `` entry_points `` with no parameters
will return an `` EntryPoints `` object. Users should rely on the selection
interface to retrieve entry points by group.
2019-05-24 20:59:01 -03:00
.. _metadata:
Distribution metadata
---------------------
Every distribution includes some metadata, which you can extract using the
`` metadata() `` function::
>>> wheel_metadata = metadata('wheel') # doctest: +SKIP
2020-12-31 13:56:43 -04:00
The keys of the returned data structure, a `` PackageMetadata `` ,
name the metadata keywords, and
the values are returned unparsed from the distribution metadata::
2019-05-24 20:59:01 -03:00
>>> wheel_metadata['Requires-Python'] # doctest: +SKIP
'>=2.7, !=3.0.*, !=3.1.* , !=3.2.*, !=3.3.* '
2021-05-02 18:03:40 -03:00
`` PackageMetadata `` also presents a `` json `` attribute that returns
all the metadata in a JSON-compatible form per :PEP: `566` ::
>>> wheel_metadata.json['requires_python']
'>=2.7, !=3.0.*, !=3.1.* , !=3.2.*, !=3.3.* '
2022-06-25 22:04:28 -03:00
.. note ::
The actual type of the object returned by `` metadata() `` is an
implementation detail and should be accessed only through the interface
described by the
`PackageMetadata protocol <https://importlib-metadata.readthedocs.io/en/latest/api.html#importlib_metadata.PackageMetadata>` .
2021-05-02 18:03:40 -03:00
.. versionchanged :: 3.10
The `` Description `` is now included in the metadata when presented
through the payload. Line continuation characters have been removed.
.. versionadded :: 3.10
The `` json `` attribute was added.
2019-05-24 20:59:01 -03:00
.. _version:
Distribution versions
---------------------
The `` version() `` function is the quickest way to get a distribution's version
number, as a string::
>>> version('wheel') # doctest: +SKIP
'0.32.3'
.. _files:
Distribution files
------------------
You can also get the full set of files contained within a distribution. The
`` files() `` function takes a distribution package name and returns all of the
files installed by this distribution. Each file object returned is a
2021-04-28 20:27:37 -03:00
`` PackagePath `` , a :class: `pathlib.PurePath` derived object with additional `` dist `` ,
2019-05-24 20:59:01 -03:00
`` size `` , and `` hash `` properties as indicated by the metadata. For example::
>>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP
>>> util # doctest: +SKIP
PackagePath('wheel/util.py')
>>> util.size # doctest: +SKIP
859
>>> util.dist # doctest: +SKIP
<importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
>>> util.hash # doctest: +SKIP
<FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>
Once you have the file, you can also read its contents::
>>> print(util.read_text()) # doctest: +SKIP
import base64
import sys
...
def as_bytes(s):
if isinstance(s, text_type):
return s.encode('utf-8')
return s
2021-04-28 20:27:37 -03:00
You can also use the `` locate `` method to get a the absolute path to the
file::
>>> util.locate() # doctest: +SKIP
PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')
2019-09-02 12:08:03 -03:00
In the case where the metadata file listing files
(RECORD or SOURCES.txt) is missing, `` files() `` will
return `` None `` . The caller may wish to wrap calls to
`` files() `` in `always_iterable
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable> `_
or otherwise guard against this condition if the target
distribution is not known to have the metadata present.
2019-05-24 20:59:01 -03:00
.. _requirements:
Distribution requirements
-------------------------
To get the full set of requirements for a distribution, use the `` requires() ``
2019-09-10 10:53:31 -03:00
function::
2019-05-24 20:59:01 -03:00
2019-09-10 10:53:31 -03:00
>>> requires('wheel') # doctest: +SKIP
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
2019-05-24 20:59:01 -03:00
2021-03-13 12:31:45 -04:00
Package distributions
---------------------
2021-10-05 18:48:44 -03:00
A convenience method to resolve the distribution or
2021-03-13 12:31:45 -04:00
distributions (in the case of a namespace package) for top-level
Python packages or modules::
>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
2021-03-14 23:20:49 -03:00
.. versionadded :: 3.10
2022-03-13 16:53:29 -03:00
.. _distributions:
2021-03-13 12:31:45 -04:00
2019-05-24 20:59:01 -03:00
Distributions
=============
While the above API is the most common and convenient usage, you can get all
of that information from the `` Distribution `` class. A `` Distribution `` is an
abstract object that represents the metadata for a Python package. You can
get the `` Distribution `` instance::
>>> from importlib.metadata import distribution # doctest: +SKIP
>>> dist = distribution('wheel') # doctest: +SKIP
Thus, an alternative way to get the version number is through the
`` Distribution `` instance::
>>> dist.version # doctest: +SKIP
'0.32.3'
There are all kinds of additional metadata available on the `` Distribution ``
instance::
2020-12-31 15:37:53 -04:00
>>> dist.metadata['Requires-Python'] # doctest: +SKIP
2019-05-24 20:59:01 -03:00
'>=2.7, !=3.0.*, !=3.1.* , !=3.2.*, !=3.3.* '
2020-12-31 15:37:53 -04:00
>>> dist.metadata['License'] # doctest: +SKIP
2019-05-24 20:59:01 -03:00
'MIT'
2019-12-29 13:26:35 -04:00
The full set of available metadata is not described here. See :pep: `566`
for additional details.
2019-05-24 20:59:01 -03:00
2022-06-25 22:04:28 -03:00
Distribution Discovery
======================
By default, this package provides built-in support for discovery of metadata for file system and zip file packages. This metadata finder search defaults to `` sys.path `` , but varies slightly in how it interprets those values from how other import machinery does. In particular:
- `` importlib.metadata `` does not honor :class: `bytes` objects on `` sys.path `` .
- `` importlib.metadata `` will incidentally honor :py:class: `pathlib.Path` objects on `` sys.path `` even though such values will be ignored for imports.
2019-05-24 20:59:01 -03:00
Extending the search algorithm
==============================
2019-12-29 13:26:35 -04:00
Because package metadata is not available through :data: `sys.path` searches, or
2019-05-24 20:59:01 -03:00
package loaders directly, the metadata for a package is found through import
2019-12-29 13:26:35 -04:00
system :ref: `finders <finders-and-loaders>` . To find a distribution package's metadata,
`` importlib.metadata `` queries the list of :term: `meta path finders <meta path finder>` on
:data: `sys.meta_path` .
2019-05-24 20:59:01 -03:00
2019-12-10 21:05:10 -04:00
The default `` PathFinder `` for Python includes a hook that calls into
`` importlib.metadata.MetadataPathFinder `` for finding distributions
loaded from typical file-system-based paths.
2019-05-24 20:59:01 -03:00
The abstract class :py:class: `importlib.abc.MetaPathFinder` defines the
interface expected of finders by Python's import system.
`` importlib.metadata `` extends this protocol by looking for an optional
`` find_distributions `` callable on the finders from
2019-12-29 13:26:35 -04:00
:data: `sys.meta_path` and presents this extended interface as the
2019-09-10 10:53:31 -03:00
`` DistributionFinder `` abstract base class, which defines this abstract
method::
2019-05-24 20:59:01 -03:00
2019-09-10 10:53:31 -03:00
@abc.abstractmethod
def find_distributions(context=DistributionFinder.Context()):
2019-05-24 20:59:01 -03:00
"""Return an iterable of all Distribution instances capable of
2019-09-10 10:53:31 -03:00
loading the metadata for packages for the indicated `` context `` .
2019-05-24 20:59:01 -03:00
"""
2019-09-10 10:53:31 -03:00
The `` DistributionFinder.Context `` object provides `` .path `` and `` .name ``
2020-06-05 17:34:16 -03:00
properties indicating the path to search and name to match and may
2019-09-10 10:53:31 -03:00
supply other relevant context.
2019-05-24 20:59:01 -03:00
What this means in practice is that to support finding distribution package
2019-12-10 21:05:10 -04:00
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
2019-09-10 10:53:31 -03:00
`` find_distributions() `` method.
2019-05-24 20:59:01 -03:00
.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api
.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html