Skip to content

[3.8] bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 (GH-17568) #17569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Doc/library/importlib.metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.


Expand Down
23 changes: 22 additions & 1 deletion Lib/importlib/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -107,6 +108,12 @@ def __iter__(self):
"""
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"""
Expand Down Expand Up @@ -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):
Expand All @@ -347,6 +365,9 @@ def __init__(self, **kwargs):
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)

Expand Down
33 changes: 33 additions & 0 deletions Lib/test/test_importlib/test_main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# coding: utf-8

import re
import json
import pickle
import textwrap
import unittest
import importlib.metadata
Expand Down Expand Up @@ -181,3 +183,34 @@ def test_egg(self):
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)
Original file line number Diff line number Diff line change
@@ -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.