66
66
"""
67
67
import sys
68
68
import inspect
69
+ import copy
69
70
import warnings
70
71
71
72
__version__ = '0.5.0'
@@ -340,10 +341,12 @@ def __init__(self, project_name, implprefix=None):
340
341
self .trace = _TagTracer ().get ("pluginmanage" )
341
342
self .hook = _HookRelay (self .trace .root .get ("hook" ))
342
343
self ._implprefix = implprefix
343
- self ._inner_hookexec = lambda hook , methods , kwargs : \
344
- _MultiCall (
345
- methods , kwargs , specopts = hook .spec_opts , hook = hook
346
- ).execute ()
344
+ self ._inner_hookexec = lambda hook , methods , kwargs : _MultiCall (
345
+ methods ,
346
+ kwargs ,
347
+ firstresult = hook .spec .opts ['firstresult' ] if hook .spec else False ,
348
+ hook = hook
349
+ ).execute ()
347
350
348
351
def _hookexec (self , hook , methods , kwargs ):
349
352
# called from all hookcaller instances.
@@ -490,7 +493,7 @@ def _verify_hook(self, hook, hookimpl):
490
493
(hookimpl .plugin_name , hook .name ))
491
494
492
495
# positional arg checking
493
- notinspec = set (hookimpl .argnames ) - set (hook .argnames )
496
+ notinspec = set (hookimpl .argnames ) - set (hook .spec . argnames )
494
497
if notinspec :
495
498
raise PluginValidationError (
496
499
"Plugin %r for hook %r\n hookimpl definition: %s\n "
@@ -583,8 +586,8 @@ def subset_hook_caller(self, name, remove_plugins):
583
586
orig = getattr (self .hook , name )
584
587
plugins_to_remove = [plug for plug in remove_plugins if hasattr (plug , name )]
585
588
if plugins_to_remove :
586
- hc = _HookCaller (orig .name , orig ._hookexec , orig ._specmodule_or_class ,
587
- orig .spec_opts )
589
+ hc = _HookCaller (orig .name , orig ._hookexec , orig .spec . namespace ,
590
+ orig .spec . opts )
588
591
for hookimpl in (orig ._wrappers + orig ._nonwrappers ):
589
592
plugin = hookimpl .plugin
590
593
if plugin not in plugins_to_remove :
@@ -605,29 +608,43 @@ class _MultiCall:
605
608
# so we can remove it soon, allowing to avoid the below recursion
606
609
# in execute() and simplify/speed up the execute loop.
607
610
608
- def __init__ (self , hook_impls , kwargs , specopts = {}, hook = None ):
609
- self .hook = hook
611
+ def __init__ (self , hook_impls , kwargs , firstresult = False , hook = None ):
610
612
self .hook_impls = hook_impls
611
613
self .caller_kwargs = kwargs # come from _HookCaller.__call__()
612
614
self .caller_kwargs ["__multicall__" ] = self
613
- self .specopts = hook .spec_opts if hook else specopts
615
+ self .firstresult = firstresult
616
+ self .hook = hook
617
+ self .spec = hook .spec if hook else None
614
618
615
619
def execute (self ):
616
620
caller_kwargs = self .caller_kwargs
617
621
self .results = results = []
618
- firstresult = self .specopts .get ("firstresult" )
622
+ firstresult = self .firstresult
623
+ spec = self .spec
619
624
620
625
while self .hook_impls :
621
626
hook_impl = self .hook_impls .pop ()
627
+ implkwargs = hook_impl .kwargs
622
628
try :
623
629
args = [caller_kwargs [argname ] for argname in hook_impl .argnames ]
630
+ # get any caller provided kwargs declared in our
631
+ # hookimpl and fail over to the spec's value if provided
632
+ if implkwargs :
633
+ kwargs = copy .copy (implkwargs )
634
+ if spec :
635
+ kwargs .update (spec .kwargs )
636
+
637
+ args += [caller_kwargs .get (argname , kwargs [argname ])
638
+ for argname in hook_impl .kwargnames ]
624
639
except KeyError :
625
640
for argname in hook_impl .argnames :
626
641
if argname not in caller_kwargs :
627
642
raise HookCallError (
628
643
"hook call must provide argument %r" % (argname ,))
644
+
629
645
if hook_impl .hookwrapper :
630
646
return _wrapped_call (hook_impl .function (* args ), self .execute )
647
+
631
648
res = hook_impl .function (* args )
632
649
if res is not None :
633
650
if firstresult :
@@ -711,28 +728,23 @@ def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None)
711
728
self ._wrappers = []
712
729
self ._nonwrappers = []
713
730
self ._hookexec = hook_execute
714
- self .argnames = None
715
- self .kwargnames = None
731
+ self .spec = None
732
+ self ._call_history = None
716
733
if specmodule_or_class is not None :
717
734
assert spec_opts is not None
718
735
self .set_specification (specmodule_or_class , spec_opts )
719
736
720
737
def has_spec (self ):
721
- return hasattr ( self , "_specmodule_or_class" )
738
+ return self . spec is not None
722
739
723
740
def set_specification (self , specmodule_or_class , spec_opts ):
724
741
assert not self .has_spec ()
725
- self ._specmodule_or_class = specmodule_or_class
726
- specfunc = getattr (specmodule_or_class , self .name )
727
- # get spec arg signature
728
- argnames , self .kwargnames = varnames (specfunc )
729
- self .argnames = ["__multicall__" ] + list (argnames )
730
- self .spec_opts = spec_opts
742
+ self .spec = HookSpec (specmodule_or_class , self .name , spec_opts )
731
743
if spec_opts .get ("historic" ):
732
744
self ._call_history = []
733
745
734
746
def is_historic (self ):
735
- return hasattr ( self , " _call_history" )
747
+ return self . _call_history is not None
736
748
737
749
def _remove_plugin (self , plugin ):
738
750
def remove (wrappers ):
@@ -768,13 +780,14 @@ def __repr__(self):
768
780
769
781
def __call__ (self , ** kwargs ):
770
782
assert not self .is_historic ()
771
- notincall = set (self .argnames ) - set (kwargs .keys ())
772
- if notincall :
773
- warnings .warn (
774
- "Positional arg(s) %s are declared in the hookspec "
775
- "but can not be found in this hook call" % notincall ,
776
- FutureWarning
777
- )
783
+ if self .spec :
784
+ notincall = set (self .spec .argnames ) - set (kwargs .keys ())
785
+ if notincall :
786
+ warnings .warn (
787
+ "Positional arg(s) %s are declared in the hookspec "
788
+ "but can not be found in this hook call" % notincall ,
789
+ FutureWarning
790
+ )
778
791
return self ._hookexec (self , self ._nonwrappers + self ._wrappers , kwargs )
779
792
780
793
def call_historic (self , proc = None , kwargs = None ):
@@ -813,10 +826,30 @@ def _maybe_apply_history(self, method):
813
826
proc (res [0 ])
814
827
815
828
829
+ class HookSpec :
830
+ def __init__ (self , namespace , name , hook_spec_opts ):
831
+ self .namespace = namespace
832
+ self .function = function = getattr (namespace , name )
833
+ self .name = name
834
+ self .argnames , self .kwargnames = varnames (function )
835
+ self .kwargvalues = inspect .getargspec (function ).defaults
836
+ self .kwargs = dict (
837
+ ((name , value ) for name , value in
838
+ zip (self .kwargnames , inspect .getargspec (function ).defaults ))
839
+ ) if self .kwargvalues else {}
840
+ self .opts = hook_spec_opts
841
+ self .argnames = ["__multicall__" ] + list (self .argnames )
842
+
843
+
816
844
class HookImpl :
817
845
def __init__ (self , plugin , plugin_name , function , hook_impl_opts ):
818
846
self .function = function
819
847
self .argnames , self .kwargnames = varnames (self .function )
848
+ self .kwargvalues = inspect .getargspec (function ).defaults
849
+ self .kwargs = dict (
850
+ ((name , value ) for name , value in
851
+ zip (self .kwargnames , inspect .getargspec (function ).defaults ))
852
+ ) if self .kwargvalues else {}
820
853
self .plugin = plugin
821
854
self .opts = hook_impl_opts
822
855
self .plugin_name = plugin_name
0 commit comments