diff --git a/changelog/177.feature.rst b/changelog/177.feature.rst new file mode 100644 index 00000000..4345a9d7 --- /dev/null +++ b/changelog/177.feature.rst @@ -0,0 +1 @@ +Add ``get_hookimpls()`` method to hook callers. diff --git a/pluggy/hooks.py b/pluggy/hooks.py index ce0335fe..70b47466 100644 --- a/pluggy/hooks.py +++ b/pluggy/hooks.py @@ -233,6 +233,10 @@ def remove(wrappers): if remove(self._nonwrappers) is None: raise ValueError("plugin %r not found" % (plugin,)) + def get_hookimpls(self): + # Order is important for _hookexec + return self._nonwrappers + self._wrappers + def _add_hookimpl(self, hookimpl): """Add an implementation to the callback chain. """ @@ -277,7 +281,7 @@ def __call__(self, *args, **kwargs): "can not be found in this hook call".format(tuple(notincall)), stacklevel=2, ) - return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) + return self._hookexec(self, self.get_hookimpls(), kwargs) def call_historic(self, result_callback=None, kwargs=None, proc=None): """Call the hook with given ``kwargs`` for all registered plugins and @@ -299,7 +303,7 @@ def call_historic(self, result_callback=None, kwargs=None, proc=None): self._call_history.append((kwargs or {}, result_callback)) # historizing hooks don't return results - res = self._hookexec(self, self._nonwrappers + self._wrappers, kwargs) + res = self._hookexec(self, self.get_hookimpls(), kwargs) if result_callback is None: return # XXX: remember firstresult isn't compat with historic diff --git a/pluggy/manager.py b/pluggy/manager.py index 427a7dbb..3a90979e 100644 --- a/pluggy/manager.py +++ b/pluggy/manager.py @@ -168,7 +168,7 @@ def add_hookspecs(self, module_or_class): else: # plugins registered this hook without knowing the spec hc.set_specification(module_or_class, spec_opts) - for hookfunction in hc._wrappers + hc._nonwrappers: + for hookfunction in hc.get_hookimpls(): self._verify_hook(hc, hookfunction) names.append(name) @@ -242,7 +242,7 @@ def check_pending(self): if name[0] != "_": hook = getattr(self.hook, name) if not hook.has_spec(): - for hookimpl in hook._wrappers + hook._nonwrappers: + for hookimpl in hook.get_hookimpls(): if not hookimpl.optionalhook: raise PluginValidationError( hookimpl.plugin, @@ -329,7 +329,7 @@ def subset_hook_caller(self, name, remove_plugins): hc = _HookCaller( orig.name, orig._hookexec, orig.spec.namespace, orig.spec.opts ) - for hookimpl in orig._wrappers + orig._nonwrappers: + for hookimpl in orig.get_hookimpls(): plugin = hookimpl.plugin if plugin not in plugins_to_remove: hc._add_hookimpl(hookimpl) diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index a53437e1..14a028a5 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -384,6 +384,38 @@ class PluginNo(object): assert out == [10] +def test_get_hookimpls(pm): + class Hooks(object): + @hookspec + def he_method1(self, arg): + pass + + pm.add_hookspecs(Hooks) + assert pm.hook.he_method1.get_hookimpls() == [] + + class Plugin1(object): + @hookimpl + def he_method1(self, arg): + pass + + class Plugin2(object): + @hookimpl + def he_method1(self, arg): + pass + + class PluginNo(object): + pass + + plugin1, plugin2, plugin3 = Plugin1(), Plugin2(), PluginNo() + pm.register(plugin1) + pm.register(plugin2) + pm.register(plugin3) + + hookimpls = pm.hook.he_method1.get_hookimpls() + hook_plugins = [item.plugin for item in hookimpls] + assert hook_plugins == [plugin1, plugin2] + + def test_add_hookspecs_nohooks(pm): with pytest.raises(ValueError): pm.add_hookspecs(10)