Skip to content

Add mocker.spy_context for scoped spies - #651

Open
Gabrieljr42 wants to merge 4 commits into
pytest-dev:mainfrom
Gabrieljr42:feat/118-scoped-spy-context
Open

Gabrieljr42 wants to merge 4 commits into
pytest-dev:mainfrom
Gabrieljr42:feat/118-scoped-spy-context

Conversation

@Gabrieljr42

@Gabrieljr42 Gabrieljr42 commented Oct 24, 2026 •

Copy link
Copy Markdown

Fixes #118.

Add mocker.spy_context(obj, name, duplicate_iterators=False) for a spy that lasts for a with block:

with mocker.spy_context(service, "send") as spy:
    service.send("hello")
    spy.assert_called_once_with("hello")

The original method is restored on exit, including when the block raises. Nested spies retain method binding and callable signatures. Invalid arguments are rejected before call tracking, including for static/class methods and nested instance spies. The context yields the usual spy object and uses the fixture's patch cache. Existing mocker.spy calls keep their interface; the separate method follows the compatibility concern raised in the issue.

Spies can be stopped with stop or stopall inside the block. Overlapping patches must be stopped in reverse order: stopping an outer patch while an inner patch depends on it raises RuntimeError before either registration changes. This prevents inner cleanup from restoring a spy that has already been removed. stopall handles reverse cleanup, including multi-attribute patches.

Regression cases cover normal and exceptional cleanup, nested class and instance spies, async methods, iterator tracking, descriptors, explicit stops and overlapping patches. The usage docs explain the new API and stop order.

Validation on Python 3.14.7 with pytest 9.1.1:

  • tox -e py314,norewrite: 190 passed with assertion rewriting; 173 passed and 17 expected skips without it. The installed plugin matches the edited source in both environments.
  • Independent probes passed all 26 lifecycle cases and 8 descriptor cases across unittest.mock and standalone mock, including three nested class scopes, descriptor restoration, multi-attribute patch overlap, signature guards and async await tracking.
  • Required rst, Ruff, formatting and mypy hooks passed. The earlier Sphinx build passed before the stop-order documentation was added; the updated text passed rst lint.

The changelog entry is under UNRELEASED.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make spy a context manager

1 participant