@@ -448,3 +448,59 @@ def conflict(self) -> None:
448448 "Hook 'conflict' is already registered within namespace "
449449 "<class 'test_hookcaller.test_hook_conflict.<locals>.Api1'>"
450450 )
451+
452+
453+ def test_hook_multi_impl (pm : PluginManager ) -> None :
454+ """Since plugins' impls are able to (optionally) specify a spec name, it is possible for a plugin to implement
455+ the same spec multiple times."""
456+
457+ class Api :
458+ @hookspec
459+ def hello (self , arg : object ) -> None :
460+ "api hook 1"
461+
462+ pm .add_hookspecs (Api )
463+ hook = pm .hook
464+ test_hc = hook .hello
465+
466+ class Plugin :
467+ @hookimpl
468+ def hello (self , arg ):
469+ return arg + 1
470+
471+ @hookimpl (specname = "hello" )
472+ def hello_again (self , arg ):
473+ return arg + 100
474+
475+ plugin = Plugin ()
476+
477+ # Confirm that registration puts the impls into all the right registries
478+ pm .register (plugin )
479+ out = test_hc (arg = 3 )
480+ assert out == [103 , 4 ]
481+
482+ assert len (pm .get_plugins ()) == 1
483+
484+ hookimpls = test_hc .get_hookimpls ()
485+ hook_plugins = [item .plugin for item in hookimpls ]
486+ assert hook_plugins == [plugin , plugin ]
487+ for impl in hookimpls :
488+ pm ._verify_hook (test_hc , impl )
489+
490+ hook_callers = pm .get_hookcallers (plugin )
491+ assert (
492+ len (hook_callers ) == 2
493+ ), "This should return two callers. Same spec but different impls."
494+ assert hook_callers [0 ].spec == hook_callers [1 ].spec
495+
496+ subset_caller = pm .subset_hook_caller ("hello" , [plugin ])
497+ out = subset_caller (arg = 3 )
498+ assert out == []
499+
500+ # Confirm that 'unregistering' does the converse
501+ pm .unregister (plugin )
502+ assert test_hc (arg = 3 ) == []
503+ hookimpls = test_hc .get_hookimpls ()
504+ assert hookimpls == []
505+ hook_callers = pm .get_hookcallers (plugin )
506+ assert hook_callers is None
0 commit comments