blob: 7f154ea02cc4f2d002323b508e906fea4e71e9d0 [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
7.. note::
8 This functionality is provisional and may deviate from the usual
9 version semantics of the standard library.
10
11``importlib.metadata`` is a library that provides for access to installed
12package metadata. Built in part on Python's import system, this library
13intends to replace similar functionality in the `entry point
14API`_ and `metadata API`_ of ``pkg_resources``. Along with
Oleg Höflingcbd04082019-12-29 18:26:35 +010015:mod:`importlib.resources` in Python 3.7
16and newer (backported as `importlib_resources`_ for older versions of
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040017Python), this can eliminate the need to use the older and less efficient
18``pkg_resources`` package.
19
20By "installed package" we generally mean a third-party package installed into
21Python's ``site-packages`` directory via tools such as `pip
22<https://pypi.org/project/pip/>`_. Specifically,
23it means a package with either a discoverable ``dist-info`` or ``egg-info``
Oleg Höflingcbd04082019-12-29 18:26:35 +010024directory, and metadata defined by :pep:`566` or its older specifications.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040025By default, package metadata can live on the file system or in zip archives on
Oleg Höflingcbd04082019-12-29 18:26:35 +010026:data:`sys.path`. Through an extension mechanism, the metadata can live almost
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040027anywhere.
28
29
30Overview
31========
32
33Let's say you wanted to get the version string for a package you've installed
34using ``pip``. We start by creating a virtual environment and installing
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070035something into it:
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040036
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070037.. code-block:: shell-session
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040038
39 $ python3 -m venv example
40 $ source example/bin/activate
41 (example) $ pip install wheel
42
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070043You can get the version string for ``wheel`` by running the following:
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040044
Anthony Sottile22ccb0b2019-05-26 07:30:52 -070045.. code-block:: pycon
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040046
47 (example) $ python
48 >>> from importlib.metadata import version # doctest: +SKIP
49 >>> version('wheel') # doctest: +SKIP
50 '0.32.3'
51
52You can also get the set of entry points keyed by group, such as
53``console_scripts``, ``distutils.commands`` and others. Each group contains a
54sequence of :ref:`EntryPoint <entry-points>` objects.
55
56You can get the :ref:`metadata for a distribution <metadata>`::
57
58 >>> list(metadata('wheel')) # doctest: +SKIP
59 ['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']
60
61You can also get a :ref:`distribution's version number <version>`, list its
62:ref:`constituent files <files>`, and get a list of the distribution's
63:ref:`requirements`.
64
65
66Functional API
67==============
68
69This package provides the following functionality via its public API.
70
71
72.. _entry-points:
73
74Entry points
75------------
76
77The ``entry_points()`` function returns a dictionary of all entry points,
78keyed by group. Entry points are represented by ``EntryPoint`` instances;
79each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
Jason R. Coombs161541a2020-06-05 16:34:16 -040080a ``.load()`` method to resolve the value. There are also ``.module``,
81``.attr``, and ``.extras`` attributes for getting the components of the
82``.value`` attribute::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040083
84 >>> eps = entry_points() # doctest: +SKIP
85 >>> list(eps) # doctest: +SKIP
86 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
87 >>> scripts = eps['console_scripts'] # doctest: +SKIP
88 >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] # doctest: +SKIP
89 >>> wheel # doctest: +SKIP
90 EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
Jason R. Coombs161541a2020-06-05 16:34:16 -040091 >>> wheel.module # doctest: +SKIP
92 'wheel.cli'
93 >>> wheel.attr # doctest: +SKIP
94 'main'
95 >>> wheel.extras # doctest: +SKIP
96 []
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -040097 >>> main = wheel.load() # doctest: +SKIP
98 >>> main # doctest: +SKIP
99 <function main at 0x103528488>
100
101The ``group`` and ``name`` are arbitrary values defined by the package author
102and usually a client will wish to resolve all entry points for a particular
103group. Read `the setuptools docs
104<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
Jason R. Coombs161541a2020-06-05 16:34:16 -0400105for more information on entry points, their definition, and usage.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400106
107
108.. _metadata:
109
110Distribution metadata
111---------------------
112
113Every distribution includes some metadata, which you can extract using the
114``metadata()`` function::
115
116 >>> wheel_metadata = metadata('wheel') # doctest: +SKIP
117
Jason R. Coombsdfdca852020-12-31 12:56:43 -0500118The keys of the returned data structure, a ``PackageMetadata``,
119name the metadata keywords, and
120the values are returned unparsed from the distribution metadata::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400121
122 >>> wheel_metadata['Requires-Python'] # doctest: +SKIP
123 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
124
125
126.. _version:
127
128Distribution versions
129---------------------
130
131The ``version()`` function is the quickest way to get a distribution's version
132number, as a string::
133
134 >>> version('wheel') # doctest: +SKIP
135 '0.32.3'
136
137
138.. _files:
139
140Distribution files
141------------------
142
143You can also get the full set of files contained within a distribution. The
144``files()`` function takes a distribution package name and returns all of the
145files installed by this distribution. Each file object returned is a
Oleg Höflingcbd04082019-12-29 18:26:35 +0100146``PackagePath``, a :class:`pathlib.Path` derived object with additional ``dist``,
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400147``size``, and ``hash`` properties as indicated by the metadata. For example::
148
149 >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP
150 >>> util # doctest: +SKIP
151 PackagePath('wheel/util.py')
152 >>> util.size # doctest: +SKIP
153 859
154 >>> util.dist # doctest: +SKIP
155 <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
156 >>> util.hash # doctest: +SKIP
157 <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>
158
159Once you have the file, you can also read its contents::
160
161 >>> print(util.read_text()) # doctest: +SKIP
162 import base64
163 import sys
164 ...
165 def as_bytes(s):
166 if isinstance(s, text_type):
167 return s.encode('utf-8')
168 return s
169
Jason R. Coombs102e9b42019-09-02 11:08:03 -0400170In the case where the metadata file listing files
171(RECORD or SOURCES.txt) is missing, ``files()`` will
172return ``None``. The caller may wish to wrap calls to
173``files()`` in `always_iterable
174<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_
175or otherwise guard against this condition if the target
176distribution is not known to have the metadata present.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400177
178.. _requirements:
179
180Distribution requirements
181-------------------------
182
183To get the full set of requirements for a distribution, use the ``requires()``
Jason R. Coombs17499d82019-09-10 14:53:31 +0100184function::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400185
Jason R. Coombs17499d82019-09-10 14:53:31 +0100186 >>> requires('wheel') # doctest: +SKIP
187 ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400188
189
190Distributions
191=============
192
193While the above API is the most common and convenient usage, you can get all
194of that information from the ``Distribution`` class. A ``Distribution`` is an
195abstract object that represents the metadata for a Python package. You can
196get the ``Distribution`` instance::
197
198 >>> from importlib.metadata import distribution # doctest: +SKIP
199 >>> dist = distribution('wheel') # doctest: +SKIP
200
201Thus, an alternative way to get the version number is through the
202``Distribution`` instance::
203
204 >>> dist.version # doctest: +SKIP
205 '0.32.3'
206
207There are all kinds of additional metadata available on the ``Distribution``
208instance::
209
Tao He3631d6d2021-01-01 03:37:53 +0800210 >>> dist.metadata['Requires-Python'] # doctest: +SKIP
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400211 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
Tao He3631d6d2021-01-01 03:37:53 +0800212 >>> dist.metadata['License'] # doctest: +SKIP
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400213 'MIT'
214
Oleg Höflingcbd04082019-12-29 18:26:35 +0100215The full set of available metadata is not described here. See :pep:`566`
216for additional details.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400217
218
219Extending the search algorithm
220==============================
221
Oleg Höflingcbd04082019-12-29 18:26:35 +0100222Because package metadata is not available through :data:`sys.path` searches, or
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400223package loaders directly, the metadata for a package is found through import
Oleg Höflingcbd04082019-12-29 18:26:35 +0100224system :ref:`finders <finders-and-loaders>`. To find a distribution package's metadata,
225``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on
226:data:`sys.meta_path`.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400227
Jason R. Coombsb7a01092019-12-10 20:05:10 -0500228The default ``PathFinder`` for Python includes a hook that calls into
229``importlib.metadata.MetadataPathFinder`` for finding distributions
230loaded from typical file-system-based paths.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400231
232The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the
233interface expected of finders by Python's import system.
234``importlib.metadata`` extends this protocol by looking for an optional
235``find_distributions`` callable on the finders from
Oleg Höflingcbd04082019-12-29 18:26:35 +0100236:data:`sys.meta_path` and presents this extended interface as the
Jason R. Coombs17499d82019-09-10 14:53:31 +0100237``DistributionFinder`` abstract base class, which defines this abstract
238method::
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400239
Jason R. Coombs17499d82019-09-10 14:53:31 +0100240 @abc.abstractmethod
241 def find_distributions(context=DistributionFinder.Context()):
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400242 """Return an iterable of all Distribution instances capable of
Jason R. Coombs17499d82019-09-10 14:53:31 +0100243 loading the metadata for packages for the indicated ``context``.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400244 """
245
Jason R. Coombs17499d82019-09-10 14:53:31 +0100246The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
Jason R. Coombs161541a2020-06-05 16:34:16 -0400247properties indicating the path to search and name to match and may
Jason R. Coombs17499d82019-09-10 14:53:31 +0100248supply other relevant context.
249
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400250What this means in practice is that to support finding distribution package
Jason R. Coombsb7a01092019-12-10 20:05:10 -0500251metadata in locations other than the file system, subclass
252``Distribution`` and implement the abstract methods. Then from
253a custom finder, return instances of this derived ``Distribution`` in the
Jason R. Coombs17499d82019-09-10 14:53:31 +0100254``find_distributions()`` method.
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400255
256
257.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
258.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400259.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html
Jason R. Coombs1bbf7b62019-05-24 19:59:01 -0400260
261
262.. rubric:: Footnotes