Skip to content

Support Annotated field configuration everywhere - #4594

Merged
patrick91 merged 5 commits into
mainfrom
fix/annotated-support-everywhere
Aug 28, 2026
Merged

Support Annotated field configuration everywhere#4594
patrick91 merged 5 commits into
mainfrom
fix/annotated-support-everywhere

Conversation

@patrick91

@patrick91 patrick91 commented Aug 28, 2026

Copy link
Copy Markdown
Member

Summary

  • process Annotated[..., strawberry.field(...)] before dataclass generation so field defaults and factories configure constructors
  • support eager and postponed annotations across object, input, and interface definitions
  • preserve sibling Strawberry metadata such as named unions, enums, lazy references, auto, and private markers
  • retain all field options, explicit GraphQL type overrides, resolvers, permissions, directives, extensions, and metadata
  • cover StrawberryField-producing APIs including federation fields, Relay node/connection fields, and directive fields
  • add permanent user documentation and release notes

Implementation notes

Annotation evaluation uses the public Format.FORWARDREF APIs first. On Python 3.10–3.13, Strawberry's existing lazy-aware evaluator is used only when the backport leaves the complete expression unresolved. No new annotation parser is introduced.

Directly unresolved postponed expressions such as Annotated[Later, strawberry.field(...)] rely on Python 3.14's PEP 649/749 support. Resolvable postponed annotations and Strawberry lazy references remain supported on all Strawberry Python versions.

Validation

  • uv run pytest tests/types tests/test_printer/test_schema_directives.py tests/federation/test_types.py tests/relay/test_fields.py -q — 530 passed, 5 skipped, 1 xfailed
  • uv run pytest tests/federation/test_types.py tests/relay/test_fields.py -q — 237 passed
  • uv run --isolated --python 3.14 pytest tests/types/test_annotated_fields_future_annotations.py tests/test_printer/test_schema_directives.py -q — 29 passed, 4 skipped
  • uv run --isolated --python 3.15 pytest tests/types/test_annotated_fields_future_annotations.py tests/test_printer/test_schema_directives.py -q — 29 passed, 4 skipped
  • uv run mypy --config-file mypy.ini strawberry/types/object_type.py strawberry/types/type_resolver.py strawberry/types/field.py
  • targeted pre-commit hooks across all changed files

Closes #4241

Summary by Sourcery

Enable full Strawberry field configuration through typing.Annotated across supported type and field definitions.

New Features:

  • Support configuring object, input, and interface fields with strawberry.field() inside typing.Annotated, including defaults and factories.
  • Extend Annotated field configuration to federation, Relay, directive fields, and combinations with other Strawberry metadata.

Bug Fixes:

  • Preserve field options, GraphQL type overrides, resolvers, permissions, directives, extensions, and metadata when processing Annotated fields.
  • Support eager and postponed annotations while retaining lazy references, named unions, enums, auto fields, and private-field validation.

Documentation:

  • Add user documentation covering Annotated field configuration, defaults, postponed annotations, lazy references, and metadata composition.
  • Add patch release notes for the expanded Annotated field support.

Tests:

  • Add coverage for Annotated fields across core type definitions, federation, Relay, schema directives, metadata combinations, validation, and forward references.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Thanks for adding the RELEASE.md file!

Below is the changelog that will be used for the release.


This release fixes fields configured with strawberry.field() inside
typing.Annotated.

You can now use this syntax consistently on object types, input types, and
interfaces, including in projects that use from __future__ import annotations:

from typing import Annotated

import strawberry

Name = Annotated[
    str,
    strawberry.field(name="displayName", default="Anonymous"),
]


@strawberry.type
class User:
    name: Name

All strawberry.field() options are supported. Fields with default or
default_factory can be omitted when creating an instance, and field
configuration can be combined with other Strawberry metadata such as named
unions.

This release was contributed by @patrick91 in #4594

Additional contributors: @ampagent, @patrick

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="strawberry/types/object_type.py" line_range="112-118" />
<code_context>
+            arg for arg in rest if not isinstance(arg, StrawberryField)
+        ]
+        field_type = (
+            Annotated[(first, *remaining_metadata)] if remaining_metadata else first
+        )
+        type_annotation = field.type_annotation or StrawberryAnnotation(
+            field_type,
+            namespace=module_namespace,
+        )
+        field.type_annotation = type_annotation
+
+        setattr(cls, field_name, field)
</code_context>
<issue_to_address>
**issue (broader_impact):** An `Annotated` field with an explicit `graphql_type` override drops all sibling metadata because the existing override annotation wins over the reconstructed annotation containing the remaining metadata. Named unions, enums, lazy references, `auto`, and private markers placed alongside that field configuration are therefore not preserved.

