[Python-checkins] bpo-39791: Refresh importlib.metadata from importlib_metadata 1.6.1. (GH-20659)

Jason R. Coombs webhook-mailer at python.org
Fri Jun 5 16:34:31 EDT 2020


https://github.com/python/cpython/commit/161541ab45278df6603dd870113b10f13e4d9e16
commit: 161541ab45278df6603dd870113b10f13e4d9e16
branch: master
author: Jason R. Coombs <jaraco at jaraco.com>
committer: GitHub <noreply at github.com>
date: 2020-06-05T16:34:16-04:00
summary:

bpo-39791: Refresh importlib.metadata from importlib_metadata 1.6.1. (GH-20659)

* Refresh importlib.metadata from importlib_metadata 1.6.1.

* 📜🤖 Added by blurb_it.

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>

files:
A Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst
M Doc/library/importlib.metadata.rst
M Lib/importlib/metadata.py
M Lib/test/test_importlib/fixtures.py
M Lib/test/test_importlib/test_main.py
M Lib/test/test_importlib/test_zip.py

diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst
index 15e58b860d97d..21da143f3bebf 100644
--- a/Doc/library/importlib.metadata.rst
+++ b/Doc/library/importlib.metadata.rst
@@ -77,7 +77,9 @@ 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.
+a ``.load()`` method to resolve the value.  There are also ``.module``,
+``.attr``, and ``.extras`` attributes for getting the components of the
+``.value`` attribute::
 
     >>> eps = entry_points()  # doctest: +SKIP
     >>> list(eps)  # doctest: +SKIP
@@ -86,6 +88,12 @@ a ``.load()`` method to resolve the value.
     >>> 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')
+    >>> wheel.module  # doctest: +SKIP
+    'wheel.cli'
+    >>> wheel.attr  # doctest: +SKIP
+    'main'
+    >>> wheel.extras  # doctest: +SKIP
+    []
     >>> main = wheel.load()  # doctest: +SKIP
     >>> main  # doctest: +SKIP
     <function main at 0x103528488>
@@ -94,7 +102,7 @@ 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.
+for more information on entry points, their definition, and usage.
 
 
 .. _metadata:
@@ -235,7 +243,7 @@ method::
         """
 
 The ``DistributionFinder.Context`` object provides ``.path`` and ``.name``
-properties indicating the path to search and names to match and may
+properties indicating the path to search and name to match and may
 supply other relevant context.
 
 What this means in practice is that to support finding distribution package
diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py
index 831f593277ccd..ffa0cba45706d 100644
--- a/Lib/importlib/metadata.py
+++ b/Lib/importlib/metadata.py
@@ -78,6 +78,16 @@ def load(self):
         attrs = filter(None, (match.group('attr') or '').split('.'))
         return functools.reduce(getattr, attrs, module)
 
+    @property
+    def module(self):
+        match = self.pattern.match(self.value)
+        return match.group('module')
+
+    @property
+    def attr(self):
+        match = self.pattern.match(self.value)
+        return match.group('attr')
+
     @property
     def extras(self):
         match = self.pattern.match(self.value)
@@ -170,7 +180,7 @@ def from_name(cls, name):
         """
         for resolver in cls._discover_resolvers():
             dists = resolver(DistributionFinder.Context(name=name))
-            dist = next(dists, None)
+            dist = next(iter(dists), None)
             if dist is not None:
                 return dist
         else:
@@ -213,6 +223,17 @@ def _discover_resolvers():
             )
         return filter(None, declared)
 
