Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 16 additions & 23 deletions Doc/library/importlib.metadata.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.. _using:

==========================
Using importlib.metadata
==========================
=================================
Using :mod:`!importlib.metadata`
=================================

.. note::
This functionality is provisional and may deviate from the usual
Expand All @@ -12,18 +12,18 @@
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
:mod:`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.
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
:data:`sys.path`. Through an extension mechanism, the metadata can live almost
anywhere.


Expand Down Expand Up @@ -134,7 +134,7 @@ 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``,
``PackagePath``, a :class:`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
Expand Down Expand Up @@ -203,18 +203,18 @@ instance::
>>> 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.
The full set of available metadata is not described here. See :pep:`566`
for additional details.


Extending the search algorithm
==============================

Because package metadata is not available through ``sys.path`` searches, or
Because package metadata is not available through :data:`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`_.
system :ref:`finders <finders-and-loaders>`. To find a distribution package's metadata,
``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on
:data:`sys.meta_path`.

The default ``PathFinder`` for Python includes a hook that calls into
``importlib.metadata.MetadataPathFinder`` for finding distributions
Expand All @@ -224,7 +224,7 @@ 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`` and presents this extended interface as the
:data:`sys.meta_path` and presents this extended interface as the
``DistributionFinder`` abstract base class, which defines this abstract
method::

Expand All @@ -247,20 +247,13 @@ a custom finder, return instances of this derived ``Distribution`` in the

.. _`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>`_
:class:`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.
5 changes: 2 additions & 3 deletions Lib/importlib/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,8 @@ def zip_children(self):
names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath

return (
posixpath.split(child)[0]
return dict.fromkeys(
child.split(posixpath.sep, 1)[0]
for child in names
)

Expand Down Expand Up @@ -475,7 +475,6 @@ def _search_paths(cls, name, paths):
)



class PathDistribution(Distribution):
def __init__(self, path):
"""Construct a distribution from a path to the metadata directory.
Expand Down
11 changes: 10 additions & 1 deletion Lib/test/test_importlib/test_zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@

from contextlib import ExitStack
from importlib.metadata import (
distribution, entry_points, files, PackageNotFoundError, version,
distribution, entry_points, files, PackageNotFoundError,
version, distributions,
)
from importlib.resources import path

from test.support import requires_zlib


@requires_zlib
class TestZip(unittest.TestCase):
root = 'test.test_importlib.data'

Expand Down Expand Up @@ -46,7 +50,12 @@ def test_files(self):
path = str(file.dist.locate_file(file))
assert '.whl/' in path, path

def test_one_distribution(self):
dists = list(distributions(path=sys.path[:1]))
assert len(dists) == 1


@requires_zlib
class TestEgg(TestZip):
def setUp(self):
# Find the path to the example-*.egg so we can add it to the front of
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
In ``importlib.metadata``, fix issue where multiple children can be returned
from ``FastPath.zip_children()``. Backport of
`python-devs/importlib_metadata#117
<https://gitlab.com/python-devs/importlib_metadata/-/issues/117>`_.