Skip to content

Releases: koxudaxi/datamodel-code-generator

0.54.0

14 Feb 16:17
2ea6244

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • Enum member names from oneOf/anyOf const constructs now use title field when provided - Previously, when creating enums from oneOf/anyOf constructs with const values, the title field was incorrectly ignored and enum member names were generated using the pattern {type}_{value} (e.g., integer_200). Now, when a title is specified, it is correctly used as the enum member name (e.g., OK instead of integer_200). Users who have code depending on the previously generated enum member names will need to update their references. (#2975)
    Before:
    class StatusCode(IntEnum):
        integer_200 = 200
        integer_404 = 404
        integer_500 = 500
    After:
    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 becomes int_: 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 $ref was combined with non-standard fields like markdownDescription, if, then, else, or other extras not in the whitelist, the generator would merge schemas and potentially create duplicate models (e.g., UserWithExtra alongside User). Now, only whitelisted schema-affecting extras (currently just const) 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:
    properties:
      user:
        $ref: "#/definitions/User"
        nullable: true
        markdownDescription: "A user object"
    Before: Could generate a merged UserWithMarkdownDescription model
    After: Directly uses User | None reference (#2993)
  • Enum member names no longer get underscore suffix with --capitalise-enum-members - Previously, enum values like replace, count, index would generate REPLACE_, COUNT_, INDEX_ when using --capitalise-enum-members. Now they correctly generate REPLACE, COUNT, INDEX. The underscore suffix is only added when --use-subclass-enum is also used AND the lowercase name conflicts with builtin type methods. Users relying on the previous naming (e.g., referencing MyEnum.REPLACE_ in code) will need to update to use the new names without trailing underscores. (#2999)
  • Fields using $ref with inline keywords now include merged metadata - When a schema property uses $ref alongside 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 like type: Type may now become type: Type = Field(..., description='Type of this object.', title='type') when the referenced schema includes those attributes. This also affects additionalProperties and 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 or anyOf'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

Full Changelog: 0.53.0...0.54.0

0.53.0

12 Jan 18:12
a6a7b04

Choose a tag to compare

Breaking Changes

Custom Template Update Required

  • Parser subclass signature change - The Parser base class now requires two generic type parameters: Parser[ParserConfigT, SchemaFeaturesT] instead of just Parser[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_features property required - Custom parser subclasses must now implement the schema_features abstract property that returns a JsonSchemaFeatures (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_config refactored to use class variable - Subclasses that override _create_default_config should now set the _config_class_name class 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.jinja2 or RootModel.jinja2, the condition for including default values has changed from field.required to (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: 1 will generate __root__: int = 1 (Pydantic v1) or root: int = 1 (Pydantic v2) instead of just __root__: int or root: 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 use Field(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:
    class Family(BaseModel):
        __root__: list[ID] = ['abc', 'efg']
    After:
    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

05 Jan 17:24
794e02b

Choose a tag to compare

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

03 Jan 17:48
361fb5a

Choose a tag to compare

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

02 Jan 20:07
ccf8794

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • Union fields with titles now wrapped in named models when --use-title-as-name is enabled - Previously, union-typed fields with a title were 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 .root attribute (Pydantic v2) or .__root__ (Pydantic v1) of the wrapper model. (#2889)
    Before:
    class ProcessingTaskTitle(BaseModel):
        processing_status_union: (
            ProcessingStatusDetail | ExtendedProcessingTask | ProcessingStatusTitle | None
        ) = Field('COMPLETED', title='Processing Status Union Title')
    After:
    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-name is enabled - Arrays, dicts, enums-as-literals, and oneOf/anyOf unions that have a title in 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, generates type MyArrayName = list[str]. For Pydantic output, generates class MyArrayName(RootModel[list[str]]). (#2889)
  • Default value handling changed for wrapped union fields - Fields that previously had simple default values now use default_factory with a lambda that calls parse_obj() (Pydantic v1) or model_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 $ref with nullable: true - When a JSON Schema property has a $ref combined with only nullable: true (and optionally metadata like title/description), the generator now uses the referenced type directly with Optional annotation instead of creating a new merged model. For example, a schema with multiple properties referencing User with nullable: true will now generate user_a: User | None instead of creating separate UserA, UserB model 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:
    class UserA(BaseModel):
        name: str
    class UserB(BaseModel):
        name: str
    class Model(BaseModel):
        user_a: UserA | None = None
        user_b: UserB | None = None
    After:
    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 title is now excluded when merging with child schemas. This prevents unintended title inheritance that could affect model naming when --use-title-as-name is enabled. (#2891)
  • allOf with single $ref no longer creates wrapper class - When a schema property uses allOf with only a single $ref and 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 as allOf: [$ref: '#/components/schemas/ACHClass'] will now generate ach_class: ACHClass | None instead 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

01 Jan 00:01
74cd08b

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • Different output when using --input-model with Set, FrozenSet, Mapping, or Sequence types - When using --input-model to convert Pydantic models or dataclasses, types that were previously converted to list or dict are now preserved as their original Python types. For example, a field typed as Set[str] now generates set[str] instead of list[str], FrozenSet[T] generates frozenset[T], Mapping[K, V] generates Mapping[K, V] instead of dict[K, V], and Sequence[T] generates Sequence[T] instead of list[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 allOf with multiple $ref items 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:
    class Person(BaseModel):
        type: str | None = 'playground:Person'
        name: constr(min_length=1) | None = None
        address: constr(min_length=5)
        age: int | None = None
    After:
    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-check formatter, the --unsafe-fixes flag 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 via pyproject.toml or ruff.toml to 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:
    ArmLeft = ArmRight
    After:
    class ArmLeft(ArmRight):
        pass
  • Fields with const values in anyOf/oneOf now generate Literal types instead of inferred base types - Previously, a const value like "MODE_2D" in an anyOf/oneOf schema would generate str type. Now it generates Literal["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:
    # 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)
    This is a bug fix that makes the generated code more type-safe, but downstream code performing type comparisons or using 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 DataType class: is_frozen_set, is_mapping, and is_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.jinja2 template, 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-foreign behavior 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 a config object and individual keyword arguments, with keyword arguments overriding config values. Now, providing both raises ValueError: "Cannot specify both 'config' and keyword arguments. Use one or the other." Users must choose one approach: either pass a GenerateConfig object 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__, and GraphQLParser.__init__ now accept either a config: ParserConfig object OR keyword arguments via **options: Unpack[ParserConfigDict], but not both simultaneously. Passing both raises a ValueError. 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, or GraphQLParser may need updates if they override __init__ and call super().__init__() with explicit parameter lists. The new signature uses **options: Unpack[ParserConfigDict] instead of explicit parameters. (#2877)
  • Config.input_model type changed from str to list[str] - The input_model field in the Config class 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 reads config.input_model will now receive a list[str] instead of str | 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...
Read more

0.50.0

28 Dec 06:54
8c02562

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • Models with unevaluatedProperties now generate extra field configuration - JSON Schemas containing unevaluatedProperties: false will now generate models with extra='forbid' (Pydantic v2) or extra = Extra.forbid (Pydantic v1), and schemas with unevaluatedProperties: true will generate extra='allow'. Previously this keyword was ignored. This may cause validation errors for data that was previously accepted. (#2797)
    Example - a schema like:
    {
      "title": "Resource",
      "type": "object",
      "properties": { "name": { "type": "string" } },
      "unevaluatedProperties": false
    }
    Previously generated:
    class Resource(BaseModel):
        name: str | None = None
    Now generates:
    class Resource(BaseModel):
        model_config = ConfigDict(extra='forbid')
        name: str | None = None

Default Behavior Changes

  • Default encoding changed from system locale to UTF-8 - The default encoding for reading input files and writing output is now always utf-8 instead of the system's locale-preferred encoding (e.g., cp1252 on Windows). Users who rely on locale-specific encoding must now explicitly use --encoding to specify their desired encoding (#2802)

What's Changed

  • Fix missing model_config in query parameter classes by @koxudaxi in #2795
  • Escape backslash and triple quotes in docstrings by @koxudaxi in #2796
  • Add unevaluatedProperties support by @koxudaxi in #2797
  • Expose schema $id and path to template context by @koxudaxi in #2798
  • Improve CLI startup time with lazy imports by @koxudaxi in #2799
  • Use UTF-8 as default encoding instead of locale-preferred by @koxudaxi in #2802
  • Add model-level json_schema_extra support for Pydantic v2 by @koxudaxi in #2803
  • Add input_model field support to cli_doc marker by @koxudaxi in #2805
  • Add dict input support for generate() function by @koxudaxi in #2806
  • Optimize extra_template_data copy in DataModel init by @koxudaxi in #2811
  • Add LRU cache for file loading and path existence checks by @koxudaxi in #2810
  • Optimize JSON/YAML loading with auto-detection and json.load by @koxudaxi in #2809
  • Migrate docs deployment to Cloudflare Pages by @koxudaxi in #2812
  • Optimize CI workflow with tox cache and remove dev check by @koxudaxi in #2815
  • Fix superfluous None when using $ref with nullable type aliases by @koxudaxi in #2814
  • Remove tox cache that breaks Windows CI by @koxudaxi in #2816
  • Add --input-model option for Pydantic models and dicts by @koxudaxi in #2804
  • Add ReadOnly support for TypedDict with --use-frozen-field by @koxudaxi in #2813
  • Exclude perf tests from regular test runs by @koxudaxi in #2817
  • Add extreme-scale performance tests with dynamic schema generation by @koxudaxi in #2818
  • Add ULID type support by @ahmetveburak in #2820
  • Add --enum-field-as-literal-map option and x-enum-field-as-literal schema extension by @koxudaxi in #2821
  • Fix propertyNames constraint ignored when using $ref to enum definition by @koxudaxi in #2824
  • Reduce CodSpeed benchmark tests for faster CI by @koxudaxi in #2826
  • Optimize propertyNames $ref handling by calling get_ref_data_type directly by @koxudaxi in #2825
  • Add missing path and ulid type mappings to TypedDict type manager by @koxudaxi in #2828
  • Fix --check to use output path's pyproject.toml settings by @koxudaxi in #2831

New Contributors

Full Changelog: 0.49.0...0.50.0

0.49.0

25 Dec 02:18
0ee5ea3

Choose a tag to compare

Breaking Changes

SchemaParseError for Invalid Schema Data

  • Schema validation errors now raise SchemaParseError instead of Pydantic ValidationError - When parsing invalid schema data (e.g., "minimum": "not_a_number"), the library now raises SchemaParseError instead of passing through Pydantic's ValidationError. Users catching pydantic.ValidationError for schema validation failures should update to catch SchemaParseError. The original error is preserved in the original_error attribute. (#2786)

Bug Fixes

CLI Now Correctly Outputs to stdout

  • Fixed CLI to actually output to stdout when --output is not specified - The --output argument has always documented (default: stdout) in --help, but previously no output was produced. Now works as documented. (#2787)

Other Notable Changes

  • generate() function now returns str | GeneratedModules | None instead of None - Existing code ignoring the return value is unaffected. (#2787)
  • Error message for multi-module output without directory changed to be more descriptive. (#2787)

What's Changed

  • Merge duplicate breaking change headings in release notes by @koxudaxi in #2776
  • Optimize O(n²) algorithms and reduce redundant iterations by @koxudaxi in #2778
  • Optimize performance with LRU caching and O(n) algorithms by @koxudaxi in #2777
  • Optimize Jinja2 environment caching and ruff batch formatting by @koxudaxi in #2779
  • Remove YAML parsing cache and deepcopy overhead by @koxudaxi in #2781
  • Add performance e2e tests with large schema fixtures by @koxudaxi in #2782
  • Convert Import class from Pydantic to dataclass for performance by @koxudaxi in #2783
  • Add schema path context to error messages by @koxudaxi in #2786
  • Return str or dict when output=None in generate() by @koxudaxi in #2787
  • Add --http-timeout CLI option by @koxudaxi in #2788
  • Pass schema extensions to templates by @koxudaxi in #2790
  • Add propertyNames and x-propertyNames support by @koxudaxi in #2789
  • Add support for additional_imports in extra-template-data JSON by @koxudaxi in #2793
  • Update zensical to 0.0.15 by @koxudaxi in #2794
  • Add --use-field-description-example option by @koxudaxi in #2792
  • Add --collapse-root-models-name-strategy option by @koxudaxi in #2791

Full Changelog: 0.48.0...0.49.0

0.48.0

24 Dec 01:44
3ffee15

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • Custom class name generator now applied consistently during duplicate name resolution - Previously, when using custom_class_name_generator, the default PascalCase naming was incorrectly applied during duplicate name resolution. Now the custom generator is respected throughout, which may change generated class names. For example, a class name like nested_object_result with a custom generator f"Custom{name}" will now produce CustomNested_object_result instead of CustomNestedObjectResult. Users relying on the previous behavior should update their code to expect the new, correct class names. (#2757)

  • YAML 1.1 boolean keywords now preserved as strings in enums - Values like YES, NO, on, off, y, n that were previously converted to Python booleans are now preserved as their original string values. This fixes issues where string enum values were incorrectly converted but may change generated output for schemas that relied on the previous behavior. For example, a YAML enum with YES will now generate YES = 'YES' instead of being converted to True. (#2767)

Default Behavior Changes

  • YAML boolean parsing restricted to YAML 1.2 semantics - Only true, false, True, False, TRUE, FALSE are now recognized as boolean values. YAML 1.1 boolean aliases (yes, no, on, off, y, n, etc.) are no longer parsed as booleans and will be treated as strings. Non-lowercase forms (True, False, TRUE, FALSE) now emit a deprecation warning indicating future versions will only support lowercase true/false. (#2767)

What's Changed

  • Fix Google Analytics config for Zensical by @koxudaxi in #2748
  • ci: add release draft workflow with Claude Code Action by @koxudaxi in #2749
  • fix: improve release-draft workflow configuration by @koxudaxi in #2750
  • fix: quote JSON schema in claude_args to preserve double quotes by @koxudaxi in #2751
  • fix: remove redundant --output-format json from claude_args by @koxudaxi in #2752
  • fix: increase max-turns from 5 to 10 for structured output by @koxudaxi in #2753
  • chore: increase max-turns to 20 for better margin by @koxudaxi in #2754
  • Add pydantic_v2.dataclass output type and remove pydantic v1 dataclass by @koxudaxi in #2746
  • Fix Pydantic v2 deprecation warnings by @koxudaxi in #2747
  • Add --use-tuple-for-fixed-items option by @koxudaxi in #2756
  • Fix custom_class_name_generator not applied consistently by @koxudaxi in #2757
  • Add --base-class-map option for model-specific base classes by @koxudaxi in #2759
  • Support 'timestamp with time zone' format by @koxudaxi in #2762
  • Add --type-overrides option to replace schema types with custom Python types by @koxudaxi in #2758
  • Add --use-root-model-type-alias option by @koxudaxi in #2763
  • Add --class-decorators option for custom model decorators by @koxudaxi in #2760
  • Add --naming-strategy and --duplicate-name-suffix CLI options by @koxudaxi in #2761
  • Add --generate-prompt option for LLM consultation by @koxudaxi in #2764
  • Add pydantic_v2.dataclass to output model types documentation by @koxudaxi in #2765
  • Clarify --input-file-type help text and CLI documentation by @koxudaxi in #2768
  • Support boolean values in patternProperties for JSON Schema 2020-12 by @koxudaxi in #2766
  • Use YAML 1.2-like bool semantics to fix YES/NO/on/off enum issues by @koxudaxi in #2767
  • Add InvalidFileFormatError with detailed error messages by @koxudaxi in #2771
  • Merge multiple patternProperties with same value type into single regex pattern by @koxudaxi in #2770
  • Sync Common Recipes and badges between README and docs by @koxudaxi in #2773
  • Fix primary-first naming for multi-file schemas with same-named definitions by @koxudaxi in #2772
  • Optimize performance for large schema processing by @koxudaxi in #2774

Full Changelog: 0.47.0...0.48.0

0.47.0

23 Dec 01:02
bb29295

Choose a tag to compare

Breaking Changes

Code Generation Changes

  • RootModel defaults use direct instantiation - RootModel fields with default values now generate ClassName(value) instead of ClassName.model_validate(value). This produces cleaner code but changes the generated output (#2714)

  • --strict-nullable now applies to JSON Schema - The --strict-nullable option is no longer OpenAPI-only and has been moved to Field customization options. It now also correctly respects nullable on array items (#2713, #2727)

Custom Template Update Required

  • If you use custom Jinja2 templates that check field.nullable, you may need to update them. The nullable field on JsonSchemaObject now defaults to None instead of False. Templates should check field.nullable is True instead of just if field.nullable (#2715)

    Example change:

    {# Before #}
    {%- if field.nullable %}...{% endif %}
    
    {# After #}
    {%- if field.nullable is true %}...{% endif %}

Error Handling Changes

  • Formatting failures emit warning instead of error - When code formatting fails (e.g., due to black errors), the generator now emits an unformatted output with a warning instead of raising an exception. This allows code generation to succeed even when formatting tools encounter issues (#2737)

What's Changed

  • Require @cli_doc marker for all CLI options by @koxudaxi in #2712
  • fix: respect nullable on array items with --strict-nullable by @koxudaxi in #2713
  • fix: wrap RootModel primitive defaults with default_factory by @koxudaxi in #2714
  • Add --use-default-factory-for-optional-nested-models option by @koxudaxi in #2711
  • Fix nullable field access in custom templates with strict_nullable by @koxudaxi in #2715
  • fix: skip non-model types in __change_field_name by @koxudaxi in #2717
  • Add requestBodies scope support for OpenAPI by @koxudaxi in #2716
  • Fix test data backspace escape by @koxudaxi in #2718
  • fix: quote forward references in recursive RootModel generic parameters by @koxudaxi in #2720
  • Add force_exec_validation option to catch runtime errors across Python versions by @koxudaxi in #2719
  • Add validation for extra_args in test helper functions by @koxudaxi in #2723
  • Fix discriminator with allOf without Literal type for Pydantic v2 by @koxudaxi in #2722
  • Fix regex_engine config not applied to RootModel generic by @koxudaxi in #2721
  • Fix hostname format with field_constraints to use Field(pattern=...) by @koxudaxi in #2724
  • Move --strict-nullable from OpenAPI-only to Field customization by @koxudaxi in #2727
  • Run CLI doc coverage test in CI without --collect-cli-docs by @koxudaxi in #2728
  • Add --use-generic-base-class option for DRY model config by @koxudaxi in #2726
  • Refactor parser base post-processing for DRY and type-safe implementation by @koxudaxi in #2730
  • Add --collapse-reuse-models option by @koxudaxi in #2731
  • Add --field-type-collision-strategy option by @koxudaxi in #2733
  • Revert "Add --field-type-collision-strategy option" by @koxudaxi in #2734
  • Add --no-treat-dot-as-module option for flat output structure by @koxudaxi in #2732
  • Add --field-type-collision-strategy option by @koxudaxi in #2735
  • Add --use-standard-primitive-types option by @koxudaxi in #2736
  • Emit unformatted output when formatting fails by @koxudaxi in #2737
  • Fix aliasing of builtin type field names by @koxudaxi in #2738
  • Add path filters to optimize CodeRabbit reviews by @koxudaxi in #2742
  • Add --output-date-class option and date-time-local format support by @koxudaxi in #2739
  • Fix custom template directory not working for included templates by @koxudaxi in #2740
  • Remove unnecessary model_config from RootModel subclasses by @koxudaxi in #2741
  • Fix incorrect --type-mappings examples in documentation by @koxudaxi in #2744
  • Add Python 3.13 deprecation warning documentation by @koxudaxi in #2743
  • Add admonition support to CLI docs and document --use-default nullable behavior by @koxudaxi in #2745

Full Changelog: 0.46.0...0.47.0