Skip to content

Commit 67041f7

Browse files
authored
Merge pull request #281 from python/bugfix/280-unique-distributions
Restrict to unique distributions in entry points
2 parents 9950845 + 10c22f3 commit 67041f7

File tree

6 files changed

+61
-1
lines changed

6 files changed

+61
-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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
v3.5.0
2+
======
3+
4+
* #280: ``entry_points`` now only returns entry points for
5+
unique distributions (by name).
6+
17
v3.4.0
28
======
39

importlib_metadata/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
Protocol,
2020
)
2121

22+
from ._itertools import unique_everseen
23+
2224
from configparser import ConfigParser
2325
from contextlib import suppress
2426
from importlib import import_module
@@ -646,7 +648,10 @@ def entry_points():
646648
647649
:return: EntryPoint objects for all installed packages.
648650
"""
649-
eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions())
651+
unique = functools.partial(unique_everseen, key=operator.attrgetter('name'))
652+
eps = itertools.chain.from_iterable(
653+
dist.entry_points for dist in unique(distributions())
654+
)
650655
by_group = operator.attrgetter('group')
651656
ordered = sorted(eps, key=by_group)
652657
grouped = itertools.groupby(ordered, by_group)

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
@@ -76,6 +76,34 @@ def test_entry_points_distribution(self):
7676
self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg'))
7777
self.assertEqual(ep.dist.version, "1.0.0")
7878

79+
def test_entry_points_unique_packages(self):
80+
"""
81+
Entry points should only be exposed for the first package
82+
on sys.path with a given name.
83+
"""
84+
alt_site_dir = self.fixtures.enter_context(fixtures.tempdir())
85+
self.fixtures.enter_context(self.add_sys_path(alt_site_dir))
86+
alt_pkg = {
87+
"distinfo_pkg-1.1.0.dist-info": {
88+
"METADATA": """
89+
Name: distinfo-pkg
90+
Version: 1.1.0
91+
""",
92+
"entry_points.txt": """
93+
[entries]
94+
main = mod:altmain
95+
""",
96+
},
97+
}
98+
fixtures.build_files(alt_pkg, alt_site_dir)
99+
entries = dict(entry_points()['entries'])
100+
assert not any(
101+
ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0'
102+
for ep in entries.values()
103+
)
104+
# ns:sub doesn't exist in alt_pkg
105+
assert 'ns:sub' not in entries
106+
79107
def test_metadata_for_this_package(self):
80108
md = metadata('egginfo-pkg')
81109
assert md['author'] == 'Steven Ma'

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ deps =
3939
ipython
4040
commands =
4141
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")'
42+
python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()'
4243

4344
[testenv:release]
4445
skip_install = True

0 commit comments

Comments
 (0)