blob: 58c582d712413773c5a446d56a3c2419f23a55d2 [file] [log] [blame]
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -04001.. _using:
2
Oleg Höflingcbd04082019-12-29 18:26:35 +01003=================================
4 Using :mod:`!importlib.metadata`
5=================================
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -04006
Jason R. Coombs23acadc2021-04-14 20:56:21 -04007.. module:: importlib.metadata
8 :synopsis: The implementation of the importlib metadata.
9
Zackery Spytzadf24bd2021-04-16 17:13:38 -060010.. versionadded:: 3.8
Miss Islington (bot)7223ce32021-07-12 17:40:51 -070011.. versionchanged:: 3.10
12 ``importlib.metadata`` is no longer provisional.
Zackery Spytzadf24bd2021-04-16 17:13:38 -060013
Jason R. Coombs23acadc2021-04-14 20:56:21 -040014**Source code:** :source:`Lib/importlib/metadata.py`
15
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040016``importlib.metadata`` is a library that provides for access to installed
17package metadata. Built in part on Python's import system, this library
18intends to replace similar functionality in the `entry point
19API`_ and `metadata API`_ of ``pkg_resources``. Along with
Oleg Höflingcbd04082019-12-29 18:26:35 +010020:mod:`importlib.resources` in Python 3.7
21and newer (backported as `importlib_resources`_ for older versions of
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040022Python), this can eliminate the need to use the older and less efficient
23``pkg_resources`` package.
24
25By "installed package" we generally mean a third-party package installed into
26Python's ``site-packages`` directory via tools such as `pip
27<https://pypi.org/project/pip/>`_. Specifically,
28it means a package with either a discoverable ``dist-info`` or ``egg-info``
Oleg Höflingcbd04082019-12-29 18:26:35 +010029directory, and metadata defined by :pep:`566` or its older specifications.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040030By default, package metadata can live on the file system or in zip archives on
Oleg Höflingcbd04082019-12-29 18:26:35 +010031:data:`sys.path`. Through an extension mechanism, the metadata can live almost
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040032anywhere.
33
34
35Overview
36========
37
38Let's say you wanted to get the version string for a package you've installed
39using ``pip``. We start by creating a virtual environment and installing
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070040something into it:
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040041
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070042.. code-block:: shell-session
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040043
44 $ python3 -m venv example
45 $ source example/bin/activate
46 (example) $ pip install wheel
47
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070048You can get the version string for ``wheel`` by running the following:
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040049
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070050.. code-block:: pycon
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040051
52 (example) $ python
53 >>> from importlib.metadata import version # doctest: +SKIP
54 >>> version('wheel') # doctest: +SKIP
55 '0.32.3'
56
57You can also get the set of entry points keyed by group, such as
58``console_scripts``, ``distutils.commands`` and others. Each group contains a
59sequence of :ref:`EntryPoint <entry-points>` objects.
60
61You can get the :ref:`metadata for a distribution <metadata>`::
62
63 >>> list(metadata('wheel')) # doctest: +SKIP
64 ['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']
65
66You can also get a :ref:`distribution's version number <version>`, list its
67:ref:`constituent files <files>`, and get a list of the distribution's
68:ref:`requirements`.
69
70
71Functional API
72==============
73
74This package provides the following functionality via its public API.
75
76
77.. _entry-points:
78
79Entry points
80------------
81
Jason R. Coombsf917efc2021-03-13 11:31:45 -050082The ``entry_points()`` function returns a collection of entry points.
83Entry points are represented by ``EntryPoint`` instances;
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040084each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
Jason R. Coombs161541a2020-06-05 16:34:16 -040085a ``.load()`` method to resolve the value. There are also ``.module``,
86``.attr``, and ``.extras`` attributes for getting the components of the
Jason R. Coombs35d50682021-03-14 22:20:49 -040087``.value`` attribute.
88
89Query all entry points::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040090
91 >>> eps = entry_points() # doctest: +SKIP
Jason R. Coombs35d50682021-03-14 22:20:49 -040092
93The ``entry_points()`` function returns an ``EntryPoints`` object,
94a sequence of all ``EntryPoint`` objects with ``names`` and ``groups``
95attributes for convenience::
96
Jason R. Coombsf917efc2021-03-13 11:31:45 -050097 >>> sorted(eps.groups) # doctest: +SKIP
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040098 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
Jason R. Coombs35d50682021-03-14 22:20:49 -040099
100``EntryPoints`` has a ``select`` method to select entry points
101matching specific properties. Select entry points in the
102``console_scripts`` group::
103
Jason R. Coombsf917efc2021-03-13 11:31:45 -0500104 >>> scripts = eps.select(group='console_scripts') # doctest: +SKIP
Jason R. Coombs35d50682021-03-14 22:20:49 -0400105
106Equivalently, since ``entry_points`` passes keyword arguments
107through to select::
108
109 >>> scripts = entry_points(group='console_scripts') # doctest: +SKIP
110
111Pick out a specific script named "wheel" (found in the wheel project)::
112
Jason R. Coombsf917efc2021-03-13 11:31:45 -0500113 >>> 'wheel' in scripts.names # doctest: +SKIP
114 True
115 >>> wheel = scripts['wheel'] # doctest: +SKIP
Jason R. Coombs35d50682021-03-14 22:20:49 -0400116
117Equivalently, query for that entry point during selection::
118
119 >>> (wheel,) = entry_points(group='console_scripts', name='wheel') # doctest: +SKIP
120 >>> (wheel,) = entry_points().select(group='console_scripts', name='wheel') # doctest: +SKIP
121
122Inspect the resolved entry point::
123
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400124 >>> wheel # doctest: +SKIP
125 EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
Jason R. Coombs161541a2020-06-05 16:34:16 -0400126 >>> wheel.module # doctest: +SKIP
127 'wheel.cli'
128 >>> wheel.attr # doctest: +SKIP
129 'main'
130 >>> wheel.extras # doctest: +SKIP
131 []
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400132 >>> main = wheel.load() # doctest: +SKIP
133 >>> main # doctest: +SKIP
134 <function main at 0x103528488>
135
136The ``group`` and ``name`` are arbitrary values defined by the package author
137and usually a client will wish to resolve all entry points for a particular
138group. Read `the setuptools docs
139<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
Jason R. Coombs161541a2020-06-05 16:34:16 -0400140for more information on entry points, their definition, and usage.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400141
Jason R. Coombs35d50682021-03-14 22:20:49 -0400142*Compatibility Note*
143
144The "selectable" entry points were introduced in ``importlib_metadata``
1453.6 and Python 3.10. Prior to those changes, ``entry_points`` accepted
146no parameters and always returned a dictionary of entry points, keyed
147by group. For compatibility, if no parameters are passed to entry_points,
148a ``SelectableGroups`` object is returned, implementing that dict
149interface. In the future, calling ``entry_points`` with no parameters
150will return an ``EntryPoints`` object. Users should rely on the selection
151interface to retrieve entry points by group.
152
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400153
154.. _metadata:
155
156Distribution metadata
157---------------------
158
159Every distribution includes some metadata, which you can extract using the
160``metadata()`` function::
161
162 >>> wheel_metadata = metadata('wheel') # doctest: +SKIP
163
Jason R. Coombsdfdca852020-12-31 12:56:43 -0500164The keys of the returned data structure, a ``PackageMetadata``,
165name the metadata keywords, and
166the values are returned unparsed from the distribution metadata::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400167
168 >>> wheel_metadata['Requires-Python'] # doctest: +SKIP
169 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
170
Jason R. Coombs37e0c782021-05-02 17:03:40 -0400171``PackageMetadata`` also presents a ``json`` attribute that returns
172all the metadata in a JSON-compatible form per :PEP:`566`::
173
174 >>> wheel_metadata.json['requires_python']
175 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
176
177.. versionchanged:: 3.10
178 The ``Description`` is now included in the metadata when presented
179 through the payload. Line continuation characters have been removed.
180
181.. versionadded:: 3.10
182 The ``json`` attribute was added.
183
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400184
185.. _version:
186
187Distribution versions
188---------------------
189
190The ``version()`` function is the quickest way to get a distribution's version
191number, as a string::
192
193 >>> version('wheel') # doctest: +SKIP
194 '0.32.3'
195
196
197.. _files:
198
199Distribution files
200------------------
201
202You can also get the full set of files contained within a distribution. The
203``files()`` function takes a distribution package name and returns all of the
204files installed by this distribution. Each file object returned is a
Paul Mooreb38b2fa2021-04-29 00:27:37 +0100205``PackagePath``, a :class:`pathlib.PurePath` derived object with additional ``dist``,
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400206``size``, and ``hash`` properties as indicated by the metadata. For example::
207
208 >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP
209 >>> util # doctest: +SKIP
210 PackagePath('wheel/util.py')
211 >>> util.size # doctest: +SKIP
212 859
213 >>> util.dist # doctest: +SKIP
214 <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
215 >>> util.hash # doctest: +SKIP
216 <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>
217
218Once you have the file, you can also read its contents::
219
220 >>> print(util.read_text()) # doctest: +SKIP
221 import base64
222 import sys
223 ...
224 def as_bytes(s):
225 if isinstance(s, text_type):
226 return s.encode('utf-8')
227 return s
228
Paul Mooreb38b2fa2021-04-29 00:27:37 +0100229You can also use the ``locate`` method to get a the absolute path to the
230file::
231
232 >>> util.locate() # doctest: +SKIP
233 PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')
234
Jason R. Coombs102e9b42019-09-02 11:08:03 -0400235In the case where the metadata file listing files
236(RECORD or SOURCES.txt) is missing, ``files()`` will
237return ``None``. The caller may wish to wrap calls to
238``files()`` in `always_iterable
239<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_
240or otherwise guard against this condition if the target
241distribution is not known to have the metadata present.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400242
243.. _requirements:
244
245Distribution requirements
246-------------------------
247
248To get the full set of requirements for a distribution, use the ``requires()``
Jason R. Coombs17499d82019-09-10 14:53:31 +0100249function::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400250
Jason R. Coombs17499d82019-09-10 14:53:31 +0100251 >>> requires('wheel') # doctest: +SKIP
252 ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400253
254
Jason R. Coombsf917efc2021-03-13 11:31:45 -0500255Package distributions
256---------------------
257
Christian Claussd15f47d2021-10-06 15:56:57 +0200258A convenience method to resolve the distribution or
Jason R. Coombsf917efc2021-03-13 11:31:45 -0500259distributions (in the case of a namespace package) for top-level
260Python packages or modules::
261
262 >>> packages_distributions()
263 {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
264
Jason R. Coombs35d50682021-03-14 22:20:49 -0400265.. versionadded:: 3.10
266
Jason R. Coombsf917efc2021-03-13 11:31:45 -0500267
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400268Distributions
269=============
270
271While the above API is the most common and convenient usage, you can get all
272of that information from the ``Distribution`` class. A ``Distribution`` is an
273abstract object that represents the metadata for a Python package. You can
274get the ``Distribution`` instance::
275
276 >>> from importlib.metadata import distribution # doctest: +SKIP
277 >>> dist = distribution('wheel') # doctest: +SKIP
278
279Thus, an alternative way to get the version number is through the
280``Distribution`` instance::
281
282 >>> dist.version # doctest: +SKIP
283 '0.32.3'
284
285There are all kinds of additional metadata available on the ``Distribution``
286instance::
287
Tao He3631d6d2021-01-01 03:37:53 +0800288 >>> dist.metadata['Requires-Python'] # doctest: +SKIP
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400289 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
Tao He3631d6d2021-01-01 03:37:53 +0800290 >>> dist.metadata['License'] # doctest: +SKIP
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400291 'MIT'
292
Oleg Höflingcbd04082019-12-29 18:26:35 +0100293The full set of available metadata is not described here. See :pep:`566`
294for additional details.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400295
296
297Extending the search algorithm
298==============================
299
Oleg Höflingcbd04082019-12-29 18:26:35 +0100300Because package metadata is not available through :data:`sys.path` searches, or
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400301package loaders directly, the metadata for a package is found through import
Oleg Höflingcbd04082019-12-29 18:26:35 +0100302system :ref:`finders <finders-and-loaders>`. To find a distribution package's metadata,
303``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on
304:data:`sys.meta_path`.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400305
Jason R. Coombsb7a01092019-12-10 20:05:10 -0500306The default ``PathFinder`` for Python includes a hook that calls into
307``importlib.metadata.MetadataPathFinder`` for finding distributions
308loaded from typical file-system-based paths.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400309
310The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the
311interface expected of finders by Python's import system.
312``importlib.metadata`` extends this protocol by looking for an optional
313``find_distributions`` callable on the finders from
Oleg Höflingcbd04082019-12-29 18:26:35 +0100314:data:`sys.meta_path` and presents this extended interface as the
Jason R. Coombs17499d82019-09-10 14:53:31 +0100315``DistributionFinder`` abstract base class, which defines this abstract
316method::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400317
Jason R. Coombs17499d82019-09-10 14:53:31 +0100318 @abc.abstractmethod
319 def find_distributions(context=DistributionFinder.Context()):
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400320 """Return an iterable of all Distribution instances capable of
Jason R. Coombs17499d82019-09-10 14:53:31 +0100321 loading the metadata for packages for the indicated ``context``.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400322 """
323
Jason R. Coombs17499d82019-09-10 14:53:31 +0100324The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
Jason R. Coombs161541a2020-06-05 16:34:16 -0400325properties indicating the path to search and name to match and may
Jason R. Coombs17499d82019-09-10 14:53:31 +0100326supply other relevant context.
327
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400328What this means in practice is that to support finding distribution package
Jason R. Coombsb7a01092019-12-10 20:05:10 -0500329metadata in locations other than the file system, subclass
330``Distribution`` and implement the abstract methods. Then from
331a custom finder, return instances of this derived ``Distribution`` in the
Jason R. Coombs17499d82019-09-10 14:53:31 +0100332``find_distributions()`` method.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400333
334
335.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
336.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400337.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400338
339
340.. rubric:: Footnotes