**Triggers:** When `strawberry.field(graphql_type=...)` is combined with another Strawberry metadata object inside the same `Annotated` annotation.

**Suggested fix:** Rebuild the annotation with the non-`StrawberryField` metadata while retaining the explicit GraphQL type override separately, rather than selecting the pre-existing `field.type_annotation` wholesale.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: strawberry/types/object_type.py:118


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread strawberry/types/object_type.py Outdated
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves Annotated[..., strawberry.field(...)] processing ahead of dataclass generation so field defaults and factories participate in generated constructors while preserving other Strawberry metadata.

  • Adds early extraction and copying of StrawberryField metadata.
  • Updates field-copy behavior to retain defaults and avoid duplicating permission extensions.
  • Extends coverage across object, input, interface, directive, federation, and Relay fields.
  • Documents the syntax and adds release notes.

Confidence Score: 4/5

This PR should not merge until postponed lazy Annotated fields continue to work on the supported Python 3.10–3.13 runtimes.

The new eager evaluator catches and discards unresolved lazy annotations before their Strawberry field metadata is extracted, while the previous later extraction path has been removed.

Files Needing Attention: strawberry/types/object_type.py, strawberry/types/type_resolver.py

Important Files Changed

Filename Overview
strawberry/types/object_type.py Adds the central early-processing flow, but its exception path drops valid lazy postponed fields on Python 3.10–3.13.
strawberry/types/type_resolver.py Removes post-dataclass Annotated extraction, making the new early processor the sole recovery point for field metadata.
strawberry/types/field.py Adjusts field copying to preserve dataclass defaults while avoiding duplicate permission extensions.
tests/types/test_annotated_fields_future_annotations.py Adds broad postponed-annotation coverage, including an unskipped lazy-reference case that exposes the unsupported-version failure path.
docs/types/object-types.md Documents Annotated field configuration, constructor defaults, and composable metadata.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Class annotation] --> B[Evaluate postponed annotation]
  B --> C[Extract StrawberryField metadata]
  C --> D[Install dataclass field]
  D --> E[Generate dataclass constructor]
  E --> F[Build Strawberry definition]
  B -->|Unresolved name on Python 3.10-3.13| G[NameError caught]
  G --> H[Field processing skipped]
Loading

Reviews (1): Last reviewed commit: "Use plain release messaging" | Re-trigger Greptile

Comment thread strawberry/types/object_type.py

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

@codspeed-hq

codspeed-hq Bot commented Aug 28, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 34 untouched benchmarks


Comparing fix/annotated-support-everywhere (b7273bc) with main (a0ce7fc)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (0230373) during the generation of this report, so a0ce7fc was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

ampagent and others added 3 commits August 28, 2026 18:23
Amp-Thread-ID: https://ampcode.com/threads/T-01a047e0-b77e-773e-b1b6-d45bd45b6494
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a047e0-b77e-773e-b1b6-d45bd45b6494
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
@patrick91
patrick91 force-pushed the fix/annotated-support-everywhere branch from 569b33f to d89bf6d Compare August 28, 2026 18:26
ampagent and others added 2 commits August 28, 2026 19:44
Amp-Thread-ID: https://ampcode.com/threads/T-01a047e0-b77e-773e-b1b6-d45bd45b6494
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a047e0-b77e-773e-b1b6-d45bd45b6494
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
@sourcery-ai

sourcery-ai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Warning

Sourcery was unable to dismiss its earlier approval of this pull request, because the latest commits introduced blocking findings. The approval above no longer reflects the current commits.

How to resolve this

Add the Sourcery app under Restrict who can dismiss pull request reviews in this repository's branch protection rules. Sourcery only ever dismisses its own reviews. It does not dismiss reviews from anyone else.

@patrick91
patrick91 enabled auto-merge (squash) August 28, 2026 23:27
@patrick91
patrick91 merged commit 9f9e840 into main Aug 28, 2026
83 checks passed
@patrick91
patrick91 deleted the fix/annotated-support-everywhere branch August 28, 2026 23:49
@botberry

Copy link
Copy Markdown
Member

This PR was published as 0.324.2. Thank you for contributing!

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.

Annotated support everywhere

3 participants