From 2f9a0b9c72887842eb06c468235e601d45458fe5 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Fri, 26 Dec 2025 21:12:44 +0000 Subject: [PATCH 01/12] Add missing path and ulid type mappings to TypedDict type manager --- src/datamodel_code_generator/model/types.py | 12 +++++++++++- tests/model/test_base.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/datamodel_code_generator/model/types.py b/src/datamodel_code_generator/model/types.py index d1a9c5065..a18b4c013 100644 --- a/src/datamodel_code_generator/model/types.py +++ b/src/datamodel_code_generator/model/types.py @@ -6,6 +6,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, ClassVar +from warnings import warn from datamodel_code_generator import DateClassType, DatetimeClassType, PythonVersion, PythonVersionMin from datamodel_code_generator.imports import ( @@ -62,12 +63,14 @@ def type_map_factory(data_type: type[DataType]) -> dict[Types, DataType]: Types.uuid3: data_type_str, Types.uuid4: data_type_str, Types.uuid5: data_type_str, + Types.ulid: data_type_str, Types.uri: data_type_str, Types.hostname: data_type_str, Types.ipv4: data_type_str, Types.ipv6: data_type_str, Types.ipv4_network: data_type_str, Types.ipv6_network: data_type_str, + Types.path: data_type_str, Types.boolean: data_type(type="bool"), Types.object: data_type.from_import(IMPORT_ANY, is_dict=True), Types.null: data_type(type="None"), @@ -151,4 +154,11 @@ def get_data_type( **_: Any, ) -> DataType: """Get data type for schema type.""" - return self.type_map[types] + if types in self.type_map: + return self.type_map[types] + warn( + f"Type mapping for {types.name!r} not found - using 'str'. " + f"Use --type-mappings to specify a custom mapping.", + stacklevel=2, + ) + return self.data_type(type="str") diff --git a/tests/model/test_base.py b/tests/model/test_base.py index 3874c7343..0d78ca484 100644 --- a/tests/model/test_base.py +++ b/tests/model/test_base.py @@ -426,3 +426,22 @@ def test_inline_field_docstring_escapes_triple_quotes() -> None: use_inline_field_description=True, ) assert field.inline_field_docstring == r'"""Contains \"\"\"quotes\"\"\""""' + + +def test_data_type_manager_unknown_type_fallback() -> None: + """Test DataTypeManager falls back to str with warning for unknown types.""" + import warnings + + from datamodel_code_generator.model.types import DataTypeManager + + manager = DataTypeManager() + del manager.type_map[Types.path] + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + result = manager.get_data_type(Types.path) + + assert len(w) == 1 + assert "Type mapping for 'path' not found" in str(w[0].message) + assert "--type-mappings" in str(w[0].message) + assert result.type == "str" From 5848f2be8b3ec85dc09659d3f3489f50ae78532c Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Fri, 26 Dec 2025 21:12:44 +0000 Subject: [PATCH 02/12] Add missing path and ulid type mappings to TypedDict type manager --- src/datamodel_code_generator/model/types.py | 10 +++++++++- tests/model/test_base.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/datamodel_code_generator/model/types.py b/src/datamodel_code_generator/model/types.py index d1a9c5065..7e75fbf00 100644 --- a/src/datamodel_code_generator/model/types.py +++ b/src/datamodel_code_generator/model/types.py @@ -62,12 +62,14 @@ def type_map_factory(data_type: type[DataType]) -> dict[Types, DataType]: Types.uuid3: data_type_str, Types.uuid4: data_type_str, Types.uuid5: data_type_str, + Types.ulid: data_type_str, Types.uri: data_type_str, Types.hostname: data_type_str, Types.ipv4: data_type_str, Types.ipv6: data_type_str, Types.ipv4_network: data_type_str, Types.ipv6_network: data_type_str, + Types.path: data_type_str, Types.boolean: data_type(type="bool"), Types.object: data_type.from_import(IMPORT_ANY, is_dict=True), Types.null: data_type(type="None"), @@ -151,4 +153,10 @@ def get_data_type( **_: Any, ) -> DataType: """Get data type for schema type.""" - return self.type_map[types] + if types in self.type_map: + return self.type_map[types] + msg = ( + f"Type mapping for {types.name!r} not implemented. " + f"Please report this at https://github.com/koxudaxi/datamodel-code-generator/issues" + ) + raise NotImplementedError(msg) diff --git a/tests/model/test_base.py b/tests/model/test_base.py index 3874c7343..56c13d9d3 100644 --- a/tests/model/test_base.py +++ b/tests/model/test_base.py @@ -426,3 +426,14 @@ def test_inline_field_docstring_escapes_triple_quotes() -> None: use_inline_field_description=True, ) assert field.inline_field_docstring == r'"""Contains \"\"\"quotes\"\"\""""' + + +def test_data_type_manager_unknown_type_raises_error() -> None: + """Test DataTypeManager raises NotImplementedError for unknown types.""" + from datamodel_code_generator.model.types import DataTypeManager + + manager = DataTypeManager() + del manager.type_map[Types.path] + + with pytest.raises(NotImplementedError, match="Type mapping for 'path' not implemented"): + manager.get_data_type(Types.path) From 1a61f49a32514f0fe2a530237d927bf5787afdb7 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Fri, 26 Dec 2025 21:51:45 +0000 Subject: [PATCH 03/12] Add GenerateConfig for unified configuration --- .github/workflows/config-types.yaml | 70 ++++++ pyproject.toml | 28 +++ src/datamodel_code_generator/__init__.py | 142 +++++++++++ .../_types/__init__.py | 11 + .../_types/base_config_dict.py | 187 +++++++++++++++ .../_types/generate_config_dict.py | 221 +++++++++++++++++ .../_types/parse_config_dict.py | 26 ++ .../_types/parser_config_dict.py | 198 +++++++++++++++ src/datamodel_code_generator/config.py | 225 ++++++++++++++++++ .../model/__init__.py | 2 +- tests/main/test_main_general.py | 42 ++++ tox.ini | 10 + 12 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/config-types.yaml create mode 100644 src/datamodel_code_generator/_types/__init__.py create mode 100644 src/datamodel_code_generator/_types/base_config_dict.py create mode 100644 src/datamodel_code_generator/_types/generate_config_dict.py create mode 100644 src/datamodel_code_generator/_types/parse_config_dict.py create mode 100644 src/datamodel_code_generator/_types/parser_config_dict.py create mode 100644 src/datamodel_code_generator/config.py diff --git a/.github/workflows/config-types.yaml b/.github/workflows/config-types.yaml new file mode 100644 index 000000000..e69995a36 --- /dev/null +++ b/.github/workflows/config-types.yaml @@ -0,0 +1,70 @@ +name: Update Config Types + +on: + push: + branches: [main] + paths: + - 'src/datamodel_code_generator/config.py' + pull_request: + branches: [main] + paths: + - 'src/datamodel_code_generator/config.py' + pull_request_target: + types: [labeled] + paths: + - 'src/datamodel_code_generator/config.py' + +permissions: + contents: write + +jobs: + update-config-types: + if: | + github.event_name == 'push' || + !github.event.pull_request.head.repo.fork || + github.actor == 'koxudaxi' || + github.actor == 'gaborbernat' || + github.actor == 'ilovelinux' || + (github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-fix' && + (github.event.sender.login == 'koxudaxi' || + github.event.sender.login == 'gaborbernat' || + github.event.sender.login == 'ilovelinux')) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + - uses: actions/checkout@v4 + if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref || github.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + token: ${{ secrets.PAT }} + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v5 + - name: Install tox + run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv + - name: Setup environment + run: tox run -vv --notest --skip-missing-interpreters false -e config-types + env: + UV_PYTHON_PREFERENCE: "only-managed" + - name: Generate TypedDict from Config models + run: | + .tox/config-types/bin/datamodel-codegen --profile base-config-dict + .tox/config-types/bin/datamodel-codegen --profile generate-config-dict + .tox/config-types/bin/datamodel-codegen --profile parser-config-dict + .tox/config-types/bin/datamodel-codegen --profile parse-config-dict + - name: Commit and push if changed + if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add src/datamodel_code_generator/_types/ + git diff --staged --quiet || git commit -m "chore: update TypedDict from Config models + + 🤖 Generated by GitHub Actions" + git push diff --git a/pyproject.toml b/pyproject.toml index 43018f3b8..ceb37eaf2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -257,3 +257,31 @@ reportPrivateImportUsage = false [tool.pydantic-pycharm-plugin] ignore-init-method-arguments = true parsable-types.str = [ "int", "float" ] + +[tool.datamodel-codegen.profiles.base-config-dict] +input-model = "src/datamodel_code_generator/config.py:BaseConfig" +output = "src/datamodel_code_generator/_types/base_config_dict.py" +output-model-type = "typing.TypedDict" +disable-timestamp = true +type-overrides = { "path" = "pathlib.Path" } + +[tool.datamodel-codegen.profiles.generate-config-dict] +input-model = "src/datamodel_code_generator/config.py:GenerateConfig" +output = "src/datamodel_code_generator/_types/generate_config_dict.py" +output-model-type = "typing.TypedDict" +disable-timestamp = true +type-overrides = { "path" = "pathlib.Path" } + +[tool.datamodel-codegen.profiles.parser-config-dict] +input-model = "src/datamodel_code_generator/config.py:ParserConfig" +output = "src/datamodel_code_generator/_types/parser_config_dict.py" +output-model-type = "typing.TypedDict" +disable-timestamp = true +type-overrides = { "path" = "pathlib.Path" } + +[tool.datamodel-codegen.profiles.parse-config-dict] +input-model = "src/datamodel_code_generator/config.py:ParseConfig" +output = "src/datamodel_code_generator/_types/parse_config_dict.py" +output-model-type = "typing.TypedDict" +disable-timestamp = true +type-overrides = { "path" = "pathlib.Path" } diff --git a/src/datamodel_code_generator/__init__.py b/src/datamodel_code_generator/__init__.py index d127979d6..3c281b7aa 100644 --- a/src/datamodel_code_generator/__init__.py +++ b/src/datamodel_code_generator/__init__.py @@ -60,6 +60,7 @@ if TYPE_CHECKING: from collections import defaultdict + from datamodel_code_generator.config import GenerateConfig from datamodel_code_generator.model.pydantic_v2 import UnionMode from datamodel_code_generator.parser.base import Parser from datamodel_code_generator.types import StrictTypes @@ -450,6 +451,7 @@ def _build_module_content( def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 input_: Path | str | ParseResult | Mapping[str, Any], *, + config: GenerateConfig | None = None, input_filename: str | None = None, input_file_type: InputFileType = InputFileType.Auto, output: Path | None = None, @@ -576,12 +578,141 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 This is the main entry point for code generation. Supports OpenAPI, JSON Schema, GraphQL, and raw data formats (JSON, YAML, Dict, CSV) as input. + Args: + input_: The input source (Path, str, ParseResult, or dict). + config: A GenerateConfig object containing all generation options. + When provided, overrides all individual option parameters. + options: Individual generation options (see GenerateConfig for details). + Ignored when the config parameter is provided. + Returns: - When output is a Path: None (writes to file system) - When output is None and single module: str (generated code) - When output is None and multiple modules: GeneratedModules (dict mapping module path tuples to generated code strings) """ + # Extract values from config if provided + if config is not None: + input_filename = config.input_filename + input_file_type = config.input_file_type + output = config.output + output_model_type = config.output_model_type + target_python_version = config.target_python_version + target_pydantic_version = config.target_pydantic_version + base_class = config.base_class + base_class_map = config.base_class_map + additional_imports = config.additional_imports + class_decorators = config.class_decorators + custom_template_dir = config.custom_template_dir + extra_template_data = cast("defaultdict[str, dict[str, Any]] | None", config.extra_template_data) + validation = config.validation + field_constraints = config.field_constraints + snake_case_field = config.snake_case_field + strip_default_none = config.strip_default_none + aliases = config.aliases + disable_timestamp = config.disable_timestamp + enable_version_header = config.enable_version_header + enable_command_header = config.enable_command_header + command_line = config.command_line + allow_population_by_field_name = config.allow_population_by_field_name + allow_extra_fields = config.allow_extra_fields + extra_fields = config.extra_fields + use_generic_base_class = config.use_generic_base_class + apply_default_values_for_required_fields = config.apply_default_values_for_required_fields + force_optional_for_required_fields = config.force_optional_for_required_fields + class_name = config.class_name + use_standard_collections = config.use_standard_collections + use_schema_description = config.use_schema_description + use_field_description = config.use_field_description + use_field_description_example = config.use_field_description_example + use_attribute_docstrings = config.use_attribute_docstrings + use_inline_field_description = config.use_inline_field_description + use_default_kwarg = config.use_default_kwarg + reuse_model = config.reuse_model + reuse_scope = config.reuse_scope + shared_module_name = config.shared_module_name + encoding = config.encoding + enum_field_as_literal = config.enum_field_as_literal + enum_field_as_literal_map = config.enum_field_as_literal_map + ignore_enum_constraints = config.ignore_enum_constraints + use_one_literal_as_default = config.use_one_literal_as_default + use_enum_values_in_discriminator = config.use_enum_values_in_discriminator + set_default_enum_member = config.set_default_enum_member + use_subclass_enum = config.use_subclass_enum + use_specialized_enum = config.use_specialized_enum + strict_nullable = config.strict_nullable + use_generic_container_types = config.use_generic_container_types + enable_faux_immutability = config.enable_faux_immutability + disable_appending_item_suffix = config.disable_appending_item_suffix + strict_types = config.strict_types + empty_enum_field_name = config.empty_enum_field_name + custom_class_name_generator = config.custom_class_name_generator + field_extra_keys = config.field_extra_keys + field_include_all_keys = config.field_include_all_keys + field_extra_keys_without_x_prefix = config.field_extra_keys_without_x_prefix + model_extra_keys = config.model_extra_keys + model_extra_keys_without_x_prefix = config.model_extra_keys_without_x_prefix + openapi_scopes = config.openapi_scopes + include_path_parameters = config.include_path_parameters + wrap_string_literal = config.wrap_string_literal + use_title_as_name = config.use_title_as_name + use_operation_id_as_name = config.use_operation_id_as_name + use_unique_items_as_set = config.use_unique_items_as_set + use_tuple_for_fixed_items = config.use_tuple_for_fixed_items + allof_merge_mode = config.allof_merge_mode + http_headers = config.http_headers + http_ignore_tls = config.http_ignore_tls + http_timeout = config.http_timeout + use_annotated = config.use_annotated + use_serialize_as_any = config.use_serialize_as_any + use_non_positive_negative_number_constrained_types = config.use_non_positive_negative_number_constrained_types + use_decimal_for_multiple_of = config.use_decimal_for_multiple_of + original_field_name_delimiter = config.original_field_name_delimiter + use_double_quotes = config.use_double_quotes + use_union_operator = config.use_union_operator + collapse_root_models = config.collapse_root_models + collapse_root_models_name_strategy = config.collapse_root_models_name_strategy + collapse_reuse_models = config.collapse_reuse_models + skip_root_model = config.skip_root_model + use_type_alias = config.use_type_alias + use_root_model_type_alias = config.use_root_model_type_alias + special_field_name_prefix = config.special_field_name_prefix + remove_special_field_name_prefix = config.remove_special_field_name_prefix + capitalise_enum_members = config.capitalise_enum_members + keep_model_order = config.keep_model_order + custom_file_header = config.custom_file_header + custom_file_header_path = config.custom_file_header_path + custom_formatters = config.custom_formatters + custom_formatters_kwargs = config.custom_formatters_kwargs + use_pendulum = config.use_pendulum + use_standard_primitive_types = config.use_standard_primitive_types + http_query_parameters = config.http_query_parameters + treat_dot_as_module = config.treat_dot_as_module + use_exact_imports = config.use_exact_imports + union_mode = cast("UnionMode | None", config.union_mode) + output_datetime_class = config.output_datetime_class + output_date_class = config.output_date_class + keyword_only = config.keyword_only + frozen_dataclasses = config.frozen_dataclasses + no_alias = config.no_alias + use_frozen_field = config.use_frozen_field + use_default_factory_for_optional_nested_models = config.use_default_factory_for_optional_nested_models + formatters = config.formatters + settings_path = config.settings_path + parent_scoped_naming = config.parent_scoped_naming + naming_strategy = config.naming_strategy + duplicate_name_suffix = config.duplicate_name_suffix + dataclass_arguments = config.dataclass_arguments + disable_future_imports = config.disable_future_imports + type_mappings = config.type_mappings + type_overrides = config.type_overrides + read_only_write_only_model_type = config.read_only_write_only_model_type + use_status_code_in_response_name = config.use_status_code_in_response_name + all_exports_scope = config.all_exports_scope + all_exports_collision_strategy = config.all_exports_collision_strategy + field_type_collision_strategy = config.field_type_collision_strategy + module_split_mode = config.module_split_mode + remote_text_cache: DefaultPutDict[str, str] = DefaultPutDict() match input_: case str(): @@ -1053,6 +1184,7 @@ def infer_input_type(text: str) -> InputFileType: "DatetimeClassType", "DefaultPutDict", "Error", + "GenerateConfig", "GeneratedModules", "InputFileType", "InvalidClassNameError", @@ -1066,3 +1198,13 @@ def infer_input_type(text: str) -> InputFileType: "TargetPydanticVersion", "generate", ] + + +def __getattr__(name: str) -> type: + """Lazy import for GenerateConfig to avoid circular imports.""" + if name == "GenerateConfig": + from datamodel_code_generator.config import GenerateConfig # noqa: PLC0415 + + return GenerateConfig + msg = f"module {__name__!r} has no attribute {name!r}" + raise AttributeError(msg) diff --git a/src/datamodel_code_generator/_types/__init__.py b/src/datamodel_code_generator/_types/__init__.py new file mode 100644 index 000000000..69833cb71 --- /dev/null +++ b/src/datamodel_code_generator/_types/__init__.py @@ -0,0 +1,11 @@ +"""Generated TypedDict types for configuration models. + +This module contains TypedDict classes auto-generated from the Config Pydantic models +using datamodel-codegen itself (dogfooding). These types enable type-safe **kwargs +patterns with Unpack[]. + +The TypedDict files in this directory are generated by running: + tox -e config-types + +Do not edit the generated files manually. +""" diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py new file mode 100644 index 000000000..e3b6f50e6 --- /dev/null +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -0,0 +1,187 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations + +from typing import Any, Literal, TypeAlias, TypedDict + +from typing_extensions import NotRequired + +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] + + +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] + + +DataModelType: TypeAlias = Literal[ + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', +] + + +class DataclassArguments(TypedDict): + init: NotRequired[bool] + repr: NotRequired[bool] + eq: NotRequired[bool] + order: NotRequired[bool] + unsafe_hash: NotRequired[bool] + frozen: NotRequired[bool] + match_args: NotRequired[bool] + kw_only: NotRequired[bool] + slots: NotRequired[bool] + weakref_slot: NotRequired[bool] + + +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] + + +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] + + +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] + + +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] + + +GraphQLScope: TypeAlias = Literal['schema'] + + +LiteralType: TypeAlias = Literal['all', 'one', 'none'] + + +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] + + +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] + + +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] + + +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] + + +ReuseScope: TypeAlias = Literal['module', 'tree'] + + +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] + + +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] + + +class BaseConfig(TypedDict): + output_model_type: NotRequired[DataModelType] + target_python_version: NotRequired[PythonVersion] + target_pydantic_version: NotRequired[TargetPydanticVersion | None] + base_class: NotRequired[str] + base_class_map: NotRequired[dict[str, str] | None] + additional_imports: NotRequired[list[str] | None] + class_decorators: NotRequired[list[str] | None] + custom_template_dir: NotRequired[str | None] + extra_template_data: NotRequired[dict[str, dict[str, Any]] | None] + validation: NotRequired[bool] + field_constraints: NotRequired[bool] + snake_case_field: NotRequired[bool] + strip_default_none: NotRequired[bool] + aliases: NotRequired[dict[str, str] | None] + allow_population_by_field_name: NotRequired[bool] + allow_extra_fields: NotRequired[bool] + extra_fields: NotRequired[str | None] + use_generic_base_class: NotRequired[bool] + apply_default_values_for_required_fields: NotRequired[bool] + force_optional_for_required_fields: NotRequired[bool] + class_name: NotRequired[str | None] + use_standard_collections: NotRequired[bool] + use_generic_container_types: NotRequired[bool] + use_union_operator: NotRequired[bool] + strict_nullable: NotRequired[bool] + strict_types: NotRequired[list[StrictTypes] | None] + use_schema_description: NotRequired[bool] + use_field_description: NotRequired[bool] + use_field_description_example: NotRequired[bool] + use_attribute_docstrings: NotRequired[bool] + use_inline_field_description: NotRequired[bool] + use_default_kwarg: NotRequired[bool] + reuse_model: NotRequired[bool] + reuse_scope: NotRequired[ReuseScope] + shared_module_name: NotRequired[str] + encoding: NotRequired[str] + enum_field_as_literal: NotRequired[LiteralType | None] + enum_field_as_literal_map: NotRequired[dict[str, str] | None] + ignore_enum_constraints: NotRequired[bool] + use_one_literal_as_default: NotRequired[bool] + use_enum_values_in_discriminator: NotRequired[bool] + set_default_enum_member: NotRequired[bool] + use_subclass_enum: NotRequired[bool] + use_specialized_enum: NotRequired[bool] + empty_enum_field_name: NotRequired[str | None] + capitalise_enum_members: NotRequired[bool] + enable_faux_immutability: NotRequired[bool] + disable_appending_item_suffix: NotRequired[bool] + special_field_name_prefix: NotRequired[str | None] + remove_special_field_name_prefix: NotRequired[bool] + field_extra_keys: NotRequired[list[str] | None] + field_include_all_keys: NotRequired[bool] + field_extra_keys_without_x_prefix: NotRequired[list[str] | None] + model_extra_keys: NotRequired[list[str] | None] + model_extra_keys_without_x_prefix: NotRequired[list[str] | None] + openapi_scopes: NotRequired[list[OpenAPIScope] | None] + include_path_parameters: NotRequired[bool] + graphql_scopes: NotRequired[list[GraphQLScope] | None] + wrap_string_literal: NotRequired[bool | None] + use_double_quotes: NotRequired[bool] + original_field_name_delimiter: NotRequired[str | None] + use_title_as_name: NotRequired[bool] + use_operation_id_as_name: NotRequired[bool] + use_unique_items_as_set: NotRequired[bool] + use_tuple_for_fixed_items: NotRequired[bool] + allof_merge_mode: NotRequired[AllOfMergeMode] + use_annotated: NotRequired[bool] + use_serialize_as_any: NotRequired[bool] + use_non_positive_negative_number_constrained_types: NotRequired[bool] + use_decimal_for_multiple_of: NotRequired[bool] + collapse_root_models: NotRequired[bool] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] + collapse_reuse_models: NotRequired[bool] + skip_root_model: NotRequired[bool] + use_type_alias: NotRequired[bool] + use_root_model_type_alias: NotRequired[bool] + keep_model_order: NotRequired[bool] + custom_formatters: NotRequired[list[str] | None] + custom_formatters_kwargs: NotRequired[dict[str, Any] | None] + use_pendulum: NotRequired[bool] + use_standard_primitive_types: NotRequired[bool] + treat_dot_as_module: NotRequired[bool | None] + use_exact_imports: NotRequired[bool] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + output_datetime_class: NotRequired[DatetimeClassType | None] + output_date_class: NotRequired[DateClassType | None] + keyword_only: NotRequired[bool] + frozen_dataclasses: NotRequired[bool] + dataclass_arguments: NotRequired[DataclassArguments | None] + no_alias: NotRequired[bool] + use_frozen_field: NotRequired[bool] + use_default_factory_for_optional_nested_models: NotRequired[bool] + formatters: NotRequired[list[Formatter]] + parent_scoped_naming: NotRequired[bool] + naming_strategy: NotRequired[NamingStrategy | None] + duplicate_name_suffix: NotRequired[dict[str, str] | None] + type_mappings: NotRequired[list[str] | None] + type_overrides: NotRequired[dict[str, str] | None] + read_only_write_only_model_type: NotRequired[ReadOnlyWriteOnlyModelType | None] + use_status_code_in_response_name: NotRequired[bool] + field_type_collision_strategy: NotRequired[FieldTypeCollisionStrategy | None] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py new file mode 100644 index 000000000..7ee8fcd26 --- /dev/null +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -0,0 +1,221 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations + +from typing import Any, Literal, TypeAlias, TypedDict + +from typing_extensions import NotRequired + +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] + + +AllExportsScope: TypeAlias = Literal['children', 'recursive'] + + +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] + + +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] + + +DataModelType: TypeAlias = Literal[ + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', +] + + +class DataclassArguments(TypedDict): + init: NotRequired[bool] + repr: NotRequired[bool] + eq: NotRequired[bool] + order: NotRequired[bool] + unsafe_hash: NotRequired[bool] + frozen: NotRequired[bool] + match_args: NotRequired[bool] + kw_only: NotRequired[bool] + slots: NotRequired[bool] + weakref_slot: NotRequired[bool] + + +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] + + +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] + + +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] + + +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] + + +GraphQLScope: TypeAlias = Literal['schema'] + + +InputFileType: TypeAlias = Literal[ + 'auto', 'openapi', 'jsonschema', 'json', 'yaml', 'dict', 'csv', 'graphql' +] + + +LiteralType: TypeAlias = Literal['all', 'one', 'none'] + + +ModuleSplitMode: TypeAlias = Literal['single'] + + +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] + + +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] + + +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] + + +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] + + +ReuseScope: TypeAlias = Literal['module', 'tree'] + + +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] + + +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] + + +class GenerateConfig(TypedDict): + output_model_type: NotRequired[DataModelType] + target_python_version: NotRequired[PythonVersion] + target_pydantic_version: NotRequired[TargetPydanticVersion | None] + base_class: NotRequired[str] + base_class_map: NotRequired[dict[str, str] | None] + additional_imports: NotRequired[list[str] | None] + class_decorators: NotRequired[list[str] | None] + custom_template_dir: NotRequired[str | None] + extra_template_data: NotRequired[dict[str, dict[str, Any]] | None] + validation: NotRequired[bool] + field_constraints: NotRequired[bool] + snake_case_field: NotRequired[bool] + strip_default_none: NotRequired[bool] + aliases: NotRequired[dict[str, str] | None] + allow_population_by_field_name: NotRequired[bool] + allow_extra_fields: NotRequired[bool] + extra_fields: NotRequired[str | None] + use_generic_base_class: NotRequired[bool] + apply_default_values_for_required_fields: NotRequired[bool] + force_optional_for_required_fields: NotRequired[bool] + class_name: NotRequired[str | None] + use_standard_collections: NotRequired[bool] + use_generic_container_types: NotRequired[bool] + use_union_operator: NotRequired[bool] + strict_nullable: NotRequired[bool] + strict_types: NotRequired[list[StrictTypes] | None] + use_schema_description: NotRequired[bool] + use_field_description: NotRequired[bool] + use_field_description_example: NotRequired[bool] + use_attribute_docstrings: NotRequired[bool] + use_inline_field_description: NotRequired[bool] + use_default_kwarg: NotRequired[bool] + reuse_model: NotRequired[bool] + reuse_scope: NotRequired[ReuseScope] + shared_module_name: NotRequired[str] + encoding: NotRequired[str] + enum_field_as_literal: NotRequired[LiteralType | None] + enum_field_as_literal_map: NotRequired[dict[str, str] | None] + ignore_enum_constraints: NotRequired[bool] + use_one_literal_as_default: NotRequired[bool] + use_enum_values_in_discriminator: NotRequired[bool] + set_default_enum_member: NotRequired[bool] + use_subclass_enum: NotRequired[bool] + use_specialized_enum: NotRequired[bool] + empty_enum_field_name: NotRequired[str | None] + capitalise_enum_members: NotRequired[bool] + enable_faux_immutability: NotRequired[bool] + disable_appending_item_suffix: NotRequired[bool] + special_field_name_prefix: NotRequired[str | None] + remove_special_field_name_prefix: NotRequired[bool] + field_extra_keys: NotRequired[list[str] | None] + field_include_all_keys: NotRequired[bool] + field_extra_keys_without_x_prefix: NotRequired[list[str] | None] + model_extra_keys: NotRequired[list[str] | None] + model_extra_keys_without_x_prefix: NotRequired[list[str] | None] + openapi_scopes: NotRequired[list[OpenAPIScope] | None] + include_path_parameters: NotRequired[bool] + graphql_scopes: NotRequired[list[GraphQLScope] | None] + wrap_string_literal: NotRequired[bool | None] + use_double_quotes: NotRequired[bool] + original_field_name_delimiter: NotRequired[str | None] + use_title_as_name: NotRequired[bool] + use_operation_id_as_name: NotRequired[bool] + use_unique_items_as_set: NotRequired[bool] + use_tuple_for_fixed_items: NotRequired[bool] + allof_merge_mode: NotRequired[AllOfMergeMode] + use_annotated: NotRequired[bool] + use_serialize_as_any: NotRequired[bool] + use_non_positive_negative_number_constrained_types: NotRequired[bool] + use_decimal_for_multiple_of: NotRequired[bool] + collapse_root_models: NotRequired[bool] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] + collapse_reuse_models: NotRequired[bool] + skip_root_model: NotRequired[bool] + use_type_alias: NotRequired[bool] + use_root_model_type_alias: NotRequired[bool] + keep_model_order: NotRequired[bool] + custom_formatters: NotRequired[list[str] | None] + custom_formatters_kwargs: NotRequired[dict[str, Any] | None] + use_pendulum: NotRequired[bool] + use_standard_primitive_types: NotRequired[bool] + treat_dot_as_module: NotRequired[bool | None] + use_exact_imports: NotRequired[bool] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + output_datetime_class: NotRequired[DatetimeClassType | None] + output_date_class: NotRequired[DateClassType | None] + keyword_only: NotRequired[bool] + frozen_dataclasses: NotRequired[bool] + dataclass_arguments: NotRequired[DataclassArguments | None] + no_alias: NotRequired[bool] + use_frozen_field: NotRequired[bool] + use_default_factory_for_optional_nested_models: NotRequired[bool] + formatters: NotRequired[list[Formatter]] + parent_scoped_naming: NotRequired[bool] + naming_strategy: NotRequired[NamingStrategy | None] + duplicate_name_suffix: NotRequired[dict[str, str] | None] + type_mappings: NotRequired[list[str] | None] + type_overrides: NotRequired[dict[str, str] | None] + read_only_write_only_model_type: NotRequired[ReadOnlyWriteOnlyModelType | None] + use_status_code_in_response_name: NotRequired[bool] + field_type_collision_strategy: NotRequired[FieldTypeCollisionStrategy | None] + input_filename: NotRequired[str | None] + input_file_type: NotRequired[InputFileType] + output: NotRequired[str | None] + disable_timestamp: NotRequired[bool] + enable_version_header: NotRequired[bool] + enable_command_header: NotRequired[bool] + command_line: NotRequired[str | None] + custom_file_header: NotRequired[str | None] + custom_file_header_path: NotRequired[str | None] + http_headers: NotRequired[list[tuple[str, str]] | None] + http_ignore_tls: NotRequired[bool] + http_timeout: NotRequired[float | None] + http_query_parameters: NotRequired[list[tuple[str, str]] | None] + settings_path: NotRequired[str | None] + disable_future_imports: NotRequired[bool] + all_exports_scope: NotRequired[AllExportsScope | None] + all_exports_collision_strategy: NotRequired[AllExportsCollisionStrategy | None] + module_split_mode: NotRequired[ModuleSplitMode | None] diff --git a/src/datamodel_code_generator/_types/parse_config_dict.py b/src/datamodel_code_generator/_types/parse_config_dict.py new file mode 100644 index 000000000..af38d3ccc --- /dev/null +++ b/src/datamodel_code_generator/_types/parse_config_dict.py @@ -0,0 +1,26 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations + +from typing import Literal, TypeAlias, TypedDict + +from typing_extensions import NotRequired + +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] + + +AllExportsScope: TypeAlias = Literal['children', 'recursive'] + + +ModuleSplitMode: TypeAlias = Literal['single'] + + +class ParseConfig(TypedDict): + settings_path: NotRequired[str | None] + disable_future_imports: NotRequired[bool] + all_exports_scope: NotRequired[AllExportsScope | None] + all_exports_collision_strategy: NotRequired[AllExportsCollisionStrategy | None] + module_split_mode: NotRequired[ModuleSplitMode | None] diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py new file mode 100644 index 000000000..6114e1e0d --- /dev/null +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -0,0 +1,198 @@ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations + +from typing import Any, Literal, TypeAlias, TypedDict + +from typing_extensions import NotRequired + +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] + + +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] + + +DataModelType: TypeAlias = Literal[ + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', +] + + +class DataclassArguments(TypedDict): + init: NotRequired[bool] + repr: NotRequired[bool] + eq: NotRequired[bool] + order: NotRequired[bool] + unsafe_hash: NotRequired[bool] + frozen: NotRequired[bool] + match_args: NotRequired[bool] + kw_only: NotRequired[bool] + slots: NotRequired[bool] + weakref_slot: NotRequired[bool] + + +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] + + +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] + + +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] + + +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] + + +GraphQLScope: TypeAlias = Literal['schema'] + + +LiteralType: TypeAlias = Literal['all', 'one', 'none'] + + +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] + + +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] + + +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] + + +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] + + +ReuseScope: TypeAlias = Literal['module', 'tree'] + + +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] + + +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] + + +class ParserConfig(TypedDict): + output_model_type: NotRequired[DataModelType] + target_python_version: NotRequired[PythonVersion] + target_pydantic_version: NotRequired[TargetPydanticVersion | None] + base_class: NotRequired[str] + base_class_map: NotRequired[dict[str, str] | None] + additional_imports: NotRequired[list[str] | None] + class_decorators: NotRequired[list[str] | None] + custom_template_dir: NotRequired[str | None] + extra_template_data: NotRequired[dict[str, dict[str, Any]] | None] + validation: NotRequired[bool] + field_constraints: NotRequired[bool] + snake_case_field: NotRequired[bool] + strip_default_none: NotRequired[bool] + aliases: NotRequired[dict[str, str] | None] + allow_population_by_field_name: NotRequired[bool] + allow_extra_fields: NotRequired[bool] + extra_fields: NotRequired[str | None] + use_generic_base_class: NotRequired[bool] + apply_default_values_for_required_fields: NotRequired[bool] + force_optional_for_required_fields: NotRequired[bool] + class_name: NotRequired[str | None] + use_standard_collections: NotRequired[bool] + use_generic_container_types: NotRequired[bool] + use_union_operator: NotRequired[bool] + strict_nullable: NotRequired[bool] + strict_types: NotRequired[list[StrictTypes] | None] + use_schema_description: NotRequired[bool] + use_field_description: NotRequired[bool] + use_field_description_example: NotRequired[bool] + use_attribute_docstrings: NotRequired[bool] + use_inline_field_description: NotRequired[bool] + use_default_kwarg: NotRequired[bool] + reuse_model: NotRequired[bool] + reuse_scope: NotRequired[ReuseScope] + shared_module_name: NotRequired[str] + encoding: NotRequired[str] + enum_field_as_literal: NotRequired[LiteralType | None] + enum_field_as_literal_map: NotRequired[dict[str, str] | None] + ignore_enum_constraints: NotRequired[bool] + use_one_literal_as_default: NotRequired[bool] + use_enum_values_in_discriminator: NotRequired[bool] + set_default_enum_member: NotRequired[bool] + use_subclass_enum: NotRequired[bool] + use_specialized_enum: NotRequired[bool] + empty_enum_field_name: NotRequired[str | None] + capitalise_enum_members: NotRequired[bool] + enable_faux_immutability: NotRequired[bool] + disable_appending_item_suffix: NotRequired[bool] + special_field_name_prefix: NotRequired[str | None] + remove_special_field_name_prefix: NotRequired[bool] + field_extra_keys: NotRequired[list[str] | None] + field_include_all_keys: NotRequired[bool] + field_extra_keys_without_x_prefix: NotRequired[list[str] | None] + model_extra_keys: NotRequired[list[str] | None] + model_extra_keys_without_x_prefix: NotRequired[list[str] | None] + openapi_scopes: NotRequired[list[OpenAPIScope] | None] + include_path_parameters: NotRequired[bool] + graphql_scopes: NotRequired[list[GraphQLScope] | None] + wrap_string_literal: NotRequired[bool | None] + use_double_quotes: NotRequired[bool] + original_field_name_delimiter: NotRequired[str | None] + use_title_as_name: NotRequired[bool] + use_operation_id_as_name: NotRequired[bool] + use_unique_items_as_set: NotRequired[bool] + use_tuple_for_fixed_items: NotRequired[bool] + allof_merge_mode: NotRequired[AllOfMergeMode] + use_annotated: NotRequired[bool] + use_serialize_as_any: NotRequired[bool] + use_non_positive_negative_number_constrained_types: NotRequired[bool] + use_decimal_for_multiple_of: NotRequired[bool] + collapse_root_models: NotRequired[bool] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] + collapse_reuse_models: NotRequired[bool] + skip_root_model: NotRequired[bool] + use_type_alias: NotRequired[bool] + use_root_model_type_alias: NotRequired[bool] + keep_model_order: NotRequired[bool] + custom_formatters: NotRequired[list[str] | None] + custom_formatters_kwargs: NotRequired[dict[str, Any] | None] + use_pendulum: NotRequired[bool] + use_standard_primitive_types: NotRequired[bool] + treat_dot_as_module: NotRequired[bool | None] + use_exact_imports: NotRequired[bool] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + output_datetime_class: NotRequired[DatetimeClassType | None] + output_date_class: NotRequired[DateClassType | None] + keyword_only: NotRequired[bool] + frozen_dataclasses: NotRequired[bool] + dataclass_arguments: NotRequired[DataclassArguments | None] + no_alias: NotRequired[bool] + use_frozen_field: NotRequired[bool] + use_default_factory_for_optional_nested_models: NotRequired[bool] + formatters: NotRequired[list[Formatter]] + parent_scoped_naming: NotRequired[bool] + naming_strategy: NotRequired[NamingStrategy | None] + duplicate_name_suffix: NotRequired[dict[str, str] | None] + type_mappings: NotRequired[list[str] | None] + type_overrides: NotRequired[dict[str, str] | None] + read_only_write_only_model_type: NotRequired[ReadOnlyWriteOnlyModelType | None] + use_status_code_in_response_name: NotRequired[bool] + field_type_collision_strategy: NotRequired[FieldTypeCollisionStrategy | None] + base_path: NotRequired[str | None] + allow_responses_without_content: NotRequired[bool] + known_third_party: NotRequired[list[str] | None] + defer_formatting: NotRequired[bool] + target_datetime_class: NotRequired[DatetimeClassType | None] + target_date_class: NotRequired[DateClassType | None] + default_field_extras: NotRequired[dict[str, Any] | None] + http_headers: NotRequired[list[tuple[str, str]] | None] + http_ignore_tls: NotRequired[bool] + http_timeout: NotRequired[float | None] + http_query_parameters: NotRequired[list[tuple[str, str]] | None] diff --git a/src/datamodel_code_generator/config.py b/src/datamodel_code_generator/config.py new file mode 100644 index 000000000..5dcf9f6fb --- /dev/null +++ b/src/datamodel_code_generator/config.py @@ -0,0 +1,225 @@ +"""Configuration models for datamodel-code-generator. + +Provides unified configuration classes for generate(), Parser, and parse() functions. +These models consolidate the 120+ parameters into structured, type-safe configurations. +""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping, Sequence +from pathlib import Path +from typing import TYPE_CHECKING, Any, Literal + +from pydantic import BaseModel, ConfigDict +from pydantic.json_schema import SkipJsonSchema + +from datamodel_code_generator.enums import ( + DEFAULT_SHARED_MODULE_NAME, + AllExportsCollisionStrategy, + AllExportsScope, + AllOfMergeMode, + CollapseRootModelsNameStrategy, + DataclassArguments, + DataModelType, + FieldTypeCollisionStrategy, + GraphQLScope, + InputFileType, + ModuleSplitMode, + NamingStrategy, + OpenAPIScope, + ReadOnlyWriteOnlyModelType, + ReuseScope, + TargetPydanticVersion, +) +from datamodel_code_generator.format import ( + DEFAULT_FORMATTERS, + DateClassType, + DatetimeClassType, + Formatter, + PythonVersion, + PythonVersionMin, +) +from datamodel_code_generator.parser import LiteralType +from datamodel_code_generator.types import StrictTypes + +if TYPE_CHECKING: + from datamodel_code_generator.model.pydantic_v2 import UnionMode + +# Use Literal type to avoid circular import +UnionModeType = Literal["smart", "left_to_right"] | None + + +class BaseConfig(BaseModel): + """Base configuration with common parameters shared across generate() and Parser. + + Contains ~88 parameters that are used by both the generate() function and + the Parser class. These are the core code generation settings. + """ + + model_config = ConfigDict(extra="forbid") + + output_model_type: DataModelType = DataModelType.PydanticBaseModel + target_python_version: PythonVersion = PythonVersionMin + target_pydantic_version: TargetPydanticVersion | None = None + base_class: str = "" + base_class_map: dict[str, str] | None = None + additional_imports: list[str] | None = None + class_decorators: list[str] | None = None + custom_template_dir: Path | None = None + extra_template_data: dict[str, dict[str, Any]] | None = None + validation: bool = False + field_constraints: bool = False + snake_case_field: bool = False + strip_default_none: bool = False + aliases: Mapping[str, str] | None = None + allow_population_by_field_name: bool = False + allow_extra_fields: bool = False + extra_fields: str | None = None + use_generic_base_class: bool = False + apply_default_values_for_required_fields: bool = False + force_optional_for_required_fields: bool = False + class_name: str | None = None + use_standard_collections: bool = True + use_generic_container_types: bool = False + use_union_operator: bool = True + strict_nullable: bool = False + strict_types: Sequence[StrictTypes] | None = None + use_schema_description: bool = False + use_field_description: bool = False + use_field_description_example: bool = False + use_attribute_docstrings: bool = False + use_inline_field_description: bool = False + use_default_kwarg: bool = False + reuse_model: bool = False + reuse_scope: ReuseScope = ReuseScope.Module + shared_module_name: str = DEFAULT_SHARED_MODULE_NAME + encoding: str = "utf-8" + enum_field_as_literal: LiteralType | None = None + enum_field_as_literal_map: dict[str, str] | None = None + ignore_enum_constraints: bool = False + use_one_literal_as_default: bool = False + use_enum_values_in_discriminator: bool = False + set_default_enum_member: bool = False + use_subclass_enum: bool = False + use_specialized_enum: bool = True + empty_enum_field_name: str | None = None + capitalise_enum_members: bool = False + enable_faux_immutability: bool = False + disable_appending_item_suffix: bool = False + custom_class_name_generator: SkipJsonSchema[Callable[[str], str]] | None = None + special_field_name_prefix: str | None = None + remove_special_field_name_prefix: bool = False + field_extra_keys: set[str] | None = None + field_include_all_keys: bool = False + field_extra_keys_without_x_prefix: set[str] | None = None + model_extra_keys: set[str] | None = None + model_extra_keys_without_x_prefix: set[str] | None = None + openapi_scopes: list[OpenAPIScope] | None = None + include_path_parameters: bool = False + graphql_scopes: list[GraphQLScope] | None = None + wrap_string_literal: bool | None = None + use_double_quotes: bool = False + original_field_name_delimiter: str | None = None + use_title_as_name: bool = False + use_operation_id_as_name: bool = False + use_unique_items_as_set: bool = False + use_tuple_for_fixed_items: bool = False + allof_merge_mode: AllOfMergeMode = AllOfMergeMode.Constraints + use_annotated: bool = False + use_serialize_as_any: bool = False + use_non_positive_negative_number_constrained_types: bool = False + use_decimal_for_multiple_of: bool = False + collapse_root_models: bool = False + collapse_root_models_name_strategy: CollapseRootModelsNameStrategy | None = None + collapse_reuse_models: bool = False + skip_root_model: bool = False + use_type_alias: bool = False + use_root_model_type_alias: bool = False + keep_model_order: bool = False + custom_formatters: list[str] | None = None + custom_formatters_kwargs: dict[str, Any] | None = None + use_pendulum: bool = False + use_standard_primitive_types: bool = False + treat_dot_as_module: bool | None = None + use_exact_imports: bool = False + union_mode: UnionModeType = None + output_datetime_class: DatetimeClassType | None = None + output_date_class: DateClassType | None = None + keyword_only: bool = False + frozen_dataclasses: bool = False + dataclass_arguments: DataclassArguments | None = None + no_alias: bool = False + use_frozen_field: bool = False + use_default_factory_for_optional_nested_models: bool = False + formatters: list[Formatter] = DEFAULT_FORMATTERS + parent_scoped_naming: bool = False + naming_strategy: NamingStrategy | None = None + duplicate_name_suffix: dict[str, str] | None = None + type_mappings: list[str] | None = None + type_overrides: dict[str, str] | None = None + read_only_write_only_model_type: ReadOnlyWriteOnlyModelType | None = None + use_status_code_in_response_name: bool = False + field_type_collision_strategy: FieldTypeCollisionStrategy | None = None + + +class GenerateConfig(BaseConfig): + """Configuration for the generate() function. + + Extends BaseConfig with generate()-specific parameters for input handling, + output routing, headers, and HTTP settings. + """ + + input_filename: str | None = None + input_file_type: InputFileType = InputFileType.Auto + output: Path | None = None + disable_timestamp: bool = False + enable_version_header: bool = False + enable_command_header: bool = False + command_line: str | None = None + custom_file_header: str | None = None + custom_file_header_path: Path | None = None + http_headers: Sequence[tuple[str, str]] | None = None + http_ignore_tls: bool = False + http_timeout: float | None = None + http_query_parameters: Sequence[tuple[str, str]] | None = None + settings_path: Path | None = None + disable_future_imports: bool = False + all_exports_scope: AllExportsScope | None = None + all_exports_collision_strategy: AllExportsCollisionStrategy | None = None + module_split_mode: ModuleSplitMode | None = None + + +class ParserConfig(BaseConfig): + """Configuration for Parser.__init__(). + + Extends BaseConfig with Parser-specific internal parameters + for the Parser class initialization. + """ + + base_path: Path | None = None + allow_responses_without_content: bool = False + known_third_party: list[str] | None = None + defer_formatting: bool = False + target_datetime_class: DatetimeClassType | None = None + target_date_class: DateClassType | None = None + default_field_extras: dict[str, Any] | None = None + http_headers: Sequence[tuple[str, str]] | None = None + http_ignore_tls: bool = False + http_timeout: float | None = None + http_query_parameters: Sequence[tuple[str, str]] | None = None + + +class ParseConfig(BaseModel): + """Configuration for Parser.parse() method. + + Contains settings that are applied after parser construction, + during the actual parsing phase. + """ + + model_config = ConfigDict(extra="forbid") + + settings_path: Path | None = None + disable_future_imports: bool = False + all_exports_scope: AllExportsScope | None = None + all_exports_collision_strategy: AllExportsCollisionStrategy | None = None + module_split_mode: ModuleSplitMode | None = None diff --git a/src/datamodel_code_generator/model/__init__.py b/src/datamodel_code_generator/model/__init__.py index 1d981ad0e..41e076e24 100644 --- a/src/datamodel_code_generator/model/__init__.py +++ b/src/datamodel_code_generator/model/__init__.py @@ -9,7 +9,7 @@ import sys from typing import TYPE_CHECKING, NamedTuple -from datamodel_code_generator import PythonVersion +from datamodel_code_generator.format import PythonVersion from .base import ConstraintsBase, DataModel, DataModelFieldBase diff --git a/tests/main/test_main_general.py b/tests/main/test_main_general.py index 769781b97..72f3252ad 100644 --- a/tests/main/test_main_general.py +++ b/tests/main/test_main_general.py @@ -1868,3 +1868,45 @@ def test_generate_with_dict_raw_data_types_raises_error(input_file_type: InputFi with pytest.raises(Error, match=f"Dict input is not supported for {input_file_type.value}"): generate(auto_error_dict, input_file_type=input_file_type) + + +def test_generate_with_config_parameter() -> None: + """Test generate() with config parameter extracts all values correctly.""" + from datamodel_code_generator import GenerateConfig + + json_schema = '{"type": "object", "properties": {"user_name": {"type": "string"}}}' + config = GenerateConfig( + input_file_type=InputFileType.JsonSchema, + snake_case_field=True, + disable_timestamp=True, + ) + result = generate(json_schema, config=config) + assert result == snapshot("""\ +# generated by datamodel-codegen: +# filename: + +from __future__ import annotations + +from pydantic import BaseModel + + +class Model(BaseModel): + user_name: str | None = None\ +""") + + +def test_generate_config_lazy_import() -> None: + """Test that GenerateConfig can be imported via __getattr__ lazy import.""" + import datamodel_code_generator + + config_class = datamodel_code_generator.GenerateConfig + assert config_class is not None + assert config_class.__name__ == "GenerateConfig" + + +def test_module_getattr_raises_for_unknown_attribute() -> None: + """Test that __getattr__ raises AttributeError for unknown attributes.""" + import datamodel_code_generator + + with pytest.raises(AttributeError, match="module 'datamodel_code_generator' has no attribute 'NonExistentClass'"): + _ = datamodel_code_generator.NonExistentClass diff --git a/tox.ini b/tox.ini index be296e36a..1efbeca87 100644 --- a/tox.ini +++ b/tox.ini @@ -112,6 +112,16 @@ commands = check-wheel-contents --no-config {env_tmp_dir} dependency_groups = pkg-meta +[testenv:config-types] +description = Generate TypedDict from Config models (dogfooding) +commands = + datamodel-codegen --profile base-config-dict + datamodel-codegen --profile generate-config-dict + datamodel-codegen --profile parser-config-dict + datamodel-codegen --profile parse-config-dict +dependency_groups = dev +no_default_groups = true + [testenv:type] description = run type check on code base commands = From 17e97c5e06d6813d8c22a8ecae57a238e008f121 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Dec 2025 23:47:56 +0000 Subject: [PATCH 04/12] chore: update TypedDict from Config models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated by GitHub Actions --- .../_types/base_config_dict.py | 54 ++++++++------- .../_types/generate_config_dict.py | 66 +++++++++++-------- .../_types/parse_config_dict.py | 8 ++- .../_types/parser_config_dict.py | 54 ++++++++------- 4 files changed, 106 insertions(+), 76 deletions(-) diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py index dbce4210a..e3b6f50e6 100644 --- a/src/datamodel_code_generator/_types/base_config_dict.py +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -36,43 +36,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class BaseConfig(TypedDict): @@ -147,7 +153,9 @@ class BaseConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -159,7 +167,7 @@ class BaseConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py index a129f73c3..7ee8fcd26 100644 --- a/src/datamodel_code_generator/_types/generate_config_dict.py +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -7,25 +7,27 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] -AllExportsScope: TypeAlias = Literal["children", "recursive"] +AllExportsScope: TypeAlias = Literal['children', 'recursive'] -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -42,49 +44,57 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -InputFileType: TypeAlias = Literal["auto", "openapi", "jsonschema", "json", "yaml", "dict", "csv", "graphql"] +InputFileType: TypeAlias = Literal[ + 'auto', 'openapi', 'jsonschema', 'json', 'yaml', 'dict', 'csv', 'graphql' +] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -ModuleSplitMode: TypeAlias = Literal["single"] +ModuleSplitMode: TypeAlias = Literal['single'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class GenerateConfig(TypedDict): @@ -159,7 +169,9 @@ class GenerateConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -171,7 +183,7 @@ class GenerateConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/parse_config_dict.py b/src/datamodel_code_generator/_types/parse_config_dict.py index fffa1a4e4..af38d3ccc 100644 --- a/src/datamodel_code_generator/_types/parse_config_dict.py +++ b/src/datamodel_code_generator/_types/parse_config_dict.py @@ -7,13 +7,15 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] -AllExportsScope: TypeAlias = Literal["children", "recursive"] +AllExportsScope: TypeAlias = Literal['children', 'recursive'] -ModuleSplitMode: TypeAlias = Literal["single"] +ModuleSplitMode: TypeAlias = Literal['single'] class ParseConfig(TypedDict): diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py index ce833cb42..6114e1e0d 100644 --- a/src/datamodel_code_generator/_types/parser_config_dict.py +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -36,43 +36,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class ParserConfig(TypedDict): @@ -147,7 +153,9 @@ class ParserConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -159,7 +167,7 @@ class ParserConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] From 5d9111416bcb18ba17ee0db0fc7f039622fedc98 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Fri, 26 Dec 2025 23:49:04 +0000 Subject: [PATCH 05/12] Update PythonVersion imports for consistency --- src/datamodel_code_generator/model/msgspec.py | 2 +- src/datamodel_code_generator/model/types.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datamodel_code_generator/model/msgspec.py b/src/datamodel_code_generator/model/msgspec.py index d59fde1f3..f6deb86a4 100644 --- a/src/datamodel_code_generator/model/msgspec.py +++ b/src/datamodel_code_generator/model/msgspec.py @@ -10,7 +10,7 @@ from pydantic import Field -from datamodel_code_generator import DateClassType, DatetimeClassType, PythonVersion, PythonVersionMin +from datamodel_code_generator.format import DateClassType, DatetimeClassType, PythonVersion, PythonVersionMin from datamodel_code_generator.imports import ( IMPORT_DATE, IMPORT_DATETIME, diff --git a/src/datamodel_code_generator/model/types.py b/src/datamodel_code_generator/model/types.py index 7e75fbf00..476298c8e 100644 --- a/src/datamodel_code_generator/model/types.py +++ b/src/datamodel_code_generator/model/types.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, ClassVar -from datamodel_code_generator import DateClassType, DatetimeClassType, PythonVersion, PythonVersionMin +from datamodel_code_generator.format import DateClassType, DatetimeClassType, PythonVersion, PythonVersionMin from datamodel_code_generator.imports import ( IMPORT_ANY, IMPORT_DECIMAL, From 28cc1d0e7da3aa06a2932bdc1d96344598864c6a Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Sat, 27 Dec 2025 00:14:39 +0000 Subject: [PATCH 06/12] Add pydantic v1 SkipJsonSchema compatibility --- src/datamodel_code_generator/config.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/datamodel_code_generator/config.py b/src/datamodel_code_generator/config.py index 8ab6e7f56..8d266467b 100644 --- a/src/datamodel_code_generator/config.py +++ b/src/datamodel_code_generator/config.py @@ -8,10 +8,22 @@ from collections.abc import Callable, Mapping, Sequence from pathlib import Path -from typing import Any, Literal +from typing import TYPE_CHECKING, Any, Literal from pydantic import BaseModel, ConfigDict -from pydantic.json_schema import SkipJsonSchema + +if TYPE_CHECKING: + from pydantic.json_schema import SkipJsonSchema +else: + # SkipJsonSchema is pydantic v2 only - for v1 we use identity via __class_getitem__ + try: + from pydantic.json_schema import SkipJsonSchema + except ImportError: + + class SkipJsonSchema: # noqa: D101 + def __class_getitem__(cls, item: Any) -> Any: # noqa: D105 + return item + from datamodel_code_generator.enums import ( DEFAULT_SHARED_MODULE_NAME, From 296359517494cd014ad6e257e6f619ad91278997 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Sat, 27 Dec 2025 00:18:51 +0000 Subject: [PATCH 07/12] Format generated TypedDict files --- .../_types/base_config_dict.py | 54 +++++++-------- .../_types/generate_config_dict.py | 66 ++++++++----------- .../_types/parse_config_dict.py | 8 +-- .../_types/parser_config_dict.py | 54 +++++++-------- 4 files changed, 76 insertions(+), 106 deletions(-) diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py index e3b6f50e6..dbce4210a 100644 --- a/src/datamodel_code_generator/_types/base_config_dict.py +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -36,49 +36,43 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class BaseConfig(TypedDict): @@ -153,9 +147,7 @@ class BaseConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -167,7 +159,7 @@ class BaseConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py index 7ee8fcd26..a129f73c3 100644 --- a/src/datamodel_code_generator/_types/generate_config_dict.py +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -7,27 +7,25 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal[ - 'error', 'minimal-prefix', 'full-prefix' -] +AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] -AllExportsScope: TypeAlias = Literal['children', 'recursive'] +AllExportsScope: TypeAlias = Literal["children", "recursive"] -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -44,57 +42,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -InputFileType: TypeAlias = Literal[ - 'auto', 'openapi', 'jsonschema', 'json', 'yaml', 'dict', 'csv', 'graphql' -] +InputFileType: TypeAlias = Literal["auto", "openapi", "jsonschema", "json", "yaml", "dict", "csv", "graphql"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -ModuleSplitMode: TypeAlias = Literal['single'] +ModuleSplitMode: TypeAlias = Literal["single"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class GenerateConfig(TypedDict): @@ -169,9 +159,7 @@ class GenerateConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -183,7 +171,7 @@ class GenerateConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/parse_config_dict.py b/src/datamodel_code_generator/_types/parse_config_dict.py index af38d3ccc..fffa1a4e4 100644 --- a/src/datamodel_code_generator/_types/parse_config_dict.py +++ b/src/datamodel_code_generator/_types/parse_config_dict.py @@ -7,15 +7,13 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal[ - 'error', 'minimal-prefix', 'full-prefix' -] +AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] -AllExportsScope: TypeAlias = Literal['children', 'recursive'] +AllExportsScope: TypeAlias = Literal["children", "recursive"] -ModuleSplitMode: TypeAlias = Literal['single'] +ModuleSplitMode: TypeAlias = Literal["single"] class ParseConfig(TypedDict): diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py index 6114e1e0d..ce833cb42 100644 --- a/src/datamodel_code_generator/_types/parser_config_dict.py +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -36,49 +36,43 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class ParserConfig(TypedDict): @@ -153,9 +147,7 @@ class ParserConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -167,7 +159,7 @@ class ParserConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] From 7ed95aaeecef51c1243b4eeb67f9fa1ea0ee4fd0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Dec 2025 00:19:20 +0000 Subject: [PATCH 08/12] chore: update TypedDict from Config models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated by GitHub Actions --- .../_types/base_config_dict.py | 54 ++++++++------- .../_types/generate_config_dict.py | 66 +++++++++++-------- .../_types/parse_config_dict.py | 8 ++- .../_types/parser_config_dict.py | 54 ++++++++------- 4 files changed, 106 insertions(+), 76 deletions(-) diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py index dbce4210a..e3b6f50e6 100644 --- a/src/datamodel_code_generator/_types/base_config_dict.py +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -36,43 +36,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class BaseConfig(TypedDict): @@ -147,7 +153,9 @@ class BaseConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -159,7 +167,7 @@ class BaseConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py index a129f73c3..7ee8fcd26 100644 --- a/src/datamodel_code_generator/_types/generate_config_dict.py +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -7,25 +7,27 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] -AllExportsScope: TypeAlias = Literal["children", "recursive"] +AllExportsScope: TypeAlias = Literal['children', 'recursive'] -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -42,49 +44,57 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -InputFileType: TypeAlias = Literal["auto", "openapi", "jsonschema", "json", "yaml", "dict", "csv", "graphql"] +InputFileType: TypeAlias = Literal[ + 'auto', 'openapi', 'jsonschema', 'json', 'yaml', 'dict', 'csv', 'graphql' +] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -ModuleSplitMode: TypeAlias = Literal["single"] +ModuleSplitMode: TypeAlias = Literal['single'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class GenerateConfig(TypedDict): @@ -159,7 +169,9 @@ class GenerateConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -171,7 +183,7 @@ class GenerateConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/parse_config_dict.py b/src/datamodel_code_generator/_types/parse_config_dict.py index fffa1a4e4..af38d3ccc 100644 --- a/src/datamodel_code_generator/_types/parse_config_dict.py +++ b/src/datamodel_code_generator/_types/parse_config_dict.py @@ -7,13 +7,15 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] +AllExportsCollisionStrategy: TypeAlias = Literal[ + 'error', 'minimal-prefix', 'full-prefix' +] -AllExportsScope: TypeAlias = Literal["children", "recursive"] +AllExportsScope: TypeAlias = Literal['children', 'recursive'] -ModuleSplitMode: TypeAlias = Literal["single"] +ModuleSplitMode: TypeAlias = Literal['single'] class ParseConfig(TypedDict): diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py index ce833cb42..6114e1e0d 100644 --- a/src/datamodel_code_generator/_types/parser_config_dict.py +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] +AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] -CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] +CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] DataModelType: TypeAlias = Literal[ - "pydantic.BaseModel", - "pydantic_v2.BaseModel", - "pydantic_v2.dataclass", - "dataclasses.dataclass", - "typing.TypedDict", - "msgspec.Struct", + 'pydantic.BaseModel', + 'pydantic_v2.BaseModel', + 'pydantic_v2.dataclass', + 'dataclasses.dataclass', + 'typing.TypedDict', + 'msgspec.Struct', ] @@ -36,43 +36,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] +DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] -DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] +DatetimeClassType: TypeAlias = Literal[ + 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' +] -FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] +FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] -Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] +Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] -GraphQLScope: TypeAlias = Literal["schema"] +GraphQLScope: TypeAlias = Literal['schema'] -LiteralType: TypeAlias = Literal["all", "one", "none"] +LiteralType: TypeAlias = Literal['all', 'one', 'none'] -NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] +NamingStrategy: TypeAlias = Literal[ + 'numbered', 'parent-prefixed', 'full-path', 'primary-first' +] -OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] +OpenAPIScope: TypeAlias = Literal[ + 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' +] -PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] +PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] -ReuseScope: TypeAlias = Literal["module", "tree"] +ReuseScope: TypeAlias = Literal['module', 'tree'] -StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] +StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] -TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] class ParserConfig(TypedDict): @@ -147,7 +153,9 @@ class ParserConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] + collapse_root_models_name_strategy: NotRequired[ + CollapseRootModelsNameStrategy | None + ] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -159,7 +167,7 @@ class ParserConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] From e0726eee45ba1ef6dd27a6289e78d0a379f58993 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Sat, 27 Dec 2025 00:25:57 +0000 Subject: [PATCH 09/12] Add formatting step to config-types workflow --- .github/workflows/config-types.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/config-types.yaml b/.github/workflows/config-types.yaml index e69995a36..0368a8190 100644 --- a/.github/workflows/config-types.yaml +++ b/.github/workflows/config-types.yaml @@ -58,6 +58,9 @@ jobs: .tox/config-types/bin/datamodel-codegen --profile generate-config-dict .tox/config-types/bin/datamodel-codegen --profile parser-config-dict .tox/config-types/bin/datamodel-codegen --profile parse-config-dict + - name: Format generated files + run: | + uv tool run ruff format src/datamodel_code_generator/_types/ - name: Commit and push if changed if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository run: | From f6ff46181fb8e0b0cefe0d053eb4f9e42053d102 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Dec 2025 00:27:03 +0000 Subject: [PATCH 10/12] chore: update TypedDict from Config models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated by GitHub Actions --- .../_types/base_config_dict.py | 54 +++++++-------- .../_types/generate_config_dict.py | 66 ++++++++----------- .../_types/parse_config_dict.py | 8 +-- .../_types/parser_config_dict.py | 54 +++++++-------- 4 files changed, 76 insertions(+), 106 deletions(-) diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py index e3b6f50e6..dbce4210a 100644 --- a/src/datamodel_code_generator/_types/base_config_dict.py +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -36,49 +36,43 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class BaseConfig(TypedDict): @@ -153,9 +147,7 @@ class BaseConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -167,7 +159,7 @@ class BaseConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py index 7ee8fcd26..a129f73c3 100644 --- a/src/datamodel_code_generator/_types/generate_config_dict.py +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -7,27 +7,25 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal[ - 'error', 'minimal-prefix', 'full-prefix' -] +AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] -AllExportsScope: TypeAlias = Literal['children', 'recursive'] +AllExportsScope: TypeAlias = Literal["children", "recursive"] -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -44,57 +42,49 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -InputFileType: TypeAlias = Literal[ - 'auto', 'openapi', 'jsonschema', 'json', 'yaml', 'dict', 'csv', 'graphql' -] +InputFileType: TypeAlias = Literal["auto", "openapi", "jsonschema", "json", "yaml", "dict", "csv", "graphql"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -ModuleSplitMode: TypeAlias = Literal['single'] +ModuleSplitMode: TypeAlias = Literal["single"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class GenerateConfig(TypedDict): @@ -169,9 +159,7 @@ class GenerateConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -183,7 +171,7 @@ class GenerateConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/parse_config_dict.py b/src/datamodel_code_generator/_types/parse_config_dict.py index af38d3ccc..fffa1a4e4 100644 --- a/src/datamodel_code_generator/_types/parse_config_dict.py +++ b/src/datamodel_code_generator/_types/parse_config_dict.py @@ -7,15 +7,13 @@ from typing_extensions import NotRequired -AllExportsCollisionStrategy: TypeAlias = Literal[ - 'error', 'minimal-prefix', 'full-prefix' -] +AllExportsCollisionStrategy: TypeAlias = Literal["error", "minimal-prefix", "full-prefix"] -AllExportsScope: TypeAlias = Literal['children', 'recursive'] +AllExportsScope: TypeAlias = Literal["children", "recursive"] -ModuleSplitMode: TypeAlias = Literal['single'] +ModuleSplitMode: TypeAlias = Literal["single"] class ParseConfig(TypedDict): diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py index 6114e1e0d..ce833cb42 100644 --- a/src/datamodel_code_generator/_types/parser_config_dict.py +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -7,19 +7,19 @@ from typing_extensions import NotRequired -AllOfMergeMode: TypeAlias = Literal['constraints', 'all', 'none'] +AllOfMergeMode: TypeAlias = Literal["constraints", "all", "none"] -CollapseRootModelsNameStrategy: TypeAlias = Literal['child', 'parent'] +CollapseRootModelsNameStrategy: TypeAlias = Literal["child", "parent"] DataModelType: TypeAlias = Literal[ - 'pydantic.BaseModel', - 'pydantic_v2.BaseModel', - 'pydantic_v2.dataclass', - 'dataclasses.dataclass', - 'typing.TypedDict', - 'msgspec.Struct', + "pydantic.BaseModel", + "pydantic_v2.BaseModel", + "pydantic_v2.dataclass", + "dataclasses.dataclass", + "typing.TypedDict", + "msgspec.Struct", ] @@ -36,49 +36,43 @@ class DataclassArguments(TypedDict): weakref_slot: NotRequired[bool] -DateClassType: TypeAlias = Literal['date', 'PastDate', 'FutureDate'] +DateClassType: TypeAlias = Literal["date", "PastDate", "FutureDate"] -DatetimeClassType: TypeAlias = Literal[ - 'datetime', 'AwareDatetime', 'NaiveDatetime', 'PastDatetime', 'FutureDatetime' -] +DatetimeClassType: TypeAlias = Literal["datetime", "AwareDatetime", "NaiveDatetime", "PastDatetime", "FutureDatetime"] -FieldTypeCollisionStrategy: TypeAlias = Literal['rename-field', 'rename-type'] +FieldTypeCollisionStrategy: TypeAlias = Literal["rename-field", "rename-type"] -Formatter: TypeAlias = Literal['black', 'isort', 'ruff-check', 'ruff-format'] +Formatter: TypeAlias = Literal["black", "isort", "ruff-check", "ruff-format"] -GraphQLScope: TypeAlias = Literal['schema'] +GraphQLScope: TypeAlias = Literal["schema"] -LiteralType: TypeAlias = Literal['all', 'one', 'none'] +LiteralType: TypeAlias = Literal["all", "one", "none"] -NamingStrategy: TypeAlias = Literal[ - 'numbered', 'parent-prefixed', 'full-path', 'primary-first' -] +NamingStrategy: TypeAlias = Literal["numbered", "parent-prefixed", "full-path", "primary-first"] -OpenAPIScope: TypeAlias = Literal[ - 'schemas', 'paths', 'tags', 'parameters', 'webhooks', 'requestbodies' -] +OpenAPIScope: TypeAlias = Literal["schemas", "paths", "tags", "parameters", "webhooks", "requestbodies"] -PythonVersion: TypeAlias = Literal['3.10', '3.11', '3.12', '3.13', '3.14'] +PythonVersion: TypeAlias = Literal["3.10", "3.11", "3.12", "3.13", "3.14"] -ReadOnlyWriteOnlyModelType: TypeAlias = Literal['request-response', 'all'] +ReadOnlyWriteOnlyModelType: TypeAlias = Literal["request-response", "all"] -ReuseScope: TypeAlias = Literal['module', 'tree'] +ReuseScope: TypeAlias = Literal["module", "tree"] -StrictTypes: TypeAlias = Literal['str', 'bytes', 'int', 'float', 'bool'] +StrictTypes: TypeAlias = Literal["str", "bytes", "int", "float", "bool"] -TargetPydanticVersion: TypeAlias = Literal['2', '2.11'] +TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] class ParserConfig(TypedDict): @@ -153,9 +147,7 @@ class ParserConfig(TypedDict): use_non_positive_negative_number_constrained_types: NotRequired[bool] use_decimal_for_multiple_of: NotRequired[bool] collapse_root_models: NotRequired[bool] - collapse_root_models_name_strategy: NotRequired[ - CollapseRootModelsNameStrategy | None - ] + collapse_root_models_name_strategy: NotRequired[CollapseRootModelsNameStrategy | None] collapse_reuse_models: NotRequired[bool] skip_root_model: NotRequired[bool] use_type_alias: NotRequired[bool] @@ -167,7 +159,7 @@ class ParserConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal['smart', 'left_to_right'] | None] + union_mode: NotRequired[Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] From a917b18457a8d7a99403f6482d64ce52df31d9f9 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Sat, 27 Dec 2025 00:45:30 +0000 Subject: [PATCH 11/12] Add config parameter to generate() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add config: GenerateConfig | None parameter to generate() - Add exclusivity check: config and **options cannot be used together - Create GenerateConfig from options when options are provided - Convert extra_template_data dict to defaultdict for compatibility - Update UnionModeType to accept UnionMode enum - Add test for config and options exclusivity check 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/datamodel_code_generator/__init__.py | 410 +++++++++-------------- src/datamodel_code_generator/config.py | 5 +- tests/main/test_main_general.py | 11 + 3 files changed, 176 insertions(+), 250 deletions(-) diff --git a/src/datamodel_code_generator/__init__.py b/src/datamodel_code_generator/__init__.py index 3c281b7aa..aa4007640 100644 --- a/src/datamodel_code_generator/__init__.py +++ b/src/datamodel_code_generator/__init__.py @@ -9,7 +9,8 @@ import contextlib import os import sys -from collections.abc import Callable, Iterator, Mapping, Sequence +from collections import defaultdict +from collections.abc import Callable, Iterator, Mapping from datetime import datetime, timezone from functools import lru_cache as _lru_cache from pathlib import Path @@ -58,8 +59,9 @@ from datamodel_code_generator.parser import DefaultPutDict, LiteralType if TYPE_CHECKING: - from collections import defaultdict - + from datamodel_code_generator._types.generate_config_dict import ( + GenerateConfig as GenerateConfigDict, + ) from datamodel_code_generator.config import GenerateConfig from datamodel_code_generator.model.pydantic_v2 import UnionMode from datamodel_code_generator.parser.base import Parser @@ -448,130 +450,11 @@ def _build_module_content( return "\n".join(lines) -def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 +def generate( # noqa: PLR0912, PLR0914, PLR0915 input_: Path | str | ParseResult | Mapping[str, Any], *, config: GenerateConfig | None = None, - input_filename: str | None = None, - input_file_type: InputFileType = InputFileType.Auto, - output: Path | None = None, - output_model_type: DataModelType = DataModelType.PydanticBaseModel, - target_python_version: PythonVersion = PythonVersionMin, - target_pydantic_version: TargetPydanticVersion | None = None, - base_class: str = "", - base_class_map: dict[str, str] | None = None, - additional_imports: list[str] | None = None, - class_decorators: list[str] | None = None, - custom_template_dir: Path | None = None, - extra_template_data: defaultdict[str, dict[str, Any]] | None = None, - validation: bool = False, - field_constraints: bool = False, - snake_case_field: bool = False, - strip_default_none: bool = False, - aliases: Mapping[str, str] | None = None, - disable_timestamp: bool = False, - enable_version_header: bool = False, - enable_command_header: bool = False, - command_line: str | None = None, - allow_population_by_field_name: bool = False, - allow_extra_fields: bool = False, - extra_fields: str | None = None, - use_generic_base_class: bool = False, - apply_default_values_for_required_fields: bool = False, - force_optional_for_required_fields: bool = False, - class_name: str | None = None, - use_standard_collections: bool = True, - use_schema_description: bool = False, - use_field_description: bool = False, - use_field_description_example: bool = False, - use_attribute_docstrings: bool = False, - use_inline_field_description: bool = False, - use_default_kwarg: bool = False, - reuse_model: bool = False, - reuse_scope: ReuseScope = ReuseScope.Module, - shared_module_name: str = DEFAULT_SHARED_MODULE_NAME, - encoding: str = "utf-8", - enum_field_as_literal: LiteralType | None = None, - enum_field_as_literal_map: dict[str, str] | None = None, - ignore_enum_constraints: bool = False, - use_one_literal_as_default: bool = False, - use_enum_values_in_discriminator: bool = False, - set_default_enum_member: bool = False, - use_subclass_enum: bool = False, - use_specialized_enum: bool = True, - strict_nullable: bool = False, - use_generic_container_types: bool = False, - enable_faux_immutability: bool = False, - disable_appending_item_suffix: bool = False, - strict_types: Sequence[StrictTypes] | None = None, - empty_enum_field_name: str | None = None, - custom_class_name_generator: Callable[[str], str] | None = None, - field_extra_keys: set[str] | None = None, - field_include_all_keys: bool = False, - field_extra_keys_without_x_prefix: set[str] | None = None, - model_extra_keys: set[str] | None = None, - model_extra_keys_without_x_prefix: set[str] | None = None, - openapi_scopes: list[OpenAPIScope] | None = None, - include_path_parameters: bool = False, - graphql_scopes: list[GraphQLScope] | None = None, # noqa: ARG001 - wrap_string_literal: bool | None = None, - use_title_as_name: bool = False, - use_operation_id_as_name: bool = False, - use_unique_items_as_set: bool = False, - use_tuple_for_fixed_items: bool = False, - allof_merge_mode: AllOfMergeMode = AllOfMergeMode.Constraints, - http_headers: Sequence[tuple[str, str]] | None = None, - http_ignore_tls: bool = False, - http_timeout: float | None = None, - use_annotated: bool = False, - use_serialize_as_any: bool = False, - use_non_positive_negative_number_constrained_types: bool = False, - use_decimal_for_multiple_of: bool = False, - original_field_name_delimiter: str | None = None, - use_double_quotes: bool = False, - use_union_operator: bool = True, - collapse_root_models: bool = False, - collapse_root_models_name_strategy: CollapseRootModelsNameStrategy | None = None, - collapse_reuse_models: bool = False, - skip_root_model: bool = False, - use_type_alias: bool = False, - use_root_model_type_alias: bool = False, - special_field_name_prefix: str | None = None, - remove_special_field_name_prefix: bool = False, - capitalise_enum_members: bool = False, - keep_model_order: bool = False, - custom_file_header: str | None = None, - custom_file_header_path: Path | None = None, - custom_formatters: list[str] | None = None, - custom_formatters_kwargs: dict[str, Any] | None = None, - use_pendulum: bool = False, - use_standard_primitive_types: bool = False, - http_query_parameters: Sequence[tuple[str, str]] | None = None, - treat_dot_as_module: bool | None = None, - use_exact_imports: bool = False, - union_mode: UnionMode | None = None, - output_datetime_class: DatetimeClassType | None = None, - output_date_class: DateClassType | None = None, - keyword_only: bool = False, - frozen_dataclasses: bool = False, - no_alias: bool = False, - use_frozen_field: bool = False, - use_default_factory_for_optional_nested_models: bool = False, - formatters: list[Formatter] = DEFAULT_FORMATTERS, - settings_path: Path | None = None, - parent_scoped_naming: bool = False, - naming_strategy: NamingStrategy | None = None, - duplicate_name_suffix: dict[str, str] | None = None, - dataclass_arguments: DataclassArguments | None = None, - disable_future_imports: bool = False, - type_mappings: list[str] | None = None, - type_overrides: dict[str, str] | None = None, - read_only_write_only_model_type: ReadOnlyWriteOnlyModelType | None = None, - use_status_code_in_response_name: bool = False, - all_exports_scope: AllExportsScope | None = None, - all_exports_collision_strategy: AllExportsCollisionStrategy | None = None, - field_type_collision_strategy: FieldTypeCollisionStrategy | None = None, - module_split_mode: ModuleSplitMode | None = None, + **options: Any, ) -> str | GeneratedModules | None: """Generate Python data models from schema definitions or structured data. @@ -581,137 +464,156 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 Args: input_: The input source (Path, str, ParseResult, or dict). config: A GenerateConfig object containing all generation options. - When provided, overrides all individual option parameters. + Cannot be used together with individual options. options: Individual generation options (see GenerateConfig for details). - Ignored when the config parameter is provided. + Cannot be used together with config parameter. Returns: - When output is a Path: None (writes to file system) - When output is None and single module: str (generated code) - When output is None and multiple modules: GeneratedModules (dict mapping module path tuples to generated code strings) + + Raises: + ValueError: If both config and individual options are provided. """ - # Extract values from config if provided - if config is not None: - input_filename = config.input_filename - input_file_type = config.input_file_type - output = config.output - output_model_type = config.output_model_type - target_python_version = config.target_python_version - target_pydantic_version = config.target_pydantic_version - base_class = config.base_class - base_class_map = config.base_class_map - additional_imports = config.additional_imports - class_decorators = config.class_decorators - custom_template_dir = config.custom_template_dir - extra_template_data = cast("defaultdict[str, dict[str, Any]] | None", config.extra_template_data) - validation = config.validation - field_constraints = config.field_constraints - snake_case_field = config.snake_case_field - strip_default_none = config.strip_default_none - aliases = config.aliases - disable_timestamp = config.disable_timestamp - enable_version_header = config.enable_version_header - enable_command_header = config.enable_command_header - command_line = config.command_line - allow_population_by_field_name = config.allow_population_by_field_name - allow_extra_fields = config.allow_extra_fields - extra_fields = config.extra_fields - use_generic_base_class = config.use_generic_base_class - apply_default_values_for_required_fields = config.apply_default_values_for_required_fields - force_optional_for_required_fields = config.force_optional_for_required_fields - class_name = config.class_name - use_standard_collections = config.use_standard_collections - use_schema_description = config.use_schema_description - use_field_description = config.use_field_description - use_field_description_example = config.use_field_description_example - use_attribute_docstrings = config.use_attribute_docstrings - use_inline_field_description = config.use_inline_field_description - use_default_kwarg = config.use_default_kwarg - reuse_model = config.reuse_model - reuse_scope = config.reuse_scope - shared_module_name = config.shared_module_name - encoding = config.encoding - enum_field_as_literal = config.enum_field_as_literal - enum_field_as_literal_map = config.enum_field_as_literal_map - ignore_enum_constraints = config.ignore_enum_constraints - use_one_literal_as_default = config.use_one_literal_as_default - use_enum_values_in_discriminator = config.use_enum_values_in_discriminator - set_default_enum_member = config.set_default_enum_member - use_subclass_enum = config.use_subclass_enum - use_specialized_enum = config.use_specialized_enum - strict_nullable = config.strict_nullable - use_generic_container_types = config.use_generic_container_types - enable_faux_immutability = config.enable_faux_immutability - disable_appending_item_suffix = config.disable_appending_item_suffix - strict_types = config.strict_types - empty_enum_field_name = config.empty_enum_field_name - custom_class_name_generator = config.custom_class_name_generator - field_extra_keys = config.field_extra_keys - field_include_all_keys = config.field_include_all_keys - field_extra_keys_without_x_prefix = config.field_extra_keys_without_x_prefix - model_extra_keys = config.model_extra_keys - model_extra_keys_without_x_prefix = config.model_extra_keys_without_x_prefix - openapi_scopes = config.openapi_scopes - include_path_parameters = config.include_path_parameters - wrap_string_literal = config.wrap_string_literal - use_title_as_name = config.use_title_as_name - use_operation_id_as_name = config.use_operation_id_as_name - use_unique_items_as_set = config.use_unique_items_as_set - use_tuple_for_fixed_items = config.use_tuple_for_fixed_items - allof_merge_mode = config.allof_merge_mode - http_headers = config.http_headers - http_ignore_tls = config.http_ignore_tls - http_timeout = config.http_timeout - use_annotated = config.use_annotated - use_serialize_as_any = config.use_serialize_as_any - use_non_positive_negative_number_constrained_types = config.use_non_positive_negative_number_constrained_types - use_decimal_for_multiple_of = config.use_decimal_for_multiple_of - original_field_name_delimiter = config.original_field_name_delimiter - use_double_quotes = config.use_double_quotes - use_union_operator = config.use_union_operator - collapse_root_models = config.collapse_root_models - collapse_root_models_name_strategy = config.collapse_root_models_name_strategy - collapse_reuse_models = config.collapse_reuse_models - skip_root_model = config.skip_root_model - use_type_alias = config.use_type_alias - use_root_model_type_alias = config.use_root_model_type_alias - special_field_name_prefix = config.special_field_name_prefix - remove_special_field_name_prefix = config.remove_special_field_name_prefix - capitalise_enum_members = config.capitalise_enum_members - keep_model_order = config.keep_model_order - custom_file_header = config.custom_file_header - custom_file_header_path = config.custom_file_header_path - custom_formatters = config.custom_formatters - custom_formatters_kwargs = config.custom_formatters_kwargs - use_pendulum = config.use_pendulum - use_standard_primitive_types = config.use_standard_primitive_types - http_query_parameters = config.http_query_parameters - treat_dot_as_module = config.treat_dot_as_module - use_exact_imports = config.use_exact_imports - union_mode = cast("UnionMode | None", config.union_mode) - output_datetime_class = config.output_datetime_class - output_date_class = config.output_date_class - keyword_only = config.keyword_only - frozen_dataclasses = config.frozen_dataclasses - no_alias = config.no_alias - use_frozen_field = config.use_frozen_field - use_default_factory_for_optional_nested_models = config.use_default_factory_for_optional_nested_models - formatters = config.formatters - settings_path = config.settings_path - parent_scoped_naming = config.parent_scoped_naming - naming_strategy = config.naming_strategy - duplicate_name_suffix = config.duplicate_name_suffix - dataclass_arguments = config.dataclass_arguments - disable_future_imports = config.disable_future_imports - type_mappings = config.type_mappings - type_overrides = config.type_overrides - read_only_write_only_model_type = config.read_only_write_only_model_type - use_status_code_in_response_name = config.use_status_code_in_response_name - all_exports_scope = config.all_exports_scope - all_exports_collision_strategy = config.all_exports_collision_strategy - field_type_collision_strategy = config.field_type_collision_strategy - module_split_mode = config.module_split_mode + # Exclusivity check: config and options cannot be used together + if config is not None and options: + msg = "Cannot specify both 'config' and individual options" + raise ValueError(msg) + + # Create GenerateConfig from options or use default + if options: + from datamodel_code_generator.config import GenerateConfig as _GenerateConfig # noqa: PLC0415 + + config = _GenerateConfig(**options) + elif config is None: + from datamodel_code_generator.config import GenerateConfig as _GenerateConfig # noqa: PLC0415 + + config = _GenerateConfig() + + # Extract values from config + input_filename = config.input_filename + input_file_type = config.input_file_type + output = config.output + output_model_type = config.output_model_type + target_python_version = config.target_python_version + target_pydantic_version = config.target_pydantic_version + base_class = config.base_class + base_class_map = config.base_class_map + additional_imports = config.additional_imports + class_decorators = config.class_decorators + custom_template_dir = config.custom_template_dir + extra_template_data: defaultdict[str, dict[str, Any]] | None = ( + defaultdict(dict, config.extra_template_data) if config.extra_template_data else None + ) + validation = config.validation + field_constraints = config.field_constraints + snake_case_field = config.snake_case_field + strip_default_none = config.strip_default_none + aliases = config.aliases + disable_timestamp = config.disable_timestamp + enable_version_header = config.enable_version_header + enable_command_header = config.enable_command_header + command_line = config.command_line + allow_population_by_field_name = config.allow_population_by_field_name + allow_extra_fields = config.allow_extra_fields + extra_fields = config.extra_fields + use_generic_base_class = config.use_generic_base_class + apply_default_values_for_required_fields = config.apply_default_values_for_required_fields + force_optional_for_required_fields = config.force_optional_for_required_fields + class_name = config.class_name + use_standard_collections = config.use_standard_collections + use_schema_description = config.use_schema_description + use_field_description = config.use_field_description + use_field_description_example = config.use_field_description_example + use_attribute_docstrings = config.use_attribute_docstrings + use_inline_field_description = config.use_inline_field_description + use_default_kwarg = config.use_default_kwarg + reuse_model = config.reuse_model + reuse_scope = config.reuse_scope + shared_module_name = config.shared_module_name + encoding = config.encoding + enum_field_as_literal = config.enum_field_as_literal + enum_field_as_literal_map = config.enum_field_as_literal_map + ignore_enum_constraints = config.ignore_enum_constraints + use_one_literal_as_default = config.use_one_literal_as_default + use_enum_values_in_discriminator = config.use_enum_values_in_discriminator + set_default_enum_member = config.set_default_enum_member + use_subclass_enum = config.use_subclass_enum + use_specialized_enum = config.use_specialized_enum + strict_nullable = config.strict_nullable + use_generic_container_types = config.use_generic_container_types + enable_faux_immutability = config.enable_faux_immutability + disable_appending_item_suffix = config.disable_appending_item_suffix + strict_types = config.strict_types + empty_enum_field_name = config.empty_enum_field_name + custom_class_name_generator = config.custom_class_name_generator + field_extra_keys = config.field_extra_keys + field_include_all_keys = config.field_include_all_keys + field_extra_keys_without_x_prefix = config.field_extra_keys_without_x_prefix + model_extra_keys = config.model_extra_keys + model_extra_keys_without_x_prefix = config.model_extra_keys_without_x_prefix + openapi_scopes = config.openapi_scopes + include_path_parameters = config.include_path_parameters + wrap_string_literal = config.wrap_string_literal + use_title_as_name = config.use_title_as_name + use_operation_id_as_name = config.use_operation_id_as_name + use_unique_items_as_set = config.use_unique_items_as_set + use_tuple_for_fixed_items = config.use_tuple_for_fixed_items + allof_merge_mode = config.allof_merge_mode + http_headers = config.http_headers + http_ignore_tls = config.http_ignore_tls + http_timeout = config.http_timeout + use_annotated = config.use_annotated + use_serialize_as_any = config.use_serialize_as_any + use_non_positive_negative_number_constrained_types = config.use_non_positive_negative_number_constrained_types + use_decimal_for_multiple_of = config.use_decimal_for_multiple_of + original_field_name_delimiter = config.original_field_name_delimiter + use_double_quotes = config.use_double_quotes + use_union_operator = config.use_union_operator + collapse_root_models = config.collapse_root_models + collapse_root_models_name_strategy = config.collapse_root_models_name_strategy + collapse_reuse_models = config.collapse_reuse_models + skip_root_model = config.skip_root_model + use_type_alias = config.use_type_alias + use_root_model_type_alias = config.use_root_model_type_alias + special_field_name_prefix = config.special_field_name_prefix + remove_special_field_name_prefix = config.remove_special_field_name_prefix + capitalise_enum_members = config.capitalise_enum_members + keep_model_order = config.keep_model_order + custom_file_header = config.custom_file_header + custom_file_header_path = config.custom_file_header_path + custom_formatters = config.custom_formatters + custom_formatters_kwargs = config.custom_formatters_kwargs + use_pendulum = config.use_pendulum + use_standard_primitive_types = config.use_standard_primitive_types + http_query_parameters = config.http_query_parameters + treat_dot_as_module = config.treat_dot_as_module + use_exact_imports = config.use_exact_imports + union_mode = cast("UnionMode | None", config.union_mode) + output_datetime_class = config.output_datetime_class + output_date_class = config.output_date_class + keyword_only = config.keyword_only + frozen_dataclasses = config.frozen_dataclasses + no_alias = config.no_alias + use_frozen_field = config.use_frozen_field + use_default_factory_for_optional_nested_models = config.use_default_factory_for_optional_nested_models + formatters = config.formatters + settings_path = config.settings_path + parent_scoped_naming = config.parent_scoped_naming + naming_strategy = config.naming_strategy + duplicate_name_suffix = config.duplicate_name_suffix + dataclass_arguments = config.dataclass_arguments + disable_future_imports = config.disable_future_imports + type_mappings = config.type_mappings + type_overrides = config.type_overrides + read_only_write_only_model_type = config.read_only_write_only_model_type + use_status_code_in_response_name = config.use_status_code_in_response_name + all_exports_scope = config.all_exports_scope + all_exports_collision_strategy = config.all_exports_collision_strategy + field_type_collision_strategy = config.field_type_collision_strategy + module_split_mode = config.module_split_mode remote_text_cache: DefaultPutDict[str, str] = DefaultPutDict() match input_: @@ -731,7 +633,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915 input_text = None if dataclass_arguments is None: - dataclass_arguments = {} + dataclass_arguments = cast("DataclassArguments", {}) if frozen_dataclasses: dataclass_arguments["frozen"] = True if keyword_only: @@ -1176,25 +1078,37 @@ def infer_input_type(text: str) -> InputFileType: ) __all__ = [ + "DEFAULT_FORMATTERS", + "DEFAULT_SHARED_MODULE_NAME", "MAX_VERSION", "MIN_VERSION", "AllExportsCollisionStrategy", "AllExportsScope", + "AllOfMergeMode", + "CollapseRootModelsNameStrategy", + "DataclassArguments", "DateClassType", "DatetimeClassType", "DefaultPutDict", "Error", + "FieldTypeCollisionStrategy", "GenerateConfig", + "GenerateConfigDict", "GeneratedModules", + "GraphQLScope", "InputFileType", "InvalidClassNameError", "InvalidFileFormatError", "LiteralType", "ModuleSplitMode", "NamingStrategy", + "OpenAPIScope", "PythonVersion", + "PythonVersionMin", "ReadOnlyWriteOnlyModelType", + "ReuseScope", "SchemaParseError", + "StrictTypes", "TargetPydanticVersion", "generate", ] diff --git a/src/datamodel_code_generator/config.py b/src/datamodel_code_generator/config.py index 8d266467b..6ef363faa 100644 --- a/src/datamodel_code_generator/config.py +++ b/src/datamodel_code_generator/config.py @@ -42,6 +42,7 @@ def __class_getitem__(cls, item: Any) -> Any: # noqa: D105 ReadOnlyWriteOnlyModelType, ReuseScope, TargetPydanticVersion, + UnionMode, ) from datamodel_code_generator.format import ( DEFAULT_FORMATTERS, @@ -54,8 +55,8 @@ def __class_getitem__(cls, item: Any) -> Any: # noqa: D105 from datamodel_code_generator.parser import LiteralType from datamodel_code_generator.types import StrictTypes -# Use Literal type to avoid circular import -UnionModeType = Literal["smart", "left_to_right"] | None +# Accept both UnionMode enum and literal string values +UnionModeType = UnionMode | Literal["smart", "left_to_right"] | None class BaseConfig(BaseModel): diff --git a/tests/main/test_main_general.py b/tests/main/test_main_general.py index 72f3252ad..65d59b526 100644 --- a/tests/main/test_main_general.py +++ b/tests/main/test_main_general.py @@ -1910,3 +1910,14 @@ def test_module_getattr_raises_for_unknown_attribute() -> None: with pytest.raises(AttributeError, match="module 'datamodel_code_generator' has no attribute 'NonExistentClass'"): _ = datamodel_code_generator.NonExistentClass + + +def test_generate_with_config_and_options_raises_error() -> None: + """Test that providing both config and options raises ValueError.""" + from datamodel_code_generator import GenerateConfig + + json_schema = '{"type": "object", "properties": {"name": {"type": "string"}}}' + config = GenerateConfig(input_file_type=InputFileType.JsonSchema) + + with pytest.raises(ValueError, match="Cannot specify both 'config' and individual options"): + generate(json_schema, config=config, snake_case_field=True) From 83c2ed647b72e1a9257c1e51c2c68749ffb107fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Dec 2025 00:46:10 +0000 Subject: [PATCH 12/12] chore: update TypedDict from Config models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated by GitHub Actions --- src/datamodel_code_generator/_types/base_config_dict.py | 5 ++++- src/datamodel_code_generator/_types/generate_config_dict.py | 5 ++++- src/datamodel_code_generator/_types/parser_config_dict.py | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/datamodel_code_generator/_types/base_config_dict.py b/src/datamodel_code_generator/_types/base_config_dict.py index dbce4210a..92834e31e 100644 --- a/src/datamodel_code_generator/_types/base_config_dict.py +++ b/src/datamodel_code_generator/_types/base_config_dict.py @@ -75,6 +75,9 @@ class DataclassArguments(TypedDict): TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +UnionMode: TypeAlias = Literal["smart", "left_to_right"] + + class BaseConfig(TypedDict): output_model_type: NotRequired[DataModelType] target_python_version: NotRequired[PythonVersion] @@ -159,7 +162,7 @@ class BaseConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[UnionMode | Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/generate_config_dict.py b/src/datamodel_code_generator/_types/generate_config_dict.py index a129f73c3..0539197ce 100644 --- a/src/datamodel_code_generator/_types/generate_config_dict.py +++ b/src/datamodel_code_generator/_types/generate_config_dict.py @@ -87,6 +87,9 @@ class DataclassArguments(TypedDict): TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +UnionMode: TypeAlias = Literal["smart", "left_to_right"] + + class GenerateConfig(TypedDict): output_model_type: NotRequired[DataModelType] target_python_version: NotRequired[PythonVersion] @@ -171,7 +174,7 @@ class GenerateConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[UnionMode | Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool] diff --git a/src/datamodel_code_generator/_types/parser_config_dict.py b/src/datamodel_code_generator/_types/parser_config_dict.py index ce833cb42..5247e8a2c 100644 --- a/src/datamodel_code_generator/_types/parser_config_dict.py +++ b/src/datamodel_code_generator/_types/parser_config_dict.py @@ -75,6 +75,9 @@ class DataclassArguments(TypedDict): TargetPydanticVersion: TypeAlias = Literal["2", "2.11"] +UnionMode: TypeAlias = Literal["smart", "left_to_right"] + + class ParserConfig(TypedDict): output_model_type: NotRequired[DataModelType] target_python_version: NotRequired[PythonVersion] @@ -159,7 +162,7 @@ class ParserConfig(TypedDict): use_standard_primitive_types: NotRequired[bool] treat_dot_as_module: NotRequired[bool | None] use_exact_imports: NotRequired[bool] - union_mode: NotRequired[Literal["smart", "left_to_right"] | None] + union_mode: NotRequired[UnionMode | Literal["smart", "left_to_right"] | None] output_datetime_class: NotRequired[DatetimeClassType | None] output_date_class: NotRequired[DateClassType | None] keyword_only: NotRequired[bool]