Skip to content

Commit f80f79d

Browse files
committed
Merge branch 'main' into feature/entry-points-by-group-and-name
2 parents 8320ade + c8d90a2 commit f80f79d

File tree

6 files changed

+63
-1
lines changed

6 files changed

+63
-1
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ omit =
33
*/.tox/*
44
tests/*
55
prepare/*
6+
*/_itertools.py
67

78
[report]
89
show_missing = True

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
v3.5.0
22
======
33

4+
* #280: ``entry_points`` now only returns entry points for
5+
unique distributions (by name).
46
* ``entry_points()`` now returns an ``GroupedEntryPoints``
57
object, a tuple of all entry points but with a convenience
68
property ``groups`` and ``__getitem__`` accessor. Further,

importlib_metadata/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
Protocol,
2222
)
2323

24+
from ._itertools import unique_everseen
25+
2426
from configparser import ConfigParser
2527
from contextlib import suppress
2628
from importlib import import_module
@@ -698,7 +700,10 @@ def entry_points(**params):
698700
699701
:return: EntryPoint objects for all installed packages.
700702
"""
701-
eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions())
703+
unique = functools.partial(unique_everseen, key=operator.attrgetter('name'))
704+
eps = itertools.chain.from_iterable(
705+
dist.entry_points for dist in unique(distributions())
706+
)
702707
return EntryPoints(eps).select(**params)
703708

704709

importlib_metadata/_itertools.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from itertools import filterfalse
2+
3+
4+
def unique_everseen(iterable, key=None):
5+
"List unique elements, preserving order. Remember all elements ever seen."
6+
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
7+
# unique_everseen('ABBCcAD', str.lower) --> A B C D
8+
seen = set()
9+
seen_add = seen.add
10+
if key is None:
11+
for element in filterfalse(seen.__contains__, iterable):
12+
seen_add(element)
13+
yield element
14+
else:
15+
for element in iterable:
16+
k = key(element)
17+
if k not in seen:
18+
seen_add(k)
19+
yield element

tests/test_api.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,34 @@ def test_entry_points_distribution(self):
8080
self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg'))
8181
self.assertEqual(ep.dist.version, "1.0.0")
8282

83+
def test_entry_points_unique_packages(self):
84+
"""
85+
Entry points should only be exposed for the first package
86+
on sys.path with a given name.
87+
"""
88+
alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
89+
self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
90+
alt_pkg = {
91+
"distinfo_pkg-1.1.0.dist-info": {
92+
"METADATA": """
93+
Name: distinfo-pkg
94+
Version: 1.1.0
95+
""",
96+
"entry_points.txt": """
97+
[entries]
98+
main = mod:altmain
99+
""",
100+
},
101+
}
102+
fixtures.build_files(alt_pkg, alt_site_dir)
103+
entries = entry_points(group='entries')
104+
assert not any(
105+
ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0'
106+
for ep in entries
107+
)
108+
# ns:sub doesn't exist in alt_pkg
109+
assert 'ns:sub' not in entries
110+
83111
def test_entry_points_missing_name(self):
84112
with self.assertRaises(KeyError):
85113
entry_points(group='entries')['missing']

tox.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,14 @@ use_develop = False
3838
deps =
3939
ipython
4040
commands =
41+
python -m 'print("Simple discovery performance")'
4142
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")'
43+
python -m 'print("Entry point discovery performance")'
44+
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()'
45+
python -c 'print("Cached lookup performance")'
46+
python -m timeit -s 'import importlib_metadata; importlib_metadata.distribution("ipython")' -- 'importlib_metadata.distribution("ipython")'
47+
python -c 'print("Uncached lookup performance")'
48+
python -m timeit -s 'import importlib, importlib_metadata' -- 'importlib.invalidate_caches(); importlib_metadata.distribution("ipython")'
4249

4350
[testenv:release]
4451
skip_install = True

0 commit comments

Comments
 (0)