]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Apply changes from importlib_metadata 0.11
authorJason R. Coombs <jaraco@jaraco.com>
Fri, 10 May 2019 13:11:01 +0000 (09:11 -0400)
committerJason R. Coombs <jaraco@jaraco.com>
Fri, 10 May 2019 13:11:01 +0000 (09:11 -0400)
Doc/library/importlib.metadata.rst
Lib/importlib/metadata/__init__.py
Lib/importlib/metadata/_hooks.py
Lib/importlib/metadata/abc.py
Lib/importlib/metadata/api.py
Lib/test/test_importlib/test_main.py
Lib/test/test_importlib/test_metadata_api.py
Lib/test/test_importlib/test_zip.py

index ad17d939d715963d7166edf635ebec0cdd108f16..8a5b66d04e1201eac605fcdcff43514f3e00c552 100644 (file)
@@ -18,17 +18,10 @@ 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
+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
 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
 ========
index 314ece65afe751625d65f0b265463ee79c240e95..8d4b0a344cd054e951f8c804d2d006f4d958ceca 100644 (file)
@@ -1,17 +1,19 @@
-from .api import distribution, Distribution, PackageNotFoundError  # noqa: F401
 from .api import (
-    metadata, entry_points, version, files, requires, distributions,
-    )
+    Distribution, PackageNotFoundError, distribution, distributions,
+    entry_points, files, metadata, requires, version)
 
 # Import for installation side-effects.
 from . import _hooks  # noqa: F401
 
 
 __all__ = [
+    'Distribution',
+    'PackageNotFoundError',
+    'distribution',
+    'distributions',
     'entry_points',
     'files',
     'metadata',
     'requires',
     'version',
-    'distributions',
     ]
index e624844217dfdfe7892ace61abb0c724b98dca1f..f6bed1a67fd3c5f26d5a4aad709ca247bc02556d 100644 (file)
@@ -25,12 +25,14 @@ class NullFinder(DistributionFinder):
         return None
 
 
-class MetadataPathBaseFinder(NullFinder):
+@install
+class MetadataPathFinder(NullFinder):
     """A degenerate finder for distribution packages on the file system.
 
     This finder supplies only a find_distributions() method for versions
     of Python that do not have a PathFinder find_distributions().
     """
+    search_template = r'{pattern}(-.*)?\.(dist|egg)-info'
 
     def find_distributions(self, name=None, path=None):
         """Return an iterable of all Distribution instances capable of
@@ -51,9 +53,15 @@ class MetadataPathBaseFinder(NullFinder):
         """
         return itertools.chain.from_iterable(
             cls._search_path(path, pattern)
-            for path in map(Path, paths)
+            for path in map(cls._switch_path, paths)
             )
 
+    @staticmethod
+    def _switch_path(path):
+        with suppress(Exception):
+            return zipfile.Path(path)
+        return Path(path)
+
     @classmethod
     def _predicate(cls, pattern, root, item):
         return re.match(pattern, str(item.name), flags=re.IGNORECASE)
@@ -68,82 +76,15 @@ class MetadataPathBaseFinder(NullFinder):
                 if cls._predicate(matcher, root, item))
 
 
-@install
-class MetadataPathFinder(MetadataPathBaseFinder):
-    search_template = r'{pattern}(-.*)?\.(dist|egg)-info'
-
-
-@install
-class MetadataPathEggInfoFileFinder(MetadataPathBaseFinder):
-    search_template = r'{pattern}(-.*)?\.egg-info'
-
-    @classmethod
-    def _predicate(cls, pattern, root, item):
-        return (
-            (root / item).is_file() and
-            re.match(pattern, str(item.name), flags=re.IGNORECASE))
-
-
 class PathDistribution(Distribution):
     def __init__(self, path):
         """Construct a distribution from a path to the metadata directory."""
         self._path = path
 
     def read_text(self, filename):
-        with suppress(FileNotFoundError, NotADirectoryError):
-            with self._path.joinpath(filename).open(encoding='utf-8') as fp:
-                return fp.read()
-        return None
+        with suppress(FileNotFoundError, NotADirectoryError, KeyError):
+            return self._path.joinpath(filename).read_text(encoding='utf-8')
     read_text.__doc__ = Distribution.read_text.__doc__
 
     def locate_file(self, path):
         return self._path.parent / path
