Skip to content

Bump datamodel-code-generator from 0.82.0 to 0.89.0 - #18

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

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

Conversation

@dependabot

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

Copy link
Copy Markdown

Bumps datamodel-code-generator from 0.82.0 to 0.89.0.

Release notes

Sourced from datamodel-code-generator's releases.

0.89.0

Breaking Changes

Code Generation Changes

  • Large integer constraints are now written as the exact integers from the schema - For integer fields, a multipleOf, exclusiveMinimum or exclusiveMaximum past 2**53 that a float holds exactly but prints as a different decimal (for example 2**62 or 2**64) used to be written as the rounded float decimal, such as multiple_of=4611686018427388000 or gt=4611686018427388000. It is now written as the exact integer, such as multiple_of=4611686018427387904. This applies to Pydantic and msgspec output with default options, to the least common multiple of merged allOf divisors, and to the values --filter-literal-values keeps. Regenerated models may accept or reject different values than before. The msgspec warnings for constraints outside the 64-bit range now show the exact integer too, for example multiple_of=9223372036854775808 instead of multiple_of=9223372036854776000 (#5989)
  • Input directories with dots in their names are now written with underscores - Unless --treat-dot-as-module is set (so in the default mode and with --no-treat-dot-as-module), every dot in an input directory's name now becomes _ in the written package, as already happened for file names. For example, api.v2/ is now written as api_v2/ and complex.directory/ as complex_directory/, and imports of their modules use the new name, such as from .api_v2 import b. Before, the output directory kept its dots, so in the default mode the generated imports pointed at .api.v2, a package that did not exist, and failed. Anyone who commits generated code, or whose scripts, sys.path entries or tooling point at the old dotted directory paths, must update them and remove the stale dotted directories. With --treat-dot-as-module, each dot still becomes a nested package as before. (#6000)
  • Generated clients now prefer the parameterless media type within one essence - When a response, request body or multipart part declares several media types with the same essence, such as application/json;profile=v1 and application/json, and the actual Content-Type matches none of them exactly, the generated client runtime now selects the declaration without parameters (as the generated FastAPI server already does) instead of the first one in declared order, so such a response may now be decoded with a different model than before (#6001)
  • Only HTTP status keys declare responses in generated clients and servers - A key of an operation's responses now declares a response only when it is default, a three-ASCII-digit status from 100 to 599, or a range 1XX to 5XX written with either case of X; other keys such as extension keys like x-note, an empty key, 0204, 099, 1000 or a status with a non-ASCII digit are now ignored by the generated client, the FastAPI server contract, the cache and polling helpers and the wire plan instead of being planned as responses (#6001)
  • Generated clients treat 1XX responses as carrying no content - The client plan now reads a declared 1XX status or the 1XX range as bodyless, as the server plan already did, so content declared under an informational response is no longer decoded by any model in the generated client (#6001)
  • Copied runtime media helpers changed - In the _runtime package copied into every generated client and server, _runtime/model_codecs/media.py no longer exports the MediaOrder alias, most_specific no longer accepts the order keyword, bodyless no longer accepts the informational keyword, and _runtime/client/media.py no longer defines most_specific; code that imported these helpers from a generated package must be updated (#6001)
  • Protocol helpers refuse query names of every field they write - In generated clients, pagers, pollers and resumable uploads now raise ConfigurationError before sending when a call's extra_query, or a client's or view's default_query, names a field the helper writes. That covers a property owned by an exploded form or deepObject object parameter the helper writes (any name when the parameter accepts additional properties) and a member of a form querystring the helper writes. Before, these helpers refused only exact header or query parameter names, so such calls were sent and could override the values the helper wrote (#6008)
  • Default pagination count positions are parsed strictly - When a client or view default sets a count pager's offset or page position, it is now read as a canonical integer. Values such as 040 or 4e1, which were previously accepted as the integer 40, now raise ProtocolDataError (#6008)
  • Root models of array items now keep their item count constraints by default - A root model generated for an array schema that is used as an array item (for example class Pair(RootModel[list[str]])) now writes the item's minItems/maxItems even without --field-constraints. It also writes the maxItems that items: false beside prefixItems implies, e.g. root: list[str] = Field(..., min_length=2) for Pydantic or Annotated[list[str], Meta(max_length=1,min_length=1)] for msgspec. Generated models now reject arrays that are shorter or longer than the schema allows, which they accepted before (#6032)
  • Map properties whose type list includes an array now keep property count constraints - An object property whose type list combines object with array (e.g. ["object", "array", "null"]) and declares minProperties/maxProperties now writes them on the dict field as Field(..., min_length=..., max_length=...), so objects with too few or too many properties are rejected. For pydantic dataclasses the field also moves after the plain required fields, which changes the positional constructor argument order (#6032)
  • Literal enum properties under --field-constraints now carry their declared constraints - With --field-constraints, an object property whose enum is written as a Literal (e.g. with --enum-field-as-literal all) now writes the length, bound and multipleOf keywords declared beside the enum, as a root model already did. For example kind: Literal[1, 2, 3] | None = None becomes kind: Literal[1, 2, 3] | None = Field(None, ge=2). Values that break those keywords are now rejected, unless --filter-literal-values has already narrowed the literal values (#6032)
  • Item counts implied by contains or boolean items no longer apply to fields that cannot hold an array - A property whose type is not an array, such as a string or a mapping, that sits beside contains/minContains or items: false used to receive the implied minItems/maxItems as a length constraint (for example Annotated[str | None, MinLen(min_length=2)] for Pydantic or Annotated[str, Meta(min_length=2)] for msgspec), which rejected values the schema accepts. Such fields are now generated without that length constraint (for example tag: str | None = None). For pydantic_v2.dataclass output the field no longer carries a Field(...) default with the constraint, which can also change the field order of the generated dataclass constructor and affect positional construction (#6037)
  • Output directories whose names are not valid Python identifiers are now renamed - When a multi-file input has a directory whose name Python cannot import (it contains a space or a symbol, or a part starts with a digit), the generated package directory is now sanitized the same way file names are. For example, my dir becomes my_dir, a+b becomes a_b, 3d becomes _3d and 1.0 becomes _1_0, where the old output kept the name (or only turned dots into underscores, giving 1_0). Imports that reach into these directories change to match, e.g. from ._1_0 import version. Directories whose parts are all valid identifiers, including Unicode names such as данные, keep their names. Imports into these directories were syntax errors before, so those cases are now fixed. But a directory that was never imported from outside, or only by its own sibling files, used to produce working modules at the old path, and those modules are now written to a new path. Scripts or importlib calls that use the old output paths must be updated. (#6039)
  • Item counts no longer applied to properties whose schema declares no array type - A property whose schema declares a type without array (for example type: object with array-valued additionalProperties, or type: string retyped with x-python-type: datetime.date) no longer gets the minItems/maxItems that contains, minContains or items: false imply as a length constraint. A map field that used to be generated as Annotated[dict[str, list[str]], Meta(min_length=2)] (msgspec) or with Field(None, min_length=2) (Pydantic) is now a plain dict[str, list[str]], so generated models now accept maps with fewer entries, which the schema also accepts. A string retyped as a date no longer gets a length constraint that failed when the model was imported (#6043)
  • Models named like an additional import keep their name when types only write the import qualified - With --additional-imports, a model whose name matches an imported name is no longer renamed when the module's types write that name only through its module path (for example a type written as ipaddress.IPv4Address alongside --additional-imports ipaddress.IPv4Address and a model titled IPv4Address). The model cannot shadow the import in that case, so it now keeps its schema name instead of the suffixed name it got before. Code that referenced the previously renamed class must switch to the original name (#6043)
  • Enum items of allOf arrays typed as frozen sets are now typed like other arrays - The internal legacy row legacy-all-of-items-not-in-frozen-sets was removed, so an allOf root that holds an array whose Python type is a frozenset (for example through x-python-type: frozenset[str]) with enum items now has its items typed with the generated enum item model, the same way set and list arrays already were. Generated code for such schemas changes from keeping the original item type to something like frozenset[FrozenTagsItem] with an extra item model class. Code that reads the frozen set's elements gets item model instances instead of plain values, and values outside the enum that used to be accepted are now rejected (#6060)
  • Client and server parameter and form text encoding now follows the bound model types - With --generate-client or --generate-server, the wire plans for parameter objects, header values, URL-encoded forms and multipart parts are now built from the types and members of the generated models instead of the JSON types written in the schema. Members that come from allOf branches and referenced schemas are now encoded with their own types rather than as untyped extra string members, and schemas with properties but no type are treated as objects. A parameter whose model binds Any is now written as one scalar value instead of an object of string members (for example a header plan loses shape='object'). A multipart body whose type holds a model through a root model, alias or nullable union now takes its part plans from that model (for example a string member now gets an explicit PartPlan('name', 'string')). Regenerated clients and servers may therefore send and parse these values differently than before. Some schemas that were refused before are now accepted, and some refusal messages point to a different schema location, such as the parameter schema instead of schema/items (#6062)
  • Client and server targets decide multipart parts from the generated model types - The generated client and FastAPI server now decide how each multipart member is sent from the type the models give it, not from the schema keywords as written (the removed legacy-target-binary-format-part, legacy-target-last-branch-member, legacy-target-own-other-properties, legacy-target-located-kinds and legacy-target-type-items-as-text behaviours). A member is a file part only when the models hold its values as raw octets. So a format: binary string with an enum or const is now sent as a text field part, while a format that --type-mappings maps to binary and a nullable binary member (a type list of string and null) are now file parts. A property that several allOf branches declare now follows the parser's merged field instead of the last branch. The other properties of a multipart body are now read through allOf branches and references, so a form closed by a referenced schema with additionalProperties: false no longer takes extra parts. Non-file parts of multipart responses are decoded with the type the response gives the member, such as its response variant. Regenerated clients may need field parts where they passed file parts before, or the reverse (#6064)
  • Binary media typed as string enums are carried as text - A request or response body in a binary media type whose models type its value as an enum or literal of strings is now carried as text read by the model, whether or not the schema writes format: binary. This adds codecs to the generated _generated/model_bindings.py and renumbers the codec_N constants after them (#6064)
  • Models of a submodule hidden by its package are now imported from the submodule directly - When a package's __init__.py binds a submodule's name itself, other generated modules now import that submodule's models by name (from .annotations import Item, from .Warn import Detail) and write Item or Detail in annotations. Before, they imported the module (from . import annotations) and wrote annotations.Item. This applies in two cases: a submodule named annotations hidden by the package's from __future__ import annotations, and a submodule hidden by a class of the same name that the __init__.py defines while also importing from that submodule. The submodule name is no longer bound in those modules, so code that reached it through them, for example api.annotations or pkg.api.Warn, breaks. In most cases the old binding pointed at the __future__ feature or the package's class rather than the submodule, so the old output crashed on import (Pydantic, dataclasses) or left msgspec annotations that could not be resolved. Modules that refer to an enum of the submodule through an enum member literal still import the module. (#6081)
  • GraphQL enum member descriptions are now written under the description options - When --use-field-description or --use-inline-field-description is used with GraphQL input, every enum value that has a description now gets that description written beside its enum member (a multi-line docstring under --use-field-description, an inline docstring under --use-inline-field-description). Previously GraphQL enum members were built as a separate graphql-enum-member field kind that ignored these options, so value descriptions were never written; regenerating with either option now adds member docstrings to GraphQL enum classes. Output without these options is unchanged (#6082)
  • Discriminator mapping keys are now typed by the tag property - A discriminator mapping key for an integer, number or boolean tag property now tags its variant with a value of that type instead of the key text. For example, it is now Literal[1], Literal[-2] or Literal[True] instead of Literal['1'] or Literal['true'], and msgspec writes tag=1 instead of tag='1'. Under --enum-field-as-literal all, an integer enum tag is now Literal[2, 3] instead of Literal['2', '3']. Code that builds or compares these variants using the string tags needs to use the typed values (#6083)
  • Float discriminator mapping keys no longer produce a discriminated union - When mapping keys name float values (such as '1.5' or '0.5'), the union is now written without discriminator=. Its variants keep their Enum or float tag field type instead of Literal['1.5']. With msgspec and --use-msgspec-tagged-unions, unions whose mapping keys name booleans or floats are now left untagged, with no tag_field or tag class options, and the generator emits a "msgspec cannot decode the union" warning (#6083)
  • Variants that inherit a fixed tag from an allOf parent are tagged with that value - A discriminated union variant whose tag property an allOf ancestor fixes (a one-value enum or a const) is now tagged with that value, such as Literal['function'] or Literal['custom'], instead of its schema name (Literal['FunctionCall']). The tag fixed by the nearest ancestor's schema now takes precedence over the mapping keys for such variants (#6083)
  • Discriminator mapping paths through dot-prefixed directories now match their variants - Mapping values such as ./.defs/cat.json, ../.defs/bird.json or ./sub/../.defs/dog.json are now resolved segment by segment, removing dot segments as RFC 3986 describes. The variant is now tagged with its mapping key (for example Literal['c'] or msgspec tag='c') instead of a tag built from its file name such as cat.json (#6083)
  • Integer mapping key shared with another variant's const now yields a plain union - When a mapping key such as '1' names an integer that another variant's const also holds, both variants now share the tag and the union is written without discriminator=. Before, Pydantic output failed at first use with "mapped to multiple choices". For msgspec, the affected Struct loses its tag_field and tag options and declares the tag field as a required field, and the other variant's tag field keeps its Enum type instead of a Literal (#6083)
  • Inherited discriminator tags use the Enum member under --use-enum-values-in-discriminator - Some variants of a discriminated union inherit their tag field from an ancestor two or more levels up, for example NamedCall below FunctionCall below a base whose type field is typed by the Enum FunctionType. The generator used to check only a variant's direct bases for the Enum. It now looks through all ancestors and takes the Enum from the nearest one that declares the field. Such a variant now gets type: Literal[FunctionType.function] instead of a plain string literal like Literal['function'], which matches how direct subclasses were already written. With a plain (non-str) Enum, the variant now accepts the Enum member passed from Python but rejects the raw JSON string "function". Code that builds or validates these variants with the raw string may need updating (#6085)
  • JSON pointer references through YAML maps with integer keys now resolve to the referenced schema - A $ref whose JSON pointer passes through a YAML mapping with unquoted integer keys (for example a pointer like /x-shared/responses/200/content/application~1json/schema, where 200: is loaded by YAML as the integer 200) previously failed to match the key, emitted an unresolved $ref warning and generated Any; the pointer token now matches the integer key, so the generated field or root model uses the actual referenced type (for example Any becomes a constrained str, or a root model now wraps a newly generated BaseModel), which adds new model classes to the output and enforces validation on values that were previously accepted unconditionally (#6100)
  • Discriminator mapping paths that leave and re-enter a directory now tag variants with the mapping key - Some discriminator mapping values use a relative path that leaves the directory of the document that writes them and comes back, for example ../../r/defs/cat.json written in r/sub/main.json or ../r/defs/fish.json written in r/owner.json. These paths are now resolved against that document's directory before they are compared with the variants' schema paths. Before, such a path matched no variant, so the variant's tag was its file name plus a trailing fragment marker, both in the Pydantic Literal tag field and in the msgspec tag= struct argument. Now the variant gets the mapping key, for example pet_type: Literal['c'] or tag='c'. A mapping path whose location is resolved is now matched by that location alone, so it no longer also selects other variants that happen to share its file name or document-relative path. The generated output also changes in the same way when the mapping is declared in a different document than the one that holds the union, for example ./sub/../cat.yaml in b/base.yaml used from a/openapi.yaml. Code that builds, serializes or compares models using the old file-name tag values has to switch to the mapping keys (#6102)
  • Enum member literals from a submodule hidden by its package now import the enum directly - In some setups, a package's __init__.py binds a submodule's name itself, either through from __future__ import annotations for a submodule named annotations or by defining a class with the same name as the submodule. Sibling modules that refer to an enum of such a submodule through an enum member literal (for example with --read-const-beside-enum, --set-default-enum-member or --use-enum-values-in-discriminator) used to keep from . import annotations and write annotations.Item and Literal[annotations.Kind.book]. They now import the models directly with from .annotations import Item, Kind and write Item and Literal[Kind.book], and enum defaults change the same way. The generated module no longer binds the submodule name, so code that reached the submodule through it (for example from pkg.api import annotations) or that depends on the old annotation text must be updated. Depending on import order, the old output could also fail to import. (#6113)
  • Generated API names are now written in NFKC form - With --generate-client and --generate-server, the snake_case and PascalCase names derived from tags and operations (methods, handlers, resources, modules, routers, services and classes) are now normalized to NFKC, the form Python reads identifiers in, so names spelled with non-NFKC characters (for example a Greek letter written with an oxia) are written differently, and a second tag that Python reads as the same name as an earlier one now takes a duplicate suffix such as _1; client resource namespaces and server router name scopes also compare names after NFC normalization, so names that differ only by normalization take a duplicate suffix instead of failing with "Two generated files resolve to the same path" (#6118)
  • Header decoder constants of operations whose names upper-case alike are now distinct - The generated client types module now claims each operation's header decoder constant in its scope, so when two method names upper-case to the same text (such as straße and strasse) the second operation's constant becomes _STRASSE_HEADERS_1 and each typed header accessor decodes with its own operation's decoders instead of sharing one redefined constant (#6118)
  • Non-boolean required values are read as the parser reads them - Client and server generation now coerce the required of a parameter, header or request body the way the model parser's Pydantic objects do, so values such as 'yes', 'true' or 1 mark it required and 'off', 'no' or 0 mark it optional, which can change whether generated client arguments are required and whether the generated server rejects requests that omit them (#6118)
  • Response headers no longer take a location from in - A response Header Object no longer records name or in facts, so a response header that declares a location such as in: query is now decoded as a simple-style header rather than with that location's style (#6118)
  • Cache helpers refuse control characters in Cache-Control quoted strings - The generated client cache helper now parses Cache-Control directive values with the RFC 9110 quoted-string rule instead of the lenient MIME rule, so a response whose directive quotes a control character or DEL is treated as malformed and is no longer stored in the cache (#6118)
  • Deep-copied data types now match their original model by model key - Some passes used to match models by Reference identity and now match them by model key: dropping the restated None of a type whose model already holds null, leaving out the union constraints under --field-constraints, and the msgspec union hints for --enum-field-as-literal all. As a result, a custom parser that replaces a field type with a deep copy now gets the same output as for the original type. For example, a field that names a nullable root model is written as NullableString instead of Optional[NullableString], and keywords that the union members already hold are left out of the field. Default CLI output is unaffected; only Python API users whose custom parsers deep-copy data types see different output (#6130)
  • Null from one position of a closed array now stays inside the container - A closed array written as a single list (prefixItems with items: false) whose positions include a nullable one, and msgspec's single dict for an object map whose pattern values include a nullable one, now put the None inside the container (list[str | None], dict[str, str | int | None]) instead of on the field (list[str] | None, dict[str, str | int] | None). The generated models now accept null items and reject a null array or map, as the schema does. A required field such as nullable_first: list[str | None] no longer accepts None, and msgspec fields lose the outer | None (for example Annotated[list[str] | None, Meta(max_length=2, min_length=2)] | UnsetType becomes Annotated[list[str | None], Meta(max_length=2, min_length=2)] | UnsetType). Code that relies on the old annotations or passes None for these fields may need updating (#6134)
  • Closed prefixItems arrays no longer add the unevaluatedItems type to their item type - An array with prefixItems, items: false and an unevaluatedItems schema used to add the unevaluatedItems type to its item union (for example list[int | str]). Because items evaluates every position after the prefix, unevaluatedItems never applies there, so the item type now holds only the prefix types (list[int]). Payloads such as ["x"], which the schema rejects, are now rejected by the generated model (#6134)
  • Template data names now follow the target Python version - the keys of ConfigDict and msgspec Struct options given through --extra-template-data, the TypedDict extra_items type name from additionalPropertiesType, the GraphQL scalar py_type and the template-data schema validator base class name are now written as names only when the target Python binds them, instead of when the running interpreter does; otherwise an option key is left out, a type name is written as a quoted string, and the schema validator base class name falls back to the default. Output therefore changes when such names use newer Unicode identifier characters and the target differs from the running interpreter (#6140)
  • Unique items as set no longer converts non-list containers - With --use-unique-items-as-set, a uniqueItems array whose type is another container, such as a Sequence given by x-python-type or a tuple, now keeps its type, its item types and its list default. Previously its list default was still turned into a set (for example sequence: Sequence[int] | None = {1, 2}), and lists nested inside such a container were turned into sets (for example Sequence[set[int]] instead of Sequence[list[int]]). Generated annotations and runtime defaults for these fields therefore change (#6141)
  • References whose fragment opens with an encoded slash now resolve as JSON Pointers - A $ref whose fragment begins with the percent-encoded slash %2F or %2f (for example other.json with the fragment %2Fdefinitions/Bar) is now read as the JSON Pointer /definitions/Bar, following RFC 6901, instead of as a plain-name anchor. Before this change, a reference of this form into another document generated a model of Any. It now generates the real type of the referenced schema (for example a RootModel[bool]), so the generated classes and field types change and validation gets stricter. References of this form within the same document or an embedded schema resource used to fail with Unresolved $id reference or Embedded schema resource has no anchor, and now they generate models. (#6165)
  • Array member of a type list with uniqueItems now generated as a set under --use-unique-items-as-set - When a property or root model schema declares a type list that includes array alongside other types (for example "type": ["array", "object"] or "type": ["array", "string", "null"]) and sets uniqueItems: true, the array member of the generated union was previously written as list[T] even with --use-unique-items-as-set. It is now written as set[T], the same way a plain array is written, so dict[str, Any] | list[int] | None becomes dict[str, Any] | set[int] | None, and list defaults on these fields become set literals such as {1, 2}. This applies to Pydantic v2, dataclasses and msgspec output. Code that relies on list behaviour for these fields, such as indexing, ordering or list methods, must be updated. Output without --use-unique-items-as-set does not change (#6166) A type-list array becomes a set only where every model that a duplicate merge (for example --reuse-model or the practical presets) folds into one class declares uniqueItems for it; where a model without uniqueItems is merged in, the field stays list[T] as in 0.88.0, so its duplicates and order are kept (#6197).
  • Patterns the Rust regex engine refuses now select Python's engine - A field whose pattern uses syntax Pydantic's default Rust engine rejects, such as look-around or back-references, is now generated so the module imports: the model sets regex_engine="python-re", and a root model of such a string is written as RootModel[str] with its pattern. In 0.88.0 these modules failed to import. With --use-generic-container-types, a typed extra is written as dict[...], and a Pydantic dataclass carries its own config (#6005)
  • Fields named like a builtin they use now import the builtin under an alias - A model with a field named like a builtin type that the module also uses, such as a field int beside int annotations, now writes from builtins import int as int_aliased and uses the alias. In 0.88.0 the field name hid the builtin and the module failed to import (#5998)
  • Constrained nullable msgspec lists no longer fail on every decode - With msgspec output, a list field with item or length constraints that may be null (written Annotated[list[str] | None, Meta(...)] in 0.88.0) raised TypeError on every decode. It is now written so that decoding works (#6134)
  • Builtin formatter output now parses and reads the same on every Python - --formatters builtin no longer splits escape sequences, f-strings, empty literals or call heads in ways that produced code that did not parse or meant something else, and no longer loops forever on such input (#5997)

... (truncated)

Changelog

Sourced from datamodel-code-generator's changelog.

0.89.0 - 2026-11-04

Breaking Changes

Code Generation Changes

  • Large integer constraints are now written as the exact integers from the schema - For integer fields, a multipleOf, exclusiveMinimum or exclusiveMaximum past 2**53 that a float holds exactly but prints as a different decimal (for example 2**62 or 2**64) used to be written as the rounded float decimal, such as multiple_of=4611686018427388000 or gt=4611686018427388000. It is now written as the exact integer, such as multiple_of=4611686018427387904. This applies to Pydantic and msgspec output with default options, to the least common multiple of merged allOf divisors, and to the values --filter-literal-values keeps. Regenerated models may accept or reject different values than before. The msgspec warnings for constraints outside the 64-bit range now show the exact integer too, for example multiple_of=9223372036854775808 instead of multiple_of=9223372036854776000 (#5989)
  • Input directories with dots in their names are now written with underscores - Unless --treat-dot-as-module is set (so in the default mode and with --no-treat-dot-as-module), every dot in an input directory's name now becomes _ in the written package, as already happened for file names. For example, api.v2/ is now written as api_v2/ and complex.directory/ as complex_directory/, and imports of their modules use the new name, such as from .api_v2 import b. Before, the output directory kept its dots, so in the default mode the generated imports pointed at .api.v2, a package that did not exist, and failed. Anyone who commits generated code, or whose scripts, sys.path entries or tooling point at the old dotted directory paths, must update them and remove the stale dotted directories. With --treat-dot-as-module, each dot still becomes a nested package as before. (#6000)
  • Generated clients now prefer the parameterless media type within one essence - When a response, request body or multipart part declares several media types with the same essence, such as application/json;profile=v1 and application/json, and the actual Content-Type matches none of them exactly, the generated client runtime now selects the declaration without parameters (as the generated FastAPI server already does) instead of the first one in declared order, so such a response may now be decoded with a different model than before (#6001)
  • Only HTTP status keys declare responses in generated clients and servers - A key of an operation's responses now declares a response only when it is default, a three-ASCII-digit status from 100 to 599, or a range 1XX to 5XX written with either case of X; other keys such as extension keys like x-note, an empty key, 0204, 099, 1000 or a status with a non-ASCII digit are now ignored by the generated client, the FastAPI server contract, the cache and polling helpers and the wire plan instead of being planned as responses (#6001)
  • Generated clients treat 1XX responses as carrying no content - The client plan now reads a declared 1XX status or the 1XX range as bodyless, as the server plan already did, so content declared under an informational response is no longer decoded by any model in the generated client (#6001)
  • Copied runtime media helpers changed - In the _runtime package copied into every generated client and server, _runtime/model_codecs/media.py no longer exports the MediaOrder alias, most_specific no longer accepts the order keyword, bodyless no longer accepts the informational keyword, and _runtime/client/media.py no longer defines most_specific; code that imported these helpers from a generated package must be updated (#6001)
  • Protocol helpers refuse query names of every field they write - In generated clients, pagers, pollers and resumable uploads now raise ConfigurationError before sending when a call's extra_query, or a client's or view's default_query, names a field the helper writes. That covers a property owned by an exploded form or deepObject object parameter the helper writes (any name when the parameter accepts additional properties) and a member of a form querystring the helper writes. Before, these helpers refused only exact header or query parameter names, so such calls were sent and could override the values the helper wrote (#6008)
  • Default pagination count positions are parsed strictly - When a client or view default sets a count pager's offset or page position, it is now read as a canonical integer. Values such as 040 or 4e1, which were previously accepted as the integer 40, now raise ProtocolDataError (#6008)
  • Root models of array items now keep their item count constraints by default - A root model generated for an array schema that is used as an array item (for example class Pair(RootModel[list[str]])) now writes the item's minItems/maxItems even without --field-constraints. It also writes the maxItems that items: false beside prefixItems implies, e.g. root: list[str] = Field(..., min_length=2) for Pydantic or Annotated[list[str], Meta(max_length=1,min_length=1)] for msgspec. Generated models now reject arrays that are shorter or longer than the schema allows, which they accepted before (#6032)
  • Map properties whose type list includes an array now keep property count constraints - An object property whose type list combines object with array (e.g. ["object", "array", "null"]) and declares minProperties/maxProperties now writes them on the dict field as Field(..., min_length=..., max_length=...), so objects with too few or too many properties are rejected. For pydantic dataclasses the field also moves after the plain required fields, which changes the positional constructor argument order (#6032)
  • Literal enum properties under --field-constraints now carry their declared constraints - With --field-constraints, an object property whose enum is written as a Literal (e.g. with --enum-field-as-literal all) now writes the length, bound and multipleOf keywords declared beside the enum, as a root model already did. For example kind: Literal[1, 2, 3] | None = None becomes kind: Literal[1, 2, 3] | None = Field(None, ge=2). Values that break those keywords are now rejected, unless --filter-literal-values has already narrowed the literal values (#6032)
  • Item counts implied by contains or boolean items no longer apply to fields that cannot hold an array - A property whose type is not an array, such as a string or a mapping, that sits beside contains/minContains or items: false used to receive the implied minItems/maxItems as a length constraint (for example Annotated[str | None, MinLen(min_length=2)] for Pydantic or Annotated[str, Meta(min_length=2)] for msgspec), which rejected values the schema accepts. Such fields are now generated without that length constraint (for example tag: str | None = None). For pydantic_v2.dataclass output the field no longer carries a Field(...) default with the constraint, which can also change the field order of the generated dataclass constructor and affect positional construction (#6037)
  • Output directories whose names are not valid Python identifiers are now renamed - When a multi-file input has a directory whose name Python cannot import (it contains a space or a symbol, or a part starts with a digit), the generated package directory is now sanitized the same way file names are. For example, my dir becomes my_dir, a+b becomes a_b, 3d becomes _3d and 1.0 becomes _1_0, where the old output kept the name (or only turned dots into underscores, giving 1_0). Imports that reach into these directories change to match, e.g. from ._1_0 import version. Directories whose parts are all valid identifiers, including Unicode names such as данные, keep their names. Imports into these directories were syntax errors before, so those cases are now fixed. But a directory that was never imported from outside, or only by its own sibling files, used to produce working modules at the old path, and those modules are now written to a new path. Scripts or importlib calls that use the old output paths must be updated. (#6039)
  • Item counts no longer applied to properties whose schema declares no array type - A property whose schema declares a type without array (for example type: object with array-valued additionalProperties, or type: string retyped with x-python-type: datetime.date) no longer gets the minItems/maxItems that contains, minContains or items: false imply as a length constraint. A map field that used to be generated as Annotated[dict[str, list[str]], Meta(min_length=2)] (msgspec) or with Field(None, min_length=2) (Pydantic) is now a plain dict[str, list[str]], so generated models now accept maps with fewer entries, which the schema also accepts. A string retyped as a date no longer gets a length constraint that failed when the model was imported (#6043)
  • Models named like an additional import keep their name when types only write the import qualified - With --additional-imports, a model whose name matches an imported name is no longer renamed when the module's types write that name only through its module path (for example a type written as ipaddress.IPv4Address alongside --additional-imports ipaddress.IPv4Address and a model titled IPv4Address). The model cannot shadow the import in that case, so it now keeps its schema name instead of the suffixed name it got before. Code that referenced the previously renamed class must switch to the original name (#6043)
  • Enum items of allOf arrays typed as frozen sets are now typed like other arrays - The internal legacy row legacy-all-of-items-not-in-frozen-sets was removed, so an allOf root that holds an array whose Python type is a frozenset (for example through x-python-type: frozenset[str]) with enum items now has its items typed with the generated enum item model, the same way set and list arrays already were. Generated code for such schemas changes from keeping the original item type to something like frozenset[FrozenTagsItem] with an extra item model class. Code that reads the frozen set's elements gets item model instances instead of plain values, and values outside the enum that used to be accepted are now rejected (#6060)
  • Client and server parameter and form text encoding now follows the bound model types - With --generate-client or --generate-server, the wire plans for parameter objects, header values, URL-encoded forms and multipart parts are now built from the types and members of the generated models instead of the JSON types written in the schema. Members that come from allOf branches and referenced schemas are now encoded with their own types rather than as untyped extra string members, and schemas with properties but no type are treated as objects. A parameter whose model binds Any is now written as one scalar value instead of an object of string members (for example a header plan loses shape='object'). A multipart body whose type holds a model through a root model, alias or nullable union now takes its part plans from that model (for example a string member now gets an explicit PartPlan('name', 'string')). Regenerated clients and servers may therefore send and parse these values differently than before. Some schemas that were refused before are now accepted, and some refusal messages point to a different schema location, such as the parameter schema instead of schema/items (#6062)
  • Client and server targets decide multipart parts from the generated model types - The generated client and FastAPI server now decide how each multipart member is sent from the type the models give it, not from the schema keywords as written (the removed legacy-target-binary-format-part, legacy-target-last-branch-member, legacy-target-own-other-properties, legacy-target-located-kinds and legacy-target-type-items-as-text behaviours). A member is a file part only when the models hold its values as raw octets. So a format: binary string with an enum or const is now sent as a text field part, while a format that --type-mappings maps to binary and a nullable binary member (a type list of string and null) are now file parts. A property that several allOf branches declare now follows the parser's merged field instead of the last branch. The other properties of a multipart body are now read through allOf branches and references, so a form closed by a referenced schema with additionalProperties: false no longer takes extra parts. Non-file parts of multipart responses are decoded with the type the response gives the member, such as its response variant. Regenerated clients may need field parts where they passed file parts before, or the reverse (#6064)
  • Binary media typed as string enums are carried as text - A request or response body in a binary media type whose models type its value as an enum or literal of strings is now carried as text read by the model, whether or not the schema writes format: binary. This adds codecs to the generated _generated/model_bindings.py and renumbers the codec_N constants after them (#6064)
  • Models of a submodule hidden by its package are now imported from the submodule directly - When a package's __init__.py binds a submodule's name itself, other generated modules now import that submodule's models by name (from .annotations import Item, from .Warn import Detail) and write Item or Detail in annotations. Before, they imported the module (from . import annotations) and wrote annotations.Item. This applies in two cases: a submodule named annotations hidden by the package's from __future__ import annotations, and a submodule hidden by a class of the same name that the __init__.py defines while also importing from that submodule. The submodule name is no longer bound in those modules, so code that reached it through them, for example api.annotations or pkg.api.Warn, breaks. In most cases the old binding pointed at the __future__ feature or the package's class rather than the submodule, so the old output crashed on import (Pydantic, dataclasses) or left msgspec annotations that could not be resolved. Modules that refer to an enum of the submodule through an enum member literal still import the module. (#6081)
  • GraphQL enum member descriptions are now written under the description options - When --use-field-description or --use-inline-field-description is used with GraphQL input, every enum value that has a description now gets that description written beside its enum member (a multi-line docstring under --use-field-description, an inline docstring under --use-inline-field-description). Previously GraphQL enum members were built as a separate graphql-enum-member field kind that ignored these options, so value descriptions were never written; regenerating with either option now adds member docstrings to GraphQL enum classes. Output without these options is unchanged (#6082)
  • Discriminator mapping keys are now typed by the tag property - A discriminator mapping key for an integer, number or boolean tag property now tags its variant with a value of that type instead of the key text. For example, it is now Literal[1], Literal[-2] or Literal[True] instead of Literal['1'] or Literal['true'], and msgspec writes tag=1 instead of tag='1'. Under --enum-field-as-literal all, an integer enum tag is now Literal[2, 3] instead of Literal['2', '3']. Code that builds or compares these variants using the string tags needs to use the typed values (#6083)
  • Float discriminator mapping keys no longer produce a discriminated union - When mapping keys name float values (such as '1.5' or '0.5'), the union is now written without discriminator=. Its variants keep their Enum or float tag field type instead of Literal['1.5']. With msgspec and --use-msgspec-tagged-unions, unions whose mapping keys name booleans or floats are now left untagged, with no tag_field or tag class options, and the generator emits a "msgspec cannot decode the union" warning (#6083)
  • Variants that inherit a fixed tag from an allOf parent are tagged with that value - A discriminated union variant whose tag property an allOf ancestor fixes (a one-value enum or a const) is now tagged with that value, such as Literal['function'] or Literal['custom'], instead of its schema name (Literal['FunctionCall']). The tag fixed by the nearest ancestor's schema now takes precedence over the mapping keys for such variants (#6083)
  • Discriminator mapping paths through dot-prefixed directories now match their variants - Mapping values such as ./.defs/cat.json, ../.defs/bird.json or ./sub/../.defs/dog.json are now resolved segment by segment, removing dot segments as RFC 3986 describes. The variant is now tagged with its mapping key (for example Literal['c'] or msgspec tag='c') instead of a tag built from its file name such as cat.json (#6083)
  • Integer mapping key shared with another variant's const now yields a plain union - When a mapping key such as '1' names an integer that another variant's const also holds, both variants now share the tag and the union is written without discriminator=. Before, Pydantic output failed at first use with "mapped to multiple choices". For msgspec, the affected Struct loses its tag_field and tag options and declares the tag field as a required field, and the other variant's tag field keeps its Enum type instead of a Literal (#6083)
  • Inherited discriminator tags use the Enum member under --use-enum-values-in-discriminator - Some variants of a discriminated union inherit their tag field from an ancestor two or more levels up, for example NamedCall below FunctionCall below a base whose type field is typed by the Enum FunctionType. The generator used to check only a variant's direct bases for the Enum. It now looks through all ancestors and takes the Enum from the nearest one that declares the field. Such a variant now gets type: Literal[FunctionType.function] instead of a plain string literal like Literal['function'], which matches how direct subclasses were already written. With a plain (non-str) Enum, the variant now accepts the Enum member passed from Python but rejects the raw JSON string "function". Code that builds or validates these variants with the raw string may need updating (#6085)
  • JSON pointer references through YAML maps with integer keys now resolve to the referenced schema - A $ref whose JSON pointer passes through a YAML mapping with unquoted integer keys (for example a pointer like /x-shared/responses/200/content/application~1json/schema, where 200: is loaded by YAML as the integer 200) previously failed to match the key, emitted an unresolved $ref warning ...

    Description has been truncated

Bumps [datamodel-code-generator](https://github.com/datamodel-code-generator/datamodel-code-generator) from 0.82.0 to 0.89.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.82.0...0.89.0)

---
updated-dependencies:
- dependency-name: datamodel-code-generator
  dependency-version: 0.89.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 9, 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