Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/763.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:meth:`PluginManager.unregister() <pluggy.PluginManager.unregister>` now also drops the plugin's entry from :meth:`~pluggy.PluginManager.list_plugin_distinfo` and :meth:`~pluggy.PluginManager.list_plugin_distributions`, so unloaded plugins no longer show up there and reloading them no longer adds duplicates.
21 changes: 12 additions & 9 deletions src/pluggy/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,7 @@ def __init__(self, project_name: str) -> None:
#: The project name.
self.project_name: Final = project_name
self._name2plugin: Final[dict[str, _Plugin]] = {}
self._plugin_distinfo: Final[
list[tuple[_Plugin, importlib.metadata.Distribution]]
] = []
self._name2dist: Final[dict[str, importlib.metadata.Distribution]] = {}
#: The "hook relay", used to call a hook on all registered plugins.
#: See :ref:`calling`.
self.hook: Final = HookRelay()
Expand Down Expand Up @@ -313,9 +311,9 @@ def unregister(
for hookcaller in hookcallers:
hookcaller._remove_plugin(plugin)

# if self._name2plugin[name] == None registration was blocked: ignore
if self._name2plugin.get(name) is not None:
del self._name2plugin[name]
if not self.is_blocked(name):
self._name2plugin.pop(name, None)
self._name2dist.pop(name, None)

return plugin

Expand Down Expand Up @@ -517,7 +515,7 @@ def load_setuptools_entrypoints(self, group: str, name: str | None = None) -> in
continue
plugin = ep.load()
self.register(plugin, name=ep.name)
self._plugin_distinfo.append((plugin, dist))
self._name2dist[ep.name] = dist
count += 1
return count

Expand All @@ -533,7 +531,10 @@ def list_plugin_distinfo(self) -> list[tuple[_Plugin, DistFacade]]:
"""
from ._compat import DistFacade

return [(plugin, DistFacade(dist)) for plugin, dist in self._plugin_distinfo]
return [
(plugin, DistFacade(dist))
for plugin, dist in self.list_plugin_distributions()
]

def list_plugin_distributions(
self,
Expand All @@ -543,7 +544,9 @@ def list_plugin_distributions(

.. versionadded:: 1.7
"""
return list(self._plugin_distinfo)
return [
(self._name2plugin[name], dist) for name, dist in self._name2dist.items()
]

def list_name_plugin(self) -> list[tuple[str, _Plugin]]:
"""Return a list of (name, plugin) pairs for all registered plugins."""
Expand Down
34 changes: 34 additions & 0 deletions testing/test_pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ class Plugin:
# bloked plugins can be unregistred many times atm
pm.unregister(p, "error")
pm.unregister(p, "error")
assert pm.is_blocked("error")


def test_register_unknown_hooks(pm: PluginManager) -> None:
Expand Down Expand Up @@ -939,6 +940,39 @@ def my_distributions():
assert ret_distributions == [(plugin, dist)]


def test_unregister_drops_plugin_distribution(
monkeypatch: pytest.MonkeyPatch, pm: PluginManager
) -> None:
class EntryPoint:
group = "hello"

def __init__(self, name: str) -> None:
self.name = name

def load(self) -> object:
return type(self.name, (), {})()

class Distribution:
entry_points = (EntryPoint("a"), EntryPoint("b"))

dist = cast(importlib.metadata.Distribution, Distribution())
monkeypatch.setattr(importlib.metadata, "distributions", lambda: (dist,))

assert pm.load_setuptools_entrypoints("hello") == 2
b = pm.get_plugin("b")
pm.unregister(name="a")
assert pm.list_plugin_distributions() == [(b, dist)]
assert [p for p, _ in pm.list_plugin_distinfo()] == [b]

# reloading must not duplicate the remaining entry
assert pm.load_setuptools_entrypoints("hello") == 1
a = pm.get_plugin("a")
assert pm.list_plugin_distributions() == [(b, dist), (a, dist)]

pm.set_blocked("b")
assert pm.list_plugin_distributions() == [(a, dist)]


def test_add_tracefuncs(he_pm: PluginManager) -> None:
out: list[Any] = []

Expand Down
Loading