Repository navigation
Raise ValueError when unregister's plugin and name disagree - #759
Merged
Merged
Conversation
unregister() looks hook callers up by plugin but removes the registry entry by name. When both are passed and refer to different plugins, one plugin loses its hook implementations while another loses its registry entry. Validate both directions before mutating anything. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6
unregister() and load_setuptools_entrypoints() tested registry entries by truthiness, so a plugin defining __len__ returning 0 lost its hookimpls but kept its registry entry. Compare against None instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6
RonnyPfannschmidt
pushed a commit
that referenced
this pull request
Oct 9, 2026
The old guard skipped the delete when the name maps to None (blocked), and the assert only existed to narrow the type for mypy (added with the 2019 type annotations). With the earlier branches, the only way to reach the pop with a blocked name is unregister(plugin, name) where the two disagree, which #759 turns into a ValueError. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ZTVJoihfHRVor2TQqGTRn
RonnyPfannschmidt
added this pull request to stack #769
October 9, 2026 12:13
bluetech
approved these changes
Oct 9, 2026
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.
Requested by Ronny · project thread
🤖 Written by Claude Opus 5.5 via Claude Code for the pluggy maintainers; I prompted it, it did the work, I read it.
Before:
PluginManager.unregister(plugin, name)looks up hook callers bypluginbut removes the registry entry byname, and never checks that the two refer to the same plugin, even though the docstring says they "must agree".pm.unregister(a, "b")stripsa's hookimpls and dropsb's registry entry, whileb's impl keeps being called. The same happens whenpluginis not registered at all butnamebelongs to another plugin. Separately, a falsy plugin object (for example one whose__len__returns 0) loses its hookimpls on unregister but keeps its registry entry, so registering it again fails.After: when both arguments are given and they disagree in either direction,
unregisterraisesValueErrorbefore changing anything. Falsy plugins are fully unregistered. Agreeing arguments and the blocked-name case (test_unregister_blocked) behave as before.How: a check in the
plugin-and-namebranch ofunregister. Registry entries are now compared withis not Noneinstead of tested for truthiness, both inunregisterand inload_setuptools_entrypoints. Added tests for both bugs and a changelog fragment.This replaces the closed #747, which skipped the check whenever
pluginwas not registered and so missed the second case. This is an independent implementation.Follow-ups found while auditing registration, kept out of this PR: #760, #761, #762, #763.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VNXnGZy9Gu3kQqgECeCMM6