1
1
import sys
2
2
import inspect
3
+ import copy
3
4
import warnings
4
5
5
6
__version__ = '0.5.0'
@@ -274,10 +275,12 @@ def __init__(self, project_name, implprefix=None):
274
275
self .trace = _TagTracer ().get ("pluginmanage" )
275
276
self .hook = _HookRelay (self .trace .root .get ("hook" ))
276
277
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 ()
281
284
282
285
def _hookexec (self , hook , methods , kwargs ):
283
286
# called from all hookcaller instances.
@@ -424,7 +427,7 @@ def _verify_hook(self, hook, hookimpl):
424
427
(hookimpl .plugin_name , hook .name ))
425
428
426
429
# positional arg checking
427
- notinspec = set (hookimpl .argnames ) - set (hook .argnames )
430
+ notinspec = set (hookimpl .argnames ) - set (hook .spec . argnames )
428
431
if notinspec :
429
432
raise PluginValidationError (
430
433
"Plugin %r for hook %r\n hookimpl definition: %s\n "
@@ -517,8 +520,8 @@ def subset_hook_caller(self, name, remove_plugins):
517
520
orig = getattr (self .hook , name )
518
521
plugins_to_remove = [plug for plug in remove_plugins if hasattr (plug , name )]
519
522
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 )
522
525
for hookimpl in (orig ._wrappers + orig ._nonwrappers ):
523
526
plugin = hookimpl .plugin
524
527
if plugin not in plugins_to_remove :
@@ -539,29 +542,43 @@ class _MultiCall(object):
539
542
# so we can remove it soon, allowing to avoid the below recursion
540
543
# in execute() and simplify/speed up the execute loop.
541
544
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 ):
544
546
self .hook_impls = hook_impls
545
547
self .caller_kwargs = kwargs # come from _HookCaller.__call__()
546
548
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
548
552
549
553
def execute (self ):
550
554
caller_kwargs = self .caller_kwargs
551
555
self .results = results = []
552
- firstresult = self .specopts .get ("firstresult" )
556
+ firstresult = self .firstresult
557
+ spec = self .spec
553
558
554
559
while self .hook_impls :
555
560
hook_impl = self .hook_impls .pop ()
561
+ implkwargs = hook_impl .kwargs
556
562
try :
557
563
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 ]
558
573
except KeyError :
559
574
for argname in hook_impl .argnames :
560
575
if argname not in caller_kwargs :
561
576
raise HookCallError (
562
577
"hook call must provide argument %r" % (argname ,))
578
+
563
579
if hook_impl .hookwrapper :
564
580
return _wrapped_call (hook_impl .function (* args ), self .execute )
581
+
565
582
res = hook_impl .function (* args )
566
583
if res is not None :
567
584
if firstresult :
@@ -645,28 +662,23 @@ def __init__(self, name, hook_execute, specmodule_or_class=None, spec_opts=None)
645
662
self ._wrappers = []
646
663
self ._nonwrappers = []
647
664
self ._hookexec = hook_execute
648
- self .argnames = None
649
- self .kwargnames = None
665
+ self .spec = None
666
+ self ._call_history = None
650
667
if specmodule_or_class is not None :
651
668
assert spec_opts is not None
652
669
self .set_specification (specmodule_or_class , spec_opts )
653
670
654
671
def has_spec (self ):
655
- return hasattr ( self , "_specmodule_or_class" )
672
+ return self . spec is not None
656
673
657
674
def set_specification (self , specmodule_or_class , spec_opts ):
658
675
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 )
665
677
if spec_opts .get ("historic" ):
666
678
self ._call_history = []
667
679
668
680
def is_historic (self ):
669
- return hasattr ( self , " _call_history" )
681
+ return self . _call_history is not None
670
682
671
683
def _remove_plugin (self , plugin ):
672
684
def remove (wrappers ):
@@ -702,8 +714,8 @@ def __repr__(self):
702
714
703
715
def __call__ (self , ** kwargs ):
704
716
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 (
707
719
kwargs .keys ())
708
720
if notincall :
709
721
warnings .warn (
@@ -749,10 +761,28 @@ def _maybe_apply_history(self, method):
749
761
proc (res [0 ])
750
762
751
763
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
+
752
778
class HookImpl (object ):
753
779
def __init__ (self , plugin , plugin_name , function , hook_impl_opts ):
754
780
self .function = function
755
781
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 {}
756
786
self .plugin = plugin
757
787
self .opts = hook_impl_opts
758
788
self .plugin_name = plugin_name
0 commit comments