@@ -312,6 +312,9 @@ For another example see the `hook function ordering`_ section of the
312
312
``tryfirst `` and ``trylast `` hooks are still invoked in LIFO order within
313
313
each category.
314
314
315
+
316
+ .. _hookwrappers :
317
+
315
318
Wrappers
316
319
^^^^^^^^
317
320
A *hookimpl * can be marked with a ``"hookwrapper" `` option which indicates that
@@ -451,6 +454,11 @@ whereas this is not:
451
454
def myhook (config , args , extra_arg ):
452
455
print (args)
453
456
457
+ .. note ::
458
+ The one exception to this rule (that a *hookspec * must have as least as
459
+ many arguments as its *hookimpls *) is the conventional `self `_ arg; this
460
+ is always ignored when *hookimpls * are defined as `methods `_.
461
+
454
462
.. _firstresult :
455
463
456
464
First result only
@@ -509,25 +517,6 @@ if a hookspec specifies a ``warn_on_impl``, pluggy will trigger it for any plugi
509
517
def oldhook ():
510
518
pass
511
519
512
-
513
- .. links
514
- .. _@contextlib.contextmanager :
515
- https://docs.python.org/3.6/library/contextlib.html#contextlib.contextmanager
516
- .. _pytest_cmdline_main :
517
- https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py#L80
518
- .. _hookspec module :
519
- https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py
520
- .. _Writing hook functions :
521
- http://doc.pytest.org/en/latest/writing_plugins.html#writing-hook-functions
522
- .. _hookwrapper :
523
- http://doc.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
524
- .. _hook function ordering :
525
- http://doc.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
526
- .. _first result :
527
- http://doc.pytest.org/en/latest/writing_plugins.html#firstresult-stop-at-first-non-none-result
528
- .. _sent :
529
- https://docs.python.org/3/reference/expressions.html#generator.send
530
-
531
520
.. _manage :
532
521
533
522
The Plugin registry
@@ -657,18 +646,21 @@ assertion should not error:
657
646
hookimpl = HookimplMarker(' myproject' )
658
647
659
648
class Plugin1 (object ):
649
+ @hookimpl
660
650
def myhook (self , args ):
661
651
""" Default implementation.
662
652
"""
663
653
return 1
664
654
665
655
class Plugin2 (object ):
656
+ @hookimpl
666
657
def myhook (self , args ):
667
658
""" Default implementation.
668
659
"""
669
660
return 2
670
661
671
662
class Plugin3 (object ):
663
+ @hookimpl
672
664
def myhook (self , args ):
673
665
""" Default implementation.
674
666
"""
@@ -694,6 +686,57 @@ its :ref:`firstresult` in which case only the first single value (which is not
694
686
695
687
.. _call_historic :
696
688
689
+ Exception handling
690
+ ------------------
691
+ If any *hookimpl * errors with an exception no further callbacks
692
+ are invoked and the exception is packaged up and delivered to
693
+ any :ref: `hookwrappers ` before being re-raised at the hook invocation
694
+ point:
695
+
696
+ .. code-block :: python
697
+
698
+ from pluggy import PluginManager, HookimplMarker
699
+
700
+ hookimpl = HookimplMarker(' myproject' )
701
+
702
+ class Plugin1 (object ):
703
+ @hookimpl
704
+ def myhook (self , args ):
705
+ return 1
706
+
707
+ class Plugin2 (object ):
708
+ @hookimpl
709
+ def myhook (self , args ):
710
+ raise RuntimeError
711
+
712
+ class Plugin3 (object ):
713
+ @hookimpl
714
+ def myhook (self , args ):
715
+ return 3
716
+
717
+ @hookimpl (hookwrapper = True )
718
+ def myhook (self , args ):
719
+ outcome = yield
720
+
721
+ try :
722
+ outcome.get_result()
723
+ except RuntimeError :
724
+ # log the error details
725
+ print (outcome.excinfo)
726
+
727
+ pm = PluginManager(' myproject' )
728
+
729
+ # register plugins
730
+ pm.register(Plugin1())
731
+ pm.register(Plugin2())
732
+ pm.register(Plugin3())
733
+
734
+ # register wrapper
735
+ pm.register(sys.modules[__name__ ])
736
+
737
+ # this raises RuntimeError due to Plugin2
738
+ pm.hook.myhook(args = ())
739
+
697
740
Historic calls
698
741
--------------
699
742
A *historic call * allows for all newly registered functions to receive all hook
@@ -799,6 +842,22 @@ in your project you should thus use a dependency restriction like
799
842
800
843
801
844
.. hyperlinks
845
+ .. _@contextlib.contextmanager :
846
+ https://docs.python.org/3.6/library/contextlib.html#contextlib.contextmanager
847
+ .. _pytest_cmdline_main :
848
+ https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py#L80
849
+ .. _hookspec module :
850
+ https://github.com/pytest-dev/pytest/blob/master/_pytest/hookspec.py
851
+ .. _Writing hook functions :
852
+ http://doc.pytest.org/en/latest/writing_plugins.html#writing-hook-functions
853
+ .. _hookwrapper :
854
+ http://doc.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
855
+ .. _hook function ordering :
856
+ http://doc.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
857
+ .. _first result :
858
+ http://doc.pytest.org/en/latest/writing_plugins.html#firstresult-stop-at-first-non-none-result
859
+ .. _sent :
860
+ https://docs.python.org/3/reference/expressions.html#generator.send
802
861
.. _pytest :
803
862
https://pytest.org
804
863
.. _request-response pattern :
@@ -823,6 +882,10 @@ in your project you should thus use a dependency restriction like
823
882
https://github.com/pytest-dev/pluggy/blob/master/tox.ini#L2
824
883
.. _200+ plugins :
825
884
http://plugincompat.herokuapp.com/
885
+ .. _self :
886
+ https://docs.python.org/3.6/tutorial/classes.html#random-remarks
887
+ .. _methods :
888
+ https://docs.python.org/3.6/tutorial/classes.html#method-objects
826
889
827
890
828
891
.. Indices and tables
0 commit comments