Install & Compatibility
Where this runs
tested against v1.6.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.038s · 17.9MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.7s · import 0.040s · 18MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
HookspecMarker
✓ from pluggy import HookspecMarker
Also accessible as pluggy.HookspecMarker. Only top-level pluggy exports are public API.
HookimplMarker
✓ from pluggy import HookimplMarker
Also accessible as pluggy.HookimplMarker. Only top-level pluggy exports are public API.
PluginManager
✓ from pluggy import PluginManager
✗ from pluggy.manager import PluginManager
Internal submodules (pluggy.manager, pluggy.hooks, pluggy.callers) are private (prefixed with _). Import only from the top-level pluggy namespace.
PluginValidationError
✓ from pluggy import PluginValidationError
Raised when a hookimpl fails validation at registration time, e.g. hookwrapper=True on a non-generator function.
Result
✓ from pluggy import Result
Used inside old-style hookwrappers (hookwrapper=True). The deprecated .result property was removed in 1.0.0; always call .get_result() which re-raises on failure.
Define a hook spec and two implementations, register them with a PluginManager scoped to a project name, then call the hook and collect results.
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)
Debug
Known issues
breakingInternal 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.fixImport all symbols exclusively from the top-level pluggy namespace: from pluggy import PluginManager, HookspecMarker, HookimplMarker, PluginValidationError, Result.
affects: <1.0.0 (made private in 1.0.0)
breakingHook calls must use keyword-only arguments. Calling pm.hook.myhook(1, 2) instead of pm.hook.myhook(arg1=1, arg2=2) raises TypeError.fixAlways pass hook arguments as keyword arguments: pm.hook.myhook(arg1=1, arg2=2).
affects: all
breakingThe _Result.result property was removed in 1.0.0. Code using outcome.result inside old-style hookwrappers will raise AttributeError.fixReplace outcome.result with outcome.get_result(), which also re-raises any exception the hook raised.
affects: <1.0.0 (removed in 1.0.0)
breakingPython 3.8 support was dropped in pluggy 1.5.0. Environments pinned to Python 3.8 must stay on pluggy <1.5.fixUpgrade to Python >=3.9 or pin pluggy<1.5 for Python 3.8 environments.
affects: >=1.5.0
deprecatedOld-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.fixMigrate hookwrapper=True generator functions to wrapper=True. The yield in new-style wrappers receives the result value directly, not a Result object.
affects: >=1.2.0
gotchaMultiple 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.fixUse @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.
affects: all
gotchaHookspecMarker, 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.fixDefine 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.
affects: all
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'pluggy'
The 'pluggy' package is not installed in the current Python environment or there's an issue with the Python path.
pluggy.manager.PluginValidationError: unknown hook 'your_hook_name' in plugin 'your_plugin_name'
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.
fixEnsure 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)`.
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
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).
fixRewrite the hook wrapper function to be a generator that contains exactly one `yield` statement. For example:
```python
@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)
``` AttributeError: module 'pluggy' has no attribute 'PluggyTeardownRaisedWarning'
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).
fixUpgrade your `pluggy` package to the latest version, or to a version compatible with the dependent library (e.g., `pytest`).
`pip install --upgrade pluggy`
ImportError: cannot import name 'HookspecMarker' from 'pluggy'
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.
fixReinstall `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`
Upgrade
Version history
1.6.0latest on PyPI · released May 15, 2025
Audit
Dependencies
No dependency data recorded yet.