Skip to content

add_hookcall_monitoring undo breaks when called out of LIFO order #815

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.

add_hookcall_monitoring() returns an undo that restores the _inner_hookexec it saw at add time (src/pluggy/_manager.py, undo() sets self._inner_hookexec = oldcall). So undo only works in strict LIFO order:

  • undoing an older monitor also removes every monitor added after it
  • a later undo can bring back a monitor that was already removed

This applies to enable_tracing() too, since it is built on add_hookcall_monitoring().

Reproducer:

import pluggy

hs = pluggy.HookspecMarker("x")
hi = pluggy.HookimplMarker("x")

class S:
    @hs
    def h(self): ...

class P:
    @hi
    def h(self): return 1

pm = pluggy.PluginManager("x")
pm.add_hookspecs(S)
pm.register(P())
ev = []

def mon(label):
    return pm.add_hookcall_monitoring(lambda *a: ev.append(label), lambda *a: None)

undo_a = mon("A")
undo_b = mon("B")

undo_a(); pm.hook.h(); print(ev)  # [] -- expected ['B']
ev.clear()
undo_b(); pm.hook.h(); print(ev)  # ['A'] -- expected []

The same thing happens on current main. pytest's own uses (--debug, pytester's HookRecorder) undo in LIFO order, so as far as we know nobody has hit this in practice.

Possible fix: keep the active (before, after) pairs in a list on the PluginManager. Each undo removes its own entry by identity, which makes repeated undo harmless, and then rebuilds _inner_hookexec from the list. Calls already in flight keep the chain they started with.

Found while reviewing #814, which fixed this with a much larger change and was closed.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    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