Releases: koxudaxi/datamodel-code-generator
Releases · koxudaxi/datamodel-code-generator
0.56.1
What's Changed
- Fix
--base-class-mapand--enum-field-as-literal-maplong inline json support by @ilovelinux in #3075 - Prefer CLI input over pyproject url by @koxudaxi in #3083
- Fix relative URL refs with path-only root ids by @koxudaxi in #3085
- tomli was merged under the name tomllib into std library 3.11 by @a-detiste in #3088
- Fix root model reuse collapse by @koxudaxi in #3089
- Docs: describe --keep-model-order as deterministic dependency-aware ordering by @koxudaxi in #3090
New Contributors
- @a-detiste made their first contribution in #3088
Full Changelog: 0.56.0...0.56.1
0.56.0
Breaking Changes
Code Generation Changes
- Generated default field syntax changed - Fields with structured defaults (dicts, lists, model references) now use
Field(default_value, validate_default=True)instead ofdefault_factory=lambda: TypeAdapter(...).validate_python(...)ordefault_factory=lambda: Model.model_validate(...). This produces simpler, more readable code but changes the generated output format. (#3050) - TypeAdapter import removed from generated code - Generated models no longer import
TypeAdapterfrom pydantic sincevalidate_default=Truehandles validation natively. (#3050) - Default value handling for model-referencing fields rewritten - Fields with defaults referencing Pydantic models (BaseModel, RootModel, type aliases) now generate
Field(<raw_value>, validate_default=True)instead ofdefault_factory=lambda: Model.model_validate(...),default_factory=lambda: TypeAdapter(...).validate_python(...), ordefault_factory=lambda: Model(...). Empty collection defaults changed fromdefault_factory=list/default_factory=dicttoField([], validate_default=True)/Field({}, validate_default=True). The generated code is semantically equivalent under Pydantic v2 but textually different, which will break snapshot tests or tooling that matches exact output.pydantic.TypeAdapteris no longer imported in generated code. (#3070) - Default values for model-referencing fields now use
validate_default=Trueinstead ofdefault_factorylambdas - Fields with structured defaults (dicts, lists, or scalars referencing Pydantic models/RootModels) previously generateddefault_factory=lambda: ModelName.model_validate(value)ordefault_factory=lambda: ModelName(value). They now generateField(value, validate_default=True), producing simpler but different output. Empty collection defaults changed fromdefault_factory=list/default_factory=dicttoField([], validate_default=True)/Field({}, validate_default=True). Users who regenerate code will see different output. (#3071)
Before:After:count: CountType | None = Field(default_factory=lambda: CountType(10)) items: dict[str, Item] | None = Field(default_factory=dict, title='Items')
count: CountType | None = Field(10, validate_default=True) items: dict[str, Item] | None = Field({}, title='Items', validate_default=True)
- Default values for fields referencing models now use
validate_default=Trueinstead ofdefault_factory=lambda:- Fields with structured defaults (dicts/lists) that reference Pydantic models previously generateddefault_factory=lambda: Model.model_validate(...)ordefault_factory=lambda: TypeAdapter(Type).validate_python(...)patterns. They now generate the raw default value directly withvalidate_default=True(e.g.,Field({'key': 'val'}, validate_default=True)instead ofField(default_factory=lambda: Model.model_validate({'key': 'val'}))). This changes the generated code output and may affect users who depend on the exact generated code structure, pin generated output in tests, or use custom post-processing. The runtime behavior should be equivalent for Pydantic v2 users. (#3072) TypeAdapterimport removed from generated code - Generated code no longer importspydantic.TypeAdapterfor default value handling. Code that previously usedTypeAdapter(...).validate_python(...)in default factories now uses inline defaults withvalidate_default=True. (#3072)- Integer and boolean discriminator values now supported in generated Literal types - Discriminator fields previously only generated string literal values. They now support
intandbooldiscriminator values (e.g.,Literal[1]instead ofLiteral['1']), which changes generated code for schemas using integer discriminator mappings. (#3072)
API/CLI Changes
ValidatedDefaultandWrappedDefaultclasses removed - These internal classes were exported fromdatamodel_code_generator.model.baseand have been removed. Code importing these types will break:(#3050)# Before (broken) from datamodel_code_generator.model.base import ValidatedDefault, WrappedDefault
SUPPORTS_WRAPPED_DEFAULTandSUPPORTS_VALIDATED_DEFAULTclass variables removed - These flags were removed from theDataModelbase class. Custom model classes that override these variables will see attribute errors. (#3050)- Internal types
ValidatedDefaultandWrappedDefaultremoved - Thedatamodel_code_generator.model._typesmodule was deleted andValidatedDefault/WrappedDefaultare no longer exported fromdatamodel_code_generator.model.base. Code that imports or subclasses these types will break. TheSUPPORTS_WRAPPED_DEFAULTandSUPPORTS_VALIDATED_DEFAULTclass variables were removed fromDataModeland its subclasses; custom model classes referencing these attributes will need updating. (#3070) - Removed
WrappedDefault,ValidatedDefaultclasses andSUPPORTS_WRAPPED_DEFAULT,SUPPORTS_VALIDATED_DEFAULTclass variables - TheWrappedDefaultandValidatedDefaultclasses fromdatamodel_code_generator.model._types(re-exported viadatamodel_code_generator.model.base) have been deleted. TheDataModelclass variablesSUPPORTS_WRAPPED_DEFAULTandSUPPORTS_VALIDATED_DEFAULThave also been removed. Code that imports or references these will break. (#3071) - New
--allow-remote-refs/--no-allow-remote-refsCLI option andallow_remote_refsconfig field - Remote$reffetching over HTTP/HTTPS now emits a deprecation warning by default. Pass--allow-remote-refsto suppress the warning, or--no-allow-remote-refsto block remote fetching entirely. In a future version, remote fetching will be disabled by default. Users relying on remote$refresolution should add--allow-remote-refsto their invocations to avoid the deprecation warning and prepare for the future default change. (#3072) - New
SchemaFetchErrorexception for HTTP fetch failures - Remote schema fetching now raisesSchemaFetchError(instead of propagating rawhttpxexceptions) on HTTP errors, non-2xx status codes, or unexpected HTML responses. Users catching specifichttpxexceptions from remote ref resolution will need to catchSchemaFetchErrorinstead. (#3072)
Error Handling Changes
- Missing local
$refnow raisesErrorinstead ofFileNotFoundError- Previously, when a$refpointed to a non-existent local file, a rawFileNotFoundErrorpropagated to callers. Now it raisesdatamodel_code_generator.Errorwith the message"$ref file not found: <path>". Programmatic users catchingFileNotFoundErrorspecifically will need to catchErrorinstead (#3051) - HTTP fetch failures now raise
SchemaFetchErrorinstead of propagating raw exceptions - HTTP errors (4xx/5xx status codes), unexpected HTML responses, and transport errors (DNS, timeout, connection) that previously resulted in downstream YAML/JSON parse errors or rawhttpxexceptions now raiseSchemaFetchError(a subclass ofError) before parsing is attempted. Users catching specific parse errors orhttpxexceptions for these scenarios will need to update their error handling (#3051) - HTTP fetch errors now raise
SchemaFetchErrorinstead of raw httpx exceptions - Theget_body()function inhttp.pynow catches HTTP errors and raisesSchemaFetchError(a newErrorsubclass) for HTTP status >= 400, network failures, and unexpected HTML responses. Code that caught rawhttpxexceptions from remote schema fetching will need to catchSchemaFetchErrorinstead. (#3071) - Remote
$reffetching now emitsFutureWarningwithout--allow-remote-refs- Fetching remote HTTP/HTTPS$refreferences without explicitly passing--allow-remote-refsnow emits aFutureWarningdeprecation warning. In a future version, remote fetching will be disabled by default. Users relying on implicit remote ref fetching should add--allow-remote-refsto suppress the warning. (#3071) - HTTP fetch errors now raise
SchemaFetchErrorwith validation of response content type - Previously, fetching a remote$refthat returned an HTML error page would silently pass the HTML through as schema content. Now it raisesSchemaFetchErrorif the response hastext/htmlcontent type or a 4xx/5xx status code. This may cause previously-silent failures to become loud errors. (#3072)
Default Behavior Changes
- Implicit remote
$reffetching now emitsFutureWarning- When a$refresolves to an HTTP(S) URL and--allow-remote-refsis not explicitly passed, the tool still fetches the remote reference but emits aFutureWarning. This may cause failures in environments running with-W error(warnings as errors) or strict warning filters. Pass--allow-remote-refsexplicitly to suppress the warning (#3051) - Remote
$reffetching now emits aFutureWarning- When the parser encounters an HTTP/HTTPS$refwithout--allow-remote-refsbeing explicitly set, aFutureWarningis emitted warning that remote fetching will be disabled by default in a future version. Pass--allow-remote-refsto silence the warning, or--no-allow-remote-refsto block remote fetching immediately. (#3070)
Custom Template Update Required
- Type alias templates updated with
fieldsguard - All six type alias templates (TypeAliasAnnotation.jinja2,TypeAliasType.jinja2,TypeStatement.jinja2,UnionTypeAliasAnnotation.jinja2,UnionTypeAliasType.jinja2,UnionTypeStatement.jinja2) now wrap the main body in{% if fields %}...{% else %}blocks that fall back to{{ base_class }}when no fields are present. Users with custom copies of these templates must add the same guard or handle the empty-fields case. (#3070) - Type alias Jinja2 templates now require
fieldsguard andbase_classfallback - The built-in templatesTypeAliasAnnotation.jinja2,TypeAliasType.jinja2, `TypeStatement.jinja2...
0.55.0
Breaking Changes
Dependency Changes
- Pydantic v1 runtime support removed - Pydantic v2 is now required as a runtime dependency. Users running datamodel-code-generator with Pydantic v1 installed must upgrade to Pydantic v2. The previously deprecated v1 compatibility layer has been completely removed. (#3025)
Default Behavior Changes
- Default output model switched from Pydantic v1 to v2 - Running
datamodel-codegenwithout specifying--output-model-typenow generates Pydantic v2 models (pydantic_v2.BaseModel) instead of Pydantic v1 models (pydantic.BaseModel). Users who depend on the previous default behavior must now explicitly specify--output-model-type pydantic.BaseModelto continue generating Pydantic v1 compatible code. (#3029)
Code Generation Changes
- Generated model syntax changed for default output - The default generated code now uses Pydantic v2 syntax including
RootModelinstead of__root__fields, native union syntax (str | None) instead ofOptional[str], and Pydantic v2 validator/serializer decorators. Existing code that consumes generated models may need updates to work with the new Pydantic v2 output format. (#3029) - Pydantic v1 output support removed - The
pydantic.BaseModeloutput model type has been completely removed. Generated code now only supports Pydantic v2 patterns includingRootModelinstead of__root__,model_rebuild()instead ofupdate_forward_refs(), andmodel_configinstead ofclass Config. Users generating Pydantic v1 models must migrate to v2 output. (#3031)
Custom Template Update Required
- Pydantic v1 templates removed - The following Jinja2 templates have been deleted and users with custom templates extending them must migrate to v2 equivalents:
pydantic/BaseModel.jinja2→ usepydantic_v2/BaseModel.jinja2pydantic/BaseModel_root.jinja2→ usepydantic_v2/RootModel.jinja2pydantic/Config.jinja2→ removed (v2 usesmodel_configdict)
(#3031)
API/CLI Changes
--output-model-type pydantic.BaseModelremoved - Thepydantic.BaseModelvalue for--output-model-typeis no longer valid. Usepydantic_v2.BaseModelinstead (now the default). (#3031)- Pydantic v1 compatibility utilities removed from Python API - The following functions were removed from
datamodel_code_generator.util:model_dump(),model_validate(),get_fields_set(),model_copy(). Use Pydantic v2 methods directly (obj.model_dump(),cls.model_validate(), etc.). (#3031) datamodel_code_generator.model.pydanticmodule removed - The entire Pydantic v1 model module includingBaseModel,CustomRootType,DataModelField,DataTypeManager, anddump_resolve_reference_actionhas been removed. Usedatamodel_code_generator.model.pydantic_v2instead. (#3031)- Pydantic v2 now required at runtime - The minimum pydantic dependency changed from
pydantic>=1.5topydantic>=2,<3. Users with pydantic v1 installed must upgrade to pydantic v2 before using datamodel-code-generator (#3027) - Removed internal pydantic compatibility utilities - The following functions/classes were removed from
datamodel_code_generator.util:get_pydantic_version(),is_pydantic_v2(),model_validator(),field_validator(), andConfigDict. Users who imported these internal utilities directly must update their code to use pydantic's native APIs (#3027) - Removed
datamodel_code_generator.pydantic_patchmodule - The entire pydantic compatibility patching module was removed. Any code importing from this module will fail (#3027) - Removed
packagingdependency - Thepackaginglibrary is no longer a dependency. Code that relied on it being transitively available should add it explicitly (#3027)
What's Changed
- Extract shared pydantic base module for v2/dataclass/msgspec by @koxudaxi in #3022
- Remove pydantic v1 runtime compat layer by @koxudaxi in #3025
- test: make integration helpers explicit about output model by @koxudaxi in #3028
- feat: switch default output model to pydantic v2 by @koxudaxi in #3029
- test: align v2 parser baselines before v1 output removal by @koxudaxi in #3032
- feat: remove pydantic v1 output support by @koxudaxi in #3031
- docs: remove pydantic v1 references by @koxudaxi in #3030
- Remove pydantic v1 runtime compat shims and update dependencies by @koxudaxi in #3027
- docs: remove final pydantic v1 traces by @koxudaxi in #3036
- Fix all-export generation for hyphenated directories by @an5t in #3033
- Fix exact imports for reused tree-scope models by @koxudaxi in #3042
- fix #3034 and #3035 by introducing
ValidatedDefaultby @keyz in #3040 - Avoid TYPE_CHECKING imports for Ruff modular output by @koxudaxi in #3043
- Add deprecated decorators for dataclass output by @koxudaxi in #3044
- Fix for #3045 by @ashipilov in #3046
- Refactor generate tests to use assertion helper by @koxudaxi in #3047
New Contributors
- @an5t made their first contribution in #3033
- @keyz made their first contribution in #3040
- @ashipilov made their first contribution in #3046
Full Changelog: 0.54.1...0.55.0
0.54.1
What's Changed
- Add dismissible announce bar to docs site by @koxudaxi in #3004
- docs: update maintainer announcement to reflect open to work status by @koxudaxi in #3007
- Support isort 8 by @cjwatson in #3011
- Fix --allow-population-by-field-name for pydantic v2 dataclass output by @butvinm in #3013
- Support
--use-annotatedand--use-non-positive-negative-number-constrained-typesby @torarvid in #3015 - Skip default_factory wrapping for non-callable type aliases by @butvinm in #3012
- Fix incorrect relative imports with --use-exact-imports and --collapse-root-models by @koxudaxi in #3020
- feat: Add --external-ref-mapping to import from external packages instead of generating by @matssun in #3006
- Build(deps): Bump cryptography from 46.0.3 to 46.0.5 by @dependabot[bot] in #3016
- Build(deps): Bump urllib3 from 2.6.2 to 2.6.3 by @dependabot[bot] in #3017
New Contributors
Full Changelog: 0.54.0...0.54.1
0.54.0
Breaking Changes
Code Generation Changes
- Enum member names from oneOf/anyOf const constructs now use
titlefield when provided - Previously, when creating enums fromoneOf/anyOfconstructs withconstvalues, thetitlefield was incorrectly ignored and enum member names were generated using the pattern{type}_{value}(e.g.,integer_200). Now, when atitleis specified, it is correctly used as the enum member name (e.g.,OKinstead ofinteger_200). Users who have code depending on the previously generated enum member names will need to update their references. (#2975)
Before:After:class StatusCode(IntEnum): integer_200 = 200 integer_404 = 404 integer_500 = 500
class StatusCode(IntEnum): OK = 200 Not_Found = 404 Server_Error = 500
- Field names matching Python builtins are now automatically sanitized - When a field name matches a Python builtin type AND the field's type annotation uses that same builtin (e.g.,
int: int,list: list[str],dict: dict[str, Any]), the field is now renamed with a trailing underscore (e.g.,int_) and an alias is added to preserve the original JSON field name. This prevents Python syntax issues and shadowing of builtin types. Previously, such fields were generated as-is (e.g.,int: int | None = None), which could cause code that shadows Python builtins. After this change, the same field becomesint_: int | None = Field(None, alias='int'). This affects fields named:int,float,bool,str,bytes,list,dict,set,frozenset,tuple, and other Python builtins when their type annotation uses the matching builtin type. (#2968) - $ref with non-standard metadata fields no longer triggers schema merging - Previously, when a
$refwas combined with non-standard fields likemarkdownDescription,if,then,else, or other extras not in the whitelist, the generator would merge schemas and potentially create duplicate models (e.g.,UserWithExtraalongsideUser). Now, only whitelisted schema-affecting extras (currently justconst) trigger merging. This means:- Fewer merged/duplicate models will be generated
- References are preserved directly instead of being expanded
- Field types may change from inline merged types to direct references
Example schema:
Before: Could generate a mergedproperties: user: $ref: "#/definitions/User" nullable: true markdownDescription: "A user object"
UserWithMarkdownDescriptionmodel
After: Directly usesUser | Nonereference (#2993) - Enum member names no longer get underscore suffix with
--capitalise-enum-members- Previously, enum values likereplace,count,indexwould generateREPLACE_,COUNT_,INDEX_when using--capitalise-enum-members. Now they correctly generateREPLACE,COUNT,INDEX. The underscore suffix is only added when--use-subclass-enumis also used AND the lowercase name conflicts with builtin type methods. Users relying on the previous naming (e.g., referencingMyEnum.REPLACE_in code) will need to update to use the new names without trailing underscores. (#2999) - Fields using
$refwith inline keywords now include merged metadata - When a schema property uses$refalongside additional keywords (e.g.,const,enum,readOnly, constraints), the generator now correctly merges metadata (description, title, constraints, defaults, readonly/writeOnly) from the referenced schema into the field definition. Previously, this metadata was lost. For example, a field liketype: Typemay now becometype: Type = Field(..., description='Type of this object.', title='type')when the referenced schema includes those attributes. This also affectsadditionalPropertiesand OpenAPI parameter schemas. (#2997)
What's Changed
- Refactor ruff check+format to use sequential subprocess calls by @koxudaxi in #2967
- Fix title ignored when creating enums from merging
allOf's oranyOf's objects by @ilovelinux in #2975 - Fix aliased imports not applied to base classes and non-matching fields by @koxudaxi in #2981
- Fix handling of falsy default values for enums in set-default-enum-member option by @kkinugasa in #2977
- Fix use_union_operator with Python builtin type field names by @koxudaxi in #2968
- Support $recursiveRef/$dynamicRef in JSON Schema and OpenAPI by @koxudaxi in #2982
- Address review feedback for recursive/dynamic ref support by @koxudaxi in #2985
- Fix RecursionError in _merge_ref_with_schema for circular $ref by @koxudaxi in #2983
- Fix missing Field import with multiple aliases on required fields by @koxudaxi in #2992
- Fix patternProperties/propertyNames key constraints lost with field_constraints by @koxudaxi in #2994
- Fix type loss when $ref is used with non-standard metadata fields by @koxudaxi in #2993
- Fix missing | None for nullable enum literals in TypedDict by @koxudaxi in #2991
- Fix exact imports with module/class name collision by @koxudaxi in #2998
- Fix extra underscore on enum members like replace with --capitalise-enum-members by @koxudaxi in #2999
- Fix merged result in parse_item not passed back to parse_object_fields by @koxudaxi in #2997
- Fix codespeed python version by @koxudaxi in #3000
- Fix incorrect relative imports with --use-exact-imports and --collapse-root-models by @koxudaxi in #2996
New Contributors
- @kkinugasa made their first contribution in #2977
Full Changelog: 0.53.0...0.54.0
0.53.0
Breaking Changes
Custom Template Update Required
- Parser subclass signature change - The
Parserbase class now requires two generic type parameters:Parser[ParserConfigT, SchemaFeaturesT]instead of justParser[ParserConfigT]. Custom parser subclasses must be updated to include the second type parameter. (#2929)# Before class MyCustomParser(Parser["MyParserConfig"]): ... # After class MyCustomParser(Parser["MyParserConfig", "JsonSchemaFeatures"]): ...
- New abstract
schema_featuresproperty required - Custom parser subclasses must now implement theschema_featuresabstract property that returns aJsonSchemaFeatures(or subclass) instance. (#2929)from functools import cached_property from datamodel_code_generator.parser.schema_version import JsonSchemaFeatures from datamodel_code_generator.enums import JsonSchemaVersion class MyCustomParser(Parser["MyParserConfig", "JsonSchemaFeatures"]): @cached_property def schema_features(self) -> JsonSchemaFeatures: return JsonSchemaFeatures.from_version(JsonSchemaVersion.Draft202012)
- Parser
_create_default_configrefactored to use class variable - Subclasses that override_create_default_configshould now set the_config_class_nameclass variable instead. The base implementation uses this variable to dynamically instantiate the correct config class. (#2929)# Before @classmethod def _create_default_config(cls, options: MyConfigDict) -> MyParserConfig: # custom implementation... # After _config_class_name: ClassVar[str] = "MyParserConfig" # No need to override _create_default_config if using standard config creation
- Template condition for default values changed - If you use custom Jinja2 templates based on
BaseModel_root.jinja2orRootModel.jinja2, the condition for including default values has changed fromfield.requiredto(field.required and not field.has_default). Update your custom templates if you override these files. (#2960)
Code Generation Changes
- RootModel default values now included in generated code - Previously, default values defined in JSON Schema or OpenAPI specifications for root models were not being applied to the generated Pydantic code. Now these defaults are correctly included. For example, a schema defining a root model with
default: 1will generate__root__: int = 1(Pydantic v1) orroot: int = 1(Pydantic v2) instead of just__root__: intorroot: int. This may affect code that relied on the previous behavior where RootModel fields had no default values. (#2960) - Required fields with list defaults now use
default_factory- Previously, required fields with list-type defaults (like__root__: list[ID] = ['abc', 'efg']) were generated with direct list assignments. Now they correctly useField(default_factory=lambda: ...)which follows Python best practices for mutable defaults. This changes the structure of generated code for root models and similar patterns with list defaults. (#2958)
Before:After:class Family(BaseModel): __root__: list[ID] = ['abc', 'efg']
class Family(BaseModel): __root__: list[ID] = Field( default_factory=lambda: [ID.parse_obj(v) for v in ['abc', 'efg']] )
What's Changed
- Separate pytest-benchmark into dedicated benchmark dependency group by @koxudaxi in #2937
- Support ClassVar for Pydantic v2 by @ubaumann in #2920
- Add schema version detection and feature flags by @koxudaxi in #2924
- Fix MRO ordering for multiple inheritance in GraphQL and JSON Schema/OpenAPI by @koxudaxi in #2941
- Add schema_features property to parsers for version detection by @koxudaxi in #2929
- Fix $ref handling in request-response mode for readOnly/writeOnly schemas by @koxudaxi in #2942
- Ensure codecov upload runs even when coverage check fails by @koxudaxi in #2944
- Add FeatureMetadata to schema feature classes for doc generation by @koxudaxi in #2945
- Add schema-docs auto-generation with pre-commit and CI by @koxudaxi in #2949
- Add comprehensive feature metadata to schema version dataclasses by @koxudaxi in #2946
- fix: move UnionMode import outside TYPE_CHECKING for Pydantic runtime… by @phil65 in #2950
- Fix IndexError when using --reuse-scope=tree with single file output by @koxudaxi in #2954
- Add --use-closed-typed-dict option to control PEP 728 TypedDict generation by @koxudaxi in #2956
- Fix RootModel default value not being applied by @koxudaxi in #2960
- Fix required list fields ignoring empty default values by @koxudaxi in #2958
- Add GenerateConfig lazy import from top-level module by @koxudaxi in #2961
- Fix allOf array property merging to preserve child $ref by @koxudaxi in #2962
- Fix array RootModel default value handling in parser by @koxudaxi in #2963
- Fix bug in handling of graphql empty list defaults by @rpmcginty in #2948
New Contributors
Full Changelog: 0.52.2...0.53.0
0.52.2
What's Changed
- Add support for multiple base classes in base_class_map and customBasePath by @koxudaxi in #2916
- Add hash to Pydantic v2 models used in sets by @koxudaxi in #2918
- fix: Handle class name prefix correctly in GraphQL parser by @siminn-arnorgj in #2926
- Add TypedDict closed and extra_items support (PEP 728) by @koxudaxi in #2922
- Fix release-draft workflow to use pull_request_target and increase max_turns to 50 by @koxudaxi in #2930
- Migrate from pyright to ty type checker by @koxudaxi in #2928
- Fix URL port handling in get_url_path_parts by @koxudaxi in #2933
Full Changelog: 0.52.1...0.52.2
0.52.1
What's Changed
- Add --validators option for Pydantic v2 field validators by @koxudaxi in #2906
- Add dynamic model generation support by @koxudaxi in #2901
- Sync zensical.toml nav with docs directory by @koxudaxi in #2908
- Add deprecation warning for default output-model-type by @koxudaxi in #2910
- Add deprecation warning and explicit --output-model-type to docs by @koxudaxi in #2911
- Add llms.txt generator for LLM-friendly documentation by @koxudaxi in #2912
- Move coverage fail_under check to combined coverage environment by @koxudaxi in #2909
- Fix YAML scientific notation parsing as float by @koxudaxi in #2913
- Add deprecated field support for Pydantic v2 by @koxudaxi in #2915
- Add deprecation warning for Pydantic v2 without --use-annotated by @koxudaxi in #2914
Full Changelog: 0.52.0...0.52.1
0.52.0
Breaking Changes
Code Generation Changes
- Union fields with titles now wrapped in named models when
--use-title-as-nameis enabled - Previously, union-typed fields with atitlewere generated as inline union types (e.g.,TypeA | TypeB | TypeC | None). Now they generate a separate wrapper model using the title name, and the field references this wrapper type (e.g.,ProcessingStatusUnionTitle | None). This affects code that directly accesses union field values, as they now need to access the.rootattribute (Pydantic v2) or.__root__(Pydantic v1) of the wrapper model. (#2889)
Before:After:class ProcessingTaskTitle(BaseModel): processing_status_union: ( ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle | None ) = Field('COMPLETED', title='Processing Status Union Title')
class ProcessingStatusUnionTitle(BaseModel): __root__: ( ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle ) = Field(..., title='Processing Status Union Title') class ProcessingTaskTitle(BaseModel): processing_status_union: ProcessingStatusUnionTitle | None = Field( default_factory=lambda: ProcessingStatusUnionTitle.parse_obj('COMPLETED'), title='Processing Status Union Title', )
- Inline types with titles now generate named type aliases when
--use-title-as-nameis enabled - Arrays, dicts, enums-as-literals, and oneOf/anyOf unions that have atitlein the schema now generate named type aliases or RootModel classes instead of being inlined. This improves readability but changes the generated type structure. For TypedDict output, generatestype MyArrayName = list[str]. For Pydantic output, generatesclass MyArrayName(RootModel[list[str]]). (#2889) - Default value handling changed for wrapped union fields - Fields that previously had simple default values now use
default_factorywith a lambda that callsparse_obj()(Pydantic v1) ormodel_validate()(Pydantic v2) to construct the wrapper model. Code that introspects field defaults will see a factory function instead of a direct value. (#2889) - Different output for
$refwithnullable: true- When a JSON Schema property has a$refcombined with onlynullable: true(and optionally metadata liketitle/description), the generator now uses the referenced type directly withOptionalannotation instead of creating a new merged model. For example, a schema with multiple properties referencingUserwithnullable: truewill now generateuser_a: User | Noneinstead of creating separateUserA,UserBmodel classes. This is a bug fix that reduces redundant model generation, but existing code that depends on the previously generated class names will break. (#2890)
Before:After:class UserA(BaseModel): name: str class UserB(BaseModel): name: str class Model(BaseModel): user_a: UserA | None = None user_b: UserB | None = None
class User(BaseModel): name: str class Model(BaseModel): user_a: User | None = None user_b: User | None = None
- Type alias generation expanded for
--use-title-as-name- When using--use-title-as-name, the generator now creates type aliases for additional cases: nested array items with titles, additionalProperties values with titles, oneOf/anyOf branches with titles, patternProperties, propertyNames, and primitive types with titles. Previously these were inlined; now they generate named type aliases. This is a bug fix per #2887, but changes generated output for schemas with titles on nested elements. (#2891) - Title no longer inherited in combined schemas - In anyOf/oneOf/allOf schemas, the parent schema's
titleis now excluded when merging with child schemas. This prevents unintended title inheritance that could affect model naming when--use-title-as-nameis enabled. (#2891) allOfwith single$refno longer creates wrapper class - When a schema property usesallOfwith only a single$refand no additional properties, the generator now directly references the target type instead of creating an unnecessary wrapper class. This may affect code that depends on the previously generated wrapper class names or structure. For example, a property defined asallOf: [$ref: '#/components/schemas/ACHClass']will now generateach_class: ACHClass | Noneinstead of creating an intermediate wrapper type. (#2902)
What's Changed
- Add ULID and Email format documentation by @koxudaxi in #2886
- Add --class-name-prefix, --class-name-suffix, and --class-name-affix-scope options by @koxudaxi in #2885
- Use class-name-suffix for parser config TypedDicts by @koxudaxi in #2888
- Create type aliases for inline types with title when use-title-as-name is enabled by @koxudaxi in #2889
- Fix duplicate model generation for $ref with nullable by @koxudaxi in #2890
- Create type aliases for nested elements with titles when use-title-as-name is enabled by @koxudaxi in #2891
- Clarify --aliases help text to explain schema field becomes Pydantic alias by @koxudaxi in #2892
- Document external library import use case for --type-overrides by @koxudaxi in #2893
- Add documentation for reducing duplicate field types by @koxudaxi in #2896
- Add FutureWarning for upcoming ruff default formatters by @koxudaxi in #2895
- Add --openapi-include-paths option for path-based model filtering by @koxudaxi in #2894
- Add --graphql-no-typename option to exclude typename field by @koxudaxi in #2899
- Add --default-values CLI option for overriding field defaults by @koxudaxi in #2897
- Fix allOf with single ref creating unnecessary wrapper class by @koxudaxi in #2902
- Fix --reuse-model --collapse-reuse-models to deduplicate identical inline definitions by @koxudaxi in #2903
- Add --use-serialization-alias option for Pydantic v2 by @koxudaxi in #2905
- Fix Pydantic v2 discriminated unions in array fields by @koxudaxi in #2907
Full Changelog: 0.51.0...0.52.0
0.51.0
Breaking Changes
Code Generation Changes
- Different output when using
--input-modelwith Set, FrozenSet, Mapping, or Sequence types - When using--input-modelto convert Pydantic models or dataclasses, types that were previously converted tolistordictare now preserved as their original Python types. For example, a field typed asSet[str]now generatesset[str]instead oflist[str],FrozenSet[T]generatesfrozenset[T],Mapping[K, V]generatesMapping[K, V]instead ofdict[K, V], andSequence[T]generatesSequence[T]instead oflist[T]. This may cause type checking differences or runtime behavior changes if your code depended on the previous output types. (#2837) - allOf multi-ref with property overrides now preserves inheritance - Schemas using
allOfwith multiple$refitems where the child schema also defines properties that override parent properties will now generate classes with multiple inheritance (e.g.,class Person(Thing, Location)) instead of a flattened single class with all properties merged inline. Previously, child property overrides were incorrectly treated as conflicts, triggering schema merging. Users relying on the flattened output may need to adjust their code. (#2838)
Before:After:class Person(BaseModel): type: str | None = 'playground:Person' name: constr(min_length=1) | None = None address: constr(min_length=5) age: int | None = None
class Thing(BaseModel): type: str name: constr(min_length=1) class Location(BaseModel): address: constr(min_length=5) class Person(Thing, Location): type: str | None = 'playground:Person' name: constr(min_length=1) | None = None age: int | None = None
- Ruff unsafe fixes now applied automatically - When using the
ruff-checkformatter, the--unsafe-fixesflag is now passed to ruff, which enables fixes that may change code behavior in potentially incorrect ways. This includes removing unused imports that might have side effects, removing unused variables that could affect debugging, and other transformations ruff considers "unsafe". Users who relied on the previous conservative safe-only fix behavior may see different generated code output. To restore the previous behavior, users can configure ruff viapyproject.tomlorruff.tomlto disable specific unsafe rules. (#2847) - Type aliases now generate as class inheritance - When using
--reuse-model(Pydantic v2 only), models that would previously generate as type aliases (ChildModel = ParentModel) now generate as explicit subclasses (class ChildModel(ParentModel): pass). This change improves type checker compatibility and maintains proper type identity, but may affect code that relied on type alias semantics or compared types directly. (#2853)
Before:After:ArmLeft = ArmRight
class ArmLeft(ArmRight): pass
- Fields with
constvalues in anyOf/oneOf now generateLiteraltypes instead of inferred base types - Previously, aconstvalue like"MODE_2D"in an anyOf/oneOf schema would generatestrtype. Now it generatesLiteral["MODE_2D"]. This change affects type hints in generated models and may require updates to code that type-checks against the generated output. For example:This is a bug fix that makes the generated code more type-safe, but downstream code performing type comparisons or using# Before (v0.x) map_view_mode: str = Field("MODE_2D", alias="mapViewMode", const=True) apiVersion: str = Field('v1', const=True) # After (this PR) map_view_mode: Literal["MODE_2D"] = Field("MODE_2D", alias="mapViewMode", const=True) apiVersion: Literal['v1'] = Field('v1', const=True)
isinstance(field, str)checks may need adjustment. (#2864)
Custom Template Update Required
- New DataType flags available for custom templates - Three new boolean flags have been added to the
DataTypeclass:is_frozen_set,is_mapping, andis_sequence. Custom Jinja2 templates that inspect DataType flags may need to be updated to handle these new type variations if they contain logic that depends on exhaustive type flag checks. (#2837) - Pydantic v2 BaseModel.jinja2 template structure changed - If you have a custom template that extends or modifies the default
pydantic_v2/BaseModel.jinja2template, you need to update it. The conditional block that generated type aliases ({% if base_class != "BaseModel" and ... %}{{ class_name }} = {{ base_class }}{% else %}...{% endif %}) has been removed. Templates should now always generate class declarations. (#2853)
Default Behavior Changes
--input-model-ref-strategy reuse-foreignbehavior changed - Previously, this strategy compared the source type family against the input model's family (e.g., if input was Pydantic, any non-Pydantic type like dataclass was considered "foreign" and reused). Now it compares against the output model's family. This means types that were previously imported/reused may now be regenerated, and vice versa. For example, when converting a Pydantic model containing a dataclass to TypedDict output, the dataclass was previously imported (it was "foreign" to Pydantic input), but now it will be regenerated (it's not the same family as TypedDict output). Enums are always reused regardless of output type. (#2854)
API/CLI Changes
- Mixing config and keyword arguments now raises ValueError - Previously,
generate()allowed passing both aconfigobject and individual keyword arguments, with keyword arguments overriding config values. Now, providing both raisesValueError: "Cannot specify both 'config' and keyword arguments. Use one or the other."Users must choose one approach: either pass aGenerateConfigobject or use keyword arguments, but not both. (#2874)# Before (worked): keyword args overrode config values generate(input_=schema, config=config, output=some_path) # After (raises ValueError): must use one or the other # Option 1: Use config only (include output in config) config = GenerateConfig(output=some_path, ...) generate(input_=schema, config=config) # Option 2: Use keyword args only generate(input_=schema, output=some_path, ...)
- Parser signature simplified to config + options pattern -
Parser.__init__,JsonSchemaParser.__init__,OpenAPIParser.__init__, andGraphQLParser.__init__now accept either aconfig: ParserConfigobject OR keyword arguments via**options: Unpack[ParserConfigDict], but not both simultaneously. Passing both raises aValueError. Existing code using only keyword arguments continues to work unchanged. (#2877)# Before: Could potentially mix config with kwargs (undefined behavior) parser = JsonSchemaParser(source="{}", config=some_config, field_constraints=True) # After: Raises ValueError - must use one approach or the other parser = JsonSchemaParser(source="{}", config=some_config) # Use config object # OR parser = JsonSchemaParser(source="{}", field_constraints=True) # Use keyword args
- Subclass compatibility - Code that subclasses
Parser,JsonSchemaParser,OpenAPIParser, orGraphQLParsermay need updates if they override__init__and callsuper().__init__()with explicit parameter lists. The new signature uses**options: Unpack[ParserConfigDict]instead of explicit parameters. (#2877) Config.input_modeltype changed fromstrtolist[str]- Theinput_modelfield in theConfigclass now stores a list of strings instead of a single string. While backward compatibility is maintained when setting the value (single strings are automatically coerced to lists), code that readsconfig.input_modelwill now receive alist[str]instead ofstr | None. Users who programmatically access this field should update their code to handle the list type. (#2881)# Before if config.input_model: process_model(config.input_model) # config.input_model was str # After if config.input_model: for model in config.input_model: # config.input_model is now list[str] process_model(model)
What's Changed
- Add public API signature baselines by @koxudaxi in #2832
- Add deprecation warning for Pydantic v1 runtime by @koxudaxi in #2833
- Fix --use-generic-container-types documentation by @koxudaxi in #2835
- Add extends support for profile inheritance by @koxudaxi in #2834
- Fix CLI option docstrings and add missing tests by @koxudaxi in #2836
- Preserve Python types (Set, Mapping, Sequence) in --input-model by @koxudaxi in #2837
- Replace docstring with option_description in cli_doc marker by @koxudaxi in #2839
- Fix allOf multi-ref to preserve inheritance with property overrides by @koxudaxi in #2838
- Fix x-python-type for Optional container types in anyOf schemas by @koxudaxi in #2840
- Support incompatible Python types in x-python-type extension by @koxudaxi in #2841
- Fix nested type imports in x-python-type override by @koxudaxi in #2842
- Fix deep hierarchy type inheritance in allOf property overrides by @koxudaxi in #2843
- Fix CLI doc option...