feat(python): add modelSerializationMethods option

2Y
4h ago
+412-32

Why

Python SDK models have no short way to turn a model into a dict or JSON string with the field names the API uses. Users have to call model_dump(by_alias=True, exclude_unset=True, ...) themselves.

What changed

Add python.modelSerializationMethods (default false). When enabled, every generated model gains:

  • to_dict(*, mode="python", use_api_names=True, exclude_unset=True, exclude_defaults=False, exclude_none=False, warnings=True)
  • to_json(*, indent=2, use_api_names=True, exclude_unset=True, exclude_defaults=False, exclude_none=False, warnings=True)

Supporting changes:

  • Model serializers emit Python field names when use_api_names=False. With the option on, model_dump(by_alias=False) also emits Python field names; the default model_dump() keeps emitting API names.
  • to_dict and to_json leave filtered fields out even with exclude_none=True. Other dumps keep the exclude_none exception from #149, which request bodies rely on.
  • Const fields are marked as set in model_post_init, so exclude_unset keeps them. Error-data models (<Name>Data) get the same model_post_init.
  • A model field named to_dict or to_json gets a trailing underscore. SDK group accessors, global-parameter and server-variable constructor arguments, and flattened method parameters keep their names, because only models reserve these methods. Security constructor arguments follow the security model's field names, so they get the underscore too.
  • Method parameter documentation (SDK docs, Markdown parameter tables and docstrings) names parameters with the same sanitizer as the method signature. Previously a flattened toJson parameter was documented as to_json_ while the method accepts to_json. This also applies with the option disabled, for parameter names that are reserved differently for parameters and model fields: the review SDK's flattened type parameter was documented as type while the method accepts type_, and is now documented as type_.
  • Usage snippets name global parameters the same way the SDK constructor does. This also applies with the option disabled.
  • With preserveUnknownFields also enabled, both helpers keep unknown fields, subject to the limitations listed in #146.

Test coverage

  • The unknown-fields fragment gains a group named toJson with a toJson query parameter, an object with toDict/toJson properties, a const object with a defaulted field, and a required const in the error body.
  • tertiary (option enabled alone): helper defaults and options, field names, excluded fields, consts (including on error data), reserved names, and the default model_dump() of nested models and model lists.
  • quaternary (enabled with preserveUnknownFields, flattened parameters): helper round trips that keep unknown fields, a flattened to_json parameter whose signature, docstring and Markdown table agree, plus checks that unrecognized enums serialize without warnings.
  • secondary (option disabled): the helper names are not reserved.

Stack

  1. #145: unrecognized enum serialization
  2. #149: honour include/exclude filters in model dumps
  3. #146: preserveUnknownFields
  4. This PR: modelSerializationMethods

Testing

  • TARGET=primary make test-pythonv2 (option disabled): 724 passed.
  • TARGET=secondary make test-pythonv2 (option disabled): 333 passed, 1 skipped.
  • TARGET=tertiary make test-pythonv2: 255 passed, 1 skipped. The nested model_dump() test fails without the serializer fix, and the error-data const test fails without model_post_init on error data.
  • TARGET=quaternary make test-pythonv2: 295 passed, 1 skipped.
  • make check-template-pythonv2 and prettier pass.
  • TARGET=review make build-pythonv2: integrity validation passes; the review SDK gains modelSerializationMethods: false in gen.yaml, and the flattened type parameter of two methods is now documented as type_ in their docstrings and Markdown tables, matching the signature.
  • git diff --check passes.

Public-safety check

  • This change contains no credentials, customer documents, private repository URLs, private filesystem paths, or unredacted private logs.
  • Title, body, comments, and commit messages name no customers or customer-derived identifiers, private paths or trackers, or workflow provenance, and are understandable without private context (.claude/skills/public-repo-communication/SKILL.md).
  • Generated fixtures and review SDK changes are public-safe.
  • I reviewed git diff --check.