@@ -274,10 +274,12 @@ def __init__(self, project_name, implprefix=None):
274274 self .trace = _TagTracer ().get ("pluginmanage" )
275275 self .hook = _HookRelay (self .trace .root .get ("hook" ))
276276 self ._implprefix = implprefix
277- self ._inner_hookexec = lambda hook , methods , kwargs : \
278- _MultiCall (
279- methods , kwargs , specopts = hook .spec_opts , hook = hook
280- ).execute ()
277+ self ._inner_hookexec = lambda hook , methods , kwargs : _MultiCall (
278+ methods ,
279+ kwargs ,
280+ firstresult = hook .spec .opts ['firstresult' ] if hook .spec else False ,
281+ hook = hook
282+ ).execute ()
281283
282284 def _hookexec (self , hook , methods , kwargs ):
283285 # called from all hookcaller instances.
@@ -424,7 +426,7 @@ def _verify_hook(self, hook, hookimpl):
424426 (hookimpl .plugin_name , hook .name ))
425427
426428 # positional arg checking
427- notinspec = set (hookimpl .argnames ) - set (hook .argnames )
429+ notinspec = set (hookimpl .argnames ) - set (hook .spec . argnames )
428430 if notinspec :
429431 raise PluginValidationError (
430432 "Plugin %r for hook %r\n hookimpl definition: %s\n "
@@ -517,8 +519,8 @@ def subset_hook_caller(self, name, remove_plugins):
517519 orig = getattr (self .hook , name )
518520 plugins_to_remove = [plug for plug in remove_plugins if hasattr (plug , name )]
519521 if plugins_to_remove :
520- hc = _HookCaller (orig .name , orig ._hookexec , orig ._specmodule_or_class ,
521- orig .spec_opts )
522+ hc = _HookCaller (orig .name , orig ._hookexec , orig .spec . namespace ,
523+ orig .spec . opts )
522524 for hookimpl in (orig ._wrappers + orig ._nonwrappers ):
523525 plugin = hookimpl .plugin
524526 if plugin not in plugins_to_remove :
@@ -539,17 +541,18 @@ class _MultiCall(object):
539541 # so we can remove it soon, allowing to avoid the below recursion
540542 # in execute() and simplify/speed up the execute loop.
541543
542- def __init__ (self , hook_impls , kwargs , specopts = {}, hook = None ):
543- self .hook = hook
544+ def __init__ (self , hook_impls , kwargs , firstresult = False , hook = None ):
544545 self .hook_impls = hook_impls
545546 self .caller_kwargs = kwargs # come from _HookCaller.__call__()
546547 self .caller_kwargs ["__multicall__" ] = self
547- self .specopts = hook .spec_opts if hook else specopts
548+ self .firstresult = firstresult
549+ self .hook = hook
550+ self .spec = hook .spec if hook else None
548551
549552 def execute (self ):
550553 caller_kwargs = self .caller_kwargs
551554 self .results = results = []
552- firstresult = self .specopts . get ( " firstresult" )
555+ firstresult = self .firstresult
553556
554557 while self .hook_impls :
555558 hook_impl = self .hook_impls .pop ()
@@ -560,8 +563,10 @@ def execute(self):
560563 if argname not in caller_kwargs :
561564 raise HookCallError (
562565 "hook call must provide argument %r" % (argname ,))
566+
563567 if hook_impl .hookwrapper :
564568 return _wrapped_call (hook_impl .function (* args ), self .execute )
569+
565570 res = hook_impl .function (* args )
566571 if res is not None :
567572 if firstresult :
@@ -645,28 +650,23 @@ def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None)
645650 self ._wrappers = []
646651 self ._nonwrappers = []
647652 self ._hookexec = hook_execute
648- self .argnames = None
649- self .kwargnames = None
653+ self .spec = None
654+ self ._call_history = None
650655 if specmodule_or_class is not None :
651656 assert spec_opts is not None
652657 self .set_specification (specmodule_or_class , spec_opts )
653658
654659 def has_spec (self ):
655- return hasattr ( self , "_specmodule_or_class" )
660+ return self . spec is not None
656661
657662 def set_specification (self , specmodule_or_class , spec_opts ):
658663 assert not self .has_spec ()
659- self ._specmodule_or_class = specmodule_or_class
660- specfunc = getattr (specmodule_or_class , self .name )
661- # get spec arg signature
662- argnames , self .kwargnames = varnames (specfunc )
663- self .argnames = ["__multicall__" ] + list (argnames )
664- self .spec_opts = spec_opts
664+ self .spec = HookSpec (specmodule_or_class , self .name , spec_opts )
665665 if spec_opts .get ("historic" ):
666666 self ._call_history = []
667667
668668 def is_historic (self ):
669- return hasattr ( self , " _call_history" )
669+ return self . _call_history is not None
670670
671671 def _remove_plugin (self , plugin ):
672672 def remove (wrappers ):
@@ -702,8 +702,8 @@ def __repr__(self):
702702
703703 def __call__ (self , ** kwargs ):
704704 assert not self .is_historic ()
705- if self .argnames :
706- notincall = set (self .argnames ) - set (['__multicall__' ]) - set (
705+ if self .spec :
706+ notincall = set (self .spec . argnames ) - set (['__multicall__' ]) - set (
707707 kwargs .keys ())
708708 if notincall :
709709 warnings .warn (
@@ -749,6 +749,16 @@ def _maybe_apply_history(self, method):
749749 proc (res [0 ])
750750
751751
752+ class HookSpec (object ):
753+ def __init__ (self , namespace , name , opts ):
754+ self .namespace = namespace
755+ self .function = function = getattr (namespace , name )
756+ self .name = name
757+ self .argnames , self .kwargnames = varnames (function )
758+ self .opts = opts
759+ self .argnames = ["__multicall__" ] + list (self .argnames )
760+
761+
752762class HookImpl (object ):
753763 def __init__ (self , plugin , plugin_name , function , hook_impl_opts ):
754764 self .function = function
0 commit comments