Skip to content

Commit

Permalink
Introduce SelectableGroups, created for the 3.x line to provide forwa…
Browse files Browse the repository at this point in the history
…rd compatibilty to the new interfaces without sacrificing backward compatibility.
  • Loading branch information
jaraco committed Feb 23, 2021
1 parent d6f7c20 commit 2db4dad
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
21 changes: 16 additions & 5 deletions CHANGES.rst
@@ -1,4 +1,4 @@
v4.0.0
v3.6.0
======

* #284: Introduces new ``EntryPoints`` object, a tuple of
Expand All @@ -13,10 +13,21 @@ v4.0.0
- Item access (e.g. ``eps[name]``) retrieves a single
entry point by name.

``entry_points()`` now returns an ``EntryPoints``
object, but provides for backward compatibility with
a ``__getitem__`` accessor by group and a ``get()``
method.
``entry_points`` now accepts "selection parameters",
same as ``EntryPoint.select()``.

``entry_points()`` now provides a future-compatible
``SelectableGroups`` object that supplies the above interface
but remains a dict for compatibility.

In the future, ``entry_points()`` will return an
``EntryPoints`` object, but provide for backward
compatibility with a deprecated ``__getitem__``
accessor by group and a ``get()`` method.

If passing selection parameters to ``entry_points``, the
future behavior is invoked and an ``EntryPoints`` is the
result.

Construction of entry points using
``dict([EntryPoint, ...])`` is now deprecated and raises
Expand Down
4 changes: 2 additions & 2 deletions docs/using.rst
Expand Up @@ -67,8 +67,8 @@ This package provides the following functionality via its public API.
Entry points
------------

The ``entry_points()`` function returns a sequence of all entry points,
keyed by group. Entry points are represented by ``EntryPoint`` instances;
The ``entry_points()`` function returns a collection of entry points.
Entry points are represented by ``EntryPoint`` instances;
each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and
a ``.load()`` method to resolve the value. There are also ``.module``,
``.attr``, and ``.extras`` attributes for getting the components of the
Expand Down
50 changes: 47 additions & 3 deletions importlib_metadata/__init__.py
Expand Up @@ -187,6 +187,37 @@ def _from_text_for(cls, text, dist):
return cls(ep._for(dist) for ep in EntryPoint._from_text(text))


class SelectableGroups(dict):
"""
A backward- and forward-compatible result from
entry_points that fully implements the dict interface.
"""

@classmethod
def load(cls, eps):
by_group = operator.attrgetter('group')
ordered = sorted(eps, key=by_group)
grouped = itertools.groupby(ordered, by_group)
return cls((group, EntryPoints(eps)) for group, eps in grouped)

@property
def groups(self):
return self.keys()

@property
def names(self):
return (ep.name for ep in self._all)

@property
def _all(self):
return itertools.chain.from_iterable(self.values())

def select(self, **params):
if not params:
return self
return EntryPoints(self._all).select(**params)


class LegacyGroupedEntryPoints(EntryPoints):
"""
Compatibility wrapper around EntryPoints to provide
Expand Down Expand Up @@ -713,16 +744,29 @@ def version(distribution_name):
return distribution(distribution_name).version


def entry_points(**params):
def entry_points(**params) -> Union[EntryPoints, SelectableGroups]:
"""Return EntryPoint objects for all installed packages.
:return: EntryPoint objects for all installed packages.
Pass selection parameters (group or name) to filter the
result to entry points matching those properties (see
EntryPoints.select()).
For compatibility, returns ``SelectableGroups`` object unless
selection parameters are supplied. In the future, this function
will return ``LegacyGroupedEntryPoints`` instead of
``SelectableGroups`` and eventually will only return
``EntryPoints``.
For maximum future compatibility, pass selection parameters
or invoke ``.select`` with parameters on the result.
:return: EntryPoints or SelectableGroups for all installed packages.
"""
unique = functools.partial(unique_everseen, key=operator.attrgetter('name'))
eps = itertools.chain.from_iterable(
dist.entry_points for dist in unique(distributions())
)
return LegacyGroupedEntryPoints(eps).select(**params)
return SelectableGroups.load(eps).select(**params)


def files(distribution_name):
Expand Down

0 comments on commit 2db4dad

Please sign in to comment.