Registry / testing / pluggy

pluggy

JSON →
library1.6.0pypypi✓ verified 24d ago

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
INSTALL
IMPORT
SIG · PLUGGY
P
pluggy
testingpythonv1.6.0
Install
1.7s avg
Import
39ms
Disk
16MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.93.13
musl
3.93.13
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
musl
py 3.103.95 runs
installs and imports cleanly · install 0.0s · import 0.038s · 17.9MB
glibc
py 3.103.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.
fix
Import 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.
fix
Always 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.
fix
Replace 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.
fix
Upgrade 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.
fix
Migrate 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.
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.
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.
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.
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.
fix
pip install pluggy
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.
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)`.
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).
fix
Rewrite 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).
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`
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.
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`
Upgrade
Version history
1.6.0latest on PyPI · released May 15, 2025
Audit
Dependencies

No dependency data recorded yet.

Agent activity
9 hits · last 30 days
node
8
Resources