Skip to content

Bump datamodel-code-generator from 0.31.2 to 0.91.0 in /sdk/python - #1031

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

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

Conversation

@dependabot

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

Copy link
Copy Markdown

Bumps datamodel-code-generator from 0.31.2 to 0.91.0.

Release notes

Sourced from datamodel-code-generator's releases.

0.91.0

Breaking Changes

Code Generation Changes

  • Empty unused classes for x-python-import schemas referenced outside $defs are no longer generated - Some schemas carry x-python-import, are reached through a JSON pointer outside $defs or definitions (for example /components/Options in the same file or other.json plus /components/Options), and are named only by fields. Previously such a schema was also written as an empty class that nothing used, such as class Options(BaseModel): pass, next to the import that types those fields. That class is now left out, which matches how these schemas are already handled in $defs. Fields that reference the schema still use the imported class, such as OrderedDict. A schema that another model uses as a base, including the subclasses made by --reuse-model, keeps its model. Code that imported or referenced the removed empty class from the generated module must be updated. (#6415)
  • Nested model names under --naming-strategy enclosing-model now follow the restored owner name in modular output - When several input files each define a model with the same name (for example Parent), the naming plan numbers the later one across the input and its own module then gives it back its base name. Models whose names were composed from that numbered owner (inline children, grandchildren, ordinal, tagged or composed models) now also take the name composed from the restored name, e.g. ParentChild and ParentChildGrand instead of names built from the numbered owner such as Parent1Child, wherever the module leaves those names free. Code that imports or references the previously generated nested class names must be updated. Default naming is unaffected (#6417)
  • Schemas with items or prefixItems but no array type are no longer typed as lists - JsonSchemaObject.is_array now treats a schema as an array only when its type is array, when array is in its type list and items are given, or when it has items but no declared type. Before, any items or prefixItems made the schema an array. A schema like {"type": ["string", "null"], "items": {...}} used to produce list[int] | None and now produces str | None. {"type": "string", "items": ...} now produces str. {"type": ["object", "null"], "items": ..., "properties": ...} now produces Object | None instead of a union that also included list[int]. {"type": ["integer", "boolean"], "prefixItems": ...} now produces int | bool instead of list[Any]. The old output rejected every valid value, but the field types in generated models, root models and type aliases change, so code that relied on the old list types may need updating (#6419)
  • Constraints on patternProperties values are now kept on a generated type alias - For Pydantic v2 output (BaseModel and dataclass), a patternProperties value schema used to be parsed without a field, so its constraints were dropped. Now, when the dict key type rejects every key that matches none of the patterns and no value is Any, those constraints go on a new type alias named after the parent model with a PatternProperty suffix, for example PArrPatternProperty = TypeAliasType("PArrPatternProperty", Annotated[list[str], Field(min_length=2)]). In every mode, including the default options, this applies to the minItems/maxItems of an array value. Under --field-constraints and --use-annotated (as in the presets), it also applies to bounds, lengths and pattern. As a result, dict value hints change from dict[constr(pattern=r'^a'), list[str]] to dict[constr(pattern=r'^a'), PArrPatternProperty], modules gain the new alias names plus TypeAliasType/Annotated imports, a Pydantic dataclass can switch to @dataclass(config=ConfigDict(regex_engine="python-re")) when the alias holds a lookahead pattern, and data that breaks the value's schema constraints but validated before is now rejected (#6424)
  • Patterns with the same value type but different constraints now generate a union of aliases - Under --field-constraints or --use-annotated, a map whose patterns share a value type but carry different constraints used to collapse into one value type. It is now written as a union of aliases, for example dict[constr(pattern=r'^a|b$'), int] becomes dict[constr(pattern=r'^a|b$'), OverlapPatternProperty | OverlapPatternProperty1]. This also applies to root models, list items and nested maps (#6424)
  • Generate-schema-validators patternProperties rules now use the constrained alias - With --generate-schema-validators, the value type in each __json_schema_pattern_properties__ rule now refers to the alias that holds the value's constraints. For example ('^x', str) becomes ('^x', RootItemPatternProperty), where RootItemPatternProperty is Annotated[str, Field(max_length=3, min_length=2)], so the runtime validator now rejects values that break minLength, maxLength, minItems and similar constraints it used to accept (#6424)
  • msgspec maps under use-msgspec-meta-for-map-keys now write a single pattern value as a Meta alias - With --use-msgspec-meta-for-map-keys, msgspec checks the key patterns. A pattern map with one value type therefore now writes that value as an alias such as PIntPatternProperty: TypeAlias = Annotated[int, Meta(ge=0)] instead of the plain type, so msgspec now enforces the value's constraints. Maps with several value types keep their plain types (#6424)
  • Properties declared in several inline allOf members now carry the constraints of every declaration - When more than one inline allOf member beside a $ref declares the same property, for example {type: string} in one member and {maxLength: 3} in another, the generated field used to take its constraints from the first declaration only. Request and response models under --read-only-write-only-model-type took them from the last one. Now each declaration gets the bounds, multipleOf and pattern of all of them, so a field that was str becomes constr(max_length=3), Annotated[str, Meta(max_length=3)] or Field(None, max_length=3). A subclass that overrides the property also gets the combined constraints, for example min_length=1 plus max_length=3, where it used to have only its own. The field keeps the type, default and description it had before. Positional tuple declarations do not get item counts, and $ref declarations neither receive nor pass on constraints. A new warning is emitted when the declarations have no common value for a keyword. This happens by default in every dialect and output model type, with or without --intersect-allof-keywords and --intersect-combined-schema-keywords, so generated models can now reject payloads they used to accept (#6425)
  • Union member classes of an allOf now redeclare shared properties with the constraints of every declaration - When an allOf holds an anyOf or oneOf and the branch and the other allOf members declare the same property with different bounds, multipleOf or pattern, the generated class for each union member now declares that property itself with all the check keywords combined. Previously the class inherited the field from its first base (Pydantic, dataclasses, TypedDict) or its last base (msgspec), so some constraints were lost. For example, Inline4(Inline1, Inline3) now declares x: conint(ge=0, le=10) | None = None, where it used to inherit only conint(ge=0). Generated models now reject values they used to accept (#6426)
  • Some union member TypedDict classes are now written without base classes - A union member TypedDict whose redeclared key differs from a base's key is now generated as a standalone TypedDict with no bases. For example, class Refs3(TypedDict) replaces class Refs3(Dog, Refs1). Code that relies on the previous class hierarchy or on static assignability to the base types may need updating (#6426)
  • Enum object properties written as a Literal now keep their schema pattern - When an object property declares both enum and pattern and the enum is written as a Literal with its constraints on the field (for example --enum-field-as-literal together with --field-constraints), the generated field now includes the pattern, e.g. inline: Literal['aa', 'b'] | None = Field(None, pattern=...). Before, the pattern was silently dropped at object properties, while root models and const fields already kept it. This affects BaseModel, Annotated and Pydantic dataclass output. Enum values that fail the pattern used to be accepted and are now rejected at validation time. If the Rust regex engine refuses the pattern, it can now also add regex_engine="python-re" to the model config. With --ignore-enum-constraints the output is unchanged, and with default options (enums written as classes) nothing changes (#6428)
  • Constraints beside a typed oneOf or anyOf of consts are now written on its Literal under --field-constraints - When a oneOf or anyOf of const values is rendered as a Literal and its schema declares the single type of those consts (for example "type": "integer" beside anyOf: [{"const": 1}, {"const": 5}] with maximum: 3), the generator now carries the sibling constraint keywords onto the generated field and onto a Literal root model. Before this change they were silently dropped. For example, consts: Literal['a', 'bb', 'ccc'] | None = None becomes consts: Literal['a', 'bb', 'ccc'] | None = Field(None, min_length=2), consts: Literal[1, 5] | None = Field(None, le=3) is now emitted, and a RootModel[Literal['a', 'bb']] gains root: Literal['a', 'bb'] = Field(..., max_length=1). Regenerated models now reject values that earlier generated models accepted. Untyped oneOf or anyOf schemas get no constraints, --filter-literal-values still drops field constraints when it has already removed the rejected values, and default output is unchanged (#6429)
  • msgspec now writes property-count lengths on mapping class types - For msgspec output, when a field's type is a mapping class that msgspec decodes as a dict, such as dict or dict[str, str] written by --type-overrides with --type-overrides-replace-whole-type, the schema's minProperties/maxProperties are now emitted as Meta(min_length=...)/Meta(max_length=...), for example sized: Annotated[dict, Meta(min_length=1)] | UnsetType. Before, they were dropped with a "msgspec.Meta does not take min_length on dict" warning. Generated Structs now reject payloads with too few or too many keys that used to decode, and those warnings no longer appear. OrderedDict and Counter classes still get no lengths, because msgspec refuses them there (#6431)
  • Defaults under --use-unique-items-as-set-by-value are now written as the sets their types write - Array defaults that sit where the final types write a set (the field's own list, or a set type alias inside a list, union, map or tuple position) are now generated as set literals, or as frozenset(...) under --use-generic-container-types, instead of nested lists or mutable sets; for example frozen dataclasses now emit levels: frozenset[int] | None = frozenset({1}) instead of field(default_factory=lambda: {1}), and pydantic emits rows: list[Row] | None = [{1, 2}] where the nested items used to stay lists, so the runtime type of these default values changes (#6440)
  • Frozen dataclasses with frozen set defaults now hash by value - Because a frozen set default is now written as a hashable frozenset and not a mutable set, frozen dataclasses whose fields carry such defaults now count as hashing by value under --use-unique-items-as-set-by-value, so uniqueItems arrays of them change type, for example limits: Sequence[Limits] | None becomes limits: frozenset[Limits] | None, which breaks code that indexes into or relies on the order of those fields (#6440)
  • TypedDict extra_items now writes uniqueItems arrays as sets under --use-unique-items-as-set - When generating typing.TypedDict models with --use-unique-items-as-set (and also --use-unique-items-as-set-by-value), the extra_items type built from an additionalProperties schema now goes through the same uniqueItems-to-set conversion as regular fields. Arrays that declare uniqueItems: true are now written as set[...], or frozenset[...] with --use-generic-container-types, where they used to be list[...] or Sequence[...]. This covers the array itself, anyOf/oneOf members, map values and the inner arrays of nested arrays. For example, extra_items=list[int] becomes extra_items=set[int] and extra_items=dict[str, list[int]] becomes extra_items=dict[str, set[int]]. Arrays whose items cannot be hashed, such as TypedDict items, stay lists. With --reuse-model, an array stays a list when a model merged into it does not declare uniqueItems. Code that treats these extra values as lists (indexing, ordering, keeping duplicates) or type-checks them against list may need updating (#6441)
  • TypedDict extra_items from array type lists now includes the other type list members - When an additionalProperties schema (or another schema read as a lightweight type) declares a type list containing array alongside other types, such as ["array", "string"] or ["array", "integer", "null"], the generated type is now a union like str | list[int] or int | list[int] | None instead of only list[int], matching the type generated for a property with the same schema; regenerated typing.TypedDict output accepts the strings, integers and nulls the schema allows, and code that assumed these values were always lists may need type narrowing to satisfy type checkers (#6442)
  • TypedDict extra_items now written unquoted when the classes it names already exist - When generating typing.TypedDict output, the extra_items= class keyword used to be a string whenever it named a generated class, for example extra_items='list[Item]', extra_items='FlaggedRequest' or extra_items='b.Bar | c.Baz | Near'. It is now written as a plain type expression, for example extra_items=list[Item] or extra_items=b.Bar, whenever every class it names is defined by the time the class statement runs: either declared earlier in the same module or imported from another module. It stays a string only when it names the class itself or a class declared later in the same module. As a result, the runtime __extra_items__ value is the real type instead of a forward reference, which lets Pydantic TypeAdapter resolve these TypedDicts where it used to fail with class-not-fully-defined. Generated files change wherever this applies. Because these unquoted names are now evaluated when the class is defined, the import-cycle check also counts them, so modular TypedDict output with cross-module extra_items in an import cycle may now merge modules into a shared _internal.py (#6443)
  • msgspec unions of differently constrained same-type members now keep their shared constraints - With --output-model-type msgspec.Struct, a union of members of one type that carry different constraints, where no member carries only the constraints they all share, used to be generated with no Meta(...) at all. It is now generated with the constraints every member implies, written once. For example, anyOf: [{type: string, minLength: 2, maxLength: 3}, {type: string, maxLength: 3, pattern: '^a'}] changes from str to Annotated[str, Meta(max_length=3)], anyOf: [{type: integer, minimum: 1, maximum: 5}, {type: integer, minimum: 7}] changes from int to Annotated[int, Meta(ge=1)], and aliases such as TwoBases: TypeAlias = int become TwoBases: TypeAlias = Annotated[int, Meta(le=100)] (list items too, e.g. list[Annotated[int, Meta(le=100)]]). Float members also take into account the int and Decimal members that msgspec decodes as floats. The generated type annotations and aliases are different, and the models now reject payloads they used to accept that every branch of the schema rejects (e.g. "abcd", 0, 101). The dropped-constraint warning for these fields now ends with , and it keeps max_length=3, which every member implies (or whichever constraints are kept). This is on by default and has no opt-out flag (#6450)
  • Required names beside the $ref of a lone allOf member now produce a site subclass under --intersect-combined-schema-keywords - With --intersect-combined-schema-keywords, a field, array item or union branch like allOf: [{$ref: A, required: [id]}] was generated as the referenced model A. Here the required names sit next to the $ref inside the single allOf member, and A accepted objects that were missing them. The required names are now read from inline allOf members as well, so the site is generated as a subclass of A (for example RefSibling(A)) that requires them. The field annotation now names a new class, and payloads such as {} or {"name": "a"} that used to validate are now rejected (#6472)
  • Inline allOf members that exclude null now make the site subclass non-nullable under --intersect-combined-schema-keywords - With --intersect-combined-schema-keywords, take a site subclass built from an allOf that references a nullable model plus an inline member whose type excludes null, such as allOf: [{$ref: Nullable}, {type: object, required: [id]}]. It used to be written as nullable (for example ObjectMember | None) because only the outer schema's own type was checked for null. Inline allOf members are now checked too, so the field is annotated ObjectMember and a null payload is rejected (#6472)
  • Referenced document roots with x-python-import no longer generate an unused empty model - When a $ref points to a whole external document whose root schema declares x-python-import and would generate an empty class (no fields and no root type), that empty class is no longer written next to the import; fields that reference the document keep the imported class. A root that generates a model with fields or a root type, such as a RootModel of a map, is written as in 0.90.0, as is a root used as a base class and the root of the input document. (#6480, #6554)
  • Document roots with x-python-import reached through a pointer no longer generate an unused empty class - When a $ref reaches a file's root through a definition that refers back to it, and the root has x-python-import and would generate an empty class, that empty class is no longer written; fields keep the imported type. A root that generates a model with fields or a root type is written as in 0.90.0. (#6484, #6554)
  • Plain Enum discriminator tags now also hold the raw value under --use-enum-values-in-discriminator - When --use-enum-values-in-discriminator is used and a variant is tagged by a member of a plain Enum (one with no value base, so not StrEnum, IntEnum or the (str, Enum) that --use-subclass-enum writes), the tag field is now written as Literal[PetType.cat, 'cat'] instead of Literal[PetType.cat]. Before, the discriminated union rejected every JSON payload. It now accepts them, but a field parsed from JSON holds the plain value (for example 'cat') instead of the Enum member, and code that type-checks against the old annotation may need updating. Python callers that pass PetType.cat keep working. Enums with a value base keep Literal[PetType.cat], and msgspec output is unchanged (#6495)
  • Discriminator removed when a plain Enum tag value collides with another variant's literal - Under --use-enum-values-in-discriminator, a plain Enum member tag now carries its value too. So when one variant is tagged by a plain Enum member (for example Plain.int_1) and another variant's tag is a literal with the same value (Literal[1]), the union no longer gets Field(None, discriminator='k') and becomes a plain union. The Enum-tagged variant's field is typed as the Enum class (k: Plain) instead of Literal[Plain.int_1] (#6495)
  • Discriminated $ref branches now take the allOf members under --distribute-allof-over-unions - With --distribute-allof-over-unions (and the 20261104 presets that enable it), a $ref branch of an anyOf or oneOf whose target has an OpenAPI discriminator is now expanded into one generated class per subtype, each inheriting from the subtype and carrying the allOf members beside or around the union (for example Owned1(Cat), Owned2(Dog)). Those variants now require the members' properties, such as owner, instead of accepting payloads without them. Classes for later branches are renumbered in union order, so the class previously generated as Owned1(Fish) is now Owned3(Fish). In an allOf that holds such a union, the discriminated subtypes are no longer dropped, which also changes the numbered class names there. Code that imports or references the old numbered class names, or relies on the looser validation, must be updated. Output without the option is unchanged (#6497)
  • Discriminated $ref branch classes beside an allOf are tagged by the schema they name under --distribute-allof-over-unions - With --distribute-allof-over-unions (and the 20261104 presets), the class generated for each $ref branch of a discriminated oneOf that sits beside an allOf used to take the branch's position as its discriminator tag (for example Literal['0'] and Literal['1'], or msgspec tag='0') when the discriminator had no mapping. Tagged msgspec Structs also lost their mapping key, so the union rejected every payload. These classes now take the tag of the schema the branch names: Literal['Cat'] or the mapping key such as Literal['c'], and tag='Cat' or tag='c' for tagged msgspec Structs. Regenerated code therefore has different tag values, and code that builds or matches the old positional tags must be updated. Output without the option is unchanged (#6498)
  • Discriminator tags of $ref branch classes with sibling properties now use the referenced schema name - When a oneOf of $ref branches has properties or required beside it and a discriminator without a mapping, each branch's generated class (for example Owned1(Cat) and Owned2(Dog)) now types the discriminator field with the name of the schema the branch references (Literal['Cat'], Literal['Dog']) instead of the branch's position in the union (Literal['0'], Literal['1']). This affects the default output for pydantic, dataclass and msgspec models. The old output rejected every payload, so this is a fix, but any code that built or matched these models using the positional literal values has to be updated (#6499)
  • Constraints an allOf site adds to its single $ref are now enforced in default msgspec and Pydantic dataclass output - When a property is an allOf of one $ref plus extra keywords (maxLength beside the allOf, minItems or pattern in a second member, or a bound beside a $ref that points to an allOf of one $ref), the default msgspec.Struct and pydantic_v2.dataclass output used to type the field by the target alone and drop the extra keywords. The field now writes them, e.g. name: Annotated[Str, Meta(max_length=5)] | UnsetType = UNSET instead of name: Str | UnsetType = UNSET. Type aliases built from such a site now carry them too, e.g. Wd: TypeAlias = Annotated[Q, Meta(le=10)] instead of Wd: TypeAlias = Q, and Score = TypeAliasType("Score", Annotated[Count, Field(..., le=100)]) instead of TypeAliasType("Score", Count). As with other $ref siblings, these keywords are also read in draft-07 and OpenAPI 3.0 schemas unless --ignore-ref-siblings-by-schema-version is set. Generated models now reject payloads they used to accept, such as an 11 where the site adds maximum: 10. Default Pydantic BaseModel output, whose field names a root model class, is unchanged, and TypedDict output writes no constraints (#6510)
  • Site constraints are now written under --collapse-root-models and --use-type-alias - When root-model collapse inlines the target type, or the field names a type alias, the field now gets the constraints its allOf site adds. For Pydantic this looks like count: int | None = Field(None, ge=5) instead of count: int | None = None and q_allof: conint(ge=1) | None = Field(None, le=10) instead of q_allof: conint(ge=1) | None = None. For msgspec it looks like wd_prop: Annotated[int, Meta(ge=1, le=10)] | UnsetType = UNSET instead of Annotated[int, Meta(ge=1)]. Validation is stricter: values the schema rejects are no longer accepted (#6510)
  • Pydantic dataclass fields wrap their type in Annotated to carry site constraints - A pydantic_v2.dataclass field that writes no Field() call now takes the site constraints as Annotated metadata, e.g. count: Annotated[Count | None, Field(ge=5)] = None instead of count: Count | None = None, and id: Annotated[Id, Field(max_length=5)] instead of id: Id. A field that already writes a Field(...) call gets the constraints added to that call. The field keeps its default and its place in the constructor, but code that inspects annotations sees a different shape (#6510)
  • Length keywords beside a union of a model and a plain string are now written on the string member - Under --collapse-root-models with --field-constraints or --use-annotated (as in the presets), a length keyword such as maxLength beside a union of a model and a plain string type used to be written on the whole field (text: Item | str | None = Field(None, max_length=3)), which made Pydantic raise TypeError for every object value. It is now written on the string member instead: Item | constr(max_length=3) | None = None with --field-constraints, or O | Annotated[str, StringConstraints(max_length=3)] | None = None with --use-annotated. The generated code gains a constr or StringConstraints import and loses the keyword from Field(). Object values now validate, and strings are checked the same way as before. Field metadata and the JSON schema Pydantic produces for these fields also change (#6519)
  • Constraints now carry through plain alias root models such as msgspec root types - The constraint walk in constrained_value_type used to treat only TypeAliasBase subclasses as aliases. It now treats any DataModel whose form is "alias" as one. With msgspec.Struct output, root types are written as plain assignments like Level = Annotated[int, Meta(ge=1)]. A reference to one of these is now followed to its underlying value type, where before it was treated like an opaque class reference. As a result, constraints declared where the type is used, such as sibling keywords next to the single $ref of an allOf, are now written on the field. For example, Annotated[int, Meta(ge=1)] becomes Annotated[Annotated[int, Meta(ge=1)], Meta(le=10)], and a plain str field can become Annotated[str, Meta(max_length=5)]. Generated models now enforce constraints that used to be silently dropped, so data that passed validation before may now be rejected (#6543)

Error Handling Changes

  • New warnings for site constraints the target type cannot take - When an allOf site adds a keyword the field's type does not accept, generation now prints a warning and leaves that constraint out, e.g. Pydantic does not take max_length on AwareDatetime; Model.stamp_len_prop is generated without this constraint.. The msgspec 64-bit integer-limit warning now also covers fields typed by such a site. Workflows that treat these warnings as failures may be affected (#6510)
  • New default warning for object defaults missing required model fields - When --omit-incomplete-model-defaults is not set and a property's object default lacks required fields of its model (its own or a base's), generation for Pydantic v2 BaseModel, Pydantic v2 dataclass and msgspec Struct output now emits one UserWarning listing every such field. It starts with The default of Model.field or The defaults of ..., says the generated models will reject input that leaves the property out, and suggests --omit-incomplete-model-defaults. Generated code does not change, and dataclass and TypedDict outputs do not warn. Builds or tests that turn warnings into errors, such as -W error or pytest filterwarnings = error, or that expect empty stderr, can now fail or see new output for these schemas (#6518)

New Features

Presets standard-py3XX-20261106 and practical-py3XX-20261106

  • New dated presets for Python 3.10 to 3.15. They build on the 20261104 presets and also turn on the opt-in options below, except where an item says otherwise. Default output (no preset) does not change.

New options

Every new option is off by default and has a --no- form.

  • --read-allof-parents-in-mro-order - Read the allOf parents of every inherited schema in the order of the class bases, so a property that several bases declare takes the nearer base's constraints and description, also where an allOf lists a base before its own descendant. Set by the 20261106 presets.

... (truncated)

Changelog

Sourced from datamodel-code-generator's changelog.

0.91.0 - 2026-11-07

Breaking Changes

Code Generation Changes

  • Empty unused classes for x-python-import schemas referenced outside $defs are no longer generated - Some schemas carry x-python-import, are reached through a JSON pointer outside $defs or definitions (for example /components/Options in the same file or other.json plus /components/Options), and are named only by fields. Previously such a schema was also written as an empty class that nothing used, such as class Options(BaseModel): pass, next to the import that types those fields. That class is now left out, which matches how these schemas are already handled in $defs. Fields that reference the schema still use the imported class, such as OrderedDict. A schema that another model uses as a base, including the subclasses made by --reuse-model, keeps its model. Code that imported or referenced the removed empty class from the generated module must be updated. (#6415)
  • Nested model names under --naming-strategy enclosing-model now follow the restored owner name in modular output - When several input files each define a model with the same name (for example Parent), the naming plan numbers the later one across the input and its own module then gives it back its base name. Models whose names were composed from that numbered owner (inline children, grandchildren, ordinal, tagged or composed models) now also take the name composed from the restored name, e.g. ParentChild and ParentChildGrand instead of names built from the numbered owner such as Parent1Child, wherever the module leaves those names free. Code that imports or references the previously generated nested class names must be updated. Default naming is unaffected (#6417)
  • Schemas with items or prefixItems but no array type are no longer typed as lists - JsonSchemaObject.is_array now treats a schema as an array only when its type is array, when array is in its type list and items are given, or when it has items but no declared type. Before, any items or prefixItems made the schema an array. A schema like {"type": ["string", "null"], "items": {...}} used to produce list[int] | None and now produces str | None. {"type": "string", "items": ...} now produces str. {"type": ["object", "null"], "items": ..., "properties": ...} now produces Object | None instead of a union that also included list[int]. {"type": ["integer", "boolean"], "prefixItems": ...} now produces int | bool instead of list[Any]. The old output rejected every valid value, but the field types in generated models, root models and type aliases change, so code that relied on the old list types may need updating (#6419)
  • Constraints on patternProperties values are now kept on a generated type alias - For Pydantic v2 output (BaseModel and dataclass), a patternProperties value schema used to be parsed without a field, so its constraints were dropped. Now, when the dict key type rejects every key that matches none of the patterns and no value is Any, those constraints go on a new type alias named after the parent model with a PatternProperty suffix, for example PArrPatternProperty = TypeAliasType("PArrPatternProperty", Annotated[list[str], Field(min_length=2)]). In every mode, including the default options, this applies to the minItems/maxItems of an array value. Under --field-constraints and --use-annotated (as in the presets), it also applies to bounds, lengths and pattern. As a result, dict value hints change from dict[constr(pattern=r'^a'), list[str]] to dict[constr(pattern=r'^a'), PArrPatternProperty], modules gain the new alias names plus TypeAliasType/Annotated imports, a Pydantic dataclass can switch to @dataclass(config=ConfigDict(regex_engine="python-re")) when the alias holds a lookahead pattern, and data that breaks the value's schema constraints but validated before is now rejected (#6424)
  • Patterns with the same value type but different constraints now generate a union of aliases - Under --field-constraints or --use-annotated, a map whose patterns share a value type but carry different constraints used to collapse into one value type. It is now written as a union of aliases, for example dict[constr(pattern=r'^a|b$'), int] becomes dict[constr(pattern=r'^a|b$'), OverlapPatternProperty | OverlapPatternProperty1]. This also applies to root models, list items and nested maps (#6424)
  • Generate-schema-validators patternProperties rules now use the constrained alias - With --generate-schema-validators, the value type in each __json_schema_pattern_properties__ rule now refers to the alias that holds the value's constraints. For example ('^x', str) becomes ('^x', RootItemPatternProperty), where RootItemPatternProperty is Annotated[str, Field(max_length=3, min_length=2)], so the runtime validator now rejects values that break minLength, maxLength, minItems and similar constraints it used to accept (#6424)
  • msgspec maps under use-msgspec-meta-for-map-keys now write a single pattern value as a Meta alias - With --use-msgspec-meta-for-map-keys, msgspec checks the key patterns. A pattern map with one value type therefore now writes that value as an alias such as PIntPatternProperty: TypeAlias = Annotated[int, Meta(ge=0)] instead of the plain type, so msgspec now enforces the value's constraints. Maps with several value types keep their plain types (#6424)
  • Properties declared in several inline allOf members now carry the constraints of every declaration - When more than one inline allOf member beside a $ref declares the same property, for example {type: string} in one member and {maxLength: 3} in another, the generated field used to take its constraints from the first declaration only. Request and response models under --read-only-write-only-model-type took them from the last one. Now each declaration gets the bounds, multipleOf and pattern of all of them, so a field that was str becomes constr(max_length=3), Annotated[str, Meta(max_length=3)] or Field(None, max_length=3). A subclass that overrides the property also gets the combined constraints, for example min_length=1 plus max_length=3, where it used to have only its own. The field keeps the type, default and description it had before. Positional tuple declarations do not get item counts, and $ref declarations neither receive nor pass on constraints. A new warning is emitted when the declarations have no common value for a keyword. This happens by default in every dialect and output model type, with or without --intersect-allof-keywords and --intersect-combined-schema-keywords, so generated models can now reject payloads they used to accept (#6425)
  • Union member classes of an allOf now redeclare shared properties with the constraints of every declaration - When an allOf holds an anyOf or oneOf and the branch and the other allOf members declare the same property with different bounds, multipleOf or pattern, the generated class for each union member now declares that property itself with all the check keywords combined. Previously the class inherited the field from its first base (Pydantic, dataclasses, TypedDict) or its last base (msgspec), so some constraints were lost. For example, Inline4(Inline1, Inline3) now declares x: conint(ge=0, le=10) | None = None, where it used to inherit only conint(ge=0). Generated models now reject values they used to accept (#6426)
  • Some union member TypedDict classes are now written without base classes - A union member TypedDict whose redeclared key differs from a base's key is now generated as a standalone TypedDict with no bases. For example, class Refs3(TypedDict) replaces class Refs3(Dog, Refs1). Code that relies on the previous class hierarchy or on static assignability to the base types may need updating (#6426)
  • Enum object properties written as a Literal now keep their schema pattern - When an object property declares both enum and pattern and the enum is written as a Literal with its constraints on the field (for example --enum-field-as-literal together with --field-constraints), the generated field now includes the pattern, e.g. inline: Literal['aa', 'b'] | None = Field(None, pattern=...). Before, the pattern was silently dropped at object properties, while root models and const fields already kept it. This affects BaseModel, Annotated and Pydantic dataclass output. Enum values that fail the pattern used to be accepted and are now rejected at validation time. If the Rust regex engine refuses the pattern, it can now also add regex_engine="python-re" to the model config. With --ignore-enum-constraints the output is unchanged, and with default options (enums written as classes) nothing changes (#6428)
  • Constraints beside a typed oneOf or anyOf of consts are now written on its Literal under --field-constraints - When a oneOf or anyOf of const values is rendered as a Literal and its schema declares the single type of those consts (for example "type": "integer" beside anyOf: [{"const": 1}, {"const": 5}] with maximum: 3), the generator now carries the sibling constraint keywords onto the generated field and onto a Literal root model. Before this change they were silently dropped. For example, consts: Literal['a', 'bb', 'ccc'] | None = None becomes consts: Literal['a', 'bb', 'ccc'] | None = Field(None, min_length=2), consts: Literal[1, 5] | None = Field(None, le=3) is now emitted, and a RootModel[Literal['a', 'bb']] gains root: Literal['a', 'bb'] = Field(..., max_length=1). Regenerated models now reject values that earlier generated models accepted. Untyped oneOf or anyOf schemas get no constraints, --filter-literal-values still drops field constraints when it has already removed the rejected values, and default output is unchanged (#6429)
  • msgspec now writes property-count lengths on mapping class types - For msgspec output, when a field's type is a mapping class that msgspec decodes as a dict, such as dict or dict[str, str] written by --type-overrides with --type-overrides-replace-whole-type, the schema's minProperties/maxProperties are now emitted as Meta(min_length=...)/Meta(max_length=...), for example sized: Annotated[dict, Meta(min_length=1)] | UnsetType. Before, they were dropped with a "msgspec.Meta does not take min_length on dict" warning. Generated Structs now reject payloads with too few or too many keys that used to decode, and those warnings no longer appear. OrderedDict and Counter classes still get no lengths, because msgspec refuses them there (#6431)
  • Defaults under --use-unique-items-as-set-by-value are now written as the sets their types write - Array defaults that sit where the final types write a set (the field's own list, or a set type alias inside a list, union, map or tuple position) are now generated as set literals, or as frozenset(...) under --use-generic-container-types, instead of nested lists or mutable sets; for example frozen dataclasses now emit levels: frozenset[int] | None = frozenset({1}) instead of field(default_factory=lambda: {1}), and pydantic emits rows: list[Row] | None = [{1, 2}] where the nested items used to stay lists, so the runtime type of these default values changes (#6440)
  • Frozen dataclasses with frozen set defaults now hash by value - Because a frozen set default is now written as a hashable frozenset and not a mutable set, frozen dataclasses whose fields carry such defaults now count as hashing by value under --use-unique-items-as-set-by-value, so uniqueItems arrays of them change type, for example limits: Sequence[Limits] | None becomes limits: frozenset[Limits] | None, which breaks code that indexes into or relies on the order of those fields (#6440)
  • TypedDict extra_items now writes uniqueItems arrays as sets under --use-unique-items-as-set - When generating typing.TypedDict models with --use-unique-items-as-set (and also --use-unique-items-as-set-by-value), the extra_items type built from an additionalProperties schema now goes through the same uniqueItems-to-set conversion as regular fields. Arrays that declare uniqueItems: true are now written as set[...], or frozenset[...] with --use-generic-container-types, where they used to be list[...] or Sequence[...]. This covers the array itself, anyOf/oneOf members, map values and the inner arrays of nested arrays. For example, extra_items=list[int] becomes extra_items=set[int] and extra_items=dict[str, list[int]] becomes extra_items=dict[str, set[int]]. Arrays whose items cannot be hashed, such as TypedDict items, stay lists. With --reuse-model, an array stays a list when a model merged into it does not declare uniqueItems. Code that treats these extra values as lists (indexing, ordering, keeping duplicates) or type-checks them against list may need updating (#6441)
  • TypedDict extra_items from array type lists now includes the other type list members - When an additionalProperties schema (or another schema read as a lightweight type) declares a type list containing array alongside other types, such as ["array", "string"] or ["array", "integer", "null"], the generated type is now a union like str | list[int] or int | list[int] | None instead of only list[int], matching the type generated for a property with the same schema; regenerated typing.TypedDict output accepts the strings, integers and nulls the schema allows, and code that assumed these values were always lists may need type narrowing to satisfy type checkers (#6442)
  • TypedDict extra_items now written unquoted when the classes it names already exist - When generating typing.TypedDict output, the extra_items= class keyword used to be a string whenever it named a generated class, for example extra_items='list[Item]', extra_items='FlaggedRequest' or extra_items='b.Bar | c.Baz | Near'. It is now written as a plain type expression, for example extra_items=list[Item] or extra_items=b.Bar, whenever every class it names is defined by the time the class statement runs: either declared earlier in the same module or imported from another module. It stays a string only when it names the class itself or a class declared later in the same module. As a result, the runtime __extra_items__ value is the real type instead of a forward reference, which lets Pydantic TypeAdapter resolve these TypedDicts where it used to fail with class-not-fully-defined. Generated files change wherever this applies. Because these unquoted names are now evaluated when the class is defined, the import-cycle check also counts them, so modular TypedDict output with cross-module extra_items in an import cycle may now merge modules into a shared _internal.py (#6443)
  • msgspec unions of differently constrained same-type members now keep their shared constraints - With --output-model-type msgspec.Struct, a union of members of one type that carry different constraints, where no member carries only the constraints they all share, used to be generated with no Meta(...) at all. It is now generated with the constraints every member implies, written once. For example, anyOf: [{type: string, minLength: 2, maxLength: 3}, {type: string, maxLength: 3, pattern: '^a'}] changes from str to Annotated[str, Meta(max_length=3)], anyOf: [{type: integer, minimum: 1, maximum: 5}, {type: integer, minimum: 7}] changes from int to Annotated[int, Meta(ge=1)], and aliases such as TwoBases: TypeAlias = int become TwoBases: TypeAlias = Annotated[int, Meta(le=100)] (list items too, e.g. list[Annotated[int, Meta(le=100)]]). Float members also take into account the int and Decimal members that msgspec decodes as floats. The generated type annotations and aliases are different, and the models now reject payloads they used to accept that every branch of the schema rejects (e.g. "abcd", 0, 101). The dropped-constraint warning for these fields now ends with , and it keeps max_length=3, which every member implies (or whichever constraints are kept). This is on by default and has no opt-out flag (#6450)
  • Required names beside the $ref of a lone allOf member now produce a site subclass under --intersect-combined-schema-keywords - With --intersect-combined-schema-keywords, a field, array item or union branch like allOf: [{$ref: A, required: [id]}] was generated as the referenced model A. Here the required names sit next to the $ref inside the single allOf member, and A accepted objects that were missing them. The required names are now read from inline allOf members as well, so the site is generated as a subclass of A (for example RefSibling(A)) that requires them. The field annotation now names a new class, and payloads such as {} or {"name": "a"} that used to validate are now rejected (#6472)
  • Inline allOf members that exclude null now make the site subclass non-nullable under --intersect-combined-schema-keywords - With --intersect-combined-schema-keywords, take a site subclass built from an allOf that references a nullable model plus an inline member whose type excludes null, such as allOf: [{$ref: Nullable}, {type: object, required: [id]}]. It used to be written as nullable (for example ObjectMember | None) because only the outer schema's own type was checked for null. Inline allOf members are now checked too, so the field is annotated ObjectMember and a null payload is rejected (#6472)
  • Referenced document roots with x-python-import no longer generate an unused empty model - When a $ref points to a whole external document whose root schema declares x-python-import and would generate an empty class (no fields and no root type), that empty class is no longer written next to the import; fields that reference the document keep the imported class. A root that generates a model with fields or a root type, such as a RootModel of a map, is written as in 0.90.0, as is a root used as a base class and the root of the input document. (#6480, #6554)
  • Document roots with x-python-import reached through a pointer no longer generate an unused empty class - When a $ref reaches a file's root through a definition that refers back to it, and the root has x-python-import and would generate an empty class, that empty class is no longer written; fields keep the imported type. A root that generates a model with fields or a root type is written as in 0.90.0. (#6484, #6554)
  • Plain Enum discriminator tags now also hold the raw value under --use-enum-values-in-discriminator - When --use-enum-values-in-discriminator is used and a variant is tagged by a member of a plain Enum (one with no value base, so not StrEnum, IntEnum or the (str, Enum) that --use-subclass-enum writes), the tag field is now written as Literal[PetType.cat, 'cat'] instead of Literal[PetType.cat]. Before, the discriminated union rejected every JSON payload. It now accepts them, but a field parsed from JSON holds the plain value (for example 'cat') instead of the Enum member, and code that type-checks against the old annotation may need updating. Python callers that pass PetType.cat keep working. Enums with a value base keep Literal[PetType.cat], and msgspec output is unchanged (#6495)
  • Discriminator removed when a plain Enum tag value collides with another variant's literal - Under --use-enum-values-in-discriminator, a plain Enum member tag now carries its value too. So when one variant is tagged by a plain Enum member (for example Plain.int_1) and another variant's tag is a literal with the same value (Literal[1]), the union no longer gets Field(None, discriminator='k') and becomes a plain union. The Enum-tagged variant's field is typed as the Enum class (k: Plain) instead of Literal[Plain.int_1] (#6495)
  • Discriminated $ref branches now take the allOf members under --distribute-allof-over-unions - With --distribute-allof-over-unions (and the 20261104 presets that enable it), a $ref branch of an anyOf or oneOf whose target has an OpenAPI discriminator is now expanded into one generated class per subtype, each inheriting from the subtype and carrying the allOf members beside or around the union (for example Owned1(Cat), Owned2(Dog)). Those variants now require the members' properties, such as owner, instead of accepting payloads without them. Classes for later branches are renumbered in union order, so the class previously generated as Owned1(Fish) is now Owned3(Fish). In an allOf that holds such a union, the discriminated subtypes are no longer dropped, which also changes the numbered class names there. Code that imports or references the old numbered class names, or relies on the looser validation, must be updated. Output without the option is unchanged (#6497)
  • Discriminated $ref branch classes beside an allOf are tagged by the schema they name under --distribute-allof-over-unions - With --distribute-allof-over-unions (and the 20261104 presets), the class generated for each $ref branch of a discriminated oneOf that sits beside an allOf used to take the branch's position a...

    Description has been truncated

Bumps [datamodel-code-generator](https://github.com/datamodel-code-generator/datamodel-code-generator) from 0.31.2 to 0.91.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.91.0)

---
updated-dependencies:
- dependency-name: datamodel-code-generator
  dependency-version: 0.91.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 11, 2026

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

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