tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

index.rst (33764B)


      1 ``pluggy``
      2 ==========
      3 **The pytest plugin system**
      4 
      5 What is it?
      6 ***********
      7 ``pluggy`` is the crystallized core of :ref:`plugin management and hook
      8 calling <pytest:writing-plugins>` for :std:doc:`pytest <pytest:index>`.
      9 It enables `1400+ plugins`_ to extend and customize ``pytest``'s default
     10 behaviour. Even ``pytest`` itself is composed as a set of ``pluggy`` plugins
     11 which are invoked in sequence according to a well defined set of protocols.
     12 
     13 It gives users the ability to extend or modify the behaviour of a
     14 ``host program`` by installing a ``plugin`` for that program.
     15 The plugin code will run as part of normal program execution, changing or
     16 enhancing certain aspects of it.
     17 
     18 In essence, ``pluggy`` enables function `hooking`_ so you can build
     19 "pluggable" systems.
     20 
     21 Why is it useful?
     22 *****************
     23 There are some established mechanisms for modifying the behavior of other
     24 programs/libraries in Python like
     25 `method overriding <https://en.wikipedia.org/wiki/Method_overriding>`_
     26 (e.g. Jinja2) or
     27 `monkey patching <https://en.wikipedia.org/wiki/Monkey_patch>`_ (e.g. gevent
     28 or for :std:doc:`writing tests <pytest:how-to/monkeypatch>`).
     29 These strategies become problematic though when several parties want to
     30 participate in the modification of the same program. Therefore ``pluggy``
     31 does not rely on these mechanisms to enable a more structured approach and
     32 avoid unnecessary exposure of state and behaviour. This leads to a more
     33 `loosely coupled <https://en.wikipedia.org/wiki/Loose_coupling>`_ relationship
     34 between ``host`` and ``plugins``.
     35 
     36 The ``pluggy`` approach puts the burden on the designer of the
     37 ``host program`` to think carefully about which objects are really
     38 needed in a hook implementation. This gives ``plugin`` creators a clear
     39 framework for how to extend the ``host`` via a well defined set of functions
     40 and objects to work with.
     41 
     42 How does it work?
     43 *****************
     44 Let us start with a short overview of what is involved:
     45 
     46 * ``host`` or ``host program``: the program offering extensibility
     47  by specifying ``hook functions`` and invoking their implementation(s) as
     48  part of program execution
     49 * ``plugin``: the program implementing (a subset of) the specified hooks and
     50  participating in program execution when the implementations are invoked
     51  by the ``host``
     52 * ``pluggy``: connects ``host`` and ``plugins`` by using ...
     53 
     54    - the hook :ref:`specifications <specs>` defining call signatures
     55      provided by the ``host`` (a.k.a ``hookspecs`` - see :ref:`marking_hooks`)
     56    - the hook :ref:`implementations <impls>` provided by registered
     57      ``plugins`` (a.k.a ``hookimpl`` - see `callbacks`_)
     58    - the hook :ref:`caller <calling>` - a call loop triggered at appropriate
     59      program positions in the ``host`` invoking the implementations and
     60      collecting the results
     61 
     62    ... where for each registered hook *specification*, a hook *call* will
     63    invoke up to ``N`` registered hook *implementations*.
     64 * ``user``: the person who installed the ``host program`` and wants to
     65  extend its functionality with ``plugins``. In the simplest case they install
     66  the ``plugin`` in the same environment as the ``host`` and the magic will
     67  happen when the ``host program`` is run the next time. Depending on
     68  the ``plugin``, there might be other things they need to do. For example,
     69  they might have to call the host with an additional commandline parameter
     70  to the host that the ``plugin`` added.
     71 
     72 A toy example
     73 -------------
     74 Let us demonstrate the core functionality in one module and show how you can
     75 start experimenting with pluggy functionality.
     76 
     77 .. literalinclude:: examples/toy-example.py
     78 
     79 Running this directly gets us::
     80 
     81    $ python docs/examples/toy-example.py
     82 
     83    inside Plugin_2.myhook()
     84    inside Plugin_1.myhook()
     85    [-1, 3]
     86 
     87 A complete example
     88 ------------------
     89 Now let us demonstrate how this plays together in a vaguely real world scenario.
     90 
     91 Let's assume our ``host program`` is called **eggsample** where some eggs will
     92 be prepared and served with a tray containing condiments. As everybody knows:
     93 the more cooks are involved the better the food, so let us make the process
     94 pluggable and write a plugin that improves the meal with some spam and replaces
     95 the steak sauce (nobody likes that anyway) with spam sauce (it's a thing - trust me).
     96 
     97 .. note::
     98 
     99    **naming markers**: ``HookSpecMarker`` and ``HookImplMarker`` must be
    100    initialized with the name of the ``host`` project (the ``name``
    101    parameter in ``setup()``) - so **eggsample** in our case.
    102 
    103    **naming plugin projects**: they should be named in the form of
    104    ``<host>-<plugin>`` (e.g. ``pytest-xdist``), therefore we call our
    105    plugin *eggsample-spam*.
    106 
    107 The host
    108 ^^^^^^^^
    109 ``eggsample/eggsample/__init__.py``
    110 
    111 .. literalinclude:: examples/eggsample/eggsample/__init__.py
    112 
    113 ``eggsample/eggsample/hookspecs.py``
    114 
    115 .. literalinclude:: examples/eggsample/eggsample/hookspecs.py
    116 
    117 ``eggsample/eggsample/lib.py``
    118 
    119 .. literalinclude:: examples/eggsample/eggsample/lib.py
    120 
    121 ``eggsample/eggsample/host.py``
    122 
    123 .. literalinclude:: examples/eggsample/eggsample/host.py
    124 
    125 ``eggsample/setup.py``
    126 
    127 .. literalinclude:: examples/eggsample/setup.py
    128 
    129 Let's get cooking - we install the host and see what a program run looks like::
    130 
    131    $ pip install --editable pluggy/docs/examples/eggsample
    132    $ eggsample
    133 
    134    Your food. Enjoy some egg, egg, salt, egg, egg, pepper, egg
    135    Some condiments? We have pickled walnuts, steak sauce, mushy peas, mint sauce
    136 
    137 The plugin
    138 ^^^^^^^^^^
    139 ``eggsample-spam/eggsample_spam.py``
    140 
    141 .. literalinclude:: examples/eggsample-spam/eggsample_spam.py
    142 
    143 ``eggsample-spam/setup.py``
    144 
    145 .. literalinclude:: examples/eggsample-spam/setup.py
    146 
    147 Let's get cooking with more cooks - we install the plugin and and see what
    148 we get::
    149 
    150    $ pip install --editable pluggy/docs/examples/eggsample-spam
    151    $ eggsample
    152 
    153    Your food. Enjoy some egg, lovely spam, salt, egg, egg, egg, wonderous spam, egg, pepper
    154    Some condiments? We have pickled walnuts, mushy peas, mint sauce, spam sauce
    155    Now this is what I call a condiments tray!
    156 
    157 More real world examples
    158 ------------------------
    159 To see how ``pluggy`` is used in the real world, have a look at these projects
    160 documentation and source code:
    161 
    162 * :ref:`pytest <pytest:writing-plugins>`
    163 * :std:doc:`tox <tox:plugins>`
    164 * :std:doc:`devpi <devpi:devguide/index>`
    165 * :std:doc:`kedro <kedro:hooks/introduction>`
    166 
    167 For more details and advanced usage please read on.
    168 
    169 .. _define:
    170 
    171 Define and collect hooks
    172 ************************
    173 A *plugin* is a :ref:`namespace <python:tut-scopes>` type (currently one of a
    174 ``class`` or module) which defines a set of *hook* functions.
    175 
    176 As mentioned in :ref:`manage`, all *plugins* which specify *hooks*
    177 are managed by an instance of a :py:class:`pluggy.PluginManager` which
    178 defines the primary ``pluggy`` API.
    179 
    180 In order for a :py:class:`~pluggy.PluginManager` to detect functions in a namespace
    181 intended to be *hooks*, they must be decorated using special ``pluggy`` *marks*.
    182 
    183 .. _marking_hooks:
    184 
    185 Marking hooks
    186 -------------
    187 The :py:class:`~pluggy.HookspecMarker` and :py:class:`~pluggy.HookimplMarker`
    188 decorators are used to *mark* functions for detection by a
    189 :py:class:`~pluggy.PluginManager`:
    190 
    191 .. code-block:: python
    192 
    193    from pluggy import HookspecMarker, HookimplMarker
    194 
    195    hookspec = HookspecMarker("project_name")
    196    hookimpl = HookimplMarker("project_name")
    197 
    198 
    199 Each decorator type takes a single ``project_name`` string as its
    200 lone argument the value of which is used to mark hooks for detection by
    201 a similarly configured :py:class:`~pluggy.PluginManager` instance.
    202 
    203 That is, a *mark* type called with ``project_name`` returns an object which
    204 can be used to decorate functions which will then be detected by a
    205 :py:class:`~pluggy.PluginManager` which was instantiated with the same
    206 ``project_name`` value.
    207 
    208 Furthermore, each *hookimpl* or *hookspec* decorator can configure the
    209 underlying call-time behavior of each *hook* object by providing special
    210 *options* passed as keyword arguments.
    211 
    212 
    213 .. note::
    214    The following sections correspond to similar documentation in
    215    ``pytest`` for :ref:`pytest:writinghooks` and can be used as
    216    a supplementary resource.
    217 
    218 .. _impls:
    219 
    220 Implementations
    221 ---------------
    222 A hook *implementation* (*hookimpl*) is just a (callback) function
    223 which has been appropriately marked.
    224 
    225 *hookimpls* are loaded from a plugin using the
    226 :py:meth:`~pluggy.PluginManager.register()` method:
    227 
    228 *hookimpls* must be hashable.
    229 
    230 .. code-block:: python
    231 
    232    import sys
    233    from pluggy import PluginManager, HookimplMarker
    234 
    235    hookimpl = HookimplMarker("myproject")
    236 
    237 
    238    @hookimpl
    239    def setup_project(config, args):
    240        """This hook is used to process the initial config
    241        and possibly input arguments.
    242        """
    243        if args:
    244            config.process_args(args)
    245 
    246        return config
    247 
    248 
    249    pm = PluginManager("myproject")
    250 
    251    # load all hookimpls from the local module's namespace
    252    plugin_name = pm.register(sys.modules[__name__])
    253 
    254 .. _optionalhook:
    255 
    256 Optional validation
    257 ^^^^^^^^^^^^^^^^^^^
    258 Normally each *hookimpl* should be validated against a corresponding
    259 hook :ref:`specification <specs>`. If you want to make an exception
    260 then the *hookimpl* should be marked with the ``"optionalhook"`` option:
    261 
    262 .. code-block:: python
    263 
    264    @hookimpl(optionalhook=True)
    265    def setup_project(config, args):
    266        """This hook is used to process the initial config
    267        and possibly input arguments.
    268        """
    269        if args:
    270            config.process_args(args)
    271 
    272        return config
    273 
    274 .. _specname:
    275 
    276 Hookspec name matching
    277 ^^^^^^^^^^^^^^^^^^^^^^
    278 
    279 During plugin :ref:`registration <registration>`, pluggy attempts to match each
    280 hook implementation declared by the *plugin* to a hook
    281 :ref:`specification <specs>` in the *host* program with the **same name** as
    282 the function being decorated by ``@hookimpl`` (e.g. ``setup_project`` in the
    283 example above).  Note: there is *no* strict requirement that each *hookimpl*
    284 has a corresponding *hookspec* (see
    285 :ref:`enforcing spec validation <enforcing>`).
    286 
    287 *new in version 1.0.0:*
    288 
    289 To override the default behavior, a *hookimpl* may also be matched to a
    290 *hookspec* in the *host* program with a non-matching function name by using
    291 the ``specname`` option.  Continuing the example above, the *hookimpl* function
    292 does not need to be named ``setup_project``, but if the argument
    293 ``specname="setup_project"`` is provided to the ``hookimpl`` decorator, it will
    294 be matched and checked against the ``setup_project`` hookspec.
    295 
    296 This is useful for registering multiple implementations of the same plugin in
    297 the same Python file, for example:
    298 
    299 .. code-block:: python
    300 
    301    @hookimpl(specname="setup_project")
    302    def setup_1(config, args):
    303        """This hook is used to process the initial config
    304        and possibly input arguments.
    305        """
    306        if args:
    307            config.process_args(args)
    308 
    309        return config
    310 
    311 
    312    @hookimpl(specname="setup_project")
    313    def setup_2(config, args):
    314        """Perform additional setup steps"""
    315        # ...
    316        return config
    317 
    318 
    319 .. _callorder:
    320 
    321 Call time order
    322 ^^^^^^^^^^^^^^^
    323 By default hooks are :ref:`called <calling>` in LIFO registered order, however,
    324 a *hookimpl* can influence its call-time invocation position using special
    325 attributes. If marked with a ``"tryfirst"`` or ``"trylast"`` option it
    326 will be executed *first* or *last* respectively in the hook call loop:
    327 
    328 .. code-block:: python
    329 
    330    import sys
    331    from pluggy import PluginManager, HookimplMarker
    332 
    333    hookimpl = HookimplMarker("myproject")
    334 
    335 
    336    @hookimpl(trylast=True)
    337    def setup_project(config, args):
    338        """Default implementation."""
    339        if args:
    340            config.process_args(args)
    341 
    342        return config
    343 
    344 
    345    class SomeOtherPlugin:
    346        """Some other plugin defining the same hook."""
    347 
    348        @hookimpl(tryfirst=True)
    349        def setup_project(self, config, args):
    350            """Report what args were passed before calling
    351            downstream hooks.
    352            """
    353            if args:
    354                print("Got args: {}".format(args))
    355 
    356            return config
    357 
    358 
    359    pm = PluginManager("myproject")
    360 
    361    # load from the local module's namespace
    362    pm.register(sys.modules[__name__])
    363    # load a plugin defined on a class
    364    pm.register(SomeOtherPlugin())
    365 
    366 For another example see the :ref:`pytest:plugin-hookorder` section of the
    367 ``pytest`` docs.
    368 
    369 .. note::
    370    ``tryfirst`` and ``trylast`` hooks are still invoked in LIFO order within
    371    each category.
    372 
    373 
    374 .. _hookwrappers:
    375 
    376 Wrappers
    377 ^^^^^^^^
    378 
    379 .. note::
    380    This section describes "new-style hook wrappers", which were added in Pluggy
    381    1.1. For earlier versions, see the "old-style hook wrappers" section below.
    382 
    383    New-style hooks wrappers are declared with ``wrapper=True``, while
    384    old-style hook wrappers are declared with ``hookwrapper=True``.
    385 
    386    The two styles are fully interoperable between plugins using different
    387    styles. However within the same plugin we recommend using only one style,
    388    for consistency.
    389 
    390 A *hookimpl* can be marked with the ``"wrapper"`` option, which indicates
    391 that the function will be called to *wrap* (or surround) all other normal
    392 *hookimpl* calls. A *hook wrapper* can thus execute some code ahead and after the
    393 execution of all corresponding non-wrapper *hookimpls*.
    394 
    395 Much in the same way as a :py:func:`@contextlib.contextmanager <python:contextlib.contextmanager>`,
    396 *hook wrappers* must be implemented as generator function with a single ``yield`` in its body:
    397 
    398 .. code-block:: python
    399 
    400    @hookimpl(wrapper=True)
    401    def setup_project(config, args):
    402        """Wrap calls to ``setup_project()`` implementations which
    403        should return json encoded config options.
    404        """
    405        # get initial default config
    406        defaults = config.tojson()
    407 
    408        if config.debug:
    409            print("Pre-hook config is {}".format(config.tojson()))
    410 
    411        # all corresponding hookimpls are invoked here
    412        result = yield
    413 
    414        for item in result:
    415            print("JSON config override is {}".format(item))
    416 
    417        if config.debug:
    418            print("Post-hook config is {}".format(config.tojson()))
    419 
    420        if config.use_defaults:
    421            return defaults
    422        else:
    423            return result
    424 
    425 The generator is :py:meth:`sent <python:generator.send>` the return value
    426 of the hook thus far, or, if the previous calls raised an exception, it is
    427 :py:meth:`thrown <python:generator.throw>` the exception.
    428 
    429 The function should do one of two things:
    430 
    431 - Return a value, which can be the same value as received from the ``yield``, or something else entirely.
    432 
    433 - Raise an exception.
    434 
    435 The return value or exception propagate to further hook wrappers, and finally
    436 to the hook caller.
    437 
    438 Also see the :ref:`pytest:hookwrapper` section in the ``pytest`` docs.
    439 
    440 .. _old_style_hookwrappers:
    441 
    442 Old-style wrappers
    443 ^^^^^^^^^^^^^^^^^^
    444 
    445 .. note::
    446    Prefer to use new-style hook wrappers, unless you need to support Pluggy
    447    versions before 1.1.
    448 
    449 A *hookimpl* can be marked with the ``"hookwrapper"`` option, which indicates
    450 that the function will be called to *wrap* (or surround) all other normal
    451 *hookimpl* calls. A *hookwrapper* can thus execute some code ahead and after the
    452 execution of all corresponding non-wrapper *hookimpls*.
    453 
    454 *hookwrappers* must be implemented as generator function with a single ``yield`` in its body:
    455 
    456 
    457 .. code-block:: python
    458 
    459    @hookimpl(hookwrapper=True)
    460    def setup_project(config, args):
    461        """Wrap calls to ``setup_project()`` implementations which
    462        should return json encoded config options.
    463        """
    464        # get initial default config
    465        defaults = config.tojson()
    466 
    467        if config.debug:
    468            print("Pre-hook config is {}".format(config.tojson()))
    469 
    470        # all corresponding hookimpls are invoked here
    471        outcome = yield
    472 
    473        try:
    474            result = outcome.get_result()
    475        except BaseException as e:
    476            outcome.force_exception(e)
    477            return
    478 
    479        for item in result:
    480            print("JSON config override is {}".format(item))
    481 
    482        if config.debug:
    483            print("Post-hook config is {}".format(config.tojson()))
    484 
    485        if config.use_defaults:
    486            outcome.force_result(defaults)
    487 
    488 The generator is :py:meth:`sent <python:generator.send>` a :py:class:`pluggy.Result` object which can
    489 be assigned in the ``yield`` expression and used to inspect
    490 the final result(s) or exceptions returned back to the caller using the
    491 :py:meth:`~pluggy.Result.get_result` method, override the result
    492 using the :py:meth:`~pluggy.Result.force_result`, or override
    493 the exception using the :py:meth:`~pluggy.Result.force_exception`
    494 method.
    495 
    496 .. note::
    497    Old-style hook wrappers can **not** return results; they can only modify
    498    them using the :py:meth:`~pluggy.Result.force_result` API.
    499 
    500    Old-style Hook wrappers should **not** raise exceptions; this will cause
    501    further hookwrappers to be skipped. They should use
    502    :py:meth:`~pluggy.Result.force_exception` to adjust the
    503    exception.
    504 
    505 .. _specs:
    506 
    507 Specifications
    508 --------------
    509 A hook *specification* (*hookspec*) is a definition used to validate each
    510 *hookimpl* ensuring that an extension writer has correctly defined their
    511 callback function *implementation* .
    512 
    513 *hookspecs* are defined using similarly marked functions however only the
    514 function *signature* (its name and names of all its arguments) is analyzed
    515 and stored. As such, often you will see a *hookspec* defined with only
    516 a docstring in its body.
    517 
    518 *hookspecs* are loaded using the
    519 :py:meth:`~pluggy.PluginManager.add_hookspecs()` method and normally
    520 should be added before registering corresponding *hookimpls*:
    521 
    522 .. code-block:: python
    523 
    524    import sys
    525    from pluggy import PluginManager, HookspecMarker
    526 
    527    hookspec = HookspecMarker("myproject")
    528 
    529 
    530    @hookspec
    531    def setup_project(config, args):
    532        """This hook is used to process the initial config and input
    533        arguments.
    534        """
    535 
    536 
    537    pm = PluginManager("myproject")
    538 
    539    # load from the local module's namespace
    540    pm.add_hookspecs(sys.modules[__name__])
    541 
    542 
    543 Registering a *hookimpl* which does not meet the constraints of its
    544 corresponding *hookspec* will result in an error.
    545 
    546 A *hookspec* can also be added **after** some *hookimpls* have been
    547 registered however this is not normally recommended as it results in
    548 delayed hook validation.
    549 
    550 .. note::
    551    The term *hookspec* can sometimes refer to the plugin-namespace
    552    which defines ``hookspec`` decorated functions as in the case of
    553    ``pytest``'s `hookspec module`_
    554 
    555 .. _enforcing:
    556 
    557 Enforcing spec validation
    558 ^^^^^^^^^^^^^^^^^^^^^^^^^
    559 By default there is no strict requirement that each *hookimpl* has
    560 a corresponding *hookspec*. However, if you'd like you enforce this
    561 behavior you can run a check with the
    562 :py:meth:`~pluggy.PluginManager.check_pending()` method. If you'd like
    563 to enforce requisite *hookspecs* but with certain exceptions for some hooks
    564 then make sure to mark those hooks as :ref:`optional <optionalhook>`.
    565 
    566 Opt-in arguments
    567 ^^^^^^^^^^^^^^^^
    568 To allow for *hookspecs* to evolve over the lifetime of a project,
    569 *hookimpls* can accept **less** arguments than defined in the spec.
    570 This allows for extending hook arguments (and thus semantics) without
    571 breaking existing *hookimpls*.
    572 
    573 In other words this is ok:
    574 
    575 .. code-block:: python
    576 
    577    @hookspec
    578    def myhook(config, args):
    579        pass
    580 
    581 
    582    @hookimpl
    583    def myhook(args):
    584        print(args)
    585 
    586 
    587 whereas this is not:
    588 
    589 .. code-block:: python
    590 
    591    @hookspec
    592    def myhook(config, args):
    593        pass
    594 
    595 
    596    @hookimpl
    597    def myhook(config, args, extra_arg):
    598        print(args)
    599 
    600 .. note::
    601    The one exception to this rule (that a *hookspec* must have as least as
    602    many arguments as its *hookimpls*) is the conventional :ref:`self <python:tut-remarks>` arg; this
    603    is always ignored when *hookimpls* are defined as :ref:`methods <python:tut-methodobjects>`.
    604 
    605 .. _firstresult:
    606 
    607 First result only
    608 ^^^^^^^^^^^^^^^^^
    609 A *hookspec* can be marked such that when the *hook* is called the call loop
    610 will only invoke up to the first *hookimpl* which returns a result other
    611 than ``None``.
    612 
    613 .. code-block:: python
    614 
    615    @hookspec(firstresult=True)
    616    def myhook(config, args):
    617        pass
    618 
    619 This can be useful for optimizing a call loop for which you are only
    620 interested in a single core *hookimpl*. An example is the
    621 :func:`~_pytest.hookspec.pytest_cmdline_main` central routine of ``pytest``.
    622 Note that all hook wrappers are still invoked with the first result.
    623 
    624 Also see the :ref:`pytest:firstresult` section in the ``pytest`` docs.
    625 
    626 .. _historic:
    627 
    628 Historic hooks
    629 ^^^^^^^^^^^^^^
    630 You can mark a *hookspec* as being *historic* meaning that the hook
    631 can be called with :py:meth:`~pluggy.HookCaller.call_historic()` **before**
    632 having been registered:
    633 
    634 .. code-block:: python
    635 
    636    @hookspec(historic=True)
    637    def myhook(config, args):
    638        pass
    639 
    640 The implication is that late registered *hookimpls* will be called back
    641 immediately at register time and **can not** return a result to the caller.
    642 
    643 This turns out to be particularly useful when dealing with lazy or
    644 dynamically loaded plugins.
    645 
    646 For more info see :ref:`call_historic`.
    647 
    648 
    649 .. _warn_on_impl:
    650 
    651 Warnings on hook implementation
    652 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    653 
    654 As projects evolve new hooks may be introduced and/or deprecated.
    655 
    656 If a hookspec specifies a ``warn_on_impl``, pluggy will trigger it for any plugin implementing the hook.
    657 
    658 
    659 .. code-block:: python
    660 
    661    @hookspec(
    662        warn_on_impl=DeprecationWarning("old_hook is deprecated and will be removed soon")
    663    )
    664    def old_hook():
    665        pass
    666 
    667 
    668 If you don't want to deprecate implementing the entire hook, but just specific
    669 parameters of it, you can specify ``warn_on_impl_args``, a dict mapping
    670 parameter names to warnings. The warnings will trigger whenever any plugin
    671 implements the hook requesting one of the specified parameters.
    672 
    673 .. code-block:: python
    674 
    675    @hookspec(
    676        warn_on_impl_args={
    677            "lousy_arg": DeprecationWarning(
    678                "The lousy_arg parameter of refreshed_hook is deprecated and will be removed soon; "
    679                "use awesome_arg instead"
    680            ),
    681        },
    682    )
    683    def refreshed_hook(lousy_arg, awesome_arg):
    684        pass
    685 
    686 .. versionadded:: 1.5
    687   The ``warn_on_impl_args`` parameter.
    688 
    689 
    690 .. _manage:
    691 
    692 The Plugin registry
    693 *******************
    694 ``pluggy`` manages plugins using instances of the
    695 :py:class:`pluggy.PluginManager`.
    696 
    697 A :py:class:`~pluggy.PluginManager` is instantiated with a single
    698 ``str`` argument, the ``project_name``:
    699 
    700 .. code-block:: python
    701 
    702    import pluggy
    703 
    704    pm = pluggy.PluginManager("my_project_name")
    705 
    706 
    707 The ``project_name`` value is used when a :py:class:`~pluggy.PluginManager`
    708 scans for *hook* functions :ref:`defined on a plugin <define>`.
    709 This allows for multiple plugin managers from multiple projects
    710 to define hooks alongside each other.
    711 
    712 .. _registration:
    713 
    714 Registration
    715 ------------
    716 Each :py:class:`~pluggy.PluginManager` maintains a *plugin* registry where each *plugin*
    717 contains a set of *hookimpl* definitions. Loading *hookimpl* and *hookspec*
    718 definitions to populate the registry is described in detail in the section on
    719 :ref:`define`.
    720 
    721 In summary, you pass a plugin namespace object to the
    722 :py:meth:`~pluggy.PluginManager.register()` and
    723 :py:meth:`~pluggy.PluginManager.add_hookspecs()` methods to collect
    724 hook *implementations* and *specifications* from *plugin* namespaces respectively.
    725 
    726 You can unregister any *plugin*'s hooks using
    727 :py:meth:`~pluggy.PluginManager.unregister()` and check if a plugin is
    728 registered by passing its name to the
    729 :py:meth:`~pluggy.PluginManager.is_registered()` method.
    730 
    731 Loading ``setuptools`` entry points
    732 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    733 You can automatically load plugins registered through
    734 :ref:`setuptools entry points <setuptools:entry_points>`
    735 with the :py:meth:`~pluggy.PluginManager.load_setuptools_entrypoints()`
    736 method.
    737 
    738 An example use of this is the :ref:`pytest entry point <pytest:pip-installable plugins>`.
    739 
    740 
    741 Blocking
    742 --------
    743 You can block any plugin from being registered using
    744 :py:meth:`~pluggy.PluginManager.set_blocked()` and check if a given
    745 *plugin* is blocked by name using :py:meth:`~pluggy.PluginManager.is_blocked()`.
    746 
    747 
    748 Inspection
    749 ----------
    750 You can use a variety of methods to inspect both the registry
    751 and particular plugins in it:
    752 
    753 - :py:meth:`~pluggy.PluginManager.list_name_plugin()` -
    754  return a list of name-plugin pairs
    755 - :py:meth:`~pluggy.PluginManager.get_plugins()` - retrieve all plugins
    756 - :py:meth:`~pluggy.PluginManager.get_canonical_name()`- get a *plugin*'s
    757  canonical name (the name it was registered with)
    758 - :py:meth:`~pluggy.PluginManager.get_plugin()` - retrieve a plugin by its
    759  canonical name
    760 
    761 
    762 Parsing mark options
    763 ^^^^^^^^^^^^^^^^^^^^
    764 You can retrieve the *options* applied to a particular
    765 *hookspec* or *hookimpl* as per :ref:`marking_hooks` using the
    766 :py:meth:`~pluggy.PluginManager.parse_hookspec_opts()` and
    767 :py:meth:`~pluggy.PluginManager.parse_hookimpl_opts()` respectively.
    768 
    769 
    770 .. _calling:
    771 
    772 Calling hooks
    773 *************
    774 The core functionality of ``pluggy`` enables an extension provider
    775 to override function calls made at certain points throughout a program.
    776 
    777 A particular *hook* is invoked by calling an instance of
    778 a :py:class:`pluggy.HookCaller` which in turn *loops* through the
    779 ``1:N`` registered *hookimpls* and calls them in sequence.
    780 
    781 Every :py:class:`~pluggy.PluginManager` has a ``hook`` attribute
    782 which is an instance of :py:class:`pluggy.HookRelay`.
    783 The :py:class:`~pluggy.HookRelay` itself contains references
    784 (by hook name) to each registered *hookimpl*'s :py:class:`~pluggy.HookCaller` instance.
    785 
    786 More practically you call a *hook* like so:
    787 
    788 .. code-block:: python
    789 
    790    import sys
    791    import pluggy
    792    import mypluginspec
    793    import myplugin
    794    from configuration import config
    795 
    796    pm = pluggy.PluginManager("myproject")
    797    pm.add_hookspecs(mypluginspec)
    798    pm.register(myplugin)
    799 
    800    # we invoke the HookCaller and thus all underlying hookimpls
    801    result_list = pm.hook.myhook(config=config, args=sys.argv)
    802 
    803 Note that you **must** call hooks using keyword :std:term:`python:argument` syntax!
    804 
    805 Hook implementations are called in LIFO registered order: *the last
    806 registered plugin's hooks are called first*. As an example, the below
    807 assertion should not error:
    808 
    809 .. code-block:: python
    810 
    811    from pluggy import PluginManager, HookimplMarker
    812 
    813    hookimpl = HookimplMarker("myproject")
    814 
    815 
    816    class Plugin1:
    817        @hookimpl
    818        def myhook(self, args):
    819            """Default implementation."""
    820            return 1
    821 
    822 
    823    class Plugin2:
    824        @hookimpl
    825        def myhook(self, args):
    826            """Default implementation."""
    827            return 2
    828 
    829 
    830    class Plugin3:
    831        @hookimpl
    832        def myhook(self, args):
    833            """Default implementation."""
    834            return 3
    835 
    836 
    837    pm = PluginManager("myproject")
    838    pm.register(Plugin1())
    839    pm.register(Plugin2())
    840    pm.register(Plugin3())
    841 
    842    assert pm.hook.myhook(args=()) == [3, 2, 1]
    843 
    844 Collecting results
    845 ------------------
    846 By default calling a hook results in all underlying :ref:`hookimpls
    847 <impls>` functions to be invoked in sequence via a loop. Any function
    848 which returns a value other than a ``None`` result will have that result
    849 appended to a :py:class:`list` which is returned by the call.
    850 
    851 The only exception to this behaviour is if the hook has been marked to return
    852 its :ref:`first result only <firstresult>` in which case only the first
    853 single value (which is not ``None``) will be returned.
    854 
    855 .. _call_historic:
    856 
    857 Exception handling
    858 ------------------
    859 If any *hookimpl* errors with an exception no further callbacks are invoked and
    860 the exception is delivered to any :ref:`wrappers <hookwrappers>` before being
    861 re-raised at the hook invocation point:
    862 
    863 .. code-block:: python
    864 
    865    from pluggy import PluginManager, HookimplMarker
    866 
    867    hookimpl = HookimplMarker("myproject")
    868 
    869 
    870    class Plugin1:
    871        @hookimpl
    872        def myhook(self, args):
    873            return 1
    874 
    875 
    876    class Plugin2:
    877        @hookimpl
    878        def myhook(self, args):
    879            raise RuntimeError
    880 
    881 
    882    class Plugin3:
    883        @hookimpl
    884        def myhook(self, args):
    885            return 3
    886 
    887 
    888    @hookimpl(wrapper=True)
    889    def myhook(self, args):
    890        try:
    891            return (yield)
    892        except RuntimeError as exc:
    893            # log runtime error details
    894            print(exc)
    895            raise
    896 
    897 
    898    pm = PluginManager("myproject")
    899 
    900    # register plugins
    901    pm.register(Plugin1())
    902    pm.register(Plugin2())
    903    pm.register(Plugin3())
    904 
    905    # register wrapper
    906    pm.register(sys.modules[__name__])
    907 
    908    # this raises RuntimeError due to Plugin2
    909    pm.hook.myhook(args=())
    910 
    911 Historic calls
    912 --------------
    913 A *historic call* allows for all newly registered functions to receive all hook
    914 calls that happened before their registration. The implication is that this is
    915 only useful if you expect that some *hookimpls* may be registered **after** the
    916 hook is initially invoked.
    917 
    918 Historic hooks must be :ref:`specially marked <historic>` and called
    919 using the :py:meth:`~pluggy.HookCaller.call_historic()` method:
    920 
    921 .. code-block:: python
    922 
    923    def callback(result):
    924        print("historic call result is {result}".format(result=result))
    925 
    926 
    927    # call with history; no results returned
    928    pm.hook.myhook.call_historic(
    929        kwargs={"config": config, "args": sys.argv}, result_callback=callback
    930    )
    931 
    932    # ... more of our program ...
    933 
    934    # late loading of some plugin
    935    import mylateplugin
    936 
    937    # historic callback is invoked here
    938    pm.register(mylateplugin)
    939 
    940 Note that if you :py:meth:`~pluggy.HookCaller.call_historic()`
    941 the :py:class:`~pluggy.HookCaller` (and thus your calling code)
    942 can not receive results back from the underlying *hookimpl* functions.
    943 Instead you can provide a *callback* for processing results (like the
    944 ``callback`` function above) which will be called as each new plugin
    945 is registered.
    946 
    947 .. note::
    948    *historic* calls are incompatible with :ref:`firstresult` marked
    949    hooks since only the first registered plugin's hook(s) would
    950    ever be called.
    951 
    952 .. _call_extra:
    953 
    954 Calling with extras
    955 -------------------
    956 You can call a hook with temporarily participating *implementation* functions
    957 (that aren't in the registry) using the
    958 :py:meth:`pluggy.HookCaller.call_extra()` method.
    959 
    960 
    961 Calling with a subset of registered plugins
    962 -------------------------------------------
    963 You can make a call using a subset of plugins by asking the
    964 :py:class:`~pluggy.PluginManager` first for a
    965 :py:class:`~pluggy.HookCaller` with those plugins removed
    966 using the :py:meth:`pluggy.PluginManager.subset_hook_caller()` method.
    967 
    968 You then can use that :py:class:`~pluggy.HookCaller`
    969 to make normal, :py:meth:`~pluggy.HookCaller.call_historic`, or
    970 :py:meth:`~pluggy.HookCaller.call_extra` calls as necessary.
    971 
    972 
    973 .. _tracing:
    974 
    975 Built-in tracing
    976 ****************
    977 ``pluggy`` comes with some batteries included hook tracing for your
    978 debugging needs.
    979 
    980 
    981 Call tracing
    982 ------------
    983 To enable tracing use the
    984 :py:meth:`pluggy.PluginManager.enable_tracing()` method which returns an
    985 undo function to disable the behaviour.
    986 
    987 .. code-block:: python
    988 
    989    pm = PluginManager("myproject")
    990    # magic line to set a writer function
    991    pm.trace.root.setwriter(print)
    992    undo = pm.enable_tracing()
    993 
    994 
    995 Call monitoring
    996 ---------------
    997 Instead of using the built-in tracing mechanism you can also add your
    998 own ``before`` and ``after`` monitoring functions using
    999 :py:class:`pluggy.PluginManager.add_hookcall_monitoring()`.
   1000 
   1001 The expected signature and default implementations for these functions is:
   1002 
   1003 .. code-block:: python
   1004 
   1005    def before(hook_name, hook_impls, kwargs):
   1006        pass
   1007 
   1008 
   1009    def after(outcome, hook_name, hook_impls, kwargs):
   1010        pass
   1011 
   1012 Public API
   1013 **********
   1014 Please see the :doc:`api_reference`.
   1015 
   1016 Development
   1017 ***********
   1018 Great care must taken when hacking on ``pluggy`` since multiple mature
   1019 projects rely on it. Our Github integrated CI process runs the full
   1020 `tox test suite`_ on each commit so be sure your changes can run on
   1021 all required `Python interpreters`_ and ``pytest`` versions.
   1022 
   1023 For development, we suggest to create a virtual environment and install ``pluggy`` in
   1024 editable mode and ``dev`` dependencies::
   1025 
   1026    $ python3 -m venv .env
   1027    $ source .env/bin/activate
   1028    $ pip install -e .[dev]
   1029 
   1030 To make sure you follow the code style used in the project, install pre-commit_ which
   1031 will run style checks before each commit::
   1032 
   1033    $ pre-commit install
   1034 
   1035 
   1036 Release Policy
   1037 **************
   1038 Pluggy uses `Semantic Versioning`_. Breaking changes are only foreseen for
   1039 Major releases (incremented X in "X.Y.Z").  If you want to use ``pluggy``
   1040 in your project you should thus use a dependency restriction like
   1041 ``"pluggy>=0.1.0,<1.0"`` to avoid surprises.
   1042 
   1043 
   1044 Table of contents
   1045 *****************
   1046 
   1047 .. toctree::
   1048    :maxdepth: 2
   1049 
   1050    api_reference
   1051    changelog
   1052 
   1053 
   1054 
   1055 .. hyperlinks
   1056 .. _hookspec module:
   1057    https://docs.pytest.org/en/latest/_modules/_pytest/hookspec.html
   1058 .. _request-response pattern:
   1059    https://en.wikipedia.org/wiki/Request%E2%80%93response
   1060 .. _publish-subscribe:
   1061    https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
   1062 .. _hooking:
   1063    https://en.wikipedia.org/wiki/Hooking
   1064 .. _callbacks:
   1065    https://en.wikipedia.org/wiki/Callback_(computer_programming)
   1066 .. _tox test suite:
   1067    https://github.com/pytest-dev/pluggy/blob/main/tox.ini
   1068 .. _Semantic Versioning:
   1069    https://semver.org/
   1070 .. _Python interpreters:
   1071    https://github.com/pytest-dev/pluggy/blob/main/tox.ini#L2
   1072 .. _1400+ plugins:
   1073    https://docs.pytest.org/en/latest/reference/plugin_list.html
   1074 .. _pre-commit:
   1075    https://pre-commit.com/
   1076 
   1077 
   1078 .. Indices and tables
   1079 .. ==================
   1080 .. * :ref:`genindex`
   1081 .. * :ref:`modindex`
   1082 .. * :ref:`search`