+    @classmethod
+    def _local(cls, root='.'):
+        from pep517 import build, meta
+        system = build.compat_system(root)
+        builder = functools.partial(
+            meta.build,
+            source_dir=root,
+            system=system,
+            )
+        return PathDistribution(zipfile.Path(meta.build_as_zip(builder)))
+
     @property
     def metadata(self):
         """Return the parsed metadata for this Distribution.
@@ -391,7 +412,7 @@ class FastPath:
 
     def __init__(self, root):
         self.root = root
-        self.base = os.path.basename(root).lower()
+        self.base = os.path.basename(self.root).lower()
 
     def joinpath(self, child):
         return pathlib.Path(self.root, child)
@@ -408,8 +429,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 +496,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/fixtures.py b/Lib/test/test_importlib/fixtures.py
index d923cec26ea8f..b25febb7fe756 100644
--- a/Lib/test/test_importlib/fixtures.py
+++ b/Lib/test/test_importlib/fixtures.py
@@ -161,6 +161,21 @@ def setUp(self):
         build_files(EggInfoFile.files, prefix=self.site_dir)
 
 
+class LocalPackage:
+    files = {
+        "setup.py": """
+            import setuptools
+            setuptools.setup(name="local-pkg", version="2.0.1")
+            """,
+        }
+
+    def setUp(self):
+        self.fixtures = contextlib.ExitStack()
+        self.addCleanup(self.fixtures.close)
+        self.fixtures.enter_context(tempdir_as_cwd())
+        build_files(self.files)
+
+
 def build_files(file_defs, prefix=pathlib.Path()):
     """Build a set of files/directories, as described by the
 
diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py
index 42a79992ecc8c..7b18c3de16eea 100644
--- a/Lib/test/test_importlib/test_main.py
+++ b/Lib/test/test_importlib/test_main.py
@@ -246,3 +246,19 @@ def test_json_dump(self):
         """
         with self.assertRaises(Exception):
             json.dumps(self.ep)
+
+    def test_module(self):
+        assert self.ep.module == 'value'
+
+    def test_attr(self):
+        assert self.ep.attr is None
+
+
+class FileSystem(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
+    def test_unicode_dir_on_sys_path(self):
+        """
+        Ensure a Unicode subdirectory of a directory on sys.path
+        does not crash.
+        """
+        fixtures.build_files({'☃': {}}, prefix=self.site_dir)
+        list(distributions())
diff --git a/Lib/test/test_importlib/test_zip.py b/Lib/test/test_importlib/test_zip.py
index fa87cd7cb1096..a5399c16682fb 100644
--- a/Lib/test/test_importlib/test_zip.py
+++ b/Lib/test/test_importlib/test_zip.py
@@ -3,9 +3,10 @@
 
 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 importlib import resources
 
 from test.support import requires_zlib
 
@@ -14,15 +15,19 @@
 class TestZip(unittest.TestCase):
     root = 'test.test_importlib.data'
 
+    def _fixture_on_path(self, filename):
+        pkg_file = resources.files(self.root).joinpath(filename)
+        file = self.resources.enter_context(resources.as_file(pkg_file))
+        assert file.name.startswith('example-'), file.name
+        sys.path.insert(0, str(file))
+        self.resources.callback(sys.path.pop, 0)
+
     def setUp(self):
         # Find the path to the example-*.whl so we can add it to the front of
         # sys.path, where we'll then try to find the metadata thereof.
         self.resources = ExitStack()
         self.addCleanup(self.resources.close)
-        wheel = self.resources.enter_context(
-            path(self.root, 'example-21.12-py3-none-any.whl'))
-        sys.path.insert(0, str(wheel))
-        self.resources.callback(sys.path.pop, 0)
+        self._fixture_on_path('example-21.12-py3-none-any.whl')
 
     def test_zip_version(self):
         self.assertEqual(version('example'), '21.12')
@@ -49,6 +54,10 @@ 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):
@@ -57,10 +66,7 @@ def setUp(self):
         # sys.path, where we'll then try to find the metadata thereof.
         self.resources = ExitStack()
         self.addCleanup(self.resources.close)
-        egg = self.resources.enter_context(
-            path(self.root, 'example-21.12-py3.6.egg'))
-        sys.path.insert(0, str(egg))
-        self.resources.callback(sys.path.pop, 0)
+        self._fixture_on_path('example-21.12-py3.6.egg')
 
     def test_files(self):
         for file in files('example'):
diff --git a/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst b/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst
new file mode 100644
index 0000000000000..73e0cbb013f84
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-06-05-19-29-10.bpo-39791._CcO3d.rst
@@ -0,0 +1 @@
+Refresh importlib.metadata from importlib_metadata 1.6.1.
\ No newline at end of file



More information about the Python-checkins mailing list