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

View File

@ -37,7 +37,8 @@ class PackageNotFoundError(ModuleNotFoundError):
"""The package was not found.""" """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. """An entry point as defined by Python packaging conventions.
See `the packaging docs on entry points See `the packaging docs on entry points
@ -107,6 +108,12 @@ class EntryPoint(collections.namedtuple('EntryPointBase', 'name value group')):
""" """
return iter((self.name, self)) return iter((self.name, self))
def __reduce__(self):
return (
self.__class__,
(self.name, self.value, self.group),
)
class PackagePath(pathlib.PurePosixPath): class PackagePath(pathlib.PurePosixPath):
"""A reference to a path in a package""" """A reference to a path in a package"""
@ -334,10 +341,21 @@ class DistributionFinder(MetaPathFinder):
""" """
class Context: 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 name = None
""" """
Specific name for which a distribution finder should match. Specific name for which a distribution finder should match.
A name of ``None`` matches all distributions.
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -347,6 +365,9 @@ class DistributionFinder(MetaPathFinder):
def path(self): def path(self):
""" """
The path that a distribution finder should search. 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) return vars(self).get('path', sys.path)

View File

@ -1,6 +1,8 @@
# coding: utf-8 # coding: utf-8
import re import re
import json
import pickle
import textwrap import textwrap
import unittest import unittest
import importlib.metadata import importlib.metadata
@ -181,3 +183,34 @@ class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase):
with self.add_sys_path(egg): with self.add_sys_path(egg):
with self.assertRaises(PackageNotFoundError): with self.assertRaises(PackageNotFoundError):
version('foo') 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.