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

Jason R. Coombs webhook-mailer at python.org
Thu Oct 15 17:05:21 EDT 2020


https://github.com/python/cpython/commit/967fddae2fe48f297563c358bdbdde1e2cfed4ee
commit: 967fddae2fe48f297563c358bdbdde1e2cfed4ee
branch: 3.8
author: Jason R. Coombs <jaraco at jaraco.com>
committer: GitHub <noreply at github.com>
date: 2020-10-15T17:05:12-04:00
summary:

[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

files:
A Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst
M Doc/library/importlib.metadata.rst
M Doc/reference/import.rst
M Lib/importlib/metadata.py
M Lib/test/test_importlib/test_zip.py

diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst
index dc6b66ca384d0..15e58b860d97d 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -1,8 +1,8 @@
 .. _using:
 
-==========================
- Using importlib.metadata
-==========================
+=================================
+ Using :mod:`!importlib.metadata`
+=================================
 
 .. note::
    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
 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.
 
@@ -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
 <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.
 
 
@@ -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
@@ -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
@@ -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::
 
@@ -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.
diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst
index 1c98aab7d83aa..ca232007dd7f0 100644
--- a/Doc/reference/import.rst
+++ b/Doc/reference/import.rst
@@ -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.
 
 
+.. _finders-and-loaders:
+
 Finders and loaders
 -------------------
 
diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py
index 831f593277ccd..9d2285cb4d46d 100644
--- a/Lib/importlib/metadata.py
+++ b/Lib/importlib/metadata.py
@@ -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
             )
 
@@ -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.
diff --git a/Lib/test/test_importlib/test_zip.py b/Lib/test/test_importlib/test_zip.py
index 9568c226af033..18d39f69aee97 100644
--- a/Lib/test/test_importlib/test_zip.py
+++ b/Lib/test/test_importlib/test_zip.py
@@ -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
 
+
+ at requires_zlib
 class TestZip(unittest.TestCase):
     root = 'test.test_importlib.data'
 
@@ -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
+
 
+ at requires_zlib
 class TestEgg(TestZip):
     def setUp(self):
         # Find the path to the example-*.egg so we can add it to the front of
diff --git a/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst b/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst
new file mode 100644
index 0000000000000..c499c10e66120
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-09-24-16-45-59.bpo-41855.q6Y1nm.rst
@@ -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>`_.



More information about the Python-checkins mailing list