Skip to content

call_historic() memorizes the caller's kwargs mapping, so later mutation changes what late plugins receive聽#808

Open

Description

@RonnyPfannschmidt

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

HookCaller.call_historic() appends kwargs to _call_history and replays it for plugins registered later. When the hookspec has no argument defaults, _apply_defaults() returns the caller's own mapping unchanged, so the history holds a reference to the caller's object:

d = {"arg": "at-call-time"}
pm.hook.myhook.call_historic(kwargs=d)
d["arg"] = "mutated"
pm.register(LatePlugin())  # LatePlugin.myhook receives arg="mutated"

When the spec has defaults, _apply_defaults() builds a new dict, so those calls are already isolated. The behavior therefore depends on whether the spec happens to declare a default.

Fix: store dict(kwargs) after applying defaults. This is a shallow copy, so mutable argument values are still shared, and that's expected. Low priority: nobody has reported it in the wild, and pytest passes fresh dicts.

Found while reviewing #806.

Activity

  1. rajnish-verma-22172 commented on Oct 21, 2026

    @rajnish-verma-22172

    I'd like to work on this. I'll add a regression test showing that mutating the original kwargs after call_historic() must not change what a plugin registered later receives, then copy the mapping at the point history is stored. I'll also check the existing default-argument behavior and run the relevant test suite.

  2. RonnyPfannschmidt commented on Oct 21, 2026

    @RonnyPfannschmidt
    MemberAuthor

    please don't - the general misaligned ai pr backlog for pytest-dev is ridiculous already - currently we are forced to reject a lot of goodwill as most external ai assists dont just miss the target, they miss the target range - and it comes down to what and how prompts are done

    we'd prefer to spend our tokens on actual changes instead of assessing ai assists -

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions