-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
bpo-34632: Add importlib.metadata #12547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
warsaw
merged 27 commits into
python:master
from
jaraco:feature/34632-importlib-metadata
May 24, 2019
Merged
Changes from 6 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
a1d873b
Commit importlib.metadata as found at https://gitlab.com/python-devs/…
jaraco 6d7e3a8
Update implementation based on importlib_metadata 0.10
jaraco dc500dd
Merge fixes to docs syntax
jaraco f0cad2b
Rename doc to appear under the correct name
jaraco a630dfa
Add reference to importlib.metadata
jaraco 9bde7fa
Update with latest changes from importlib_metadata
jaraco 175603f
Merge branch 'master' into feature/34632-importlib-metadata
jaraco 2347d3a
Merge remote-tracking branch 'origin/master' into feature/34632-impor…
jaraco 6816cc0
Add ignores to suspicious check
jaraco 071cbd4
Update with latest changes from importlib_metadata
jaraco 21eaaf4
Apply changes from importlib_metadata 0.11
jaraco a277d85
Merge branch 'master' into feature/34632-importlib-metadata
jaraco 58533a7
Refresh from importlib_metadata@cpython
jaraco 3b19b9d
Inline the find_distributions functionality on PathFinder.
jaraco 6e5cc29
Disable all the doctests
jaraco 8f04bf5
📜🤖 Added by blurb_it.
blurb-it[bot] 7f9924a
make regen-all
jaraco 34142cd
Apply changes from importlib_metadata 0.12.
jaraco 569e1ac
Make regen-all
jaraco 2a6b361
Add provisional disclaimer
jaraco b462010
Refresh from importlib_metadata@cpython
jaraco c2bf9a5
Refresh from importlib_metadata@cpython
jaraco dcb580f
Add missing file
jaraco 1b52d5b
Apply changes from importlib_metadata
jaraco de0f89d
Apply changes from importlib_metadata refresh.
jaraco a1c3d9c
Merge branch 'master' into feature/34632-importlib-metadata
jaraco ccbccce
Refresh from importlib_metadata@cpython (0.15)
jaraco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| .. _using: | ||
|
|
||
| ========================== | ||
| Using importlib.metadata | ||
| ========================== | ||
|
|
||
| ``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 wheels on | ||
| ``sys.path``. Through an extension mechanism, the metadata can live almost | ||
| anywhere. | ||
|
|
||
| .. note:: Although this package supports loading metadata from wheels | ||
| on ``sys.path``, that support is provisional and does not serve to | ||
| contravene the `PEP 427 directive | ||
| <https://www.python.org/dev/peps/pep-0427/#is-it-possible-to-import-python-code-directly-from-a-wheel-file>`_, | ||
| which states that relying on this format is discouraged, and use is | ||
| at your own risk. | ||
|
|
||
|
|
||
| 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 | ||
| >>> version('wheel') | ||
| '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')) | ||
| ['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() | ||
| >>> list(eps) | ||
| ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] | ||
| >>> scripts = eps['console_scripts'] | ||
| >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] | ||
| >>> wheel | ||
| EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') | ||
| >>> main = wheel.load() | ||
| >>> main | ||
| <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') | ||
|
|
||
| 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'] | ||
| '>=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') | ||
| '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] | ||
| >>> util | ||
| PackagePath('wheel/util.py') | ||
| >>> util.size | ||
| 859 | ||
| >>> util.dist | ||
| <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0> | ||
| >>> util.hash | ||
| <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI> | ||
|
|
||
| Once you have the file, you can also read its contents:: | ||
|
|
||
| >>> print(util.read_text()) | ||
| 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')) | ||
| ["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 | ||
| >>> dist = distribution('wheel') | ||
|
|
||
| Thus, an alternative way to get the version number is through the | ||
| ``Distribution`` instance:: | ||
|
|
||
| >>> dist.version | ||
| '0.32.3' | ||
|
|
||
| There are all kinds of additional metadata available on the ``Distribution`` | ||
| instance:: | ||
|
|
||
| >>> d.metadata['Requires-Python'] | ||
| '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' | ||
| >>> d.metadata['License'] | ||
| '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=sys.path): | ||
| """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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| from .api import distribution, Distribution, PackageNotFoundError # noqa: F401 | ||
| from .api import ( | ||
| metadata, entry_points, version, files, requires, distributions, | ||
| ) | ||
|
|
||
| # Import for installation side-effects. | ||
| from . import _hooks # noqa: F401 | ||
|
|
||
|
|
||
| __all__ = [ | ||
| 'entry_points', | ||
| 'files', | ||
| 'metadata', | ||
| 'requires', | ||
| 'version', | ||
| 'distributions', | ||
| ] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We actually changed this signature to
find_distributions(name=None, path=None)to account for changes insys.pathafter the function's definition. See this MR.