Skip to content

Commit 80e854f

Browse files
committed
fix(codegen): emit single-value Literals for discriminator fields
Pydantic 2.12+ requires a discriminated-union member's discriminator field to be typed as `Literal[...]`. datamodel-codegen was emitting it as a single-value `StrEnum` (e.g. `kind: Kind` where `Kind(StrEnum)` had one entry), which causes Pydantic to refuse to build the union with: PydanticUserError: Model 'X' needs field 'kind' to be of type `Literal` This affects any sealed hierarchy on the API side that currently has only one variant (and trips immediately when an oneOf is exposed before the second variant lands, e.g. `AuditMetadata` introduced in mono PR #283). Fix: - scripts/typegen.sh: pass `--enum-field-as-literal one` and `--use-one-literal-as-default` to datamodel-codegen. - Single-value enums now render as `kind: Literal["..."] = "..."`, which both satisfies the discriminator requirement and lets callers omit the obvious value. - Regenerate src/devhelm/_generated.py (net -310/+78 lines: most of the noisy single-value StrEnum classes collapse into inline Literals). - Update one negative test (`TestConfirmationPolicyNegative.test_missing_type`) to reflect the new defaulting behaviour and document the rollback when a second variant is added. No public-API changes for callers that already passed the discriminator explicitly. Branch name matches the mono PR so the spec-evolution harness on devhelmhq/mono#283 picks it up automatically. Made-with: Cursor
1 parent 7c661d9 commit 80e854f

3 files changed

Lines changed: 103 additions & 315 deletions

File tree

scripts/typegen.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,22 @@ uv run datamodel-codegen \
5252
--use-annotated \
5353
--field-constraints \
5454
--snake-case-field \
55+
--enum-field-as-literal one \
56+
--use-one-literal-as-default \
5557
--input-file-type openapi \
5658
--formatters ruff-format
5759

60+
# Why --enum-field-as-literal=one + --use-one-literal-as-default?
61+
#
62+
# Without these, single-value enums (used as discriminators on sealed unions
63+
# like AuditMetadata.kind) generate as `kind: Kind` where `Kind(StrEnum)` has
64+
# one entry. Pydantic 2.12+ rejects this when the parent is a discriminated
65+
# union: "Model 'X' needs field 'kind' to be of type `Literal`".
66+
#
67+
# These flags make the codegen emit `kind: Literal["..."] = "..."` instead,
68+
# satisfying the discriminator requirement and making the field optional at
69+
# construction (callers don't need to repeat the discriminator value).
70+
5871
# Post-process: inject `model_config = ConfigDict(extra='forbid')` into every
5972
# generated class so that requests with unknown fields and responses with
6073
# unknown fields BOTH fail loudly. Implements P1 + P2 from

0 commit comments

Comments
 (0)