Problem
Today @typespec/http-client-python exchanges the code model between the TypeScript emitter and the Python generator as YAML. This is very slow and dominates end-to-end generation time, especially under Pyodide where PyYAML runs as pure Python.
Measured on the Azure network spec (large real-world spec):
| Stage |
YAML (js-yaml / PyYAML) |
| JS serialize |
~1000 ms |
| Python load |
~11000 ms |
| Python dump |
~5500 ms |
| File size |
14.4 MB |
Total serialization overhead: ~29 s.
Switching to a faster JS YAML library (yaml/eemeli) does not help — it is ~1.8x slower to serialize and ~70x slower to parse than js-yaml, and it rejects the model by default due to the alias-count guard. The bottleneck is the Python side, which only a non-YAML format fixes.
Proposal
Replace YAML with a reference-preserving JSON format, using the same $id/$ref convention as the C# emitter (System.Text.Json ReferenceHandler.Preserve):
- First time an object is seen:
{ "$id": "N", ...properties }
- First time an array is seen:
{ "$id": "N", "$values": [...] }
- Any later reference to an already-seen node:
{ "$ref": "N" }
The code model is a cyclic/shared object graph (on the network spec: 35,175 objects and 32,075 arrays, of which 3,739 objects and 2,855 arrays are shared, plus 1,022 cycle back-edges). Because $id must be registered before recursing into a node's children, cycles are encoded as a $ref back to the enclosing node, so cycles are preserved (the C# writer drops them, which the Python model cannot tolerate). The Python decoder then reconstructs a graph with identical shared identity and cycles, so the generator's model layer needs zero changes.
Expected result: ~29 s -> ~0.2 s and file size 14.4 MB -> 8.8 MB.
Work items
Validation criteria
- Full package build (emitter + pygen wheel)
- End-to-end: compile the network spec ->
$id/$ref code model -> generator produces correct output
- Python codec reconstructs an identical graph (structural metrics match) and round-trips stably
- Generator unit tests, lint, format, and mypy pass
Reference
POC: #11178
Problem
Today
@typespec/http-client-pythonexchanges the code model between the TypeScript emitter and the Python generator as YAML. This is very slow and dominates end-to-end generation time, especially under Pyodide where PyYAML runs as pure Python.Measured on the Azure
networkspec (large real-world spec):Total serialization overhead: ~29 s.
Switching to a faster JS YAML library (
yaml/eemeli) does not help — it is ~1.8x slower to serialize and ~70x slower to parse thanjs-yaml, and it rejects the model by default due to the alias-count guard. The bottleneck is the Python side, which only a non-YAML format fixes.Proposal
Replace YAML with a reference-preserving JSON format, using the same
$id/$refconvention as the C# emitter (System.Text.JsonReferenceHandler.Preserve):{ "$id": "N", ...properties }{ "$id": "N", "$values": [...] }{ "$ref": "N" }The code model is a cyclic/shared object graph (on the network spec: 35,175 objects and 32,075 arrays, of which 3,739 objects and 2,855 arrays are shared, plus 1,022 cycle back-edges). Because
$idmust be registered before recursing into a node's children, cycles are encoded as a$refback to the enclosing node, so cycles are preserved (the C# writer drops them, which the Python model cannot tolerate). The Python decoder then reconstructs a graph with identical shared identity and cycles, so the generator's model layer needs zero changes.Expected result: ~29 s -> ~0.2 s and file size 14.4 MB -> 8.8 MB.
Work items
emitter/src/code-model-serializer.ts— cycle-safe$id/$refserializergenerator/pygen/_codemodel_json.py— matching Pythonloads/dumpsdecoder/encoderjs-yaml/@types/js-yaml(emitter) andPyYAML/types-PyYAML(generator) dependenciesemit-yaml-onlyemitter option toemit-codemodel-onlyARCHITECTURE.mdValidation criteria
$id/$refcode model -> generator produces correct outputReference
POC: #11178