diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2019-05-24 23:59:01 (GMT) |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 2019-05-24 23:59:01 (GMT) |
commit | 1bbf7b661f0ac8aac12d5531928d9a85c98ec1a9 (patch) | |
tree | c0c36b2d2756929655da47e4843188363886fce5 /Doc | |
parent | 6dbbe748e101a173b4cff8aada41e9313e287e0f (diff) | |
download | cpython-1bbf7b661f0ac8aac12d5531928d9a85c98ec1a9.zip cpython-1bbf7b661f0ac8aac12d5531928d9a85c98ec1a9.tar.gz cpython-1bbf7b661f0ac8aac12d5531928d9a85c98ec1a9.tar.bz2 |
bpo-34632: Add importlib.metadata (GH-12547)
Add importlib.metadata module as forward port of the standalone importlib_metadata.
Diffstat (limited to 'Doc')
-rw-r--r-- | Doc/library/importlib.metadata.rst | 257 | ||||
-rw-r--r-- | Doc/library/modules.rst | 1 | ||||
-rw-r--r-- | Doc/tools/susp-ignored.csv | 3 |
3 files changed, 261 insertions, 0 deletions
diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst new file mode 100644 index 0000000..3d10b5a --- /dev/null +++ b/Doc/library/importlib.metadata.rst @@ -0,0 +1,257 @@ +.. _using: + +========================== + Using importlib.metadata +========================== + +.. note:: + This functionality is provisional and may deviate from the usual + version semantics of the standard library. + +``importlib.metadata`` is a library that provides for access to installed +package metadata. Built in part on Python's import system, this library +intends to replace similar functionality in the `entry point +API`_ and `metadata API`_ of ``pkg_resources``. Along with +``importlib.resources`` in `Python 3.7 +and newer`_ (backported as `importlib_resources`_ for older versions of +Python), this can eliminate the need to use the older and less efficient +``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`` +directory, and metadata defined by `PEP 566`_ or its older specifications. +By default, package metadata can live on the file system or in zip archives on +``sys.path``. Through an extension mechanism, the metadata can live almost +anywhere. + + +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 +something into it:: + +.. highlight:: none + + $ python3 -m venv example + $ source example/bin/activate + (example) $ pip install wheel + +You can get the version string for ``wheel`` by running the following:: + +.. highlight:: none + + (example) $ python + >>> from importlib.metadata import version # doctest: +SKIP + >>> version('wheel') # doctest: +SKIP + '0.32.3' + +You can also get the set of entry points keyed by group, such as +``console_scripts``, ``distutils.commands`` and others. Each group contains a +sequence of :ref:`EntryPoint <entry-points>` objects. + +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 +------------ + +The ``entry_points()`` function returns a dictionary of all entry points, +keyed by group. Entry points are represented by ``EntryPoint`` instances; +each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and +a ``.load()`` method to resolve the value. + + >>> eps = entry_points() # doctest: +SKIP + >>> list(eps) # doctest: +SKIP + ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] + >>> scripts = eps['console_scripts'] # doctest: +SKIP + >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] # doctest: +SKIP + >>> wheel # doctest: +SKIP + EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') + >>> 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 +<https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins>`_ +for more information on entrypoints, their definition, and usage. + + +.. _metadata: + +Distribution metadata +--------------------- + +Every distribution includes some metadata, which you can extract using the +``metadata()`` function:: + + >>> wheel_metadata = metadata('wheel') # doctest: +SKIP + +The keys of the returned data structure [#f1]_ name the metadata keywords, and +their values are returned unparsed from the distribution metadata:: + + >>> wheel_metadata['Requires-Python'] # doctest: +SKIP + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + + +.. _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 +``PackagePath``, a `pathlib.Path`_ derived object with additional ``dist``, +``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 + + +.. _requirements: + +Distribution requirements +------------------------- + +To get the full set of requirements for a distribution, use the ``requires()`` +function. Note that this returns an iterator:: + + >>> list(requires('wheel')) # doctest: +SKIP + ["pytest (>=3.0.0) ; extra == 'test'"] + + +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:: + + >>> d.metadata['Requires-Python'] # doctest: +SKIP + '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' + >>> d.metadata['License'] # doctest: +SKIP + 'MIT' + +The full set of available metadata is not described here. See `PEP 566 +<https://www.python.org/dev/peps/pep-0566/>`_ for additional details. + + +Extending the search algorithm +============================== + +Because package metadata is not available through ``sys.path`` searches, or +package loaders directly, the metadata for a package is found through import +system `finders`_. To find a distribution package's metadata, +``importlib.metadata`` queries the list of `meta path finders`_ on +`sys.meta_path`_. + +By default ``importlib.metadata`` installs a finder for distribution packages +found on the file system. This finder doesn't actually find any *packages*, +but it can find the packages' metadata. + +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 +``sys.meta_path``. If the finder has this method, it must return +an iterator over instances of the ``Distribution`` abstract class. This +method must have the signature:: + + def find_distributions(name=None, path=None): + """Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the name + (or all names if not supplied) along the paths in the list + of directories ``path`` (defaults to sys.path). + """ + +What this means in practice is that to support finding distribution package +metadata in locations other than the file system, you should derive from +``Distribution`` and implement the ``load_metadata()`` method. This takes a +single argument which is the name of the package whose metadata is being +found. This instance of the ``Distribution`` base abstract class is what your +finder's ``find_distributions()`` method should return. + + +.. _`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 +.. _`Python 3.7 and newer`: https://docs.python.org/3/library/importlib.html#module-importlib.resources +.. _`importlib_resources`: https://importlib-resources.readthedocs.io/en/latest/index.html +.. _`PEP 566`: https://www.python.org/dev/peps/pep-0566/ +.. _`finders`: https://docs.python.org/3/reference/import.html#finders-and-loaders +.. _`meta path finders`: https://docs.python.org/3/glossary.html#term-meta-path-finder +.. _`sys.meta_path`: https://docs.python.org/3/library/sys.html#sys.meta_path +.. _`pathlib.Path`: https://docs.python.org/3/library/pathlib.html#pathlib.Path + + +.. rubric:: Footnotes + +.. [#f1] Technically, the returned distribution metadata object is an + `email.message.Message + <https://docs.python.org/3/library/email.message.html#email.message.EmailMessage>`_ + instance, but this is an implementation detail, and not part of the + stable API. You should only use dictionary-like methods and syntax + to access the metadata contents. diff --git a/Doc/library/modules.rst b/Doc/library/modules.rst index 6b2a40a..565ce05 100644 --- a/Doc/library/modules.rst +++ b/Doc/library/modules.rst @@ -17,3 +17,4 @@ The full list of modules described in this chapter is: modulefinder.rst runpy.rst importlib.rst + importlib.metadata.rst diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv index 31b2266..85ff9d0 100644 --- a/Doc/tools/susp-ignored.csv +++ b/Doc/tools/susp-ignored.csv @@ -350,3 +350,6 @@ whatsnew/3.7,,::,error::BytesWarning whatsnew/changelog,,::,error::BytesWarning whatsnew/changelog,,::,default::BytesWarning whatsnew/changelog,,::,default::DeprecationWarning +library/importlib.metadata,,.. highlight:,.. highlight:: none +library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')" +library/importlib.metadata,,`,of directories ``path`` (defaults to sys.path). |