-
-
-@install
-class WheelMetadataFinder(NullFinder):
-    """A degenerate finder for distribution packages in wheels.
-
-    This finder supplies only a find_distributions() method for versions
-    of Python that do not have a PathFinder find_distributions().
-    """
-    search_template = r'{pattern}(-.*)?\.whl'
-
-    def find_distributions(self, name=None, path=None):
-        """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).
-        """
-        if path is None:
-            path = sys.path
-        pattern = '.*' if name is None else re.escape(name)
-        found = self._search_paths(pattern, path)
-        return map(WheelDistribution, found)
-
-    @classmethod
-    def _search_paths(cls, pattern, paths):
-        return (
-            path
-            for path in map(Path, paths)
-            if re.match(
-                cls.search_template.format(pattern=pattern),
-                str(path.name),
-                flags=re.IGNORECASE,
-                )
-            )
-
-
-class WheelDistribution(Distribution):
-    def __init__(self, archive):
-        self._archive = zipfile.Path(archive)
-        name, version = archive.name.split('-')[0:2]
-        self._dist_info = '{}-{}.dist-info'.format(name, version)
-
-    def read_text(self, filename):
-        target = self._archive / self._dist_info / filename
-        return target.read_text() if target.exists() else None
-    read_text.__doc__ = Distribution.read_text.__doc__
-
-    def locate_file(self, path):
-        return self._archive / path
index 1785cf3c1c27ba7eeaf192680315b271f0298761..845e41afff7a33b8a5b26ce604c0e39daba70a24 100644 (file)
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-
-
 import abc
 
 from importlib.abc import MetaPathFinder
index b95bc454cc6f92fc289db2b21b8729cfcea22f9d..ba5c17120b96a49caebe9dce2065cd361557b000 100644 (file)
@@ -1,5 +1,3 @@
-from __future__ import absolute_import
-
 import re
 import abc
 import csv
index 64270850a9fe725b2488ed25bfa2969dc9e8a3d8..926064ed486a95c9e3a37f730eb154a1484727c8 100644 (file)
@@ -1,33 +1,31 @@
 # coding: utf-8
-from __future__ import unicode_literals
 
 import re
 import textwrap
 import unittest
 import importlib
-import importlib.metadata
 
 from . import fixtures
-from importlib.metadata import _hooks
+from importlib.metadata import (
+    Distribution, PackageNotFoundError, _hooks, api, distributions,
+    entry_points, metadata, version)
 
 
 class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
     version_pattern = r'\d+\.\d+(\.\d)?'
 
     def test_retrieves_version_of_self(self):
-        dist = importlib.metadata.Distribution.from_name('distinfo-pkg')
+        dist = Distribution.from_name('distinfo-pkg')
         assert isinstance(dist.version, str)
         assert re.match(self.version_pattern, dist.version)
 
     def test_for_name_does_not_exist(self):
-        with self.assertRaises(importlib.metadata.PackageNotFoundError):
-            importlib.metadata.Distribution.from_name('does-not-exist')
+        with self.assertRaises(PackageNotFoundError):
+            Distribution.from_name('does-not-exist')
 
     def test_new_style_classes(self):
-        self.assertIsInstance(importlib.metadata.Distribution, type)
+        self.assertIsInstance(Distribution, type)
         self.assertIsInstance(_hooks.MetadataPathFinder, type)
-        self.assertIsInstance(_hooks.WheelMetadataFinder, type)
-        self.assertIsInstance(_hooks.WheelDistribution, type)
 
 
 class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):
@@ -38,17 +36,17 @@ class ImportTests(fixtures.DistInfoPkg, unittest.TestCase):
             importlib.import_module('does_not_exist')
 
     def test_resolve(self):
-        entries = dict(importlib.metadata.entry_points()['entries'])
+        entries = dict(entry_points()['entries'])
         ep = entries['main']
         self.assertEqual(ep.load().__name__, "main")
 
     def test_resolve_without_attr(self):
