@@ -272,6 +272,39 @@ function postProcessExternalUnionAliasesForPython(code: string, aliases: Map<str
272272 return code . replace ( / \n { 3 , } / g, "\n\n" ) ;
273273}
274274
275+ /**
276+ * Literal value of a union discriminator. The API schema discriminates on
277+ * string `const`s (`kind: "text"`) and on boolean ones
278+ * (`SessionListEntry.isRemote`, `QueuedCommandResult.handled`), so the JSON
279+ * type has to survive codegen: coercing `true` to `"true"` emits a dispatcher
280+ * arm that the decoded Python `True` can never match. Mirrors
281+ * `GoDiscriminatorValue` in `go.ts`.
282+ */
283+ type PyDiscriminatorValue = string | boolean ;
284+
285+ /**
286+ * Capture a schema `const` as a discriminator value, keeping booleans as
287+ * booleans and stringifying everything else.
288+ */
289+ function pyDiscriminatorValue ( constValue : unknown ) : PyDiscriminatorValue {
290+ return typeof constValue === "boolean" ? constValue : String ( constValue ) ;
291+ }
292+
293+ /**
294+ * Render a discriminator value as a Python literal. Booleans need Python's
295+ * `True` / `False` spelling, since the JSON `true` would parse as a capture
296+ * pattern in a `match` arm rather than as a literal.
297+ */
298+ function pyDiscriminatorValueExpr ( value : PyDiscriminatorValue ) : string {
299+ if ( typeof value === "boolean" ) return value ? "True" : "False" ;
300+ return JSON . stringify ( value ) ;
301+ }
302+
303+ /** Python type of a discriminator constant, for its `ClassVar` annotation. */
304+ function pyDiscriminatorValueType ( value : PyDiscriminatorValue ) : string {
305+ return typeof value === "boolean" ? "bool" : "str" ;
306+ }
307+
275308/**
276309 * Replace flat-merged dataclasses emitted by quicktype for $ref-based
277310 * discriminated unions with proper Python unions: a `Name = VariantA | ...`
@@ -293,7 +326,7 @@ function postProcessExternalUnionAliasesForPython(code: string, aliases: Map<str
293326interface ResolvedRefBasedUnion {
294327 aliasName : string ;
295328 discriminatorProp : string ;
296- dispatch : Array < { value : string ; typeName : string } > ;
329+ dispatch : Array < { value : PyDiscriminatorValue ; typeName : string } > ;
297330}
298331function postProcessRefBasedDiscriminatedUnionsForPython (
299332 code : string ,
@@ -304,7 +337,7 @@ function postProcessRefBasedDiscriminatedUnionsForPython(
304337 aliasName : string ;
305338 variantNames : string [ ] ;
306339 discriminatorProp : string ;
307- dispatch : Array < { value : string ; typeName : string } > ;
340+ dispatch : Array < { value : PyDiscriminatorValue ; typeName : string } > ;
308341 description : string | undefined ;
309342 }
310343 const unions : UnionInfo [ ] = [ ] ;
@@ -334,7 +367,7 @@ function postProcessRefBasedDiscriminatedUnionsForPython(
334367 discriminator . property
335368 ] ;
336369 return {
337- value : String ( discProp . const ) ,
370+ value : pyDiscriminatorValue ( discProp . const ) ,
338371 typeName : toPascalCase ( variantRefNames [ i ] ) ,
339372 } ;
340373 } ) ;
@@ -387,7 +420,7 @@ function postProcessRefBasedDiscriminatedUnionsForPython(
387420 for ( const union of unions ) {
388421 const actualAliasName = resolveActualName ( union . aliasName ) ;
389422 const actualVariantNames : string [ ] = [ ] ;
390- const actualDispatch : Array < { value : string ; typeName : string } > = [ ] ;
423+ const actualDispatch : Array < { value : PyDiscriminatorValue ; typeName : string } > = [ ] ;
391424 let allResolved = true ;
392425 for ( let i = 0 ; i < union . variantNames . length ; i ++ ) {
393426 const actual = resolveActualName ( union . variantNames [ i ] ) ;
@@ -450,7 +483,7 @@ function postProcessRefBasedDiscriminatedUnionsForPython(
450483 dispatcherLines . push ( ` kind = obj.get(${ JSON . stringify ( union . discriminatorProp ) } )` ) ;
451484 dispatcherLines . push ( ` match kind:` ) ;
452485 for ( const m of actualDispatch ) {
453- dispatcherLines . push ( ` case ${ JSON . stringify ( m . value ) } : return ${ m . typeName } .from_dict(obj)` ) ;
486+ dispatcherLines . push ( ` case ${ pyDiscriminatorValueExpr ( m . value ) } : return ${ m . typeName } .from_dict(obj)` ) ;
454487 }
455488 dispatcherLines . push (
456489 ` case _: raise ValueError(f"Unknown ${ actualAliasName } ${ union . discriminatorProp } : {kind!r}")`
@@ -500,7 +533,7 @@ function postProcessDiscriminatorDefaultsForPython(
500533 unions : ResolvedRefBasedUnion [ ]
501534) : string {
502535 // Build variant lookup: variant class name → { prop, value }.
503- const variantInfo = new Map < string , { prop : string ; value : string } > ( ) ;
536+ const variantInfo = new Map < string , { prop : string ; value : PyDiscriminatorValue } > ( ) ;
504537 for ( const union of unions ) {
505538 for ( const d of union . dispatch ) {
506539 // First-wins; multiple unions referencing the same variant share a
@@ -571,9 +604,9 @@ function postProcessDiscriminatorDefaultsForPython(
571604 continue ;
572605 }
573606 const fieldIndent = ( block [ fieldIdx ] . match ( / ^ ( \s + ) / ) ?? [ "" , "" ] ) [ 1 ] ;
574- const literal = JSON . stringify ( info . value ) ;
607+ const literal = pyDiscriminatorValueExpr ( info . value ) ;
575608 // Replace the field with a class-level constant.
576- block [ fieldIdx ] = `${ fieldIndent } ${ info . prop } : ClassVar[str ] = ${ literal } ` ;
609+ block [ fieldIdx ] = `${ fieldIndent } ${ info . prop } : ClassVar[${ pyDiscriminatorValueType ( info . value ) } ] = ${ literal } ` ;
577610 usedClassVar = true ;
578611
579612 // Drop any field-trailing docstring lines that immediately followed the
@@ -1590,7 +1623,7 @@ function tryEmitPyRefBasedDiscriminatedUnion(
15901623 if ( ! discriminator ) return undefined ;
15911624
15921625 const variantTypeNames : string [ ] = [ ] ;
1593- const dispatch : Array < { value : string ; typeName : string } > = [ ] ;
1626+ const dispatch : Array < { value : PyDiscriminatorValue ; typeName : string } > = [ ] ;
15941627 for ( let i = 0 ; i < variants . length ; i ++ ) {
15951628 const variantTypeName = toPascalCase ( variantRefNames [ i ] ) ;
15961629 const variantSchema = resolveObjectSchema ( variants [ i ] , ctx . definitions ) ;
@@ -1599,7 +1632,7 @@ function tryEmitPyRefBasedDiscriminatedUnion(
15991632 }
16001633 variantTypeNames . push ( variantTypeName ) ;
16011634 const discProp = resolvedVariants [ i ] . properties ?. [ discriminator . property ] as JSONSchema7 ;
1602- dispatch . push ( { value : String ( discProp . const ) , typeName : variantTypeName } ) ;
1635+ dispatch . push ( { value : pyDiscriminatorValue ( discProp . const ) , typeName : variantTypeName } ) ;
16031636 }
16041637
16051638 if ( ! ctx . aliasesByName . has ( aliasName ) ) {
@@ -1627,7 +1660,7 @@ function tryEmitPyRefBasedDiscriminatedUnion(
16271660 lines . push ( ` match kind:` ) ;
16281661 for ( const m of dispatch ) {
16291662 lines . push (
1630- ` case ${ JSON . stringify ( m . value ) } : return ${ m . typeName } .from_dict(obj)`
1663+ ` case ${ pyDiscriminatorValueExpr ( m . value ) } : return ${ m . typeName } .from_dict(obj)`
16311664 ) ;
16321665 }
16331666 lines . push (
0 commit comments