bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 (GH-17568)

* bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 including improved docs for custom finders and better serialization support in EntryPoints.

* 📜🤖 Added by blurb_it.

* Correct module reference
This commit is contained in:
Jason R. Coombs 2019-12-10 20:05:10 -05:00 committed by GitHub
parent d0802d07d2
commit b7a0109cd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 7 deletions

View File

@ -216,9 +216,9 @@ system `finders`_. To find a distribution package's metadata,
``importlib.metadata`` queries the list of `meta path finders`_ on
`sys.meta_path`_.
By default ``importlib.metadata`` installs a finder for distribution packages
found on the file system. This finder doesn't actually find any *packages*,
but it can find the packages' metadata.
The default ``PathFinder`` for Python includes a hook that calls into
``importlib.metadata.MetadataPathFinder`` for finding distributions
loaded from typical file-system-based paths.
The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the
interface expected of finders by Python's import system.
@ -239,9 +239,9 @@ properties indicating the path to search and names to match and may
supply other relevant context.
What this means in practice is that to support finding distribution package
metadata in locations other than the file system, you should derive from
``Distribution`` and implement the ``load_metadata()`` method. Then from
your finder, return instances of this derived ``Distribution`` in the
metadata in locations other than the file system, subclass
``Distribution`` and implement the abstract methods. Then from
a custom finder, return instances of this derived ``Distribution`` in the
``find_distributions()`` method.

View File

@ -37,7 +37,8 @@ class PackageNotFoundError(ModuleNotFoundError):
"""The package was not found."""
class EntryPoint(collections.namedtuple('EntryPointBase', 'name value group')):
class EntryPoint(
collections.namedtuple('EntryPointBase', 'name value group')):
"""An entry point as defined by Python packaging conventions.
See `the packaging docs on entry points
@ -107,6 +108,12 @@ class EntryPoint(collections.namedtuple('EntryPointBase', 'name value group')):
"""
return iter((self.name, self))
def __reduce__(self):
return (
self.__class__,
(self.name, self.value, self.group),
)
class PackagePath(pathlib.PurePosixPath):
"""A reference to a path in a package"""
@ -334,10 +341,21 @@ class DistributionFinder(MetaPathFinder):
"""
class Context:
"""
Keyword arguments presented by the caller to
``distributions()`` or ``Distribution.discover()``
to narrow the scope of a search for distributions
in all DistributionFinders.
Each DistributionFinder may expect any parameters
and should attempt to honor the canonical
parameters defined below when appropriate.
"""
name = None
"""
Specific name for which a distribution finder should match.
A name of ``None`` matches all distributions.
"""
def __init__(self, **kwargs):
@ -347,6 +365,9 @@ class DistributionFinder(MetaPathFinder):
def path(self):
"""
The path that a distribution finder should search.
Typically refers to Python package paths and defaults
to ``sys.path``.
"""
return vars(self).get('path', sys.path)

View File

@ -1,6 +1,8 @@
# coding: utf-8
import re
import json
import pickle
import textwrap
import unittest
import importlib.metadata
@ -181,3 +183,34 @@ class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
with self.add_sys_path(egg):
with self.assertRaises(PackageNotFoundError):
version('foo')
class TestEntryPoints(unittest.TestCase):
def __init__(self, *args):
super(TestEntryPoints, self).__init__(*args)
self.ep = importlib.metadata.EntryPoint('name', 'value', 'group')
def test_entry_point_pickleable(self):
revived = pickle.loads(pickle.dumps(self.ep))
assert revived == self.ep
def test_immutable(self):
"""EntryPoints should be immutable"""
with self.assertRaises(AttributeError):
self.ep.name = 'badactor'
def test_repr(self):
assert 'EntryPoint' in repr(self.ep)
assert 'name=' in repr(self.ep)
assert "'name'" in repr(self.ep)
def test_hashable(self):
"""EntryPoints should be hashable"""
hash(self.ep)
def test_json_dump(self):
"""
json should not expect to be able to dump an EntryPoint
"""
with self.assertRaises(Exception):
json.dumps(self.ep)

View File

@ -0,0 +1 @@
Update importliib.metadata to include improvements from importlib_metadata 1.3 including better serialization of EntryPoints and improved documentation for custom finders.