Skip to content

fix(python): try integer before number in union deserialization#3059

Merged
schani merged 1 commit into
masterfrom
agent/fix-issue-1318-b
Jul 21, 2026
Merged

fix(python): try integer before number in union deserialization#3059
schani merged 1 commit into
masterfrom
agent/fix-issue-1318-b

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

Bug

For a schema whose type is a union of integer and number (e.g. "type": ["integer", "number"]), the Python target generated:

def from_union(fs, x):
    for f in fs:
        try:
            return f(x)
        except:
            pass
    assert False

def geo_from_dict(s: Any) -> float | int:
    return from_union([from_float, from_int], s)

from_float is tried first and its assertion (isinstance(x, (float, int)) and not isinstance(x, bool)) accepts plain Python ints too, coercing them
to float. Because of that, from_int was unreachable for this union: every
integer value silently became a float, so to_dict(from_dict(x)) did not
round-trip (5 became 5.0).

Root cause

JSONPythonRenderer built the list of union-member (de)serializer lambdas in
the union's natural member order, without regard for the fact that the
double/float deserializer in this renderer also accepts integers. When
double happened to precede integer in that order, the more specific
integer case became dead code.

Fix

Added a small unionMembers helper in JSONPythonRenderer.ts that, when a
union contains both an integer and a double member, moves integer
ahead of double in the emitted dispatch list (for both deserialization and
serialization), while leaving other members' relative order untouched.

Test coverage

  • New JSON Schema fixture test/inputs/schema/integer-before-number.schema
    ("type": ["integer", "number"], matching the issue's repro) with a plain
    integer sample, a real float sample, and a .fail.union.json negative
    sample. Wired to run only for the python fixture — I added an explicit
    skipSchema entry (with comment) to every other language config, since
    this fixture's purpose is specifically to pin down Python's union member
    ordering and I didn't want to change behavior/coverage for ~30 other
    unrelated language targets.
  • Note on why a fixture alone can't fully verify this bug: the schema
    fixture round-trip check (compareJsonFileToJsonJSON.parse +
    numeric equality) can't actually distinguish 5 from 5.0, since
    JavaScript has a single number type and 5 === 5.0. So a
    test/unit/python-union-order.test.ts unit test was added as a
    necessary complement, asserting the generated source literally contains
    from_union([from_int, from_float] (this is exactly the "fixtures can't
    express this" case called out in the repo's testing conventions).

Verification

  • npm run build passes.
  • npm run test:unit passes (177 tests, including the new one).
  • QUICKTEST=true FIXTURE=schema-python script/test passes (72/72 fixtures,
    including the new integer-before-number.schema).
  • Manually reproduced the original issue's geo.schema example and confirmed
    the generated code now emits from_union([from_int, from_float], ...),
    and that in a real Python run geo_from_dict(5) returns int 5 (not
    5.0) and round-trips correctly, while 5.5 still round-trips as a float.
  • CI will additionally validate the other language fixtures untouched by
    this change.

Fixes #1318

🤖 Generated with Claude Code

For a union of `integer` and `number` (e.g. JSON Schema `type: ["integer",
"number"]`), the Python target emitted `from_union([from_float, from_int],
...)`. `from_float` accepts plain ints and coerces them to `float`, so
`from_int` was unreachable: every integer value silently turned into a
float and the round-trip was not bijective (`5` became `5.0`).

Reorder Python union member (de)serializer dispatch so the `integer` branch
is always tried before `number`/`double`.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
@schani
schani merged commit fa893ca into master Jul 21, 2026
28 checks passed
@schani
schani deleted the agent/fix-issue-1318-b branch July 21, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type with array of integer and number should test for integer before number

1 participant