config: add plugin hookspecs at registration, not at Config.parse() - #15015
Draft
RonnyPfannschmidt wants to merge 1 commit into
Draft
config: add plugin hookspecs at registration, not at Config.parse()#15015RonnyPfannschmidt wants to merge 1 commit into
RonnyPfannschmidt wants to merge 1 commit into
Conversation
pytest_addhooks was only ever driven from Config.parse(), so a PytestPluginManager used outside a full pytest run - for instance one obtained from get_plugin_manager(), which is documented for integration with other tools - never called it at all. A plugin registered on such a manager never got to contribute its own hookspecs, and its matching hook implementations were then rejected by check_pending() as unknown hooks. Move the historic call into PytestPluginManager.__init__, so that hookspec contribution belongs to plugin registration rather than to command line parsing. It has to move rather than be duplicated: calling it twice replays the history twice, and add_hookspecs() rejects the second pass. Fixes pytest-dev#2720. Co-Authored-By: Claude Opus 5 (1M context) via Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AI-authored, posted by me. I prompted an agent (Claude Opus 5 via Claude Code) to investigate #2720; the analysis, the patch, the tests and this description are its work. I have read the change and I am putting it up — as a draft, for discussion of whether this is the right place for the call.
Closes #2720.
The bug
pytest_addhooks.call_historic(...)was issued from exactly one place: the top ofConfig.parse(). Plugin registration did not drive it, and historic replay only works once a call has been recorded — so on aPytestPluginManagerthat never belonged to a parsedConfig, no registered plugin ever gotpytest_addhookscalled at all.That is reachable through
get_plugin_manager(), which is non-underscored and whose docstring says it "can be used by integration with other tools, like hooking into pytest to run tests into an IDE":The plugin adds the hookspec and implements it, so it is self-consistent; it is rejected over its own hook. This has been the behaviour since 3.1.0 (8bcf88e), and the original report in #2720 hit the same defect from the setuptools-entry-point /
pytest_pluginsside — see #2720 (comment) for the full archaeology.The change
Move the historic call from
Config.parse()intoPytestPluginManager.__init__, so that contributing hookspecs belongs to plugin registration rather than to command line parsing.It has to move rather than be duplicated: two
call_historiccalls replay the history twice andadd_hookspecs()rejects the second pass.No builtin plugin implements
pytest_addhooks, so nothing in-tree changes timing.pytest_addhooks(pluginmanager)only ever receives the pluginmanager, never aConfig, so firing it earlier cannot starve an implementation of anything it is entitled to.Compatibility
Every path that already worked still works, verified individually:
pytestrunpytest.main(plugins=[P()])— registered beforeparse()register()frompytest_configure— afterparse()Config.fromdictargs_prepareconfig→parse())get_plugin_manager()+register()get_config()+load_setuptools_entrypoints()withoutparse()testing/:4555 passed, 48 skipped, 13 xfailed, 7 xpassed, against4554, 48, 13, 7onmain— exactly the one added test, same pre-existing xpasses.pre-commit run -ais clean.Notes for review
test_addhooks_conftestpluginloses its explicitpm.hook.pytest_addhooks.call_historic(...)line. That line was hand-simulating whatConfig.parse()used to do, and now double-replays the history.PytestPluginManager.__init__makes the invariant unconditional, but it does meanpytest_addhooksfires duringget_config()'s default-plugin import rather than at a point where aConfigexists. If you would rather keep it bound toConfigand instead make the unparsed-manager case fail loudly, say so and this can be reshaped.🤖 Generated with Claude Code