pluggy
raw JSON → 1.6.0 verified Tue May 12 auth: no python install: verified quickstart: verified
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.
pip install pluggy Common errors
error ModuleNotFoundError: No module named 'pluggy' ↓
cause The 'pluggy' package is not installed in the current Python environment or there's an issue with the Python path.
fix
pip install pluggy
error pluggy.manager.PluginValidationError: unknown hook 'your_hook_name' in plugin 'your_plugin_name' ↓
cause A hook implementation (hookimpl) is registered for a hook name that does not have a corresponding hook specification (hookspec) defined in the plugin manager, or the hookimpl's signature does not match the hookspec.
fix
Ensure that all hook implementations have a matching hook specification defined using
pm.add_hookspecs(MySpecClass) and that their function signatures (name and arguments) are compatible. If the hook is optional, mark the hook implementation with @pluggy.hookimpl(optionalhook=True). error pluggy.manager.PluginValidationError: Plugin 'your_plugin_name' for hook 'your_hook_name'\nhookimpl definition: ...\nDeclared as wrapper=True or hookwrapper=True but function is not a generator function ↓
cause A hook implementation decorated with `wrapper=True` (new style) or `hookwrapper=True` (old style) is not implemented as a Python generator function (i.e., it's missing a `yield` statement).
fix
Rewrite the hook wrapper function to be a generator that contains exactly one
yield statement. For example:
@hookimpl(wrapper=True)
def my_hook_wrapper(arg1, arg2):
print('before hook')
outcome = yield
print('after hook')
# Optionally process or force result/exception
# result = outcome.get_result()
# outcome.force_result(new_result) error AttributeError: module 'pluggy' has no attribute 'PluggyTeardownRaisedWarning' ↓
cause This error typically occurs when an older version of `pluggy` is used with a library (like `pytest`) that expects a newer `pluggy` feature or attribute that was introduced in a later version (e.g., pluggy>=1.4.0).
fix
Upgrade your
pluggy package to the latest version, or to a version compatible with the dependent library (e.g., pytest).
pip install --upgrade pluggy error ImportError: cannot import name 'HookspecMarker' from 'pluggy' ↓
cause This usually indicates a corrupted installation, a conflict with another package that might shadow `pluggy`, or an attempt to import from an incorrect location. While `HookspecMarker` is a core `pluggy` component, this specific import error suggests the `pluggy` package itself might be misconfigured.
fix
Reinstall
pluggy to ensure all its components are correctly placed. It's also advisable to check your Python environment's site-packages for any conflicting files or directories named pluggy or pluggy.py.
pip uninstall pluggy
pip install pluggy 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. ↓
fix Import all symbols exclusively from the top-level pluggy namespace: from pluggy import PluginManager, HookspecMarker, HookimplMarker, PluginValidationError, Result.
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. ↓
fix Always pass hook arguments as keyword arguments: pm.hook.myhook(arg1=1, arg2=2).
breaking The _Result.result property was removed in 1.0.0. Code using outcome.result inside old-style hookwrappers will raise AttributeError. ↓
fix Replace outcome.result with outcome.get_result(), which also re-raises any exception the hook raised.
breaking Python 3.8 support was dropped in pluggy 1.5.0. Environments pinned to Python 3.8 must stay on pluggy <1.5. ↓
fix Upgrade to Python >=3.9 or pin pluggy<1.5 for Python 3.8 environments.
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. ↓
fix Migrate hookwrapper=True generator functions to wrapper=True. The yield in new-style wrappers receives the result value directly, not a Result object.
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. ↓
fix Use @hookimpl(tryfirst=True) or @hookimpl(trylast=True) to explicitly control execution order, or @hookspec(firstresult=True) on the spec to stop after the first non-None result.
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. ↓
fix Define the project_name as a module-level constant (e.g. PROJECT_NAME = 'myproject') and reference it in all three places to prevent typo-driven silent failures.
Install
uv add pluggy Install compatibility verified last tested: 2026-05-12
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.03s 17.9M
3.10 slim (glibc) - - 0.02s 18M
3.11 alpine (musl) - - 0.06s 19.8M
3.11 slim (glibc) - - 0.04s 20M
3.12 alpine (musl) - - 0.05s 11.7M
3.12 slim (glibc) - - 0.05s 12M
3.13 alpine (musl) - - 0.04s 11.3M
3.13 slim (glibc) - - 0.04s 12M
3.9 alpine (musl) - - 0.02s 17.4M
3.9 slim (glibc) - - 0.02s 18M
Imports
- HookspecMarker
from pluggy import HookspecMarker - HookimplMarker
from pluggy import HookimplMarker - PluginManager wrong
from pluggy.manager import PluginManagercorrectfrom pluggy import PluginManager - PluginValidationError
from pluggy import PluginValidationError - Result
from pluggy import Result
Quickstart verified last tested: 2026-04-23
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)