[python] Add DPG TypedDict generation opt-out - #11372
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Co-authored-by: l0lawrence <100643745+l0lawrence@users.noreply.github.com>
|
@copilot if we dont generate the typeddict overload we should generate the JSON overload |
Co-authored-by: l0lawrence <100643745+l0lawrence@users.noreply.github.com>
commit: |
|
All changed packages have been documented.
Show changes
|
Python emitter diffBaseline No changes to generated output. Rendered diff: inline on the run summary, or the emitter-diff-html artifact. Informational check (eng/emitter-diff); does not block the PR. |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 753f3788-98a9-4e39-8655-9c6a3ce90536
…alse When generate-typeddict: false, the opt-out now performs a true revert to pre-TypedDict behavior by restoring the standalone raw-JSON dict @overload (and JSON in the impl Union) for both the explicit/model body path and the spread path, instead of only dropping the TypedDict overload. - add_overloads_for_body_param: only skip the single-body JSON overload when a TypedDict overload was actually inserted; otherwise keep it. - add_body_param_type: insert the raw-JSON (any-object) overload via new _insert_json_overload helper when TypedDict generation is disabled. - Update test_typeddict_overloads.py opt-out tests to assert the restored [model, JSON, binary] / flattened+JSON+binary overload sets. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 753f3788-98a9-4e39-8655-9c6a3ce90536
There was a problem hiding this comment.
Pull request overview
This PR adds a new @typespec/http-client-python emitter option (generate-typeddict) to let users opt out of generating TypedDict request-body overloads while keeping models-mode: dpg, preserving default behavior (true) for back-compat.
Changes:
- Added
generate-typeddictoption surfaced via the emitter’s TypeScript options schema and the Python generator’sOptionsDictdefaults. - Updated the Python preprocess plugin to conditionally insert TypedDict vs raw-JSON overload types based on
generate-typeddict. - Added unit tests and updated docs/reference material to cover and describe the new option.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/content/docs/docs/emitters/clients/http-client-python/reference/emitter.md | Documents the new generate-typeddict option in the website emitter reference. |
| packages/http-client-python/tests/unit/test_typeddict_overloads.py | Adds regression tests for disabling TypedDict overload generation in DPG mode. |
| packages/http-client-python/tests/unit/test_options_dict.py | Adds unit coverage for the option defaulting to true and being settable to false. |
| packages/http-client-python/README.md | Documents the new generate-typeddict option in the package README. |
| packages/http-client-python/generator/pygen/preprocess/init.py | Implements the opt-out behavior and adjusts overload generation logic. |
| packages/http-client-python/generator/pygen/init.py | Adds the option default (generate-typeddict: True) to OptionsDict. |
| packages/http-client-python/emitter/src/lib.ts | Adds the option to the TypeScript emitter options interface and JSON schema. |
| .chronus/changes/python-disable-typeddict-generation-2026-7-24-12-59-0.md | Adds a Chronus changelog entry for the new feature. |
…bodies The skip condition sniffed for a combined-type member with base == typeddict, but in models-mode: typeddict a spread body inserts the original base: dpg model as its overload (it renders as a TypedDict via models-mode). That made the sniff false, so the single-body JSON overload was wrongly kept, regressing the prior behavior where the TypedDict overload replaced it. Track this explicitly with a jsonOverloadReplacedByTypeddict flag set by add_body_param_type wherever a TypedDict-style overload is inserted (both the generate-typeddict dpg path via _insert_typeddict_overload and the typeddict-only spread branch). add_overloads_for_body_param now checks the flag instead of sniffing base == typeddict, so: - models-mode: dpg (generate-typeddict on) and models-mode: typeddict both skip the single-body JSON overload, and - only the generate-typeddict: false opt-out keeps it (pre-TypedDict behavior). Addresses the PR reviewer comment. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 753f3788-98a9-4e39-8655-9c6a3ce90536
…e_body_json param Refactor the single-body JSON overload skip decision from a persisted `jsonOverloadReplacedByTypeddict` YAML side-channel flag to an explicit function parameter. `add_body_param_type` now returns whether a TypedDict-style overload was inserted in place of the single-body raw-JSON overload, and the single caller passes that to `add_overloads_for_body_param` as `skip_single_body_json`. This removes the implicit cross-function coupling and gives one source-of-truth decision point. Pure refactor: generated output is byte-identical across all configs (dpg+generate-typeddict on/off, models-mode: typeddict) - verified by regenerating parameters/body-optionality both ways and diffing all files. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 753f3788-98a9-4e39-8655-9c6a3ce90536
|
You can try these changes here
|
Co-authored-by: l0lawrence <100643745+l0lawrence@users.noreply.github.com>
iscai-msft
left a comment
There was a problem hiding this comment.
i think one thing we can do is remove typeddict from models-mode. models-mode would just toggle dpg and none (and msrest for backcompat, but let's ignore that). We would deprecate models-mode=typeddict and the different scenarios would look like
- default (by default
models-mode=dpgandgenerate-typeddicts=true - td only (just specify
models-mode=nonein tspconfig - no td (specify
generate-typeddicts=falsein tspconfig
… models-mode none Rework the DPG TypedDict opt-out so models-mode only toggles dpg/none (msrest kept for back-compat) and TypedDict output is controlled by the generate-typeddict option. models-mode: none now remaps internally to the typeddict-only path, reusing all existing machinery; models-mode: typeddict is deprecated (still accepted with a warning). Update option docs, changelog, SKILL, and regen-common dogfooding, and clarify internal preprocess comments. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 511e3e1d-5321-4281-8d91-d5be7a28d81d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 511e3e1d-5321-4281-8d91-d5be7a28d81d
- Remove synthetic internal `models-mode: typeddict`; represent the deprecated value as `models-mode: none` + `generate-typeddict: true`, and drive TypedDict-only behavior via a `generate_typeddict_only` helper instead of a fake models-mode. - Replace generic `**kwargs` in the `_plugin` test helper with an explicit `generate_typeddict: bool` parameter. - Drop msrest from the models-mode ValueError help text (back-compat only). - Reword the `generate-typeddict` description: it adds TypedDict typing for JSON dict input, not an extra request-body overload. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c8b26a94-682b-4350-a424-97093fd1c183
…ecated Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c8b26a94-682b-4350-a424-97093fd1c183
…ization comment Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c8b26a94-682b-4350-a424-97093fd1c183
… in is_typeddict_only Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c8b26a94-682b-4350-a424-97093fd1c183
…dels-mode check Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c8b26a94-682b-4350-a424-97093fd1c183
msyyc
left a comment
There was a problem hiding this comment.
Looks good to me. Approved but better to wait for @iscai-msft's approval too in case of more review comments.
DPG mode in the Python emitter always generated TypedDict request-body overloads, with no
tspconfigescape hatch, and TypedDict-only output required the separatemodels-mode: typeddictvalue. This reworks the design so thatmodels-modeonly selects the model style and a dedicatedgenerate-typeddictoption controls TypedDict output independently.Option design
models-modeselects the model style:dpgornone(msrestkept for back-compat)generate-typeddict(defaulttrue) independently controls whether TypedDict overloads/models are generatedmodels-mode: typeddictis deprecated — it is still accepted (with a deprecation warning) and behaves as TypedDict-only, but callers should migrate tomodels-mode: noneScenarios
models-mode: dpgmodels-mode: nonemodels-mode: dpg+generate-typeddict: falseGenerator behavior
models-mode: none+generate-typeddict: trueis remapped internally to the existing TypedDict-only path, so all existing machinery is reused unchangeddpgmode withgenerate-typeddict: false, the TypedDict body overload is replaced by the pre-TypedDict raw-JSON overload (so the dict-body shape is still callable)models-mode: noneis left untouched (still produces no models)Tests
OptionsDictcoverage for thenone→ TypedDict-only remap, thegenerate-typeddict: falseopt-out, and themodels-mode: typeddictdeprecation warningmodels-mode: nonemaps to TypedDict-only overloadsDocs / config
dpgandnone) in the package README and generated emitter referencegenerate-from-typespecskill guidancetypetest-model-usage-typeddictonlytest package tomodels-mode: none(byte-identical generated output)Example: