Skip to content

Preserve type hints when decoding payload-backed generic Python models - #129

Merged
VegetarianOrc merged 2 commits into
mainfrom
amazzeo/type-aware-python
Aug 18, 2026
Merged

Preserve type hints when decoding payload-backed generic Python models#129
VegetarianOrc merged 2 commits into
mainfrom
amazzeo/type-aware-python

Conversation

@VegetarianOrc

Copy link
Copy Markdown
Contributor

Summary

  • Propagate concrete Python type arguments through nested proto-backed generic models.
  • Pass concrete type hints to Temporal when decoding single-value Payload fields.
  • Preserve untyped decoding for unparameterized generic models using typing.Any.
  • Keep Payloads decoding as an untyped sequence.
  • Add generated and runtime coverage for parameterized and unparameterized decoding.
  • Document the new behavior in the changelog, author guide, and advanced sample guide.

Motivation

Proto-backed generic models previously lost their concrete type arguments during nested decoding.

For example, decoding:

PayloadBackedEnvelope[EchoOutput, MyContext]

could produce dictionaries for payload-backed fields instead of EchoOutput and MyContext instances.

The generated transfer converters now extract runtime generic arguments, propagate them into nested model converters, and provide them to
Temporal’s payload converter.

Unparameterized models continue to decode successfully. They use typing.Any as the fallback instead of object, which Temporal cannot
deserialize as a type hint.

Tests

  • Added generator assertions for nested type-hint propagation and typing.Any fallback.
  • Added a runtime round-trip test for concrete nested types.
  • Added a regression test for decoding an unparameterized generic model.
  • Regenerated the checked-in Python samples.

Validation

  • ./scripts/validate.sh
  • Python runtime tests
  • BasedPyright
  • Rust generator tests
  • git diff --check

@VegetarianOrc
VegetarianOrc marked this pull request as ready for review August 17, 2026 22:52
@VegetarianOrc
VegetarianOrc requested a review from a team as a code owner August 17, 2026 22:52
type_hint: type[typing.Any],
) -> typing.Any:
proto = value
(_context_type,) = typing.get_args(type_hint) or (typing.Any,)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe don't put _ just in case it isn't used.

return typing.cast(
object,
converter.from_payload(_clone_payload(proto), type_hint),
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Check on these casts I think.

(_output_type,) = typing.get_args(type_hint) or (typing.Any,)
if not proto.HasField("details"):
raise ValueError("missing required field PayloadBackedOutput.details")
details = typing.cast(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That's an awful lot of casting.

)


def payloads_from_proto(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does payloads also need to use the hint?

@VegetarianOrc
VegetarianOrc merged commit 17becb0 into main Aug 18, 2026
4 checks passed
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.

2 participants