Skip to content

Commit

Permalink
Merge branch 'main' into feature/entry-points-by-group-and-name
Browse files Browse the repository at this point in the history
  • Loading branch information
jaraco committed Feb 21, 2021
2 parents 8320ade + c8d90a2 commit fbf9599
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 1 deletion.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ omit =
*/.tox/*
tests/*
prepare/*
*/_itertools.py

[report]
show_missing = True
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
v3.5.0
======

* #280: ``entry_points`` now only returns entry points for
unique distributions (by name).
* ``entry_points()`` now returns an ``GroupedEntryPoints``
object, a tuple of all entry points but with a convenience
property ``groups`` and ``__getitem__`` accessor. Further,
Expand Down
7 changes: 6 additions & 1 deletion importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
Protocol,
)

from ._itertools import unique_everseen

from configparser import ConfigParser
from contextlib import suppress
from importlib import import_module
Expand Down Expand Up @@ -698,7 +700,10 @@ def entry_points(**params):
:return: EntryPoint objects for all installed packages.
"""
eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions())
unique = functools.partial(unique_everseen, key=operator.attrgetter('name'))
eps = itertools.chain.from_iterable(
dist.entry_points for dist in unique(distributions())
)
return EntryPoints(eps).select(**params)


Expand Down
19 changes: 19 additions & 0 deletions importlib_metadata/_itertools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from itertools import filterfalse


def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
28 changes: 28 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,34 @@ def test_entry_points_distribution(self):
self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg'))
self.assertEqual(ep.dist.version, "1.0.0")

def test_entry_points_unique_packages(self):
"""
Entry points should only be exposed for the first package
on sys.path with a given name.
"""
alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
alt_pkg = {
"distinfo_pkg-1.1.0.dist-info": {
"METADATA": """
Name: distinfo-pkg
Version: 1.1.0
""",
"entry_points.txt": """
[entries]
main = mod:altmain
""",
},
}
fixtures.build_files(alt_pkg, alt_site_dir)
entries = dict(entry_points()['entries'])
assert not any(
ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0'
for ep in entries.values()
)
# ns:sub doesn't exist in alt_pkg
assert 'ns:sub' not in entries

def test_entry_points_missing_name(self):
with self.assertRaises(KeyError):
entry_points(group='entries')['missing']
Expand Down
7 changes: 7 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,14 @@ use_develop = False
deps =
ipython
commands =
python -m 'print("Simple discovery performance")'
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")'
python -m 'print("Entry point discovery performance")'
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()'
python -c 'print("Cached lookup performance")'
python -m timeit -s 'import importlib_metadata; importlib_metadata.distribution("ipython")' -- 'importlib_metadata.distribution("ipython")'
python -c 'print("Uncached lookup performance")'
python -m timeit -s 'import importlib, importlib_metadata' -- 'importlib.invalidate_caches(); importlib_metadata.distribution("ipython")'

[testenv:release]
skip_install = True
Expand Down

0 comments on commit fbf9599

Please sign in to comment.