|
1 | 1 | ``pluggy``
|
2 | 2 | ==========
|
3 | 3 |
|
4 |
| -The five minute elevator pitch |
5 |
| -****************************** |
| 4 | +The ``pytest`` plugin system |
| 5 | +**************************** |
6 | 6 |
|
7 |
| -``pluggy`` gives `users` the possibility to extend or modify the behaviour of |
8 |
| -a program like e.g. `pytest`_, `tox`_ or `devpi`_ by installing a `plugin` for |
9 |
| -that program. The plugin code will run as part of normal program execution |
10 |
| -changing or enhancing certain aspects of it. |
| 7 | +``pluggy`` is the crystallized core of `plugin management and hook |
| 8 | +calling`_ for `pytest`_. It gives users the possibility to extend or modify |
| 9 | +the behaviour of a host program (e.g. `pytest`_, `tox`_ or `devpi`_) by |
| 10 | +installing a `plugin` for that program. The plugin code will run as part of |
| 11 | +normal program execution changing or enhancing certain aspects of it. |
| 12 | + |
| 13 | +In fact, ``pytest`` is itself composed as a set of ``pluggy`` plugins |
| 14 | +which are invoked in sequence according to a well defined set of protocols. |
| 15 | +Some `200+ plugins`_ use ``pluggy`` to extend and customize ``pytest``'s default behaviour. |
| 16 | + |
| 17 | +In essence, ``pluggy`` enables function `hooking`_ so you can build "pluggable" systems. |
| 18 | + |
| 19 | +Introduction |
| 20 | +------------ |
| 21 | + |
| 22 | +To explain how pluggy can be put to use we need to introduce three roles and explain their interaction with pluggy: |
11 | 23 |
|
12 |
| -There are three roles in which people might interact with ``pluggy``. |
| 24 | +* The `hoster` - the person who wants to add extensibility to their program |
| 25 | +* The `implementer` - the person who wants to extend the `host` functionality by writing a plugin |
| 26 | +* the `user` - the person who installed the `host` and now wants to extend its functionality by using a plugin |
13 | 27 |
|
14 |
| -Publisher |
15 |
| -+++++++++ |
| 28 | +Hoster |
| 29 | +++++++ |
16 | 30 |
|
17 |
| -The `publisher` wants to enable an arbitrary number of `subscribers` to extend |
| 31 | +The `hoster` wants to enable an arbitrary number of `implementers` to extend |
18 | 32 | or modify some aspects of their programs execution. They write hook specifications,
|
19 | 33 | register them with a :py:class:`pluggy.PluginManager` and instantiate a marker
|
20 |
| -object for the `subscriber` - a :py:class:`~pluggy.HookimplMarker` - used |
| 34 | +object for the `implementer` - a :py:class:`~pluggy.HookimplMarker` - used |
21 | 35 | to `decorate <https://www.python.org/dev/peps/pep-0318/#current-syntax>` hook
|
22 | 36 | implementation functions. This marker is usually called `hookimpl` and
|
23 |
| -importable from the root package of the `publisher` project. They then add |
| 37 | +importable from the root package of the `host` project. They then add |
24 | 38 | calls to their specified hooks in appropriate phases of program execution.
|
25 | 39 | ``pluggy`` will then do all the magic.
|
26 | 40 |
|
27 |
| -**FIXME not sure what is the right advice here. Do I need to vendor in pluggy as a publisher to avoid versioning conflicts? Should I just depend on pluggy without version and hope all is good?** |
28 |
| -To avoid compatibility issues they should always pin their ``pluggy`` version |
29 |
| -to the minor version used at implementation time (at the time of writing |
30 |
| -(spring 2018) ``pluggy`` is not at 1.0 yet, so breaking changes should be |
31 |
| -expected with any minor release). |
| 41 | +To avoid compatibility issues with other pluggy hosts installed in the same |
| 42 | +environment we recommend to not specify specific version ranges or keep them |
| 43 | +as broad as possible (at time of writing (Spring 2018) this would be |
| 44 | +>=0.3,<1.0). |
32 | 45 |
|
33 |
| -necessary knowledge about ``pluggy``: |
| 46 | +Necessary knowledge about ``pluggy``: |
34 | 47 |
|
35 | 48 | They need to learn about ``pluggy`` and its capabilities and should at least
|
36 | 49 | loosely follow the development of the ``pluggy`` project.
|
37 | 50 |
|
38 |
| -Subscriber |
39 |
| -++++++++++ |
| 51 | +Implementer |
| 52 | ++++++++++++ |
40 | 53 |
|
41 |
| -The `subscriber` wants to modify or extend the program execution of a |
42 |
| -`publisher` project. They install the project and import it wherever they want |
| 54 | +The `implementer` wants to modify or extend the program execution of a |
| 55 | +host project. They install the project and import it wherever they want |
43 | 56 | to use the `hookimpl` marker. They create one or more hook implementation functions
|
44 |
| -matching the exact name of the `publisher` `hookspec` functions and decorate |
45 |
| -them with `@<publisher>.<hookimpl>`. They only need to declare the subset of |
46 |
| -parameters they actually use in their hook implementation. To make their plugin |
47 |
| -discoverable by the `publisher` they define an |
| 57 | +matching the exact name of the `hookspec` functions defined in the host project |
| 58 | +and decorate them with `@<hoster>.<hookimpl>`. They only need to declare the |
| 59 | +subset of parameters they actually use in their hook implementation. |
| 60 | +To make their plugin discoverable by the `hoster` they define an |
48 | 61 | `entry point <https://packaging.python.org/specifications/entry-points/>`_ in
|
49 | 62 | their `setup.py <https://docs.python.org/3.6/distutils/setupscript.html>`_
|
50 |
| -(defined by the publisher (e.g. `pytest11`, `tox` or `devpi_server`). The result |
| 63 | +(defined by the hoster (e.g. `pytest11`, `tox` or `devpi_server`). The result |
51 | 64 | of this is called a plugin for a certain project. The plugin project naming rule
|
52 |
| -is `<publisher>-<subscriber>` (e.g. `pytest-sugar`, `tox-conda` or `devpi-ldap`). |
| 65 | +is `<host>-<implementation>` (e.g. `pytest-sugar`, `tox-conda` or `devpi-ldap`). |
53 | 66 |
|
54 |
| -necessary knowledge about ``pluggy``: |
| 67 | +Necessary knowledge about ``pluggy``: |
55 | 68 |
|
56 | 69 | Depending on how involved the modifications are, they need to learn about how
|
57 |
| -the `publisher` project works and about the parameters the hook functions provide. |
| 70 | +the host project works and about the parameters the hook functions provide. |
58 | 71 | They also might have to learn a bit more about how ``pluggy`` executes hooks
|
59 | 72 | regarding ordering and other behavioral details.
|
60 | 73 |
|
61 | 74 | User
|
62 | 75 | ++++
|
63 | 76 |
|
64 |
| -The `user` wants to use new or changed features made available by `subscribers` |
65 |
| -as so-called `plugins` for a `publisher` project. They install the `publisher` |
| 77 | +The `user` wants to use new or changed features made available by `implementers` |
| 78 | +as so-called `plugins` for a host project. They install the host |
66 | 79 | project and the `plugin` they want to use. Through the magic of entry points
|
67 |
| -the `publisher` will discover the `plugin` hooks and start calling them as part |
| 80 | +the host project will discover the `plugin` hooks and start calling them as part |
68 | 81 | of their normal program execution.
|
69 | 82 |
|
70 | 83 | If a `user` doesn't need the changed behaviour of a `plugin` anymore, they
|
71 | 84 | uninstall it and all is back to normal.
|
72 | 85 |
|
73 |
| -necessary knowledge about ``pluggy``: |
| 86 | +Necessary knowledge about ``pluggy``: |
74 | 87 |
|
75 |
| -`Users` don't need to know that the mechanism making this possible is provided by ``pluggy``. |
| 88 | +`Users` don't need any knowledge about pluggy as such, but they need to know |
| 89 | +that they can extend the behaviour of the host program by installing plugins |
| 90 | +through their package manager. |
76 | 91 |
|
77 |
| -Origins: the ``pytest`` plugin system |
78 |
| -************************************* |
79 |
| - |
80 |
| -``pluggy`` is the crystallized core of `plugin management and hook |
81 |
| -calling`_ for `pytest`_. |
82 |
| - |
83 |
| -In fact, ``pytest`` is itself composed as a set of ``pluggy`` plugins |
84 |
| -which are invoked in sequence according to a well defined set of protocols. |
85 |
| -Some `200+ plugins`_ use ``pluggy`` to extend and customize ``pytest``'s default behaviour. |
86 |
| - |
87 |
| -In essence, ``pluggy`` enables function `hooking`_ so you can build "pluggable" systems. |
| 92 | +Technical overview |
| 93 | +------------------ |
88 | 94 |
|
89 |
| -How's it work? |
90 |
| --------------- |
91 | 95 | A `plugin` is a `namespace`_ which defines hook functions.
|
92 | 96 |
|
93 | 97 | ``pluggy`` manages *plugins* by relying on:
|
@@ -748,11 +752,7 @@ in your project you should thus use a dependency restriction like
|
748 | 752 |
|
749 | 753 | .. hyperlinks
|
750 | 754 | .. _pytest:
|
751 |
| - https://pytest.org |
752 |
| -.. _tox: |
753 |
| - https://tox.readthedocs.io |
754 |
| -.. _devpi: |
755 |
| - https://devpi.net |
| 755 | + http://pytest.org |
756 | 756 | .. _request-response pattern:
|
757 | 757 | https://en.wikipedia.org/wiki/Request%E2%80%93response
|
758 | 758 | .. _publish-subscribe:
|
|
0 commit comments