1.. _using: 2 3================================= 4 Using :mod:`!importlib.metadata` 5================================= 6 7.. module:: importlib.metadata 8 :synopsis: The implementation of the importlib metadata. 9 10.. versionadded:: 3.8 11 12**Source code:** :source:`Lib/importlib/metadata.py` 13 14.. note:: 15 This functionality is provisional and may deviate from the usual 16 version semantics of the standard library. 17 18``importlib.metadata`` is a library that provides for access to installed 19package metadata. Built in part on Python's import system, this library 20intends to replace similar functionality in the `entry point 21API`_ and `metadata API`_ of ``pkg_resources``. Along with 22:mod:`importlib.resources` in Python 3.7 23and newer (backported as `importlib_resources`_ for older versions of 24Python), this can eliminate the need to use the older and less efficient 25``pkg_resources`` package. 26 27By "installed package" we generally mean a third-party package installed into 28Python's ``site-packages`` directory via tools such as `pip 29<https://pypi.org/project/pip/>`_. Specifically, 30it means a package with either a discoverable ``dist-info`` or ``egg-info`` 31directory, and metadata defined by :pep:`566` or its older specifications. 32By default, package metadata can live on the file system or in zip archives on 33:data:`sys.path`. Through an extension mechanism, the metadata can live almost 34anywhere. 35 36 37Overview 38======== 39 40Let's say you wanted to get the version string for a package you've installed 41using ``pip``. We start by creating a virtual environment and installing 42something into it: 43 44.. code-block:: shell-session 45 46 $ python3 -m venv example 47 $ source example/bin/activate 48 (example) $ pip install wheel 49 50You can get the version string for ``wheel`` by running the following: 51 52.. code-block:: pycon 53 54 (example) $ python 55 >>> from importlib.metadata import version # doctest: +SKIP 56 >>> version('wheel') # doctest: +SKIP 57 '0.32.3' 58 59You can also get the set of entry points keyed by group, such as 60``console_scripts``, ``distutils.commands`` and others. Each group contains a 61sequence of :ref:`EntryPoint <entry-points>` objects. 62 63You can get the :ref:`metadata for a distribution <metadata>`:: 64 65 >>> list(metadata('wheel')) # doctest: +SKIP 66 ['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'] 67 68You can also get a :ref:`distribution's version number <version>`, list its 69:ref:`constituent files <files>`, and get a list of the distribution's 70:ref:`requirements`. 71 72 73Functional API 74============== 75 76This package provides the following functionality via its public API. 77 78 79.. _entry-points: 80 81Entry points 82------------ 83 84The ``entry_points()`` function returns a dictionary of all entry points, 85keyed by group. Entry points are represented by ``EntryPoint`` instances; 86each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and 87a ``.load()`` method to resolve the value. 88 89 >>> eps = entry_points() # doctest: +SKIP 90 >>> list(eps) # doctest: +SKIP 91 ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] 92 >>> scripts = eps['console_scripts'] # doctest: +SKIP 93 >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] # doctest: +SKIP 94 >>> wheel # doctest: +SKIP 95 EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') 96 >>> main = wheel.load() # doctest: +SKIP 97 >>> main # doctest: +SKIP 98 <function main at 0x103528488> 99 100The ``group`` and ``name`` are arbitrary values defined by the package author 101and usually a client will wish to resolve all entry points for a particular 102group. Read `the setuptools docs 103<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_ 104for more information on entrypoints, their definition, and usage. 105 106 107.. _metadata: 108 109Distribution metadata 110--------------------- 111 112Every distribution includes some metadata, which you can extract using the 113``metadata()`` function:: 114 115 >>> wheel_metadata = metadata('wheel') # doctest: +SKIP 116 117The keys of the returned data structure [#f1]_ name the metadata keywords, and 118their values are returned unparsed from the distribution metadata:: 119 120 >>> wheel_metadata['Requires-Python'] # doctest: +SKIP 121 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' 122 123 124.. _version: 125 126Distribution versions 127--------------------- 128 129The ``version()`` function is the quickest way to get a distribution's version 130number, as a string:: 131 132 >>> version('wheel') # doctest: +SKIP 133 '0.32.3' 134 135 136.. _files: 137 138Distribution files 139------------------ 140 141You can also get the full set of files contained within a distribution. The 142``files()`` function takes a distribution package name and returns all of the 143files installed by this distribution. Each file object returned is a 144``PackagePath``, a :class:`pathlib.Path` derived object with additional ``dist``, 145``size``, and ``hash`` properties as indicated by the metadata. For example:: 146 147 >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP 148 >>> util # doctest: +SKIP 149 PackagePath('wheel/util.py') 150 >>> util.size # doctest: +SKIP 151 859 152 >>> util.dist # doctest: +SKIP 153 <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0> 154 >>> util.hash # doctest: +SKIP 155 <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI> 156 157Once you have the file, you can also read its contents:: 158 159 >>> print(util.read_text()) # doctest: +SKIP 160 import base64 161 import sys 162 ... 163 def as_bytes(s): 164 if isinstance(s, text_type): 165 return s.encode('utf-8') 166 return s 167 168In the case where the metadata file listing files 169(RECORD or SOURCES.txt) is missing, ``files()`` will 170return ``None``. The caller may wish to wrap calls to 171``files()`` in `always_iterable 172<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_ 173or otherwise guard against this condition if the target 174distribution is not known to have the metadata present. 175 176.. _requirements: 177 178Distribution requirements 179------------------------- 180 181To get the full set of requirements for a distribution, use the ``requires()`` 182function:: 183 184 >>> requires('wheel') # doctest: +SKIP 185 ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] 186 187 188Distributions 189============= 190 191While the above API is the most common and convenient usage, you can get all 192of that information from the ``Distribution`` class. A ``Distribution`` is an 193abstract object that represents the metadata for a Python package. You can 194get the ``Distribution`` instance:: 195 196 >>> from importlib.metadata import distribution # doctest: +SKIP 197 >>> dist = distribution('wheel') # doctest: +SKIP 198 199Thus, an alternative way to get the version number is through the 200``Distribution`` instance:: 201 202 >>> dist.version # doctest: +SKIP 203 '0.32.3' 204 205There are all kinds of additional metadata available on the ``Distribution`` 206instance:: 207 208 >>> dist.metadata['Requires-Python'] # doctest: +SKIP 209 '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' 210 >>> dist.metadata['License'] # doctest: +SKIP 211 'MIT' 212 213The full set of available metadata is not described here. See :pep:`566` 214for additional details. 215 216 217Extending the search algorithm 218============================== 219 220Because package metadata is not available through :data:`sys.path` searches, or 221package loaders directly, the metadata for a package is found through import 222system :ref:`finders <finders-and-loaders>`. To find a distribution package's metadata, 223``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on 224:data:`sys.meta_path`. 225 226The default ``PathFinder`` for Python includes a hook that calls into 227``importlib.metadata.MetadataPathFinder`` for finding distributions 228loaded from typical file-system-based paths. 229 230The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the 231interface expected of finders by Python's import system. 232``importlib.metadata`` extends this protocol by looking for an optional 233``find_distributions`` callable on the finders from 234:data:`sys.meta_path` and presents this extended interface as the 235``DistributionFinder`` abstract base class, which defines this abstract 236method:: 237 238 @abc.abstractmethod 239 def find_distributions(context=DistributionFinder.Context()): 240 """Return an iterable of all Distribution instances capable of 241 loading the metadata for packages for the indicated ``context``. 242 """ 243 244The ``DistributionFinder.Context`` object provides ``.path`` and ``.name`` 245properties indicating the path to search and names to match and may 246supply other relevant context. 247 248What this means in practice is that to support finding distribution package 249metadata in locations other than the file system, subclass 250``Distribution`` and implement the abstract methods. Then from 251a custom finder, return instances of this derived ``Distribution`` in the 252``find_distributions()`` method. 253 254 255.. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points 256.. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api 257.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html 258 259 260.. rubric:: Footnotes 261 262.. [#f1] Technically, the returned distribution metadata object is an 263 :class:`email.message.EmailMessage` 264 instance, but this is an implementation detail, and not part of the 265 stable API. You should only use dictionary-like methods and syntax 266 to access the metadata contents. 267