11import sys
22import inspect
3+ import copy
34import warnings
45
56__version__ = '0.5.0'
@@ -274,10 +275,12 @@ def __init__(self, project_name, implprefix=None):
274275 self .trace = _TagTracer ().get ("pluginmanage" )
275276 self .hook = _HookRelay (self .trace .root .get ("hook" ))
276277 self ._implprefix = implprefix
277- self ._inner_hookexec = lambda hook , methods , kwargs : \
278- _MultiCall (
279- methods , kwargs , specopts = hook .spec_opts , hook = hook
280- ).execute ()
278+ self ._inner_hookexec = lambda hook , methods , kwargs : _MultiCall (
279+ methods ,
280+ kwargs ,
281+ firstresult = hook .spec .opts ['firstresult' ] if hook .spec else False ,
282+ hook = hook
283+ ).execute ()
281284
282285 def _hookexec (self , hook , methods , kwargs ):
283286 # called from all hookcaller instances.
@@ -424,7 +427,7 @@ def _verify_hook(self, hook, hookimpl):
424427 (hookimpl .plugin_name , hook .name ))
425428
426429 # positional arg checking
427- notinspec = set (hookimpl .argnames ) - set (hook .argnames )
430+ notinspec = set (hookimpl .argnames ) - set (hook .spec . argnames )
428431 if notinspec :
429432 raise PluginValidationError (
430433 "Plugin %r for hook %r\n hookimpl definition: %s\n "
@@ -517,8 +520,8 @@ def subset_hook_caller(self, name, remove_plugins):
517520 orig = getattr (self .hook , name )
518521 plugins_to_remove = [plug for plug in remove_plugins if hasattr (plug , name )]
519522 if plugins_to_remove :
520- hc = _HookCaller (orig .name , orig ._hookexec , orig ._specmodule_or_class ,
521- orig .spec_opts )
523+ hc = _HookCaller (orig .name , orig ._hookexec , orig .spec . namespace ,
524+ orig .spec . opts )
522525 for hookimpl in (orig ._wrappers + orig ._nonwrappers ):
523526 plugin = hookimpl .plugin
524527 if plugin not in plugins_to_remove :
@@ -539,29 +542,43 @@ class _MultiCall(object):
539542 # so we can remove it soon, allowing to avoid the below recursion
540543 # in execute() and simplify/speed up the execute loop.
541544
542- def __init__ (self , hook_impls , kwargs , specopts = {}, hook = None ):
543- self .hook = hook
545+ def __init__ (self , hook_impls , kwargs , firstresult = False , hook = None ):
544546 self .hook_impls = hook_impls
545547 self .caller_kwargs = kwargs # come from _HookCaller.__call__()
546548 self .caller_kwargs ["__multicall__" ] = self
547- self .specopts = hook .spec_opts if hook else specopts
549+ self .firstresult = firstresult
550+ self .hook = hook
551+ self .spec = hook .spec if hook else None
548552
549553 def execute (self ):
550554 caller_kwargs = self .caller_kwargs
551555 self .results = results = []
552- firstresult = self .specopts .get ("firstresult" )
556+ firstresult = self .firstresult
557+ spec = self .spec
553558
554559 while self .hook_impls :
555560 hook_impl = self .hook_impls .pop ()
561+ implkwargs = hook_impl .kwargs
556562 try :
557563 args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
564+ # get any caller provided kwargs declared in our
565+ # hookimpl and fail over to the spec's value if provided
566+ if implkwargs :
567+ kwargs = copy .copy (implkwargs )
568+ if spec :
569+ kwargs .update (spec .kwargs )
570+
571+ args += [caller_kwargs .get (argname , kwargs [argname ])
572+ for argname in hook_impl .kwargnames ]
558573 except KeyError :
559574 for argname in hook_impl .argnames :
560575 if argname not in caller_kwargs :
561576 raise HookCallError (
562577 "hook call must provide argument %r" % (argname ,))
578+
563579 if hook_impl .hookwrapper :
564580 return _wrapped_call (hook_impl .function (* args ), self .execute )
581+
565582 res = hook_impl .function (* args )
566583 if res is not None :
567584 if firstresult :
@@ -645,28 +662,23 @@ def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None)
645662 self ._wrappers = []
646663 self ._nonwrappers = []
647664 self ._hookexec = hook_execute
648- self .argnames = None
649- self .kwargnames = None
665+ self .spec = None
666+ self ._call_history = None
650667 if specmodule_or_class is not None :
651668 assert spec_opts is not None
652669 self .set_specification (specmodule_or_class , spec_opts )
653670
654671 def has_spec (self ):
655- return hasattr ( self , "_specmodule_or_class" )
672+ return self . spec is not None
656673
657674 def set_specification (self , specmodule_or_class , spec_opts ):
658675 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
676+ self .spec = HookSpec (specmodule_or_class , self .name , spec_opts )
665677 if spec_opts .get ("historic" ):
666678 self ._call_history = []
667679
668680 def is_historic (self ):
669- return hasattr ( self , " _call_history" )
681+ return self . _call_history is not None
670682
671683 def _remove_plugin (self , plugin ):
672684 def remove (wrappers ):
@@ -702,8 +714,8 @@ def __repr__(self):
702714
703715 def __call__ (self , ** kwargs ):
704716 assert not self .is_historic ()
705- if self .argnames :
706- notincall = set (self .argnames ) - set (['__multicall__' ]) - set (
717+ if self .spec :
718+ notincall = set (self .spec . argnames ) - set (['__multicall__' ]) - set (
707719 kwargs .keys ())
708720 if notincall :
709721 warnings .warn (
@@ -749,10 +761,28 @@ def _maybe_apply_history(self, method):
749761 proc (res [0 ])
750762
751763
764+ class HookSpec (object ):
765+ def __init__ (self , namespace , name , hook_spec_opts ):
766+ self .namespace = namespace
767+ self .function = function = getattr (namespace , name )
768+ self .name = name
769+ self .argnames , self .kwargnames = varnames (function )
770+ self .kwargvalues = inspect .getargspec (function ).defaults
771+ self .kwargs = dict (
772+ zip (self .kwargnames , self .kwargvalues )
773+ ) if self .kwargvalues else {}
774+ self .opts = hook_spec_opts
775+ self .argnames = ["__multicall__" ] + list (self .argnames )
776+
777+
752778class HookImpl (object ):
753779 def __init__ (self , plugin , plugin_name , function , hook_impl_opts ):
754780 self .function = function
755781 self .argnames , self .kwargnames = varnames (self .function )
782+ self .kwargvalues = inspect .getargspec (function ).defaults
783+ self .kwargs = dict (
784+ zip (self .kwargnames , self .kwargvalues )
785+ ) if self .kwargvalues else {}
756786 self .plugin = plugin
757787 self .opts = hook_impl_opts
758788 self .plugin_name = plugin_name
0 commit comments