[3.8] bpo-41855: Fix duplicate results in FastPath.zip_children() (#22404)

* bpo-41855: Backport fixes from importlib_metadata 1.5.2.

* Add blurb.

* Add anchor for finders and loaders
This commit is contained in:
Jason R. Coombs 2020-10-15 17:05:12 -04:00 committed by GitHub
parent aeb66c1abb
commit 967fddae2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 27 deletions

View File

@ -1,8 +1,8 @@
.. _using: .. _using:
========================== =================================
Using importlib.metadata Using :mod:`!importlib.metadata`
========================== =================================
.. note:: .. note::
This functionality is provisional and may deviate from the usual This functionality is provisional and may deviate from the usual
@ -12,8 +12,8 @@
package metadata. Built in part on Python's import system, this library package metadata. Built in part on Python's import system, this library
intends to replace similar functionality in the `entry point intends to replace similar functionality in the `entry point
API`_ and `metadata API`_ of ``pkg_resources``. Along with API`_ and `metadata API`_ of ``pkg_resources``. Along with
``importlib.resources`` in `Python 3.7 :mod:`importlib.resources` in Python 3.7
and newer`_ (backported as `importlib_resources`_ for older versions of and newer (backported as `importlib_resources`_ for older versions of
Python), this can eliminate the need to use the older and less efficient Python), this can eliminate the need to use the older and less efficient
``pkg_resources`` package. ``pkg_resources`` package.
@ -21,9 +21,9 @@ By "installed package" we generally mean a third-party package installed into
Python's ``site-packages`` directory via tools such as `pip Python's ``site-packages`` directory via tools such as `pip
<https://pypi.org/project/pip/>`_. Specifically, <https://pypi.org/project/pip/>`_. Specifically,
it means a package with either a discoverable ``dist-info`` or ``egg-info`` 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 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. anywhere.
@ -134,7 +134,7 @@ Distribution files
You can also get the full set of files contained within a distribution. The 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()`` function takes a distribution package name and returns all of the
files installed by this distribution. Each file object returned is a 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:: ``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 = [p for p in files('wheel') if 'util.py' in str(p)][0] # doctest: +SKIP
@ -203,18 +203,18 @@ instance::
>>> d.metadata['License'] # doctest: +SKIP >>> d.metadata['License'] # doctest: +SKIP
'MIT' 'MIT'
The full set of available metadata is not described here. See `PEP 566 The full set of available metadata is not described here. See :pep:`566`
<https://www.python.org/dev/peps/pep-0566/>`_ for additional details. for additional details.
Extending the search algorithm 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 package loaders directly, the metadata for a package is found through import
system `finders`_. To find a distribution package's metadata, system :ref:`finders <finders-and-loaders>`. To find a distribution package's metadata,
``importlib.metadata`` queries the list of `meta path finders`_ on ``importlib.metadata`` queries the list of :term:`meta path finders <meta path finder>` on
`sys.meta_path`_. :data:`sys.meta_path`.
The default ``PathFinder`` for Python includes a hook that calls into The default ``PathFinder`` for Python includes a hook that calls into
``importlib.metadata.MetadataPathFinder`` for finding distributions ``importlib.metadata.MetadataPathFinder`` for finding distributions
@ -224,7 +224,7 @@ The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the
interface expected of finders by Python's import system. interface expected of finders by Python's import system.
``importlib.metadata`` extends this protocol by looking for an optional ``importlib.metadata`` extends this protocol by looking for an optional
``find_distributions`` callable on the finders from ``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 ``DistributionFinder`` abstract base class, which defines this abstract
method:: method::
@ -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 .. _`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 .. _`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 .. _`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 .. rubric:: Footnotes
.. [#f1] Technically, the returned distribution metadata object is an .. [#f1] Technically, the returned distribution metadata object is an
`email.message.Message :class:`email.message.EmailMessage`
<https://docs.python.org/3/library/email.message.html#email.message.EmailMessage>`_
instance, but this is an implementation detail, and not part of the instance, but this is an implementation detail, and not part of the
stable API. You should only use dictionary-like methods and syntax stable API. You should only use dictionary-like methods and syntax
to access the metadata contents. to access the metadata contents.

View File

@ -202,6 +202,8 @@ named module, the two module objects will *not* be the same. By contrast,
reinitialise the module contents by rerunning the module's code. reinitialise the module contents by rerunning the module's code.
.. _finders-and-loaders:
Finders and loaders Finders and loaders
------------------- -------------------

View File

@ -408,8 +408,8 @@ class FastPath:
names = zip_path.root.namelist() names = zip_path.root.namelist()
self.joinpath = zip_path.joinpath self.joinpath = zip_path.joinpath
return ( return dict.fromkeys(
posixpath.split(child)[0] child.split(posixpath.sep, 1)[0]
for child in names for child in names
) )
@ -475,7 +475,6 @@ class MetadataPathFinder(DistributionFinder):
) )
class PathDistribution(Distribution): class PathDistribution(Distribution):
def __init__(self, path): def __init__(self, path):
"""Construct a distribution from a path to the metadata directory. """Construct a distribution from a path to the metadata directory.

View File

@ -3,11 +3,15 @@ import unittest
from contextlib import ExitStack from contextlib import ExitStack
from importlib.metadata import ( from importlib.metadata import (
distribution, entry_points, files, PackageNotFoundError, version, distribution, entry_points, files, PackageNotFoundError,
version, distributions,
) )
from importlib.resources import path from importlib.resources import path
from test.support import requires_zlib
@requires_zlib
class TestZip(unittest.TestCase): class TestZip(unittest.TestCase):
root = 'test.test_importlib.data' root = 'test.test_importlib.data'
@ -46,7 +50,12 @@ class TestZip(unittest.TestCase):
path = str(file.dist.locate_file(file)) path = str(file.dist.locate_file(file))
assert '.whl/' in path, path 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): class TestEgg(TestZip):
def setUp(self): def setUp(self):
# Find the path to the example-*.egg so we can add it to the front of # Find the path to the example-*.egg so we can add it to the front of

View File

@ -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>`_.