Skip to content

Bump datamodel-code-generator from 0.31.2 to 0.90.0 in /sdk/python - #1030

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pip/sdk/python/datamodel-code-generator-0.90.0
Closed

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pip/sdk/python/datamodel-code-generator-0.90.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Nov 10, 2026

Copy link
Copy Markdown

Bumps datamodel-code-generator from 0.31.2 to 0.90.0.

Release notes

Sourced from datamodel-code-generator's releases.

0.90.0

Breaking Changes

Default Behavior Changes

  • Default remote lockfile of generate() now lives beside the loaded pyproject.toml - When generate() is given a config returned by load_pyproject_config() and no explicit lockfile, its default datamodel-codegen.lock is now read, verified and updated beside the pyproject.toml that was loaded, the same place the CLI uses. Previously it was always in the caller's working directory. Projects that run generate() from a subdirectory and rely on a lock there must move the lock to the project root or pass lockfile explicitly. Remote lock support is experimental (#6201)
  • Generated models now enforce item counts on nested arrays - Because the counts are now kept, models generated with default options reject data they used to accept. An anyOf or oneOf array member, an inner list, or a map value that breaks its maxItems or minItems now fails validation with a too_long or too_short error. This fixes a mismatch with the schema, but consumers that relied on the old, looser validation will see new validation errors (#6292)

Error Handling Changes

  • A lone surrogate where Pydantic or msgspec would read it (an alias, a Literal or enum value, a msgspec field name) now stops generation with an error that names the schema location; before, the generated module failed at import (#6365).
  • Output, model metadata and lockfile paths that cannot be created now raise Error before generation - If the deepest existing parent of output, emit_model_metadata or the remote lockfile is a regular file, generate() now raises datamodel_code_generator.Error with a message such as Output path cannot be created: Not a directory: path. Previously the write failed later with a raw NotADirectoryError. The same applies to a root that does not exist, such as a missing Windows drive, which previously kept the walk testing that root. The CLI now prints a clean error instead of a traceback, and staged and job runs now name the blocking file instead of a random private staging directory. Code that catches OSError for these cases must also catch Error (#6201)
  • Missing input with an existing output now reports the usual file-not-found Error - generate() and the CLI now report a missing input as the normal File not found Error even when the output path already exists. Previously the check that an output does not overwrite its input raised a FileNotFoundError (#6201)
  • Encoding keys that name no property of the form schema are now refused for every target, with a new message - Every encoding key of an application/x-www-form-urlencoded or multipart/form-data request body is now checked against the schema's properties in the shared wire plan, for both the client and the FastAPI server targets and whatever style or contentType the encoding gives. A key that names no property fails generation with the MC_PARAMETER_ENCODING diagnostic, whose message reads like The other encoding of the multipart/form-data media of POST /upload names no property of its schema. This replaces the old messages An encoding names no member of its form and names no member. Documents with such stray keys that a target used to generate can now fail; for example, a FastAPI server whose multipart encoding only gives a contentType was not checked before. An encoding for a property the request model leaves out, such as a readOnly one, is no longer an error and is ignored. (#6210)
  • Generation now fails when a package class is named like one of its submodules - If a package's __init__.py would define a model class with the same name as a module or subpackage below it (for example the class Warn in pkg/__init__.py next to pkg/Warn.py or pkg/Warn/__init__.py, or a root-level Warn class next to Warn.py), the new reject_class_named_like_submodule check now raises an error that names the class and the module, and no output is written. Earlier versions wrote these modules even though importing the submodule replaces the class name in the package. Packages whose __init__.py already loads that module before defining its classes (by importing from it, or through --all-exports-scope re-exports) are still written as before. In every other case, rename the class or the module in the input schema. (#6209) The opt-in --rename-submodules-hidden-by-classes (in the 20261104 presets) renames such a submodule instead (pkg/Warn_.py).
  • Generation now fails for msgspec Literal annotations that mix bytes and str values - With --output-model-type msgspec.Struct, a field or type alias may have an annotation from x-python-type or --type-overrides that holds a Literal, or a union of Literal members, with both bytes and str values, such as Literal[b'ab', 'a'] or Literal[b'ab'] | Literal['a']. Before, generation succeeded with a warning and wrote it as Literal['a'] | bytes, which msgspec cannot build a decoder for. Now generation stops with an error that names the Literal and the field or type alias. The error suggests rewriting the values as one kind, such as Literal['ab', 'a'] or bytes, or using another output model type. Users must change these annotations before they can regenerate. Other output model types are unaffected, and such a Literal inside the metadata of Annotated is still written as before (#6237)
  • New UserWarning for enum and const values that no Literal can hold - When an enum or const value cannot be written as a Literal (float, list or object values, and bool values for msgspec output, which writes no bool literals unless both True and False are listed), the generator writes the value's type instead and now emits a UserWarning such as a Literal of the output model cannot hold the value 1.5, so it is written as its type, which admits other values; Model.const_float is generated without this constraint. The generated code does not change. The warning does fire under default options, though: for example a const: 1.5 property in Pydantic or dataclass output, a const: true property in msgspec output, or float enums in TypedDict output, which writes enums as literals. Runs that treat warnings as errors or assert on captured warnings may now fail. When a field already has a dropped-constraints warning, the new text is added to that same warning message, so that message changes too (#6344)
  • Version-specific client and FastAPI errors now follow --schema-version - Client and FastAPI generation now raise "lists scopes for scheme ..., whose type takes none in OpenAPI 3.0" whenever the parser reads the document as 3.0, so a 3.1 or 3.2 document whose security requirements list role scopes for apiKey or http schemes now fails when generated with --schema-version 3.0, while a 3.0 document generated with --schema-version 3.1 is now accepted; likewise "Cookie style requires OpenAPI 3.2" is now decided by the version the parser reads rather than by the openapi text (#6346)

Code Generation Changes

  • A module import no longer shadows an imported model of the same name - When another import of a module binds a name that a model import also takes (for example BaseModel from pydantic and a submodule BaseModel), the model is now imported from its module, so the output imports and decodes (#6105).
  • A reference into a property of another file now follows the embedded $id - Such a reference used to fall back to Any; it now resolves to the referenced schema (for example str) (#5126).
  • A pattern holding a lone surrogate now selects Python's regex engine - The model gets regex_engine="python-re" where every other pattern under the same config allows it; otherwise it keeps the default engine and warns (#6320).
  • A lone surrogate is now written as its escape in enum values and TypedDict keys, and replaced by U+FFFD in docstrings and comments - Before, writing the module failed with UnicodeEncodeError (#5983).
  • Discriminator tags are written without their pattern under --field-constraints and --use-annotated - The pattern beside a Literal tag made the module fail to import; it is now left out, with a warning where it rejects the tag (#6238).
  • Class-name keys of --model-name-map now name models as they appear in the output without the map - A key that is not a schema path is now resolved against the class names a run without the map's class-name keys generates, and every other model keeps the name it has in that output instead of moving into the name the renamed model freed. Generated class names change for some users of class-name keys: for example with {"Pet": "Cat"} and three same-named Pet schemas the output was previously Cat, Pet, PetModel and is now Cat, PetModel, PetModel1; similar shifts occur for typed nullable map value models (Tags is now Tags1) and numbered names (Tag1 is now Tag2, Pet is now Pet1). Code that imports the affected classes by name must be updated; schema-path keys are unaffected (#6202)
  • Model-level --type-overrides now apply to root models under --collapse-root-models - A root model named by a model-level --type-overrides key is no longer inlined by --collapse-root-models; its holders now take the override type, as they already did without collapse, instead of the inlined root type. For Avro, fixed types with a logical type that a model-level override replaces now take the override type under --collapse-root-models instead of the logical type, so fields previously generated as timedelta with logical defaults now become the override type (for example bytes) with physical byte defaults (#6202)
  • Discriminator tags are now read through a root model or type alias of a one-value enum - With --enum-field-as-literal (for example all), a one-value enum that a union variant's tag property references is generated as a RootModel[Literal[...]], or as a TypeAliasType of the Literal for Pydantic dataclasses. The variant's tag now comes from that value instead of falling back to the schema name. When you regenerate, the variant models' tag field types change, for example type: Literal['FunctionToolCall'] becomes type: Literal['function']. Code that built or matched the old schema-name tags must be updated. The old output rejected valid payloads. Variants reached through nested choices, and inline variants, keep the previous behaviour. (#6205)
  • Discriminator tags of models merged into _internal.py by an import cycle now use the mapping values - In modular output, models that an import cycle moves into _internal.py now have their discriminator mapping values resolved from the original schema paths, not the moved paths. Tag literals such as Literal['_internal.Cat_1'] become the mapped values, such as Literal['c']. The old output rejected every payload. (#6205)
  • Parameter schemas with keywords beside $ref now merge in the referenced schema in generated clients and FastAPI servers - In an OpenAPI 3.1 document, a parameter schema with keywords such as description or default next to its $ref used to stop at that reference. The target bindings read only the keywords beside the $ref and dropped the referenced schema's default, title and other documentation keywords. They now follow the reference chain and merge both sets, with the keywords beside the $ref winning, which is what the model parser already does. As a result, generated FastAPI routers can gain annotations such as Query(alias='least', title='Size'). A parameter whose $ref only carries a description now gets the referenced schema's default, so the generated client sends that default and the generated server fills it in when a request leaves the parameter out. Whether these keywords count is now decided by the version the parser uses, for example --schema-version 3.0, instead of the entry document's openapi text. OpenAPI 3.0 still ignores them. (#6210)
  • Generated FastAPI servers now read multipart members of any type as text when their encoding gives a media type or style - Before, a multipart/form-data request member whose schema accepts any value, such as {}, was decoded as JSON by the generated server whenever the part parsed as JSON. So 1 arrived as the integer 1 and null as None, even when the member's encoding named text/plain or a style, which tells the client to write it as text. Such members are now read as strings, so handlers receive '1' and 'null'. Members with no encoding media type or style are still decoded as JSON. (#6210)
  • An allOf of value-only anyOf or oneOf members now generates a union instead of an empty model - An allOf whose anyOf or oneOf members admit only values and no objects, for example an anyOf of string and integer combined with minLength: 2, used to be generated as a model class without fields that rejected every value. It is now generated as the union of the intersections of one branch from each anyOf or oneOf with the other parts, such as RootModel[constr(min_length=2) | int] for Pydantic v2, or a TypeAlias like str | int for dataclasses, TypedDict and msgspec. This happens by default, without --intersect-allof-keywords or --intersect-combined-schema-keywords. For these sites, the generated kind (class vs root model or alias), field annotations, constraints, defaults and base classes change. Numbered classes these sites used to produce, such as Beside3 and Beside4, are no longer written. --reuse-model no longer merges other models into the empty class, and --collapse-root-models now inlines the union into fields. Code that imports, instantiates or subclasses the old empty classes or their numbered siblings must be updated. Sites that have their own anyOf or oneOf, that admit an object, that have no satisfiable combination, or that exceed 128 branch combinations keep the previous output (#6258)
  • Unhashable uniqueItems arrays stay lists under --use-unique-items-as-set - With --use-unique-items-as-set (and any preset that turns it on), a uniqueItems array whose items can never be set members is now generated as a list instead of a set. This covers plain objects or dicts, arrays, sequences, mappings, and tuples that hold such a value, including when the tuple is reached through a type alias. For example, set[dict[str, Any]] becomes list[dict[str, Any]], set[tuple[int, list[int]] | dict[str, Any]] becomes a list, and a root model RootModel[set[list[int]]] becomes RootModel[list[list[int]]]. The old set types rejected every non-empty array in Pydantic and failed msgspec decoding. Also, when a field's list default cannot be turned into a set, the whole field now stays a list, so Optional[Set[Dict[str, Any]]] = [{'a': 1}] becomes Optional[List[Dict[str, Any]]] = [{'a': 1}]. Before, the list members of the field's union were turned into sets even though the list default was kept, which could leave fields such as Optional[Set[Any]] with a list default. Those fields now validate to lists instead of sets, and their imports change from Set to List. Arrays of hashable items, unions with at least one hashable member, nullable items and untyped items still become sets (#6290)
  • Nested uniqueItems arrays are now generated as sets under --use-unique-items-as-set - With --use-unique-items-as-set, any array whose own schema declares uniqueItems: true is now written as a set wherever it appears. Before, this only happened for a property or root model. It now also covers an anyOf or oneOf member, the items of an array, the values of a map or of patternProperties, and a lone allOf member. For example, a map of unique integer arrays changes from dict[str, list[int]] to dict[str, set[int]], and an array of unique arrays changes from list[list[int]] to list[set[int]]. Code that indexes or appends to these nested values, or that relies on their order or duplicates, must be updated. A nested array stays a list in three cases: its items cannot be hashed; a model that --reuse-model or deduplication merged into it does not declare uniqueItems at the same position; or it sits in a union beside another array or map member that keeps its lists. Output without the option is unchanged (#6291)
  • Unique-items arrays of models that can never be hashed are now lists under --use-unique-items-as-set - With --use-unique-items-as-set, a uniqueItems array whose items are models that can never be hashed now generates list[Model] instead of set[Model]. This covers typing.TypedDict models, non-frozen dataclasses.dataclass and pydantic_v2.dataclass models without unsafe_hash, non-frozen msgspec.Struct models (including the struct chain and the generic base class config), and RootModel aliases written as Name = RootModel[...] under --use-root-model-type-alias. The old set annotation rejected every non-empty array, and msgspec could not decode it at all. Code or type checks that expected the field to be a set will now see a list. Frozen classes, and classes with a custom base class, class code or custom templates, still get a set (#6294)
  • Models reached through a type alias inside a set now get an identity hash - With --use-unique-items-as-set, a Pydantic model named in the value of a type alias that a set holds now gets __hash__ = object.__hash__ in its class body. For example, Circle gets it when a field is set[Shape] with Shape = TypeAliasType("Shape", Circle | str). Models named directly by a set already got this line. Instances of these models used to be unhashable and now hash by identity (#6294)
  • AllOf value sites now write the keywords of their parts on the root field - When an allOf of value parts keeps the form it is written in (the default, without --intersect-allof-keywords), the generated root model, type alias or field now gets the constraints its parts declare (minLength, maxLength, pattern, minItems, maxItems, minimum, maximum, multipleOf). Before, it took its constraints from the allOf site itself, which declares none. As a result, array lengths were silently dropped in default Pydantic output, and every keyword was dropped under --field-constraints, --use-annotated and in msgspec output. For example, a lone allOf array member with minItems: 2 now generates root: list[int] = Field(..., min_length=2), the msgspec alias Nickname1: TypeAlias = str becomes Annotated[str, Meta(max_length=8)], and level_prop: int becomes Annotated[int, Meta(ge=0, le=10)]. The allOf of a propertyNames schema now also constrains map keys, so dict[str, int] becomes dict[Annotated[str, Meta(min_length=2, pattern='^a')], int]. Generated models are stricter and reject data they used to accept. In msgspec output with --collapse-root-models, new type aliases such as MultipleItem and LevelItem can also appear (#6293)
  • Root models with unhashable root types are no longer inlined as set items under --collapse-root-models - When --collapse-root-models and --use-unique-items-as-set are both on (including the presets that turn them on), a root model whose root type has no hashable value, such as a list, a dict or a closed prefixItems array, is no longer inlined when it is the item of a set or frozenset. Instead, the root model stays named, as it does without collapse. Fields that were generated as set[list[str]], set[dict[str, int]] or frozenset[list[int]] are now typed as set[Tags], set[Object] or frozenset[Group], and the root model class is written to the output with __hash__ = object.__hash__. The old inlined types rejected every non-empty array. Still, regenerated code now has extra root model classes, and set items are now model instances instead of plain lists or dicts. Root models written as aliases, such as msgspec output, are not affected and keep a list. (#6295)
  • Client and FastAPI targets follow the parser's OpenAPI version - The generated client and FastAPI server now take the OpenAPI version from the parser (--schema-version when given, otherwise the version the root document declares as the parser reads it) instead of reading the root document's openapi text with their own legacy rules (the first three characters, or the digits around the first dot). A run whose --schema-version differs from the declared version, or a document whose openapi text is missing or not recognized by the parser, can now generate different code: path parameters with allowReserved: true are written with allow_reserved=True only when the document is read as 3.2 (otherwise the client percent-encodes reserved characters in them), multipart encoding styles are ignored only when read as 3.0, the server's handling of repeated path templates depends on whether the document is read as 3.2, and the served source document decides $id resources and the inlining of pathItems and mediaTypes components by the same version (#6346)
  • Generated clients and FastAPI servers now read keywords beside $ref in OpenAPI 3.0 documents - Generated API clients and FastAPI servers used to ignore the default and documentation keywords written beside a $ref in a parameter schema when the document was OpenAPI 3.0, including under --schema-version 3.0. They now apply them in every version, which matches the models. For example, a query parameter whose schema is a $ref with a sibling default: 7 used to have no default. It now gets that default on both sides: the generated client sends page=7 when the caller leaves it out, and the server uses 7 when the request leaves it out. Regenerated clients and servers may therefore send or accept different parameter defaults. Pass --ignore-ref-siblings-by-schema-version to keep ignoring these sibling keywords where the schema version says so (#6347)
  • Protobuf UInt64Value and UInt32Value wrapper fields now include an upper bound - Fields typed as google.protobuf.UInt64Value or google.protobuf.UInt32Value are now built from the same scalar schema as plain uint64 and uint32 fields. Before, they only got a lower bound. Generated code changes from conint(ge=0) to conint(ge=0, le=18446744073709551615) for UInt64Value and conint(ge=0, le=4294967295) for UInt32Value. The msgspec output also gains the matching le bound, for example Meta(ge=0,le=4294967295). Models that used to accept integers above the wire range now reject them. Class names, field names and types stay the same. (#6349)
  • Inherited fields are no longer redeclared in child models generated from --input-model - When a child input model has a field that matches an inherited parent field by wire name, with the same schema and the same required status, _partition_inherited_fields now always treats it as inherited. Before, it only did this when the name was found in the parent's field set. So generated child classes such as AliasSameChild, AliasPairParent and InheritedWins no longer redeclare these fields (for example old: int = Field(1, title='Old') or shared: list[int] = Field(..., title='Shared')) and now get them only from the parent class. The child's generated JSON schema also no longer repeats these properties or lists them twice in required (previously required could contain shared twice). Code that relied on the child declaring the field itself, or on the child-level attribute name when it differed from the parent's, may need updating (#6350)
  • TypedDict subclasses that narrow an inherited key no longer inherit their base - With --output-model-type typing.TypedDict, a generated TypedDict whose key redeclares a key of a generated base with a different type, requiredness or ReadOnly (for example an allOf member that drops the null of an inherited property, narrows its type to a model or literal, or makes it required) is now written as a subclass of TypedDict (or of the configured base class from --base-class) that declares every key of its bases itself, with the bases' keys first, instead of subclassing the generated base. Type checkers now accept the generated file, which they previously rejected with incompatible-override errors. Class names, key names, key types and requiredness stay the same, but the class hierarchy and key order in the generated source change. In modular output, the module of such a class now imports the models its copied keys reference and no longer imports the base class. Code that relies on the generated subclass relationship, or that imports the base through the child module, needs updating. Subclasses that redeclare a key exactly as the base does, and classes whose bases include a stand-in, a type override or another configured class, keep their bases (#6356)
  • Tree-scope shared module now gives same-named canonical models distinct names - With --reuse-model --reuse-scope tree, when duplicates from different modules produce canonical models with the same class name in the shared module, the earlier ones now get a numeric suffix (e.g. Item_1, Rank_1) and only the one written last keeps the original name. Before this, the shared module defined the class twice, so the later definition silently shadowed the earlier one and the earlier one's users validated against the wrong schema. Users of the earlier canonical now import, alias or subclass the suffixed name (e.g. from .shared import Item_1), and the re-exporting shared.py lists both names in __all__. In _internal modules created for import cycles, the Item and Item_1 names can swap, so _internal.Item may now point to a different model than before. Code that imports these shared or _internal names directly, or relied on the old, incorrect validation, may need updating (#6357)
  • Integral float minContains and maxContains now produce array length constraints - Values such as 2.0 and 3.0 for minContains and maxContains used to be dropped, so the generated models accepted arrays with fewer or more items than the schema allows. They are now read as the integers they spell, the same way minItems is, so generated fields get min_length and max_length constraints (for example Field(None, max_length=3, min_length=2) for Pydantic or Meta(max_length=3, min_length=2) for msgspec). Also, contains: false with an integral float minContains such as 1.0 now produces a model that rejects every array. Data that used to validate may now be rejected (#6363)

New Features

Presets standard-py3XX-20261104 and practical-py3XX-20261104

  • New dated presets for Python 3.10 to 3.15. They build on the 20261027 presets and also turn on the opt-in options below that fix schema readings and names (for example --intersect-combined-schema-keywords, --distribute-allof-over-unions, --read-length-keywords-by-type, --read-schema-version-per-document, --merge-shared-variant-tags, --use-one-model-per-ref-site, --keep-reference-null-on-merged-models, --use-unique-items-as-set-by-value, --type-overrides-replace-whole-type); the practical presets also use --naming-strategy definitions-first. Default output (no preset) does not change. See the presets documentation for the full list per preset.

... (truncated)

Changelog

Sourced from datamodel-code-generator's changelog.

0.90.0 - 2026-11-06

Breaking Changes

Default Behavior Changes

  • Default remote lockfile of generate() now lives beside the loaded pyproject.toml - When generate() is given a config returned by load_pyproject_config() and no explicit lockfile, its default datamodel-codegen.lock is now read, verified and updated beside the pyproject.toml that was loaded, the same place the CLI uses. Previously it was always in the caller's working directory. Projects that run generate() from a subdirectory and rely on a lock there must move the lock to the project root or pass lockfile explicitly. Remote lock support is experimental (#6201)
  • Generated models now enforce item counts on nested arrays - Because the counts are now kept, models generated with default options reject data they used to accept. An anyOf or oneOf array member, an inner list, or a map value that breaks its maxItems or minItems now fails validation with a too_long or too_short error. This fixes a mismatch with the schema, but consumers that relied on the old, looser validation will see new validation errors (#6292)

Error Handling Changes

  • A lone surrogate where Pydantic or msgspec would read it (an alias, a Literal or enum value, a msgspec field name) now stops generation with an error that names the schema location; before, the generated module failed at import (#6365).
  • Output, model metadata and lockfile paths that cannot be created now raise Error before generation - If the deepest existing parent of output, emit_model_metadata or the remote lockfile is a regular file, generate() now raises datamodel_code_generator.Error with a message such as Output path cannot be created: Not a directory: path. Previously the write failed later with a raw NotADirectoryError. The same applies to a root that does not exist, such as a missing Windows drive, which previously kept the walk testing that root. The CLI now prints a clean error instead of a traceback, and staged and job runs now name the blocking file instead of a random private staging directory. Code that catches OSError for these cases must also catch Error (#6201)
  • Missing input with an existing output now reports the usual file-not-found Error - generate() and the CLI now report a missing input as the normal File not found Error even when the output path already exists. Previously the check that an output does not overwrite its input raised a FileNotFoundError (#6201)
  • Encoding keys that name no property of the form schema are now refused for every target, with a new message - Every encoding key of an application/x-www-form-urlencoded or multipart/form-data request body is now checked against the schema's properties in the shared wire plan, for both the client and the FastAPI server targets and whatever style or contentType the encoding gives. A key that names no property fails generation with the MC_PARAMETER_ENCODING diagnostic, whose message reads like The other encoding of the multipart/form-data media of POST /upload names no property of its schema. This replaces the old messages An encoding names no member of its form and names no member. Documents with such stray keys that a target used to generate can now fail; for example, a FastAPI server whose multipart encoding only gives a contentType was not checked before. An encoding for a property the request model leaves out, such as a readOnly one, is no longer an error and is ignored. (#6210)
  • Generation now fails when a package class is named like one of its submodules - If a package's __init__.py would define a model class with the same name as a module or subpackage below it (for example the class Warn in pkg/__init__.py next to pkg/Warn.py or pkg/Warn/__init__.py, or a root-level Warn class next to Warn.py), the new reject_class_named_like_submodule check now raises an error that names the class and the module, and no output is written. Earlier versions wrote these modules even though importing the submodule replaces the class name in the package. Packages whose __init__.py already loads that module before defining its classes (by importing from it, or through --all-exports-scope re-exports) are still written as before. In every other case, rename the class or the module in the input schema. (#6209) The opt-in --rename-submodules-hidden-by-classes (in the 20261104 presets) renames such a submodule instead (pkg/Warn_.py).
  • Generation now fails for msgspec Literal annotations that mix bytes and str values - With --output-model-type msgspec.Struct, a field or type alias may have an annotation from x-python-type or --type-overrides that holds a Literal, or a union of Literal members, with both bytes and str values, such as Literal[b'ab', 'a'] or Literal[b'ab'] | Literal['a']. Before, generation succeeded with a warning and wrote it as Literal['a'] | bytes, which msgspec cannot build a decoder for. Now generation stops with an error that names the Literal and the field or type alias. The error suggests rewriting the values as one kind, such as Literal['ab', 'a'] or bytes, or using another output model type. Users must change these annotations before they can regenerate. Other output model types are unaffected, and such a Literal inside the metadata of Annotated is still written as before (#6237)
  • New UserWarning for enum and const values that no Literal can hold - When an enum or const value cannot be written as a Literal (float, list or object values, and bool values for msgspec output, which writes no bool literals unless both True and False are listed), the generator writes the value's type instead and now emits a UserWarning such as a Literal of the output model cannot hold the value 1.5, so it is written as its type, which admits other values; Model.const_float is generated without this constraint. The generated code does not change. The warning does fire under default options, though: for example a const: 1.5 property in Pydantic or dataclass output, a const: true property in msgspec output, or float enums in TypedDict output, which writes enums as literals. Runs that treat warnings as errors or assert on captured warnings may now fail. When a field already has a dropped-constraints warning, the new text is added to that same warning message, so that message changes too (#6344)
  • Version-specific client and FastAPI errors now follow --schema-version - Client and FastAPI generation now raise "lists scopes for scheme ..., whose type takes none in OpenAPI 3.0" whenever the parser reads the document as 3.0, so a 3.1 or 3.2 document whose security requirements list role scopes for apiKey or http schemes now fails when generated with --schema-version 3.0, while a 3.0 document generated with --schema-version 3.1 is now accepted; likewise "Cookie style requires OpenAPI 3.2" is now decided by the version the parser reads rather than by the openapi text (#6346)

Code Generation Changes

  • A module import no longer shadows an imported model of the same name - When another import of a module binds a name that a model import also takes (for example BaseModel from pydantic and a submodule BaseModel), the model is now imported from its module, so the output imports and decodes (#6105).
  • A reference into a property of another file now follows the embedded $id - Such a reference used to fall back to Any; it now resolves to the referenced schema (for example str) (#5126).
  • A pattern holding a lone surrogate now selects Python's regex engine - The model gets regex_engine="python-re" where every other pattern under the same config allows it; otherwise it keeps the default engine and warns (#6320).
  • A lone surrogate is now written as its escape in enum values and TypedDict keys, and replaced by U+FFFD in docstrings and comments - Before, writing the module failed with UnicodeEncodeError (#5983).
  • Discriminator tags are written without their pattern under --field-constraints and --use-annotated - The pattern beside a Literal tag made the module fail to import; it is now left out, with a warning where it rejects the tag (#6238).
  • Class-name keys of --model-name-map now name models as they appear in the output without the map - A key that is not a schema path is now resolved against the class names a run without the map's class-name keys generates, and every other model keeps the name it has in that output instead of moving into the name the renamed model freed. Generated class names change for some users of class-name keys: for example with {"Pet": "Cat"} and three same-named Pet schemas the output was previously Cat, Pet, PetModel and is now Cat, PetModel, PetModel1; similar shifts occur for typed nullable map value models (Tags is now Tags1) and numbered names (Tag1 is now Tag2, Pet is now Pet1). Code that imports the affected classes by name must be updated; schema-path keys are unaffected (#6202)
  • Model-level --type-overrides now apply to root models under --collapse-root-models - A root model named by a model-level --type-overrides key is no longer inlined by --collapse-root-models; its holders now take the override type, as they already did without collapse, instead of the inlined root type. For Avro, fixed types with a logical type that a model-level override replaces now take the override type under --collapse-root-models instead of the logical type, so fields previously generated as timedelta with logical defaults now become the override type (for example bytes) with physical byte defaults (#6202)
  • Discriminator tags are now read through a root model or type alias of a one-value enum - With --enum-field-as-literal (for example all), a one-value enum that a union variant's tag property references is generated as a RootModel[Literal[...]], or as a TypeAliasType of the Literal for Pydantic dataclasses. The variant's tag now comes from that value instead of falling back to the schema name. When you regenerate, the variant models' tag field types change, for example type: Literal['FunctionToolCall'] becomes type: Literal['function']. Code that built or matched the old schema-name tags must be updated. The old output rejected valid payloads. Variants reached through nested choices, and inline variants, keep the previous behaviour. (#6205)
  • Discriminator tags of models merged into _internal.py by an import cycle now use the mapping values - In modular output, models that an import cycle moves into _internal.py now have their discriminator mapping values resolved from the original schema paths, not the moved paths. Tag literals such as Literal['_internal.Cat_1'] become the mapped values, such as Literal['c']. The old output rejected every payload. (#6205)
  • Parameter schemas with keywords beside $ref now merge in the referenced schema in generated clients and FastAPI servers - In an OpenAPI 3.1 document, a parameter schema with keywords such as description or default next to its $ref used to stop at that reference. The target bindings read only the keywords beside the $ref and dropped the referenced schema's default, title and other documentation keywords. They now follow the reference chain and merge both sets, with the keywords beside the $ref winning, which is what the model parser already does. As a result, generated FastAPI routers can gain annotations such as Query(alias='least', title='Size'). A parameter whose $ref only carries a description now gets the referenced schema's default, so the generated client sends that default and the generated server fills it in when a request leaves the parameter out. Whether these keywords count is now decided by the version the parser uses, for example --schema-version 3.0, instead of the entry document's openapi text. OpenAPI 3.0 still ignores them. (#6210)
  • Generated FastAPI servers now read multipart members of any type as text when their encoding gives a media type or style - Before, a multipart/form-data request member whose schema accepts any value, such as {}, was decoded as JSON by the generated server whenever the part parsed as JSON. So 1 arrived as the integer 1 and null as None, even when the member's encoding named text/plain or a style, which tells the client to write it as text. Such members are now read as strings, so handlers receive '1' and 'null'. Members with no encoding media type or style are still decoded as JSON. (#6210)
  • An allOf of value-only anyOf or oneOf members now generates a union instead of an empty model - An allOf whose anyOf or oneOf members admit only values and no objects, for example an anyOf of string and integer combined with minLength: 2, used to be generated as a model class without fields that rejected every value. It is now generated as the union of the intersections of one branch from each anyOf or oneOf with the other parts, such as RootModel[constr(min_length=2) | int] for Pydantic v2, or a TypeAlias like str | int for dataclasses, TypedDict and msgspec. This happens by default, without --intersect-allof-keywords or --intersect-combined-schema-keywords. For these sites, the generated kind (class vs root model or alias), field annotations, constraints, defaults and base classes change. Numbered classes these sites used to produce, such as Beside3 and Beside4, are no longer written. --reuse-model no longer merges other models into the empty class, and --collapse-root-models now inlines the union into fields. Code that imports, instantiates or subclasses the old empty classes or their numbered siblings must be updated. Sites that have their own anyOf or oneOf, that admit an object, that have no satisfiable combination, or that exceed 128 branch combinations keep the previous output (#6258)
  • Unhashable uniqueItems arrays stay lists under --use-unique-items-as-set - With --use-unique-items-as-set (and any preset that turns it on), a uniqueItems array whose items can never be set members is now generated as a list instead of a set. This covers plain objects or dicts, arrays, sequences, mappings, and tuples that hold such a value, including when the tuple is reached through a type alias. For example, set[dict[str, Any]] becomes list[dict[str, Any]], set[tuple[int, list[int]] | dict[str, Any]] becomes a list, and a root model RootModel[set[list[int]]] becomes RootModel[list[list[int]]]. The old set types rejected every non-empty array in Pydantic and failed msgspec decoding. Also, when a field's list default cannot be turned into a set, the whole field now stays a list, so Optional[Set[Dict[str, Any]]] = [{'a': 1}] becomes Optional[List[Dict[str, Any]]] = [{'a': 1}]. Before, the list members of the field's union were turned into sets even though the list default was kept, which could leave fields such as Optional[Set[Any]] with a list default. Those fields now validate to lists instead of sets, and their imports change from Set to List. Arrays of hashable items, unions with at least one hashable member, nullable items and untyped items still become sets (#6290)
  • Nested uniqueItems arrays are now generated as sets under --use-unique-items-as-set - With --use-unique-items-as-set, any array whose own schema declares uniqueItems: true is now written as a set wherever it appears. Before, this only happened for a property or root model. It now also covers an anyOf or oneOf member, the items of an array, the values of a map or of patternProperties, and a lone allOf member. For example, a map of unique integer arrays changes from dict[str, list[int]] to dict[str, set[int]], and an array of unique arrays changes from list[list[int]] to list[set[int]]. Code that indexes or appends to these nested values, or that relies on their order or duplicates, must be updated. A nested array stays a list in three cases: its items cannot be hashed; a model that --reuse-model or deduplication merged into it does not declare uniqueItems at the same position; or it sits in a union beside another array or map member that keeps its lists. Output without the option is unchanged (#6291)
  • Unique-items arrays of models that can never be hashed are now lists under --use-unique-items-as-set - With --use-unique-items-as-set, a uniqueItems array whose items are models that can never be hashed now generates list[Model] instead of set[Model]. This covers typing.TypedDict models, non-frozen dataclasses.dataclass and pydantic_v2.dataclass models without unsafe_hash, non-frozen msgspec.Struct models (including the struct chain and the generic base class config), and RootModel aliases written as Name = RootModel[...] under --use-root-model-type-alias. The old set annotation rejected every non-empty array, and msgspec could not decode it at all. Code or type checks that expected the field to be a set will now see a list. Frozen classes, and classes with a custom base class, class code or custom templates, still get a set (#6294)
  • Models reached through a type alias inside a set now get an identity hash - With --use-unique-items-as-set, a Pydantic model named in the value of a type alias that a set holds now gets __hash__ = object.__hash__ in its class body. For example, Circle gets it when a field is set[Shape] with Shape = TypeAliasType("Shape", Circle | str). Models named directly by a set already got this line. Instances of these models used to be unhashable and now hash by identity (#6294)
  • AllOf value sites now write the keywords of their parts on the root field - When an allOf of value parts keeps the form it is written in (the default, without --intersect-allof-keywords), the generated root model, type alias or field now gets the constraints its parts declare (minLength, maxLength, pattern, minItems, maxItems, minimum, maximum, multipleOf). Before, it took its constraints from the allOf site itself, which declares none. As a result, array lengths were silently dropped in default Pydantic output, and every keyword was dropped under --field-constraints, --use-annotated and in msgspec output. For example, a lone allOf array member with minItems: 2 now generates root: list[int] = Field(..., min_length=2), the msgspec alias Nickname1: TypeAlias = str becomes Annotated[str, Meta(max_length=8)], and level_prop: int becomes Annotated[int, Meta(ge=0, le=10)]. The allOf of a propertyNames schema now also constrains map keys, so dict[str, int] becomes dict[Annotated[str, Meta(min_length=2, pattern='^a')], int]. Generated models are stricter and reject data they used to accept. In msgspec output with --collapse-root-models, new type aliases such as MultipleItem and LevelItem can also appear (#6293)
  • Root models with unhashable root types are no longer inlined as set items under --collapse-root-models - When --collapse-root-models and --use-unique-items-as-set are both on (including the presets that turn them on), a root model whose root type has no hashable value, such as a list, a dict or a closed prefixItems array, is no longer inlined when it is the item of a set or frozenset. Instead, the root model stays named, as it does without collapse. Fields that were generated as set[list[str]], set[dict[str, int]] or frozenset[list[int]] are now typed as set[Tags], set[Object] or frozenset[Group], and the root model class is written to the output with __hash__ = object.__hash__. The old inlined types rejected every non-empty array. Still, regenerated code now has extra root model classes, and set items are now model instances instead of plain lists or dicts. Root models written as aliases, such as msgspec output, are not affected and keep a list. (#6295)
  • Client and FastAPI targets follow the parser's OpenAPI version - The generated client and FastAPI server now take the OpenAPI version from the parser (--schema-version when given, otherwise the version the root document declares as the parser reads it) instead of reading the root document's openapi text with their own legacy rules (the first three characters, or the digits around the first dot). A run whose --schema-version differs from the declared version, or a document whose openapi text is missing or not recognized by the parser, can now generate different code: path parameters with allowReserved: true are written with allow_reserved=True only when the document is read as 3.2 (otherwise the client percent-encodes reserved characters in them), multipart encoding styles are ignored only when read as 3.0, the server's handling of repeated path templates depends on whether the document is read as 3.2, and the served source document decides $id resources and the inlining of pathItems and mediaTypes components by the same version (#6346)
  • Generated clients and FastAPI servers now read keywords beside $ref in OpenAPI 3.0 documents - Generated API clients and FastAPI servers used to ignore the default and documentation keywords written beside a $ref in a parameter schema when the document was OpenAPI 3.0, including under --schema-version 3.0. They now apply them in every version, which matches the models. For example, a query parameter whose schema is a $ref with a sibling default: 7 used to have no default. It now gets that default on both sides: the generated client sends page=7 when the caller leaves it out, and the server uses 7 when the request leaves it out. Regenerated clients and servers may therefore send or accept different parameter defaults. Pass --ignore-ref-siblings-by-schema-version to keep ignoring these sibling keywords where the schema version says so (#6347)
  • Protobuf UInt64Value and UInt32Value wrapper fields now include an upper bound - Fields typed as google.protobuf.UInt64Value or google.protobuf.UInt32Value are now built from the same scalar schema as plain uint64 and uint32 fields. Before, they only got a lower bound. Generated code changes from conint(ge=0) to conint(ge=0, le=18446744073709551615) for UInt64Value and conint(ge=0, le=4294967295) for UInt32Value. The msgspec output also gains the matching le bound, for example Meta(ge=0,le=4294967295). Models that used to accept integers above the wire range now reject them. Class names, field names and types stay the same. (#6349)
  • Inherited fields are no longer redeclared in child models generated from --input-model - When a child input model has a field that matches an inherited parent field by wire name, with the same schema and the same required status, _partition_inherited_fields now always treats it as inherited. Before, it only did this when the name was found in the parent's field set. So generated child classes such as AliasSameChild, AliasPairParent and InheritedWins no longer redeclare these fields (for example old: int = Field(1, title='Old') or shared: list[int] = Field(..., title='Shared')) and now get them only from the parent class. The child's generated JSON schema also no longer repeats these properties or lists them twice in required (previously required could co...

    Description has been truncated

Bumps [datamodel-code-generator](https://github.com/datamodel-code-generator/datamodel-code-generator) from 0.31.2 to 0.90.0.
- [Release notes](https://github.com/datamodel-code-generator/datamodel-code-generator/releases)
- [Changelog](https://github.com/datamodel-code-generator/datamodel-code-generator/blob/main/CHANGELOG.md)
- [Commits](datamodel-code-generator/datamodel-code-generator@0.31.2...0.90.0)

---
updated-dependencies:
- dependency-name: datamodel-code-generator
  dependency-version: 0.90.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file python Pull requests that update python code labels Nov 10, 2026
@dependabot @github

dependabot Bot commented on behalf of github Nov 11, 2026

Copy link
Copy Markdown
Author

Superseded by #1031.

@dependabot dependabot Bot closed this Nov 11, 2026
@dependabot
dependabot Bot deleted the dependabot/pip/sdk/python/datamodel-code-generator-0.90.0 branch November 11, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python Pull requests that update python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants