mirror of
https://github.com/python/cpython.git
synced 2025-08-10 03:49:18 +00:00
Apply changes from importlib_metadata 0.11
This commit is contained in:
parent
071cbd4ea1
commit
21eaaf47ac
8 changed files with 73 additions and 151 deletions
|
@ -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
|
||||
========
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
|
||||
import abc
|
||||
|
||||
from importlib.abc import MetaPathFinder
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import abc
|
||||
import csv
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue