Skip to content

Commit

Permalink
Deduplicate identical plugin entry points (#32)
Browse files Browse the repository at this point in the history
* Deduplicate plugin entry points and add test

* Fix formatting

* Add explanation on second check

* Make mod names generator, remove hypothesis file add to gitignore
  • Loading branch information
DragaDoncila committed Aug 2, 2021
1 parent fa727f5 commit a1da253
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ pip-wheel-metadata/
.DS_Store
.vscode
*.mypy_cache*

# hypothesis
.hypothesis/*
25 changes: 24 additions & 1 deletion napari_plugin_engine/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ def __init__(
self._plugin2hookcallers: Dict[Any, List[HookCaller]] = {}

self._blocked: Set[str] = set()
# multiple plugins might register the same entry point
# _id_counts tracks the count of each identical entry point
self._id_counts: Dict[str, int] = {}

self.trace = _tracing.TagTracer().get("pluginmanage")
self.hook = _HookRelay(self)
Expand Down Expand Up @@ -236,12 +239,32 @@ def discover(
for name, mod_name, dist_name in self.iter_available(
path, entry_point, prefix
):
if self.is_registered(name) or self.is_blocked(name):
old_name = name
# different plugin has already registered this entry point
if self.is_registered(name):
mod_names = (
plugin_mod.__name__ for plugin_mod in self.plugins.values()
)
# we may have registered this entry point under a different name,
# so check module names to avoid duplicate registration
if mod_name not in mod_names:
new_name = f"{name}-{self._id_counts[name]}"
previously_registered_mod = self.plugins[name].__name__
warnings.warn(
f"Plugin {name} already registered by module "
+ f"{previously_registered_mod}! Registering as {new_name}."
)
name = new_name
else:
continue
elif self.is_blocked(name):
continue

try:
if self._load_and_register(mod_name, name):
count += 1
self._id_counts[name] = 1
self._id_counts[old_name] += 1
except PluginError as e:
errs.append(e)
self.set_blocked(name)
Expand Down
38 changes: 38 additions & 0 deletions testing/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,44 @@ def test_specification(arg1, arg2):
test_plugin_manager.discover(ignore_errors=False)


def test_identical_entry_point_discovery(
good_entrypoint_plugin, test_plugin_manager, tmp_path
):
test_plugin_manager.discover_entry_point = 'app.plugin'
test_plugin_manager.discover_path = tmp_path

# make second plugin with same entry point
(tmp_path / "good_entrypoint_plugin2.py").write_text(GOOD_PLUGIN)
distinfo = tmp_path / "good_entrypoint_plugin2-1.2.3.dist-info"
distinfo.mkdir()
(distinfo / "top_level.txt").write_text('good_entrypoint_plugin2')
(distinfo / "entry_points.txt").write_text(
"[app.plugin]\ngood_entry = good_entrypoint_plugin2"
)
(distinfo / "METADATA").write_text(
"Metadata-Version: 2.1\n"
"Name: good_entry\n"
"Version: 1.2.3\n"
"Author-Email: example@example.com\n"
"Home-Page: https://www.example.com\n"
"Requires-Python: >=3.6\n"
)

with pytest.warns(UserWarning):
cnt, err = test_plugin_manager.discover()
assert cnt == 2
assert 'good_entry-2' in test_plugin_manager.plugins
assert not err

# test discovering again doesn't rediscover same plugins
cnt, err = test_plugin_manager.discover()
assert cnt == 0
assert not err
assert 'good_entry' in test_plugin_manager.plugins
assert 'good_entry-2' in test_plugin_manager.plugins
assert len(test_plugin_manager.plugins) == 2


def test_lazy_autodiscovery(
tmp_path, add_specification, test_plugin_manager, good_entrypoint_plugin
):
Expand Down

0 comments on commit a1da253

Please sign in to comment.