pluggy
pluggy is the crystallized core of plugin management and hook calling for pytest, powering 1400+ pytest plugins and pytest itself. It provides HookspecMarker and HookimplMarker decorators to define typed hook contracts, a PluginManager to register plugins and dispatch calls, and supports hook ordering (tryfirst/trylast), firstresult short-circuiting, historic hooks, and new-style generator-based hook wrappers. Current stable version is 1.6.0 (released May 15, 2025); releases follow Semantic Versioning with breaking changes reserved for major version bumps.
Warnings
- breaking Internal submodules pluggy.callers, pluggy.manager, and pluggy.hooks are private. Importing from them (e.g. from pluggy.manager import PluginManager) will break across any release.
- breaking Hook calls must use keyword-only arguments. Calling pm.hook.myhook(1, 2) instead of pm.hook.myhook(arg1=1, arg2=2) raises TypeError.
- breaking The _Result.result property was removed in 1.0.0. Code using outcome.result inside old-style hookwrappers will raise AttributeError.
- breaking Python 3.8 support was dropped in pluggy 1.5.0. Environments pinned to Python 3.8 must stay on pluggy <1.5.
- deprecated Old-style hook wrappers (hookwrapper=True) are soft-deprecated in favor of new-style wrappers (wrapper=True, added in 1.2.0). Old-style wrappers now emit PluggyTeardownRaisedWarning when an exception is raised during teardown.
- gotcha Multiple hook implementations are called in Last In First Out (LIFO) order by default: the last-registered plugin runs first. Return values are collected into a list in that same order.
- gotcha HookspecMarker, HookimplMarker, and PluginManager must all be initialized with the SAME project_name string. A mismatch silently causes implementations to be ignored — no error is raised.
Install
-
pip install pluggy -
uv add pluggy
Imports
- HookspecMarker
from pluggy import HookspecMarker
- HookimplMarker
from pluggy import HookimplMarker
- PluginManager
from pluggy import PluginManager
- PluginValidationError
from pluggy import PluginValidationError
- Result
from pluggy import Result
Quickstart
import pluggy
# Both markers MUST share the same project name as PluginManager
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
class MySpec:
"""Hook specification namespace."""
@hookspec
def process(self, value: int) -> int:
"""Transform a value. Each implementation's return is collected into a list."""
class PluginA:
@hookimpl
def process(self, value: int) -> int:
return value * 2
class PluginB:
@hookimpl(tryfirst=True) # runs before PluginA despite LIFO default
def process(self, value: int) -> int:
return value + 10
pm = pluggy.PluginManager("myproject")
pm.add_hookspecs(MySpec) # register the spec BEFORE plugins
pm.register(PluginA())
pm.register(PluginB())
# Hook calls MUST use keyword arguments only
results = pm.hook.process(value=5)
print(results) # [15, 10] — PluginB (tryfirst) then PluginA, LIFO order
# New-style wrapper (>=1.2.0): use wrapper=True, plain function with yield
class WrapperPlugin:
@hookimpl(wrapper=True)
def process(self, value: int) -> int:
print("before")
result = yield # receives return value of inner calls
print("after")
return result
pm.register(WrapperPlugin())
results2 = pm.hook.process(value=3)
print(results2)