Skip to content

fix(python): reserve names that from_dict uses - #3555

Open
EloB wants to merge 2 commits into
glideapps:masterfrom
EloB:fix/python-from-dict-shadowing
Open

EloB wants to merge 2 commits into
glideapps:masterfrom
EloB:fix/python-from-dict-shadowing

Conversation

@EloB

@EloB EloB commented Nov 15, 2026

Copy link
Copy Markdown

Description

JSONPythonRenderer.forbiddenForObjectProperties now reserves, for
properties, every name that from_dict uses besides its own locals:

  • its parameter obj, and isinstance;
  • the converter functions it can call (from_none, from_bool,
    from_int, from_float, from_str, from_list, from_union,
    from_date, from_time, from_datetime, from_stringified_bool;
    from_dict was already reserved);
  • what the constraint checks emitted into from_dict call: len
    (minItems, maxItems, minLength, maxLength), re (pattern)
    and cast.

The converter names come from a Record<ConverterFunction, boolean> that
says which converters from_dict calls. They are named by the same
function as conv (now converterName), so a new converter has to be
added to the record and cannot be missed. The converters that only
to_dict calls (to_float, to_enum, to_class, is_type) shadow
nothing and are not reserved. The constraint checks now use constants
for len, re and cast, and the reserved list uses the same
constants.

A test commit adds inputs that cover these names:

  • test/keywords.txt gets from_str (with a string value, so the
    property calls from_str), isinstance and obj. keywords.json,
    keyword-enum.schema and keyword-unions.schema were regenerated with
    test/make-keyword-tests.sh.
  • pattern.schema, min-max-items.schema and minmaxlength.schema each
    get an optional string property re, len or cast, and their .1.json
    samples get a value for it.

Related Issue

Fixes #3550

Motivation and Context

from_dict assigns each property to a local variable named after the
property, and that local shadows the name for the whole function. So a
property named from_date (as in test/inputs/json/misc/a0496.json)
gives UnboundLocalError at run time, and mypy reports
"date" not callable. Properties named obj, isinstance, len, re
or cast break from_dict the same way.

Previous Behaviour / Output

        from_date = from_date(obj["from_date"])
        to_date = from_date(obj["to_date"])
UnboundLocalError: cannot access local variable 'from_date' where it is not associated with a value

New Behaviour / Output

        top_level_from_date = from_date(obj["from_date"])
        to_date = from_date(obj["to_date"])

The JSON name stays from_date. A reserved property gets renamed like
any other reserved name, so the Python attribute name changes for
existing users with properties named obj, isinstance, from_*,
len, re or cast. Of the repository's inputs at master, only
a0496.json's Python output changes.

The new keywords and properties are test inputs for every language, so
the other languages' outputs for keywords.json, the two keyword schemas
and the three constraint schemas change too.

How Has This Been Tested?

Environment: macOS 26.6 (arm64), Node 24.16.0, Python 3.12.16 with
mypy 2.4.1. The other languages ran with OpenJDK 21.0.11 and Maven 3.10.0,
Ruby 4.0.6, Apple clang 21 (C, C++, Objective-C), Swift, Rust (cargo)
and Dart.

On this branch:

  • npm ci, npm run build, npm run lint (passes), npm run test:unit
    (54 files, 241 tests pass).
  • QUICKTEST=true FIXTURE=python,schema-python npm run test:fixtures:
    all 156 runs pass.
  • FIXTURE=python npm run test:fixtures -- test/inputs/json/misc/a0496.json
    and test/inputs/json/priority/keywords.json: pass.
  • The changed inputs in other languages:
    QUICKTEST=true FIXTURE=typescript,javascript,ruby,cjson,java on
    keywords.json, and schema-typescript,schema-javascript,schema-ruby,schema-cjson,schema-java
    on pattern.schema, min-max-items.schema, minmaxlength.schema,
    keyword-enum.schema and keyword-unions.schema: all pass.
  • The same inputs with swift, rust, dart, typescript-zod,
    typescript-effect-schema, flow, javascript-prop-types,
    objective-c and cplusplus (and their schema- fixtures, plus
    schema-java-datetime-legacy): all pass.

With only the test commit (the fix not applied):

  • python fails on keywords.json (Name "isinstance" is used before definition)
    and on misc/a0496.json ("date" not callable).
  • schema-python fails on pattern.schema (Name "re" is used before definition),
    min-max-items.schema (len), minmaxlength.schema (cast) and
    keyword-unions.schema (isinstance). keyword-enum.schema passes.

Not run here, because the toolchains are not installed: C#, Go, Kotlin,
Scala, Elm, PHP, Elixir, Haskell, Crystal and Pike.

Screenshots (if appropriate):

n/a


Written by Claude (an AI assistant made by Anthropic) and posted on behalf of the account owner (EloB). The reproduction, fix and tests were checked by running them as described above.

🤖 Generated with Claude Code

EloB and others added 2 commits November 15, 2026 07:50
`from_str` (with a string value), `isinstance` and `obj` join the
keywords; `re`, `len` and `cast` are added as plain properties next to
the `pattern`, `minItems` and `minLength` checks that call them. In
Python's `from_dict` each becomes a local variable that shadows what it
is named after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`from_dict` assigns each property to a local variable of the property's
name, so a property named like something `from_dict` uses shadowed it
for the whole function: a converter it calls (`from_date` in
`misc/a0496.json` gave `UnboundLocalError`, and mypy `"date" not
callable`), its parameter `obj`, `isinstance`, or what the constraint
checks call (`len`, `re`, `cast`). Reserve those names for properties.
The converters come from a `Record<ConverterFunction, boolean>` of the
ones `from_dict` calls, named as `conv` names them, so a new converter
cannot be missed; names only `to_dict` uses (`to_class`, `to_enum`,
`to_float`, `is_type`) are not reserved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

[BUG]: Python from_dict breaks when a property is named like a converter it calls (from_date, from_str), obj, isinstance, len, re or cast

1 participant