-        ep = importlib.metadata.api.EntryPoint(
+        ep = api.EntryPoint(
             name='ep',
             value='importlib.metadata.api',
             group='grp',
             )
-        assert ep.load() is importlib.metadata.api
+        assert ep.load() is api
 
 
 class NameNormalizationTests(fixtures.SiteDir, unittest.TestCase):
@@ -71,7 +69,7 @@ class NameNormalizationTests(fixtures.SiteDir, unittest.TestCase):
         uses underscores in the name. Ensure the metadata loads.
         """
         pkg_name = self.pkg_with_dashes(self.site_dir)
-        assert importlib.metadata.version(pkg_name) == '1.0'
+        assert version(pkg_name) == '1.0'
 
     @staticmethod
     def pkg_with_mixed_case(site_dir):
@@ -91,9 +89,9 @@ class NameNormalizationTests(fixtures.SiteDir, unittest.TestCase):
         Ensure the metadata loads when queried with any case.
         """
         pkg_name = self.pkg_with_mixed_case(self.site_dir)
-        assert importlib.metadata.version(pkg_name) == '1.0'
-        assert importlib.metadata.version(pkg_name.lower()) == '1.0'
-        assert importlib.metadata.version(pkg_name.upper()) == '1.0'
+        assert version(pkg_name) == '1.0'
+        assert version(pkg_name.lower()) == '1.0'
+        assert version(pkg_name.upper()) == '1.0'
 
 
 class NonASCIITests(fixtures.SiteDir, unittest.TestCase):
@@ -129,22 +127,23 @@ class NonASCIITests(fixtures.SiteDir, unittest.TestCase):
 
     def test_metadata_loads(self):
         pkg_name = self.pkg_with_non_ascii_description(self.site_dir)
-        meta = importlib.metadata.metadata(pkg_name)
+        meta = metadata(pkg_name)
         assert meta['Description'] == 'pôrˈtend'
 
     def test_metadata_loads_egg_info(self):
         pkg_name = self.pkg_with_non_ascii_description_egg_info(self.site_dir)
-        meta = importlib.metadata.metadata(pkg_name)
+        meta = metadata(pkg_name)
         assert meta.get_payload() == 'pôrˈtend\n'
 
 
-class DiscoveryTests(fixtures.EggInfoPkg, fixtures.DistInfoPkg,
+class DiscoveryTests(fixtures.EggInfoPkg,
+                     fixtures.DistInfoPkg,
                      unittest.TestCase):
 
     def test_package_discovery(self):
-        dists = list(importlib.metadata.api.distributions())
+        dists = list(distributions())
         assert all(
-            isinstance(dist, importlib.metadata.Distribution)
+            isinstance(dist, Distribution)
             for dist in dists
             )
         assert any(
index 8e4c95cc672e69572e8804bd9b46893cdc0d9680..f837a6343eb8420e9bcead96be536e2470091876 100644 (file)
@@ -1,11 +1,15 @@
 import re
 import textwrap
 import unittest
-import importlib.metadata
 
 from collections.abc import Iterator
 
 from . import fixtures
+from importlib.metadata import (
+    Distribution, PackageNotFoundError, distribution,
+    entry_points, files, metadata, requires, version,
+    )
+from importlib.metadata.api import local_distribution
 
 
 class APITests(
@@ -17,41 +21,39 @@ class APITests(
     version_pattern = r'\d+\.\d+(\.\d)?'
 
     def test_retrieves_version_of_self(self):
-        version = importlib.metadata.version('egginfo-pkg')
-        assert isinstance(version, str)
-        assert re.match(self.version_pattern, version)
+        pkg_version = version('egginfo-pkg')
+        assert isinstance(pkg_version, str)
+        assert re.match(self.version_pattern, pkg_version)
 
-    def test_retrieves_version_of_pip(self):
-        version = importlib.metadata.version('distinfo-pkg')
-        assert isinstance(version, str)
-        assert re.match(self.version_pattern, version)
+    def test_retrieves_version_of_distinfo_pkg(self):
+        pkg_version = version('distinfo-pkg')
+        assert isinstance(pkg_version, str)
+        assert re.match(self.version_pattern, pkg_version)
 
     def test_for_name_does_not_exist(self):
-        with self.assertRaises(importlib.metadata.PackageNotFoundError):
-            importlib.metadata.distribution('does-not-exist')
+        with self.assertRaises(PackageNotFoundError):
+            distribution('does-not-exist')
 
     def test_for_top_level(self):
-        distribution = importlib.metadata.distribution('egginfo-pkg')
         self.assertEqual(
-            distribution.read_text('top_level.txt').strip(),
+            distribution('egginfo-pkg').read_text('top_level.txt').strip(),
             'mod')
 
     def test_read_text(self):
         top_level = [
-            path for path in importlib.metadata.files('egginfo-pkg')
+            path for path in files('egginfo-pkg')
             if path.name == 'top_level.txt'
             ][0]
         self.assertEqual(top_level.read_text(), 'mod\n')
 
     def test_entry_points(self):
-        entires = importlib.metadata.entry_points()['entries']
-        entries = dict(entires)
+        entries = dict(entry_points()['entries'])
         ep = entries['main']
         self.assertEqual(ep.value, 'mod:main')
         self.assertEqual(ep.extras, [])
 
     def test_metadata_for_this_package(self):
-        md = importlib.metadata.metadata('egginfo-pkg')
+        md = metadata('egginfo-pkg')
         assert md['author'] == 'Steven Ma'
         assert md['LICENSE'] == 'Unknown'
         assert md['Name'] == 'egginfo-pkg'
@@ -77,7 +79,7 @@ class APITests(
         assertRegex = self.assertRegex
 
         util = [
-            p for p in importlib.metadata.files('distinfo-pkg')
+            p for p in files('distinfo-pkg')
             if p.name == 'mod.py'
             ][0]
         assertRegex(
@@ -85,28 +87,27 @@ class APITests(
             '<FileHash mode: sha256 value: .*>')
 
     def test_files_dist_info(self):
-        self._test_files(importlib.metadata.files('distinfo-pkg'))
+        self._test_files(files('distinfo-pkg'))
 
     def test_files_egg_info(self):
-        self._test_files(importlib.metadata.files('egginfo-pkg'))
+        self._test_files(files('egginfo-pkg'))
 
     def test_version_egg_info_file(self):
-        version = importlib.metadata.version('egginfo-file')
-        self.assertEqual(version, '0.1')
+        self.assertEqual(version('egginfo-file'), '0.1')
 
     def test_requires_egg_info_file(self):
-        requirements = importlib.metadata.requires('egginfo-file')
+        requirements = requires('egginfo-file')
         self.assertIsNone(requirements)
 
     def test_requires(self):
-        deps = importlib.metadata.requires('egginfo-pkg')
+        deps = requires('egginfo-pkg')
         assert any(
             dep == 'wheel >= 1.0; python_version >= "2.7"'
             for dep in deps
             )
 
     def test_requires_dist_info(self):
-        deps = list(importlib.metadata.requires('distinfo-pkg'))
+        deps = list(requires('distinfo-pkg'))
         assert deps and all(deps)
 
     def test_more_complex_deps_requires_text(self):
@@ -123,10 +124,7 @@ class APITests(
             [extra2:python_version < "3"]
             dep5
             """)
-        deps = sorted(
-            importlib.metadata.api.Distribution._deps_from_requires_text(
-                requires)
-            )
+        deps = sorted(Distribution._deps_from_requires_text(requires))
         expected = [
             'dep1',
             'dep2',
@@ -143,5 +141,5 @@ class APITests(
 
 class LocalProjectTests(fixtures.LocalPackage, unittest.TestCase):
     def test_find_local(self):
-        dist = importlib.metadata.api.local_distribution()
+        dist = local_distribution()
         assert dist.metadata['Name'] == 'egginfo-pkg'
index 4af027a12bef1cbd618f5911a30105831179637b..c7c8c0b1843b204e7788952921abb6d0b4909a86 100644 (file)
@@ -1,14 +1,10 @@
 import sys
 import unittest
-import importlib.metadata
 
+from contextlib import ExitStack
+from importlib.metadata import distribution, entry_points, files, version
 from importlib.resources import path
 
-try:
-    from contextlib import ExitStack
-except ImportError:
-    from contextlib2 import ExitStack
-
 
 class BespokeLoader:
     archive = 'bespoke'
@@ -27,22 +23,20 @@ class TestZip(unittest.TestCase):
         self.resources.callback(sys.path.pop, 0)
 
     def test_zip_version(self):
-        self.assertEqual(importlib.metadata.version('example'), '21.12')
+        self.assertEqual(version('example'), '21.12')
 
     def test_zip_entry_points(self):
-        scripts = dict(importlib.metadata.entry_points()['console_scripts'])
+        scripts = dict(entry_points()['console_scripts'])
         entry_point = scripts['example']
         self.assertEqual(entry_point.value, 'example:main')
 
     def test_missing_metadata(self):
-        distribution = importlib.metadata.distribution('example')
-        self.assertIsNone(distribution.read_text('does not exist'))
+        self.assertIsNone(distribution('example').read_text('does not exist'))
 
     def test_case_insensitive(self):
-        self.assertEqual(importlib.metadata.version('Example'), '21.12')
+        self.assertEqual(version('Example'), '21.12')
 
     def test_files(self):
-        files = importlib.metadata.files('example')
-        for file in files:
+        for file in files('example'):
             path = str(file.dist.locate_file(file))
             assert '.whl/' in path, path