Skip to content

Show return type annotations in --fixtures and --fixtures-per-test - #15162

Open
IslamElsayed wants to merge 4 commits into
pytest-dev:mainfrom
IslamElsayed:fixtures-return-annotation
Open

IslamElsayed wants to merge 4 commits into
pytest-dev:mainfrom
IslamElsayed:fixtures-return-annotation

Conversation

@IslamElsayed

Copy link
Copy Markdown

Closes #13676.

Fixtures with a return annotation now show it in --fixtures and --fixtures-per-test:

answer -> int -- test_x.py:4
    Answer to the Ultimate Question of Life, the Universe, and Everything.

This picks up where #13680 left off (thanks @kianelbo), following what was agreed there:

  • On Python 3.14+ the annotation is read as a string with annotationlib.get_annotations(func, format=Format.STRING), as @The-Compiler suggested. Nothing is evaluated, which covers @RonnyPfannschmidt's point about unresolvable annotations: a name imported only under TYPE_CHECKING is shown, not raised.
  • Before 3.14 it uses inspect.formatannotation, with no regex post-processing, per @nicoddemus. It passes base_module=func.__module__, so a class from the fixture's own module shows as Answer rather than test_x.Answer. String annotations (from __future__ import annotations) are shown as written.
  • -> None is shown as None, and fixtures without a return annotation are unchanged.

Built-in fixtures now show their annotations too (tmp_path -> Generator[Path]), so the existing TestShowFixtures expectations are updated. The new tests use pytester modules without the __future__ import, so the object path is covered on older Pythons, plus one test for an unresolvable TYPE_CHECKING name with the import and one, run on 3.14+ only, without it. I ran the affected tests on 3.10, 3.13 and 3.14.

I left the display format as in the issue (name -> type -- path) and only touched --fixtures and --fixtures-per-test.

AI assistance (Claude) was used while preparing this; I've reviewed the change, understand it, and will handle the review myself.

A fixture with a return annotation is now listed as `answer -> int -- test_x.py:4`. On Python 3.14+ the annotation is read as a string with annotationlib, so names that only exist when checking types are shown instead of evaluated. On earlier versions it is formatted with inspect.formatannotation, relative to the fixture's module.

Closes pytest-dev#13676
@pyba-chronographer pyba-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Oct 11, 2026

@RonnyPfannschmidt RonnyPfannschmidt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Written by Claude Opus 5.5 via Claude Code for the pytest maintainers; I prompted it, it did the work, I read it.

Thanks, this follows the plan from #13680 well. Three changes before merge:

  1. Yield fixtures: show the yielded type, not the wrapper. tmp_path -> Generator[Path] should read tmp_path -> Path. Strip Generator/Iterator/Iterable and their async variants to the first type argument, but only when the fixture function is actually a generator, so a plain fixture that returns an Iterator keeps its annotation. Please add tests for both cases.
  2. Module prefixes: nested names such as Optional[test_x.Answer] before 3.14 are fine as they are. No need to strip them.
  3. Odd fixture functions: if reading or formatting the annotation fails for any reason, don't crash or silently drop it. Print a short note under the fixture saying the return type can't be shown and where the fixture is defined. Please add a test for that path.

Generated by Claude Code

…an't be shown

A yield fixture annotated Generator[T, ...], Iterator[T] or Iterable[T] (or an async one) is now listed with T, so tmp_path reads tmp_path -> Path. A fixture that is not a generator keeps its annotation as written.

If reading or formatting a fixture's return annotation fails, --fixtures and --fixtures-per-test print a short note under the fixture instead of failing.
@IslamElsayed

Copy link
Copy Markdown
Author

Thanks @RonnyPfannschmidt , all three are done:

  1. Yield fixtures show the yielded type now (tmp_path -> Path). Generator/Iterator/Iterable and their async forms are unwrapped to the first argument only when the fixture function is a generator, so a plain fixture returning Iterator[int] keeps it. Covered with and without from __future__ import annotations, plus yield fixtures whose annotation isn't a wrapper (they keep it as written).
  2. Module prefixes: left as they are.
  3. Odd fixture functions: if reading or formatting the annotation fails, the fixture is still listed, with a note saying the return type can't be shown and where the fixture is defined. One catch: on 3.14 pytest's own collection reads the same string annotations (inspect.signature in getfuncargnames), so a fixture whose annotations can't be read already fails collection before --fixtures runs. The end-to-end test therefore runs below 3.14 only, and a unit test covers the helper on 3.14 as well.

Changes are in 21a6d40 and a48e007. The Read the Docs failure is a pluggy cross-reference warning (pluggy._compat.DistFacade) that also showed up on #15209, so it doesn't look related to this PR.

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

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show return type annotations in --fixtures and --fixtures-per-test

2 participants