Skip to content

Validate all hookimpls before registering a plugin - #741

Merged
RonnyPfannschmidt merged 2 commits into
pytest-dev:mainfrom
RonnyPfannschmidt:register-two-pass
Sep 25, 2026
Merged

RonnyPfannschmidt merged 2 commits into
pytest-dev:mainfrom
RonnyPfannschmidt:register-two-pass

Conversation

@RonnyPfannschmidt

Copy link
Copy Markdown
Member

Written by Claude Opus 5.5 via Claude Code for the pluggy maintainers; I prompted it, it did the work, I read it.

Fixes #733. Supersedes #740 with a smaller change.

register() now works in two passes. The first collects every hookimpl and verifies those whose hook has a spec, without touching manager state. The second stores the plugin, creates missing hook callers, replays history and adds the impls. A PluginValidationError now leaves the manager unchanged, and the fixed plugin can be registered again.

Unlike #740 there is no rollback. If a historic impl raises during replay, the plugin stays registered, as it does on main today.

The second commit covers an ordering case the split would otherwise have broken. When replaying one impl calls add_hookspecs for a hook that a later impl of the same plugin implements, that later impl is verified in the second pass. On main this was only caught when dir() order put the replayed impl first. A failure there still leaves the plugin partially registered, as on main, but only user code running during replay can trigger it.

Comment thread testing/test_pluginmanager.py Outdated


def test_register_validation_failure_leaves_no_state(he_pm: PluginManager) -> None:
"""dir() order puts the valid impls and the unknown hook before the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit hard to parse. Better not to refer to "old pass" if it's unexplained.

Comment thread testing/test_pluginmanager.py Outdated
assert he_pm.hook.he_method1.get_hookimpls() == []
assert not hasattr(he_pm.hook, "a_new_hook")

del hello.he_method1_invalid

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit weird. Is this section necessary for the test?

Comment thread testing/test_pluginmanager.py Outdated


def test_register_verifies_spec_added_by_historic_replay(pm: PluginManager) -> None:
"""The first pass verifies before any replay runs, so a spec added by

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "pass" parts are referring to the implementation, which can change. I think can just drop the "The first pass verifies before any replay runs, so" part.

RonnyPfannschmidt and others added 2 commits September 24, 2026 19:43
register() stored the plugin and installed each hookimpl while walking
the plugin, so a PluginValidationError on a later hookimpl left the
plugin registered with its earlier hookimpls and hook callers in place.

Split registration into two passes: collect and verify every hookimpl
without touching state, then store the plugin, create missing hook
callers, replay history and add the impls. A validation failure now
leaves the manager unchanged and the fixed plugin can be registered.

Fixes pytest-dev#733

Co-Authored-By: Claude Opus 5.5 (1M context) via Claude Code <noreply@anthropic.com>
The first pass verifies before any historic replay runs. If replaying
one impl calls add_hookspecs for a hook that a later impl of the same
plugin implements, that impl was never checked. The single-pass loop
caught this whenever dir() order put the replayed impl first.

Verify such impls in the second pass, before they are added.

Co-Authored-By: Claude Opus 5.5 (1M context) via Claude Code <noreply@anthropic.com>
@RonnyPfannschmidt
RonnyPfannschmidt merged commit f51e3a7 into pytest-dev:main Sep 25, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PluginManager.register leaves partial state after validation failure

2 participants