Skip to content

fix(idempotency): resolve postponed return annotations for serializer classes - #8738

Open
sumedh-aerram wants to merge 2 commits into
aws-powertools:developfrom
sumedh-aerram:fix/idempotency-pydantic-serializer-future-annotations
Open

sumedh-aerram wants to merge 2 commits into
aws-powertools:developfrom
sumedh-aerram:fix/idempotency-pydantic-serializer-future-annotations

Conversation

@sumedh-aerram

Copy link
Copy Markdown

Issue number:

closes #8722

Summary

Changes

Resolve postponed return annotations when a serializer class infers its model. Resolve only the return type in the original function's namespace, preserve evaluated types and Annotated metadata for custom serializers, and leave unrelated type-only input annotations out of inference. Python 3.14 uses public deferred-annotation retrieval. Pydantic rejects parameterized containers with IdempotencyModelTypeError, including Python 3.10 with the minimum supported Pydantic.

The change adds public decorator regressions and updates both inference examples and the utility guide. Existing test expectations, serializer signatures and stored response formats are unchanged. Whole-function get_type_hints was rejected because it strips metadata by default, honors no_type_check by discarding usable returns, and fails on unrelated unresolved parameters. Container serialization is a separate feature; explicit serializer instances remain the option for locally defined models.

User experience

Previously, importing a module using from __future__ import annotations and output_serializer=PydanticSerializer could fail before invocation. Pydantic and dataclass return models now infer correctly and replay without running the function again. Unsupported containers and unresolvable local output-model names receive a model-type error; supplying an explicit model avoids inference.

Verification

AI assistants (Claude and Codex) helped implement this change. Local commands were executed and output inspected during development.

  • Original regression module plus new compatibility module against scratch source with only the two implementation files restored to upstream/develop: 10 failed, 2 passed, 1 skipped. With the fix, all Pydantic idempotency tests pass.
  • Python 3.10.22 / Pydantic 2.4.0: 35 passed, 1 skipped. Python 3.11.17 and 3.13.17 / Pydantic 2.14.2: 35 passed, 1 skipped each. Python 3.14.9 / Pydantic 2.14.2: 36 passed, including native deferred annotations. The one skip on earlier runtimes is that Python 3.14-only case.
  • Python 3.12 full repository suite: 4014 passed, 74 skipped, 10 deselected; all 10 performance tests passed. Ruff, mypy (949 source files), ty, docs lint, pre-commit checks on every changed file, security baseline and complexity checks passed. The first full gate caught an overlong docs sentence; it was wrapped and docs lint rerun successfully.
  • Full gate command: make -k pr ty. Alternate-runtime checks used isolated environments, including minimum Pydantic on 3.10.
  • No AWS resources were called: the issue explicitly requires no AWS resources, and inference/replay reproduces locally. Full repository gates on runtimes other than 3.12 and end-to-end AWS tests have not been run.

The final committed, clean-tree make -k pr ty repeated successfully (exit 0). make test-dependencies also passed all 15 Nox sessions on Python 3.12.14 with newly recreated environments. Independent re-review closed all five prior findings and found no new actionable regression. The reviewer separately reran the focused suite on Python 3.10/minimum Pydantic, Python 3.12, and Python 3.14; all passed and the worktree remained clean.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.

… classes

With `from __future__ import annotations`, every annotation is a string.
idempotent_function passed function.__annotations__["return"] straight to
PydanticSerializer.instantiate, which ran issubclass() on a string and
raised TypeError at decoration time, so the module failed to import.
DataclassSerializer had the same root cause and rejected every postponed
annotation with IdempotencyModelTypeError.

Resolve the return annotation with typing.get_type_hints, falling back to
the annotation as written when it can't be resolved (e.g. a model defined
inside another function), which keeps today's behaviour for those cases.
Guard PydanticSerializer.instantiate with inspect.isclass so annotations
that aren't classes, like list[Model], raise IdempotencyModelTypeError
instead of TypeError.
Resolve only postponed return annotations, retaining evaluated types and custom Annotated metadata. Unrelated type-only parameters and no_type_check must not prevent serializer construction. Retrieve native deferred annotations safely on Python 3.14 and reject parameterized model aliases before subclass checks on Python 3.10.
@sumedh-aerram
sumedh-aerram requested a review from a team as a code owner November 1, 2026 21:53
@powertools-for-aws-oss-automation powertools-for-aws-oss-automation Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Nov 1, 2026
@boring-cyborg boring-cyborg Bot added documentation Improvements or additions to documentation tests labels Nov 1, 2026
@boring-cyborg

boring-cyborg Bot commented Nov 1, 2026

Copy link
Copy Markdown

Thanks a lot for your first contribution! Please check out our contributing guidelines and don't hesitate to ask whatever you need.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: Invite link

@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@leandrodamascena leandrodamascena added this to the 3.41.0 milestone Nov 1, 2026
@leandrodamascena

Copy link
Copy Markdown
Contributor

Thanks for the PR and for filling in the verification section, that helps. Heads up that the review may take a few days: this goes into 3.41.0 and I review every PR against a real service before approving, one at a time. No need to rebase or ping in the meantime, I'll come back here.

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

documentation Improvements or additions to documentation size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: output_serializer=PydanticSerializer crashes at import under from __future__ import annotations

2 participants