Skip to content

Add multiple --input-model support with inheritance preservation#2881

Merged
koxudaxi merged 29 commits intomainfrom
feature/multiple-input-model-inheritance
Dec 31, 2025
Merged

Add multiple --input-model support with inheritance preservation#2881
koxudaxi merged 29 commits intomainfrom
feature/multiple-input-model-inheritance

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Dec 31, 2025

Summary by CodeRabbit

  • New Features

    • CLI accepts multiple --input-model entries; new model-loading API and public error type for input-model processing.
  • Refactor

    • Parser config types unified under a shared ParserConfig; Config.input_model now normalized to a list.
  • Bug Fixes

    • Improved input-model validation and error mapping during loading; better import/type resolution for model inputs.
  • Tests

    • Many new and updated tests plus regenerated expected outputs covering multi-model inheritance, callable/collection types, ref strategies, and error paths.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 31, 2025

Warning

Rate limit exceeded

@koxudaxi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 22 minutes and 32 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b36b6d5 and 28418ea.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/input_model.py
  • tests/test_input_model.py
📝 Walkthrough

Walkthrough

Config.input_model changed from an optional string to an optional list of MODULE:NAME entries; CLI accepts repeated --input-model. Input-model loading/validation/merging moved into a new public loader load_model_schema (and public Error), and many inline type/schema helpers were removed from main.py and relocated to input_model.py.

Changes

Cohort / File(s) Summary
CLI / config entrypoint
src/datamodel_code_generator/__main__.py
Config.input_model type changed to Optional[list[str]]; added pre-validator coerce_input_model_to_list; replaced in-file model-loading logic with imported load_model_schema and map InputModelErrorError; removed many internal Python-type/schema helper functions.
New input-model loader
src/datamodel_code_generator/input_model.py
New public load_model_schema(...) and public Error exception that load/validate one-or-more MODULE:NAME or file inputs, extract model_json_schema, transform inheritance to allOf, aggregate $defs, and apply ref-strategy merging.
CLI argument parsing
src/datamodel_code_generator/arguments.py
--input-model now uses action="append" allowing multiple MODULE:NAME entries; help text updated and usage constraints preserved.
Parser TypedDict refactor
src/datamodel_code_generator/_types/...
.../graphql_parser_config_dict.py, .../jsonschema_parser_config_dict.py, .../openapi_parser_config_dict.py
Introduced shared ParserConfig TypedDict; parser-specific TypedDicts renamed/inherit from ParserConfig; added parser-specific optional fields (e.g., data_model_scalar_type, data_model_union_type).
Parser jsonschema tweaks
src/datamodel_code_generator/parser/jsonschema.py
PYTHON_TYPE_IMPORTS now includes "BaseModel": Import.from_full_path("pydantic.BaseModel"); guard added rejecting `"
Tests, fixtures & expected outputs
tests/test_input_model.py, tests/data/python/input_model/inheritance_models.py, tests/data/expected/main/input_model/*, tests/*
Added multi-input-model tests, deterministic timestamping, many expected output files covering inheritance, multiple input models, callable/complex types, dataclass/TypedDict/msgspec outputs, ref_strategy permutations, and unit tests for new jsonschema resolver.
Removals / internal cleanup
src/datamodel_code_generator/__main__.py (internal sections)
Removed numerous inline helpers for Python-type serialization, origin/name inspection, and JSON Schema annotation; responsibilities moved into input_model.py and parser modules.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CLI as CLI / ArgParser
    participant Main as __main__.py
    participant Loader as input_model.load_model_schema
    participant Importer as Module/File Importer
    participant SchemaGen as model_json_schema
    participant Transformer as Inheritance Transformer
    participant Merger as Schema Merger

    rect rgb(0,128,128,0.06)
    CLI->>Main: parse args (repeated --input-model)
    end

    Main->>Loader: load_model_schema(input_models, input_file_type, ref_strategy, output_model_type)
    loop per input_model
        Loader->>Importer: import MODULE:NAME or load file path
        alt import success
            Importer-->>Loader: model class
            Loader->>SchemaGen: call model_json_schema / extract schema
            SchemaGen-->>Loader: schema fragment (with x-python-* metadata)
            alt model has inheritance
                Loader->>Transformer: convert inheritance → allOf
                Transformer-->>Loader: transformed fragment
            end
        else import failure
            Importer-->>Loader: raise InputModelError
        end
    end
    Loader->>Merger: merge fragments into final schema (anyOf root + aggregated $defs) and apply ref_strategy
    Merger-->>Loader: merged_schema
    Loader-->>Main: return merged_schema or raise Error
    Main->>Main: continue generation using merged_schema
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

breaking-change-analyzed

Suggested reviewers

  • ilovelinux

Poem

🐇 I hop through modules, nibble fields with cheer,
I stitch parents to children so types all appear,
I gather many inputs, merge schemas neat and bright,
I mark Python hints and shape inheritance right,
Hooray — one tidy schema, hopped into the light!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add multiple --input-model support with inheritance preservation' accurately summarizes the main change: enabling multiple input models with preserved inheritance relationships.
Docstring Coverage ✅ Passed Docstring coverage is 96.92% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 31, 2025

📚 Docs Preview: https://pr-2881.datamodel-code-generator.pages.dev

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 31, 2025

Codecov Report

❌ Patch coverage is 99.87790% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 99.37%. Comparing base (efe8dfa) to head (28418ea).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/datamodel_code_generator/parser/jsonschema.py 93.75% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2881      +/-   ##
==========================================
- Coverage   99.38%   99.37%   -0.02%     
==========================================
  Files          91       92       +1     
  Lines       15643    16040     +397     
  Branches     1848     1893      +45     
==========================================
+ Hits        15547    15939     +392     
- Misses         50       52       +2     
- Partials       46       49       +3     
Flag Coverage Δ
unittests 99.37% <99.87%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 31, 2025

CodSpeed Performance Report

Merging #2881 will not alter performance

Comparing feature/multiple-input-model-inheritance (28418ea) with main (36530b1)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

✅ 11 untouched
⏩ 98 skipped1

Footnotes

  1. 98 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/datamodel_code_generator/__main__.py (1)

1764-1784: generate_cli_command and multi‑valued input_model don’t yet match the new argparse semantics

With Config.input_model: Optional[list[str]] and --input-model now using action="append", a pyproject config like:

input-model = ["mod_a:ModelA", "mod_b:ModelB"]

will cause generate_cli_command to emit:

datamodel-codegen --input-model "mod_a:ModelA" "mod_b:ModelB"

However, argparse expects one value per --input-model flag in this configuration, so the second value will be misparsed as a positional/next option.

To make generate_cli_command round‑trip correctly with the new multi‑model behavior, you’ll likely need to special‑case input_model (and any similar “append” options) so that lists are rendered as repeated flags, e.g.:

datamodel-codegen --input-model mod_a:ModelA --input-model mod_b:ModelB

rather than a single flag with a space‑separated list.

Also applies to: 465-465

🧹 Nitpick comments (2)
src/datamodel_code_generator/_types/openapi_parser_config_dict.py (1)

34-155: OpenAPI config now cleanly extends the shared JSON Schema parser config

Having ParserConfig(TypedDict) as the shared base and JSONSchemaParserConfig(ParserConfig) as an intermediate, with OpenAPIParserConfigDict(JSONSchemaParserConfig) adding OpenAPI‑specific options, makes the hierarchy explicit and consistent with the runtime ParserConfig model. Only nit is the slight name asymmetry with JSONSchemaParserConfigDict in the JSON schema module, but that’s cosmetic.

src/datamodel_code_generator/__main__.py (1)

388-389: Consider cleaning up unused # noqa directives to satisfy Ruff hints

Ruff reports several RUF100 warnings for unused # noqa codes (e.g., PLC0415, UP007, UP045, PLR0912, PLR0914, PLR0915) on:

  • The local field_validator import inside the Pydantic v2 branch.
  • Some field declarations (input, input_model, etc.).
  • Function definitions marked with complexity‑related noqa.

If these codes are no longer enabled in your ruff configuration, dropping the corresponding # noqa fragments (or narrowing them to only active codes you still need) will quiet the linter without changing behavior.

Also applies to: 464-465, 1202-1202, 1287-1287, 1304-1305

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36530b1 and 7e63c17.

📒 Files selected for processing (7)
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/_types/graphql_parser_config_dict.py
  • src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py
  • src/datamodel_code_generator/_types/openapi_parser_config_dict.py
  • src/datamodel_code_generator/arguments.py
  • tests/data/python/input_model/inheritance_models.py
  • tests/test_input_model.py
🧰 Additional context used
🧬 Code graph analysis (3)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
src/datamodel_code_generator/enums.py (3)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
  • DataModelType (48-56)
src/datamodel_code_generator/_types/graphql_parser_config_dict.py (2)
src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (1)
  • ParserConfig (33-144)
src/datamodel_code_generator/_types/openapi_parser_config_dict.py (1)
  • ParserConfig (34-145)
src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (3)
src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)
  • ParserConfig (33-144)
src/datamodel_code_generator/_types/openapi_parser_config_dict.py (1)
  • ParserConfig (34-145)
src/datamodel_code_generator/config.py (1)
  • ParserConfig (201-324)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/__main__.py

[error] 1276-1276: ruff: PLC0206 Extracting value from dictionary without calling .items()


[error] 1367-1367: ruff: E501 Line too long (123 > 120)

🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1202-1202: Unused noqa directive (non-enabled: PLR0912)

Remove unused noqa directive

(RUF100)


1287-1287: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


1304-1304: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1305-1305: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: benchmarks
  • GitHub Check: py312-black23 on Ubuntu
  • GitHub Check: Analyze (python)
  • GitHub Check: 3.13 on Windows
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.11 on Windows
🔇 Additional comments (9)
src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (1)

33-148: Shared ParserConfig base for JSON Schema looks consistent and backwards‑compatible

Defining ParserConfig(TypedDict) and having JSONSchemaParserConfigDict(ParserConfig) as a thin subclass cleanly preserves the old public name while aligning with the new shared config structure used by OpenAPI/GraphQL. No issues from a typing or compatibility standpoint.

src/datamodel_code_generator/arguments.py (1)

161-169: --input-model append semantics align with new multi‑model support

Switching --input-model to action="append" with updated help text is consistent with Config.input_model: Optional[list[str]] and the new multi‑model loading path. No issues spotted here.

src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)

33-149: GraphQL parser config layering is coherent with JSONSchema/OpenAPI

Using a shared ParserConfig(TypedDict) and extending it via GraphQLParserConfigDict for the scalar/union model types matches the pattern in the other parser config modules and keeps the config surfaces aligned. Looks good.

tests/data/python/input_model/inheritance_models.py (1)

1-59: Inheritance fixture models cover key scenarios and look correct

The Pydantic models capture straight, forked, empty‑child, and optional‑only inheritance patterns needed for the new tests, with clean typing and docstrings. No issues.

tests/test_input_model.py (3)

1150-1199: Multi‑model test helpers correctly exercise repeated --input-model

The new run_multiple_input_models_and_assert/run_multiple_input_models_error_and_assert helpers build args via repeated --input-model flags, assert exit codes, and centralize output/stderr checks. This nicely mirrors the new CLI semantics and keeps the later tests concise.


1202-1382: Inheritance and multi‑model tests provide strong coverage of new behavior

The added tests around single‑model inheritance, multi‑level chains, forked inheritance, mixed depths, union/TypeAlias generation, and Pydantic/dataclass outputs collectively validate the new inheritance‑aware schema transformation and multi‑model loader. The negative cases for non‑BaseModel, Pydantic v1 runtime, invalid formats, and incompatible --input-file-type are also well specified and match the new error messages.


1668-1731: sys.path/CWD and filename‑only tests are a good sanity check on module loading

The tests for cwd already in sys.path and .py files without path separators validate that the new loader neither duplicates cwd entries nor fails on filename‑only imports. This guards against subtle regressions in the sys.path manipulation and path vs module detection logic.

src/datamodel_code_generator/__main__.py (2)

388-398: Config.input_model list type and coercion validators are appropriate

Normalizing input_model to Optional[list[str]] with coerce_input_model_to_list in both the Pydantic v2 and v1 branches keeps pyproject (string or list) and CLI (action="append") inputs compatible while presenting a single internal shape. This is a clean way to extend behavior without breaking existing configs.

If you want extra safety, double‑check Pydantic v1/v2 docs to ensure mode="before" field validators with @classmethod signatures are still the recommended pattern for this style of coercion.

Also applies to: 456-463, 465-467


2156-2167: main() correctly routes all input_model usage through the new multi‑model loader

The updated main path unconditionally calls _load_multiple_model_schemas when config.input_model is set, letting that function dispatch to the existing _load_model_schema for single‑model cases and to the new multi‑model logic otherwise. This keeps backward behavior for single models while centralizing validation and schema generation for the new multi‑model scenarios.

Comment thread src/datamodel_code_generator/__main__.py Outdated
Comment thread src/datamodel_code_generator/__main__.py Outdated
@koxudaxi koxudaxi force-pushed the feature/multiple-input-model-inheritance branch from 7e63c17 to 00a3dff Compare December 31, 2025 05:07
Comment thread tests/data/expected/main/input_model/forked_inheritance.py Dismissed
Comment thread tests/data/expected/main/input_model/mixed_inheritance.py Dismissed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/datamodel_code_generator/__main__.py (2)

1202-1275: Fix dictionary iteration pattern (PLC0206) and remove unused noqa directive.

Two issues need attention:

  1. Line 1202: The # noqa: PLR0912 directive is unused per Ruff and should be removed.
  2. Lines 1271-1273: The loop that copies extra schema keys triggers PLC0206 by re-indexing the dictionary during iteration.
🔎 Proposed fixes
-def _transform_single_model_to_inheritance(  # noqa: PLR0912
+def _transform_single_model_to_inheritance(
     schema: dict[str, object],
     model_class: type,
     schema_generator: type,
     processed_parents: dict[str, dict[str, object]] | None = None,
 ) -> dict[str, object]:

And fix the dictionary iteration:

-    for key in schema:
-        if key not in {"$defs", "properties", "required", "title", "type", "allOf"}:
-            new_schema[key] = schema[key]
+    for key, value in schema.items():
+        if key not in {"$defs", "properties", "required", "title", "type", "allOf"}:
+            new_schema[key] = value

1278-1415: Fix long line (E501) and remove unused noqa directives.

Several issues need attention:

  1. Line 1278: The # noqa: PLR0912, PLR0914, PLR0915 directives are unused.
  2. Lines 1295-1296: The # noqa: PLC0415 directives are unused.
  3. Line 1359: The error message exceeds 120 characters.
🔎 Proposed fixes

Remove unused noqa directives:

-def _load_multiple_model_schemas(  # noqa: PLR0912, PLR0914, PLR0915
+def _load_multiple_model_schemas(
     input_models: list[str],
     input_file_type: InputFileType,
     ref_strategy: InputModelRefStrategy | None = None,
     output_model_type: DataModelType = DataModelType.PydanticBaseModel,
 ) -> dict[str, object]:
-    import importlib.util  # noqa: PLC0415
-    import sys  # noqa: PLC0415
+    import importlib.util
+    import sys

Break the long error message:

-        if not hasattr(obj, "model_json_schema"):
-            msg = "Multiple --input-model with Pydantic model requires Pydantic v2 runtime. Please upgrade Pydantic to v2."
-            raise Error(msg)
+        if not hasattr(obj, "model_json_schema"):
+            msg = (
+                "Multiple --input-model with Pydantic model requires Pydantic v2 runtime. "
+                "Please upgrade Pydantic to v2."
+            )
+            raise Error(msg)
🧹 Nitpick comments (4)
tests/test_input_model.py (2)

1316-1336: Consider consolidating duplicate test.

test_input_model_multiple_generates_anyof appears to test the same scenario as test_input_model_multiple_forked_inheritance (same inputs, same expected output file). If the intent is to emphasize the TypeAlias/union behavior, consider adding a unique assertion or documenting the distinction more clearly.


1581-1585: Consider using a closure variable instead of function attribute.

The pattern of using hasattr(mock_spec, "called") and setting mock_spec.called = True works but is unconventional. A closure variable would be clearer.

🔎 Alternative using closure
-    def mock_spec(*args: object, **kwargs: object) -> None:
-        if hasattr(mock_spec, "called"):
-            return None
-        mock_spec.called = True  # type: ignore[attr-defined]
-        return original_spec_from_file_location(*args, **kwargs)
+    called = False
+
+    def mock_spec(*args: object, **kwargs: object) -> None:
+        nonlocal called
+        if called:
+            return None
+        called = True
+        return original_spec_from_file_location(*args, **kwargs)
src/datamodel_code_generator/__main__.py (2)

388-396: Remove unused noqa directive on Line 388.

The # noqa: PLC0415 is flagged as unused. While the project keeps defensive noqa directives on lazy imports inside functions/methods, this import is at class-level within a validator block, not truly "lazy" in the same sense.

🔎 Suggested fix
-        from pydantic import field_validator as _field_validator  # noqa: PLC0415
+        from pydantic import field_validator as _field_validator

465-465: Remove unused noqa directive.

The # noqa: UP045 is flagged as unused by Ruff.

🔎 Suggested fix
-    input_model: Optional[list[str]] = None  # noqa: UP045
+    input_model: Optional[list[str]] = None
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e63c17 and 00a3dff.

📒 Files selected for processing (12)
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/_types/graphql_parser_config_dict.py
  • src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py
  • src/datamodel_code_generator/_types/openapi_parser_config_dict.py
  • src/datamodel_code_generator/arguments.py
  • tests/data/expected/main/input_model/forked_inheritance.py
  • tests/data/expected/main/input_model/mixed_inheritance.py
  • tests/data/expected/main/input_model/multi_level_inheritance.py
  • tests/data/expected/main/input_model/no_inheritance.py
  • tests/data/expected/main/input_model/single_inheritance.py
  • tests/data/python/input_model/inheritance_models.py
  • tests/test_input_model.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/datamodel_code_generator/_types/openapi_parser_config_dict.py
  • src/datamodel_code_generator/arguments.py
  • tests/data/python/input_model/inheritance_models.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/__main__.py
🧬 Code graph analysis (6)
tests/data/expected/main/input_model/mixed_inheritance.py (3)
src/datamodel_code_generator/model/type_alias.py (1)
  • TypeAlias (37-42)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/expected/main/input_model/multi_level_inheritance.py (4)
  • GrandParent (10-11)
  • Parent (14-15)
  • Intermediate (18-19)
  • GrandChild (22-23)
tests/data/expected/main/input_model/multi_level_inheritance.py (2)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/python/input_model/inheritance_models.py (4)
  • GrandParent (8-11)
  • Parent (14-17)
  • Intermediate (32-35)
  • GrandChild (38-41)
tests/data/expected/main/input_model/no_inheritance.py (2)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/python/input_model/inheritance_models.py (1)
  • NoInheritance (44-47)
src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (2)
src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)
  • ParserConfig (33-144)
src/datamodel_code_generator/_types/openapi_parser_config_dict.py (1)
  • ParserConfig (34-145)
tests/data/expected/main/input_model/single_inheritance.py (5)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/expected/main/input_model/forked_inheritance.py (3)
  • GrandParent (10-11)
  • Parent (14-15)
  • ChildA (18-19)
tests/data/expected/main/input_model/mixed_inheritance.py (3)
  • GrandParent (10-11)
  • Parent (14-15)
  • ChildA (18-19)
tests/data/expected/main/input_model/multi_level_inheritance.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/python/input_model/inheritance_models.py (3)
  • GrandParent (8-11)
  • Parent (14-17)
  • ChildA (20-23)
tests/test_input_model.py (1)
src/datamodel_code_generator/__main__.py (2)
  • main (1909-2213)
  • Exit (125-131)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/__main__.py

[error] 1267-1269: ruff: PLC0206 Extracting value from dictionary without calling .items()


[error] 1355-1355: ruff: E501 Line too long (123 > 120)


[warning] ruff-format reformatted 2 files during pre-commit hooks


[warning] ruff-format reformatted 2 files during pre-commit hooks

🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1202-1202: Unused noqa directive (non-enabled: PLR0912)

Remove unused noqa directive

(RUF100)


1278-1278: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


1295-1295: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1296-1296: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: 3.14 on macOS
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (19)
tests/data/expected/main/input_model/no_inheritance.py (1)

1-11: LGTM!

This test fixture correctly demonstrates the expected output for a simple model with no inheritance chain, properly converted from BaseModel to TypedDict.

tests/data/expected/main/input_model/single_inheritance.py (1)

1-19: LGTM!

This test fixture correctly demonstrates single-branch inheritance preservation when converting from Pydantic models to TypedDict. The class ordering (base → derived) and field types are accurate.

src/datamodel_code_generator/_types/jsonschema_parser_config_dict.py (1)

33-148: LGTM!

The refactoring to establish ParserConfig as the base TypedDict is well-structured. Keeping JSONSchemaParserConfigDict as an empty subclass maintains backward compatibility while enabling the shared inheritance hierarchy across parser configurations.

tests/data/expected/main/input_model/forked_inheritance.py (1)

1-26: LGTM!

This test fixture correctly demonstrates forked inheritance preservation with a TypeAlias union representing the multiple child types. The structure properly captures the diamond-shaped inheritance pattern.

tests/data/expected/main/input_model/multi_level_inheritance.py (1)

1-23: LGTM!

This test fixture correctly demonstrates deep multi-level inheritance preservation (4 levels) with proper handling of generic types like list[str].

src/datamodel_code_generator/_types/graphql_parser_config_dict.py (1)

33-149: LGTM!

The refactoring correctly separates the shared ParserConfig base from GraphQL-specific fields (data_model_scalar_type, data_model_union_type). This maintains backward compatibility while enabling type hierarchy reuse.

tests/data/expected/main/input_model/mixed_inheritance.py (1)

1-30: LGTM!

This test fixture correctly demonstrates complex mixed-depth inheritance where ChildA and GrandChild (at different levels) share common ancestors. The TypeAlias properly represents the union of the specified input models.

tests/test_input_model.py (8)

20-21: LGTM!

Constants are well-defined. The timestamp format with timezone (-07:00) is consistent with the fixture files showing UTC offset conversion.


1154-1203: LGTM!

The helper functions are well-structured and follow the same pattern as the single-model helpers. The expected_output_not_contains parameter is a useful addition for verifying exclusions.


1206-1266: LGTM!

Single-model inheritance tests provide good coverage of basic, multi-level, and no-inheritance scenarios with deterministic output verification.


1338-1376: LGTM!

Good coverage of different output model types (pydantic.BaseModel, dataclasses.dataclass) for multiple input models.


1396-1423: LGTM!

The Pydantic v1 error simulation using mock_hasattr is a reasonable approach for testing the version detection logic without requiring actual Pydantic v1 installation.


1503-1564: LGTM!

Good edge case coverage including module reuse optimization, file path format, and ref-strategy compatibility.


1678-1703: LGTM!

The sys.path duplication test correctly verifies that cwd is not added multiple times. The assertion final_count <= initial_count + 1 properly handles both cases (cwd already present vs. newly added).


1706-1736: LGTM!

Good test coverage for mixed input formats (module path + file path) in the same invocation.

src/datamodel_code_generator/__main__.py (4)

456-462: LGTM! Consistent backwards compatibility handling.

The Pydantic v1 branch correctly mirrors the v2 implementation, ensuring input_model accepts both string and list inputs.


1193-1199: LGTM! Clean parent extraction logic.

The function correctly identifies direct BaseModel parents while excluding BaseModel itself. The single-inheritance limitation (only first parent honored in Line 1227) is a known design choice.


1508-1509: LGTM! Correct integration of inheritance transformation.

The single-model path now correctly applies the inheritance transformation, ensuring consistency with the multi-model flow.


2139-2144: LGTM! Clean integration of multi-model loading.

The main function correctly dispatches to _load_multiple_model_schemas, passing all necessary parameters including the ref strategy and output model type.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/datamodel_code_generator/__main__.py (3)

388-396: Clean backwards-compatible field validators (with minor lint cleanup).

The coerce_input_model_to_list validators for both Pydantic v1 and v2 nicely handle the transition from single to multiple --input-model values, preserving backwards compatibility. The type change to Optional[list[str]] on line 465 is consistent.

Minor cleanup: remove the unused noqa: PLC0415 directive on line 388, as flagged by static analysis.

🔎 Proposed cleanup
-        from pydantic import field_validator as _field_validator  # noqa: PLC0415
+        from pydantic import field_validator as _field_validator

Also applies to: 456-462, 465-465


1276-1408: Well-architected multi-model loader with comprehensive validation.

The _load_multiple_model_schemas function provides solid multi-model support:

  • Delegates to single-model path for backwards compatibility
  • Reuses module loading via loaded_modules cache
  • Validates all models are Pydantic v2 BaseModel classes
  • Transforms each model to inheritance structure
  • Merges all models into a unified schema with anyOf root references
  • Applies ref strategy filtering

The implementation handles both module and file-path formats correctly and provides clear error messages.

Minor cleanup: remove unused noqa directives on lines 1276, 1293, and 1294 as flagged by static analysis.

🔎 Proposed cleanup
-def _load_multiple_model_schemas(  # noqa: PLR0912, PLR0914, PLR0915
+def _load_multiple_model_schemas(
     input_models: list[str],
     input_file_type: InputFileType,
     ref_strategy: InputModelRefStrategy | None = None,
     output_model_type: DataModelType = DataModelType.PydanticBaseModel,
 ) -> dict[str, object]:
     """Load and merge schemas from multiple Python import paths with inheritance support.
 
     Args:
         input_models: List of import paths in 'module.path:ObjectName' format
         input_file_type: Current input file type setting for validation
         ref_strategy: Strategy for handling referenced types
         output_model_type: Target output model type for reuse-foreign strategy
 
     Returns:
         Merged schema dict with anyOf referencing all root models
     """
-    import importlib.util  # noqa: PLC0415
-    import sys  # noqa: PLC0415
+    import importlib.util
+    import sys

Note on potential edge case: As mentioned in past reviews, $defs entries are keyed purely by model_class.__name__. If users pass multiple models with the same name from different modules, later definitions will overwrite earlier ones. This is an acceptable edge case for initial implementation but could be addressed in future enhancements by using fully-qualified names.


464-465: Optional: Investigate unused noqa directives.

Static analysis flags noqa directives on lines 464-465 as unused. These may be legitimate suppressions for certain type-checking rules or false positives. Consider verifying and removing if truly unnecessary.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00a3dff and fd34ae2.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/__main__.py
  • tests/test_input_model.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_input_model.py (2)
tests/conftest.py (2)
  • assert_output (545-560)
  • freeze_time (352-355)
src/datamodel_code_generator/__main__.py (2)
  • main (1902-2206)
  • Exit (125-131)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
src/datamodel_code_generator/enums.py (3)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
  • DataModelType (48-56)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1276-1276: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


1293-1293: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1294-1294: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on macOS
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: 3.11 on Ubuntu
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.11 on macOS
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: 3.14 on macOS
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on macOS
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (8)
tests/test_input_model.py (4)

6-6: LGTM! Clean imports and well-defined constants.

The addition of Path import and test utilities (assert_output, freeze_time) aligns with the new inheritance test requirements. The EXPECTED_INPUT_MODEL_PATH and TIMESTAMP constants provide clean reusable test data references.

Also applies to: 15-15, 20-21


1154-1183: Excellent test helper abstraction.

The run_multiple_input_models_and_assert function provides clean support for testing multiple --input-model invocations. The addition of expected_output_not_contains for negative assertions is a thoughtful enhancement over the single-model variant.


1185-1203: LGTM! Solid error-path test helper.

The run_multiple_input_models_error_and_assert function mirrors the success-path helper appropriately, ensuring comprehensive error scenario coverage for multi-model inputs.


1206-1744: Outstanding comprehensive test coverage for inheritance scenarios!

The inheritance test suite is exceptionally thorough, covering:

  • Single and multi-level inheritance chains
  • Forked inheritance (multiple models sharing a common parent)
  • Multiple output types (TypedDict, Pydantic, dataclass)
  • Error conditions (invalid formats, missing modules/files, Pydantic v1 runtime)
  • Edge cases (empty children, optional-only fields, config string coercion)

The use of freeze_time and assert_output with expected fixture files ensures deterministic, maintainable tests.

src/datamodel_code_generator/__main__.py (4)

1193-1195: LGTM! Clean parent extraction logic.

The _get_base_model_parents function correctly identifies direct BaseModel parents while excluding BaseModel itself, preventing infinite recursion in inheritance transformation.


1198-1273: Solid inheritance transformation implementation.

The _transform_single_model_to_inheritance function correctly:

  • Recursively processes parent inheritance chains using the processed_parents cache
  • Merges $defs from parent schemas
  • Creates an allOf structure with parent reference
  • Filters out parent fields from child properties and required
  • Preserves extra schema properties (like x-python-type) using .items() iteration

The limitation of only honoring the first BaseModel parent in multi-inheritance scenarios is acceptable and aligns with previous design discussions.


1501-1502: LGTM! Clean integration of inheritance transformation.

The call to _transform_single_model_to_inheritance is correctly placed after schema generation and type preservation, ensuring single-model inputs also benefit from inheritance structure transformation.


2132-2137: Perfect integration of multi-model loader in main flow.

The switch to _load_multiple_model_schemas correctly passes all required parameters and handles both single and multiple --input-model scenarios transparently.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/datamodel_code_generator/__main__.py (3)

388-396: Clean up unused noqa directive.

Line 388 has an unused noqa: PLC0415 directive. The PLC0415 check is not enabled in your linter configuration, so this directive can be safely removed.

🔎 Proposed cleanup
-        from pydantic import field_validator as _field_validator  # noqa: PLC0415
+        from pydantic import field_validator as _field_validator

Based on static analysis hints.


465-465: Clean up unused noqa directive.

The noqa: UP045 directive is not needed as the UP045 check is not enabled.

🔎 Proposed cleanup
-    input_model: Optional[list[str]] = None  # noqa: UP045
+    input_model: Optional[list[str]] = None

Based on static analysis hints.


1269-1400: LGTM with optional cleanup.

The multi-model loading logic is well-implemented:

  • Properly delegates to single-model path for backward compatibility
  • Handles both module imports and file paths correctly
  • Validates that objects are Pydantic v2 BaseModel classes
  • Applies inheritance transformation and merges schemas appropriately
  • Creates proper anyOf root references

Previous review concerns about error message formatting have been addressed.

Optional cleanup: Lines 1269, 1286, and 1287 have unused noqa directives that could be removed:

🔎 Proposed cleanup
-def _load_multiple_model_schemas(  # noqa: PLR0912, PLR0914, PLR0915
+def _load_multiple_model_schemas(
     input_models: list[str],
     input_file_type: InputFileType,
     ref_strategy: InputModelRefStrategy | None = None,
     output_model_type: DataModelType = DataModelType.PydanticBaseModel,
 ) -> dict[str, object]:
     """Load and merge schemas from multiple Python import paths with inheritance support.

     Args:
         input_models: List of import paths in 'module.path:ObjectName' format
         input_file_type: Current input file type setting for validation
         ref_strategy: Strategy for handling referenced types
         output_model_type: Target output model type for reuse-foreign strategy

     Returns:
         Merged schema dict with anyOf referencing all root models
     """
-    import importlib.util  # noqa: PLC0415
-    import sys  # noqa: PLC0415
+    import importlib.util
+    import sys

Based on static analysis hints.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd34ae2 and 38c777a.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/__main__.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1269-1269: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


1286-1286: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1287-1287: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.14 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (5)
src/datamodel_code_generator/__main__.py (5)

456-462: LGTM!

The validator correctly uses the cross-version field_validator utility and maintains backward compatibility by coercing string inputs to lists.


1193-1195: LGTM!

The function correctly identifies BaseModel parent classes while excluding BaseModel itself. The logic is clean and straightforward.


1198-1266: LGTM!

The inheritance transformation logic is sound and comprehensive:

  • Correctly identifies parent BaseModel classes
  • Recursively processes parent schemas with caching
  • Properly merges $defs and creates allOf references
  • Filters child-specific properties and required fields
  • Preserves extra schema properties

Previous review concerns about dictionary iteration have been addressed.


1493-1494: LGTM!

The inheritance transformation is correctly applied to single models as well, ensuring consistent behavior across both single and multi-model code paths.


2124-2129: LGTM!

The integration correctly calls _load_multiple_model_schemas with all required parameters. Single-model cases are handled via internal delegation, preserving backward compatibility.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38c777a and fe421ce.

📒 Files selected for processing (3)
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/input_model.py
  • tests/test_input_model.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/__main__.py
🧬 Code graph analysis (2)
tests/test_input_model.py (3)
tests/conftest.py (1)
  • assert_output (545-560)
src/datamodel_code_generator/__main__.py (3)
  • main (973-1283)
  • Exit (125-131)
  • get (148-150)
src/datamodel_code_generator/input_model.py (1)
  • load_model_schema (625-763)
src/datamodel_code_generator/input_model.py (1)
src/datamodel_code_generator/enums.py (3)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
🪛 GitHub Actions: Lint
tests/test_input_model.py

[error] 1748-1748: ARG001 Unused function argument: tmp_path

src/datamodel_code_generator/input_model.py

[error] 459-459: N814 Camelcase DataModelType imported as constant DT


[error] 491-491: N814 Camelcase InputModelRefStrategy imported as constant IRS


[error] 645-645: N814 Camelcase DataModelType imported as constant DT


[error] 646-646: N817 CamelCase InputFileType imported as acronym IFT


[error] 647-647: N814 Camelcase InputModelRefStrategy imported as constant IRS


[error] 789-789: N817 CamelCase InputFileType imported as acronym IFT


[error] 790-790: N814 Camelcase InputModelRefStrategy imported as constant IRS

🪛 Ruff (0.14.10)
tests/test_input_model.py

1748-1748: Unused function argument: tmp_path

(ARG001)

src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1203-1203: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1204-1204: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


83-83: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


127-127: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


143-143: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


158-158: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


185-185: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


233-233: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


234-234: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


235-235: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


236-236: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


237-237: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


238-238: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


239-239: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


260-260: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


266-266: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


268-268: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


269-269: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


286-286: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


316-316: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


353-353: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


354-354: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


355-355: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


375-375: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


431-431: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


433-433: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


434-434: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


459-459: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


491-491: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


534-534: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


535-535: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


536-536: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


547-547: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


548-548: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


625-625: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


642-642: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


643-643: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


645-645: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


646-646: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


647-647: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


766-766: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


786-786: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


787-787: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


789-789: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


790-790: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


872-872: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


884-884: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.14 on macOS
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on macOS
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (10)
tests/test_input_model.py (3)

1154-1183: Well-structured test helper for multiple input models.

The run_multiple_input_models_and_assert helper is well designed with:

  • Clear parameter names and types
  • Support for both positive and negative content assertions
  • Proper use of __tracebackhide__ for cleaner test output

1185-1203: Error assertion helper follows consistent pattern.

The run_multiple_input_models_error_and_assert helper mirrors the single-model version appropriately and handles the multiple input model case cleanly.


1206-1223: Good use of freeze_time and external expected files for deterministic tests.

The inheritance tests properly use freeze_time to ensure consistent timestamps and assert_output to compare against expected output files, making tests reproducible.

src/datamodel_code_generator/__main__.py (3)

388-396: Correct coercion validator for backwards compatibility.

The coerce_input_model_to_list validator correctly handles the migration from str to list[str] for the input_model config field, ensuring existing single-model configs continue to work.


456-462: Pydantic v1 validator mirrors v2 implementation correctly.

The v1 branch implementation is consistent with the v2 version, maintaining backwards compatibility across Pydantic versions.


1202-1214: Clean integration with new input_model module.

The loading logic properly:

  • Imports the new load_model_schema function and Error class
  • Passes all required parameters including the new list-based input_model
  • Wraps InputModelError into the generic Error for consistent error handling
src/datamodel_code_generator/input_model.py (4)

564-622: Inheritance transformation logic is well-designed.

The _transform_single_model_to_inheritance function correctly:

  • Recursively processes parent classes first using processed_parents cache
  • Extracts child-only properties and required fields
  • Builds proper allOf structure with parent $ref
  • Merges $defs from parent schemas

The caching mechanism prevents redundant schema generation for shared ancestors in diamond inheritance patterns.


625-763: Core multi-model loading logic is sound.

The load_model_schema function handles:

  • Single model delegation to _load_single_model_schema
  • Module caching via loaded_modules dict
  • Both file path and module import formats
  • Proper Pydantic v2 runtime validation
  • Merged $defs with anyOf root references

One note: when multiple models with the same __name__ from different modules are provided, later ones will overwrite earlier ones in merged_defs. This edge case is documented in past review comments as an optional future improvement.


15-17: Simple and clear Error class.

The Error exception class provides a clean abstraction for input model loading errors, keeping the module's error handling separate from the main module's error types.


642-651: Fix import aliases in load_model_schema to resolve pipeline failures.

Multiple N814/N817 violations here need to be addressed.

🔎 Proposed fix
-    from datamodel_code_generator import DataModelType as DT  # noqa: PLC0415
-    from datamodel_code_generator import InputFileType as IFT  # noqa: PLC0415
-    from datamodel_code_generator.arguments import InputModelRefStrategy as IRS  # noqa: PLC0415
+    from datamodel_code_generator import DataModelType  # noqa: PLC0415
+    from datamodel_code_generator import InputFileType  # noqa: PLC0415
+    from datamodel_code_generator.arguments import InputModelRefStrategy  # noqa: PLC0415

     if output_model_type is None:
-        output_model_type = DT.PydanticBaseModel
+        output_model_type = DataModelType.PydanticBaseModel

Then update all usages of IFT and IRS throughout the function.

⛔ Skipped due to learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Comment thread src/datamodel_code_generator/input_model.py
Comment thread src/datamodel_code_generator/input_model.py
Comment thread src/datamodel_code_generator/input_model.py Outdated
Comment thread tests/test_input_model.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe421ce and 2be74fd.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/input_model.py
  • tests/test_input_model.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🧬 Code graph analysis (2)
tests/test_input_model.py (6)
tests/conftest.py (2)
  • assert_output (545-560)
  • freeze_time (352-355)
src/datamodel_code_generator/parser/base.py (1)
  • parent (612-614)
src/datamodel_code_generator/__main__.py (3)
  • main (973-1283)
  • Exit (125-131)
  • get (148-150)
src/datamodel_code_generator/model/base.py (2)
  • name (827-829)
  • path (908-910)
src/datamodel_code_generator/enums.py (1)
  • InputFileType (35-45)
src/datamodel_code_generator/input_model.py (1)
  • load_model_schema (625-765)
src/datamodel_code_generator/input_model.py (1)
src/datamodel_code_generator/enums.py (5)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
  • UnionMode (199-203)
  • StrictTypes (220-227)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/input_model.py

[error] 645-648: PL C0415 import should be at the top-level of a file.

🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


83-83: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


127-127: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


143-143: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


158-158: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


185-185: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


233-233: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


234-234: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


235-235: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


236-236: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


237-237: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


238-238: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


239-239: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


260-260: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


266-266: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


268-268: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


269-269: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


286-286: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


316-316: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


353-353: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


354-354: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


355-355: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


375-375: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


431-431: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


433-433: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


434-434: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


459-459: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


491-491: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


534-534: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


535-535: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


536-536: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


547-547: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


548-548: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


625-625: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


642-642: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


643-643: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


649-649: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


768-768: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


788-788: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


789-789: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


791-791: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


792-792: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


874-874: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


886-886: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: 3.14 on macOS
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: 3.11 on macOS
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: py312-black24 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on macOS
  • GitHub Check: 3.13 on Windows
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks

Comment thread src/datamodel_code_generator/input_model.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/test_input_model.py (2)

1389-1416: Consider documenting the mock behavior.

The mock for hasattr uses a counter-based approach that returns False for the first two calls to model_json_schema check. While this works, adding a brief comment explaining why call_count <= 2 is the threshold would improve maintainability.

🔎 Suggested documentation
     def mock_hasattr(obj: object, name: str) -> bool:
         nonlocal call_count
         if name == "model_json_schema":
             call_count += 1
+            # Return False for first two calls to trigger Pydantic v1 error path
+            # (covers both model classes being checked)
             if call_count <= 2:
                 return False
         return original_hasattr(obj, name)

1671-1696: Verify sys.path state restoration.

The test modifies sys.path and checks counts, but there's no cleanup if the test fails mid-execution. Consider using a try-finally or fixture to ensure sys.path is restored.

🔎 Suggested improvement
 @SKIP_PYDANTIC_V1
 def test_input_model_cwd_already_in_path(
     tmp_path: Path,
 ) -> None:
     """Test that cwd is not duplicated in sys.path when already present."""
     import sys
     from pathlib import Path as _Path

     cwd = str(_Path.cwd())
     initial_count = sys.path.count(cwd)
-    if cwd not in sys.path:
-        sys.path.insert(0, cwd)
-
-    output_path = tmp_path / "output.py"
-    run_multiple_input_models_and_assert(
-        input_models=[
-            "tests.data.python.input_model.inheritance_models:ChildA",
-            "tests.data.python.input_model.inheritance_models:ChildB",
-        ],
-        output_path=output_path,
-        expected_output_contains=[
-            "class ChildA(Parent):",
-            "class ChildB(Parent):",
-        ],
-    )
-    final_count = sys.path.count(cwd)
-    assert final_count <= initial_count + 1
+    added_cwd = cwd not in sys.path
+    try:
+        if added_cwd:
+            sys.path.insert(0, cwd)
+
+        output_path = tmp_path / "output.py"
+        run_multiple_input_models_and_assert(
+            input_models=[
+                "tests.data.python.input_model.inheritance_models:ChildA",
+                "tests.data.python.input_model.inheritance_models:ChildB",
+            ],
+            output_path=output_path,
+            expected_output_contains=[
+                "class ChildA(Parent):",
+                "class ChildB(Parent):",
+            ],
+        )
+        final_count = sys.path.count(cwd)
+        assert final_count <= initial_count + 1
+    finally:
+        if added_cwd and cwd in sys.path:
+            sys.path.remove(cwd)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2be74fd and 1901f86.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/input_model.py
  • tests/test_input_model.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🧬 Code graph analysis (2)
tests/test_input_model.py (5)
src/datamodel_code_generator/parser/base.py (1)
  • parent (612-614)
src/datamodel_code_generator/__main__.py (3)
  • main (973-1283)
  • Exit (125-131)
  • get (148-150)
src/datamodel_code_generator/model/base.py (2)
  • name (827-829)
  • path (908-910)
src/datamodel_code_generator/__init__.py (1)
  • chdir (246-256)
src/datamodel_code_generator/input_model.py (1)
  • load_model_schema (625-765)
src/datamodel_code_generator/input_model.py (1)
src/datamodel_code_generator/enums.py (4)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
  • UnionMode (199-203)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


83-83: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


127-127: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


143-143: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


158-158: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


185-185: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


233-233: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


234-234: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


235-235: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


236-236: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


237-237: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


238-238: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


239-239: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


260-260: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


266-266: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


268-268: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


269-269: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


286-286: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


316-316: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


353-353: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


354-354: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


355-355: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


375-375: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


431-431: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


433-433: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


434-434: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


459-459: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


491-491: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


534-534: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


535-535: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


536-536: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


547-547: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


548-548: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


625-625: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


642-642: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


643-643: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


645-645: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


649-649: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


768-768: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


788-788: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


789-789: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


791-791: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


792-792: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


874-874: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


886-886: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: 3.11 on Windows
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: 3.11 on macOS
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.14 on macOS
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.11 on Ubuntu
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (18)
tests/test_input_model.py (5)

20-21: LGTM!

The constants EXPECTED_INPUT_MODEL_PATH and TIMESTAMP are well-defined for use in the new inheritance tests. The timestamp format is appropriate for freeze_time usage.


1159-1188: LGTM!

The run_multiple_input_models_and_assert helper is well-structured, following the same pattern as the single-model helper. It properly builds the args list with multiple --input-model entries and includes comprehensive assertions.


1190-1208: LGTM!

The run_multiple_input_models_error_and_assert helper is consistent with the single-model error helper and properly handles multiple input models for error case testing.


1211-1227: LGTM!

The test properly uses freeze_time for deterministic output and validates against expected output files. The test covers the inheritance chain generation for a single input model.


1752-1765: LGTM!

Good unit test for the load_model_schema function directly, verifying the default output_model_type behavior.

src/datamodel_code_generator/input_model.py (13)

15-31: LGTM!

The Error exception class and type family constants are well-defined. The constants provide clear semantics for type categorization.


34-77: LGTM!

The _serialize_python_type_full function comprehensively handles Python type serialization including Union types, Callable types, and generic types with proper module path preservation.


125-153: LGTM!

The custom InputModelJsonSchema class properly handles unserializable types by marking them with x-python-unserializable. The lazy import pattern avoids Pydantic v1 compatibility issues. Based on learnings, the # noqa: PLR6301 directives should be kept due to Ruff preview mode.


200-228: LGTM!

The _add_python_type_for_unserializable function properly handles recursive schema processing with the visited_defs set to prevent infinite loops. The function correctly processes both root properties and nested definitions.


258-263: LGTM!

The lazy initialization pattern with the global variable is appropriate here to avoid import overhead at module load time while caching the result for subsequent calls.


327-348: LGTM!

The _collect_nested_models function properly handles recursive model collection with cycle detection via the visited set, supporting both Pydantic models and other types using type hints.


431-454: LGTM!

The _get_type_family function correctly identifies different type families including the special case for Pydantic dataclasses (checking __pydantic_fields__ before is_dataclass).


484-524: LGTM!

The _filter_defs_by_strategy function correctly implements the three ref strategies, adding x-python-import metadata for types that should be reused and keeping schema definitions for types that need regeneration.


527-556: LGTM!

The _try_rebuild_model function properly handles Pydantic model rebuilding with special namespace handling for config classes that reference forward-declared types.


564-622: LGTM!

The _transform_single_model_to_inheritance function correctly transforms Pydantic model schemas to use allOf for inheritance. The recursive processing of parent chains and the processed_parents cache prevent redundant schema generation.


625-765: LGTM!

The load_model_schema function is well-structured with comprehensive error handling. The module caching via loaded_modules dict is efficient, and the schema merging logic correctly produces an anyOf structure for multiple root models while sharing processed_parents across models for efficient inheritance processing.


768-904: LGTM!

The _load_single_model_schema function provides comprehensive handling for different input types (dict, Pydantic BaseModel, dataclass, TypedDict) with appropriate validation and schema generation for each type.


36-37: Retain defensive noqa directives per project conventions.

Based on learnings, the # noqa: PLC0415 directives on lazy imports should be kept even when Ruff reports them as unused. This is a defensive measure for potential future Ruff configuration changes. The static analysis hints about "unused noqa directive" can be safely ignored.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/datamodel_code_generator/__main__.py (1)

388-388: Consider removing unused noqa directives.

The static analysis tool indicates several noqa directives are suppressing rules that aren't currently enabled (PLC0415, UP045, UP007). Removing these would reduce noise without affecting functionality.

🔎 Suggested cleanup
-        from pydantic import field_validator as _field_validator  # noqa: PLC0415
+        from pydantic import field_validator as _field_validator

-    input: Optional[Union[Path, str]] = None  # noqa: UP007, UP045
-    input_model: Optional[list[str]] = None  # noqa: UP045
+    input: Optional[Union[Path, str]] = None
+    input_model: Optional[list[str]] = None

-            from datamodel_code_generator.input_model import Error as InputModelError  # noqa: PLC0415
-            from datamodel_code_generator.input_model import load_model_schema  # noqa: PLC0415
+            from datamodel_code_generator.input_model import Error as InputModelError
+            from datamodel_code_generator.input_model import load_model_schema

Also applies to: 464-465, 1203-1204

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1901f86 and 2962e97.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/__main__.py
  • tests/test_input_model.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_input_model.py (3)
tests/conftest.py (1)
  • assert_output (545-560)
src/datamodel_code_generator/__main__.py (3)
  • main (973-1283)
  • Exit (125-131)
  • get (148-150)
src/datamodel_code_generator/input_model.py (1)
  • load_model_schema (625-765)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/util.py (1)
  • field_validator (175-191)
src/datamodel_code_generator/input_model.py (2)
  • Error (15-16)
  • load_model_schema (625-765)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py

388-388: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


464-464: Unused noqa directive (non-enabled: UP007, UP045)

Remove unused noqa directive

(RUF100)


465-465: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)


1203-1203: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1204-1204: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: 3.13 on Windows
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: Analyze (python)
🔇 Additional comments (9)
tests/test_input_model.py (5)

6-6: LGTM! Well-organized imports and constants.

The new imports and constants support the inheritance testing infrastructure effectively. Using freeze_time and assert_output enables deterministic comparisons against expected output files.

Also applies to: 15-15, 20-21


57-83: LGTM! Efficient helper enhancement.

The addition of expected_output_not_contains complements the existing positive assertions well. Reading the file content once and checking both positive and negative expectations is efficient.


1160-1210: LGTM! Well-structured test helpers.

The new helpers for multiple input models follow the established patterns and handle both success and error cases consistently. The code is clear and maintainable.


1213-1550: LGTM! Comprehensive inheritance test coverage.

The inheritance tests thoroughly cover:

  • Single and multiple input models with various inheritance patterns
  • Different output model types (TypedDict, Pydantic BaseModel, dataclass)
  • Error conditions with clear assertions
  • Edge cases like same module imports and file path formats

The use of freeze_time and assert_output ensures deterministic, maintainable comparisons.


1553-1782: LGTM! Thorough edge case and integration testing.

The remaining tests provide excellent coverage of:

  • Integration with --input-model-ref-strategy
  • Various error scenarios with clear error message validation
  • Edge cases like empty inheritance chains and path handling
  • Config file string-to-list coercion
  • Default parameter behavior

All tests follow established patterns and include proper assertions.

src/datamodel_code_generator/__main__.py (4)

388-396: Excellent backwards-compatible validator implementation!

The coerce_input_model_to_list validator properly normalizes the input_model field to always be a list, enabling the new multi-model feature while preserving backwards compatibility with single-string inputs. The use of mode="before" is correct for type coercion.


456-462: LGTM - Pydantic v1 compatibility maintained.

The validator is correctly duplicated for Pydantic v1 users, ensuring consistent behavior across both Pydantic versions.


465-465: Type annotation correctly reflects the normalized format.

The type change from Optional[str] to Optional[list[str]] properly represents the post-validation state after the coerce_input_model_to_list validator runs.


1203-1214: Clean integration with the new multi-model loader!

The refactored loading logic properly:

  • Delegates to the new public load_model_schema function
  • Catches InputModelError and converts to the generic Error for consistent error handling
  • Maintains the existing JSON serialization flow

The guard at line 1202 ensures config.input_model is non-empty before this code executes, so type safety is maintained.

Comment thread tests/data/expected/main/input_model/multiple_same_module.py Dismissed
Comment thread tests/data/expected/main/input_model/multiple_with_dataclass_output.py Dismissed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
tests/data/expected/main/input_model/dataclass_with_python_types.py (1)

13-14: unique_items=True is redundant but harmless on set/frozenset fields.

The set and frozenset types already guarantee uniqueness by definition, so the unique_items=True parameter in Field() is redundant. However, since this is a test expected output file, this might be intentionally preserved from the input schema to maintain metadata fidelity for round-trip schema generation.

tests/data/expected/main/input_model/config_class.py (1)

121-121: Inconsistent union syntax.

Line 121 uses Union[defaultdict[str, dict[str, Any]], None] while the rest of the file consistently uses the modern | operator for unions (enabled by line 5's future import). For consistency with the rest of the file, consider using defaultdict[str, dict[str, Any]] | None.

🔎 Proposed fix for consistency
-    extra_template_data: NotRequired[Union[defaultdict[str, dict[str, Any]], None]]
+    extra_template_data: NotRequired[defaultdict[str, dict[str, Any]] | None]
tests/test_input_model.py (1)

1512-1534: Consider cleanup of sys.path modification.

The test adds cwd to sys.path (lines 1521-1522) but doesn't restore the original state after the test. While the impact is minimal since it only adds cwd once, consider using a try/finally block for proper cleanup to avoid potential test isolation issues.

🔎 Proposed fix
 @SKIP_PYDANTIC_V1
 def test_input_model_cwd_already_in_path(
     tmp_path: Path,
 ) -> None:
     """Test that cwd is not duplicated in sys.path when already present."""
     import sys
     from pathlib import Path as _Path

     cwd = str(_Path.cwd())
     initial_count = sys.path.count(cwd)
+    added_cwd = False
     if cwd not in sys.path:  # pragma: no cover
         sys.path.insert(0, cwd)
+        added_cwd = True

-    run_multiple_input_models_and_assert(
-        input_models=[
-            "tests.data.python.input_model.inheritance_models:ChildA",
-            "tests.data.python.input_model.inheritance_models:ChildB",
-        ],
-        output_path=tmp_path / "output.py",
-        expected_file=EXPECTED_INPUT_MODEL_PATH / "multiple_with_pydantic_output.py",
-        extra_args=["--output-model-type", "pydantic.BaseModel"],
-    )
-    final_count = sys.path.count(cwd)
-    assert final_count <= initial_count + 1
+    try:
+        run_multiple_input_models_and_assert(
+            input_models=[
+                "tests.data.python.input_model.inheritance_models:ChildA",
+                "tests.data.python.input_model.inheritance_models:ChildB",
+            ],
+            output_path=tmp_path / "output.py",
+            expected_file=EXPECTED_INPUT_MODEL_PATH / "multiple_with_pydantic_output.py",
+            extra_args=["--output-model-type", "pydantic.BaseModel"],
+        )
+        final_count = sys.path.count(cwd)
+        assert final_count <= initial_count + 1
+    finally:
+        if added_cwd:  # pragma: no cover
+            sys.path.remove(cwd)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2962e97 and 23eee28.

📒 Files selected for processing (41)
  • tests/data/expected/main/input_model/config_class.py
  • tests/data/expected/main/input_model/custom_class.py
  • tests/data/expected/main/input_model/custom_generic_type_import.py
  • tests/data/expected/main/input_model/dataclass_with_python_types.py
  • tests/data/expected/main/input_model/default_put_dict_import.py
  • tests/data/expected/main/input_model/dict_openapi.py
  • tests/data/expected/main/input_model/dict_with_jsonschema.py
  • tests/data/expected/main/input_model/empty_child_no_properties.py
  • tests/data/expected/main/input_model/model_with_callable_types.py
  • tests/data/expected/main/input_model/model_with_python_types.py
  • tests/data/expected/main/input_model/model_with_python_types_dataclass.py
  • tests/data/expected/main/input_model/model_with_python_types_typeddict.py
  • tests/data/expected/main/input_model/multiple_same_module.py
  • tests/data/expected/main/input_model/multiple_with_dataclass_output.py
  • tests/data/expected/main/input_model/multiple_with_pydantic_output.py
  • tests/data/expected/main/input_model/nested_model_with_callable.py
  • tests/data/expected/main/input_model/optional_only_child_no_required.py
  • tests/data/expected/main/input_model/path_format.py
  • tests/data/expected/main/input_model/path_format_filename_only.py
  • tests/data/expected/main/input_model/pydantic_basemodel.py
  • tests/data/expected/main/input_model/pydantic_dataclass.py
  • tests/data/expected/main/input_model/pydantic_to_typeddict.py
  • tests/data/expected/main/input_model/pydantic_with_jsonschema.py
  • tests/data/expected/main/input_model/recursive_model_types.py
  • tests/data/expected/main/input_model/ref_strategy_dataclass_reuse_foreign.py
  • tests/data/expected/main/input_model/ref_strategy_no_nested_types.py
  • tests/data/expected/main/input_model/ref_strategy_regenerate_all.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_all.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_different_family.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_mixed_types.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_msgspec_output.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_pydantic_output.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_dataclass.py
  • tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_typeddict.py
  • tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_all.py
  • tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_foreign.py
  • tests/data/expected/main/input_model/std_dataclass.py
  • tests/data/expected/main/input_model/typeddict.py
  • tests/data/expected/main/input_model/union_callable.py
  • tests/test_input_model.py
✅ Files skipped from review due to trivial changes (2)
  • tests/data/expected/main/input_model/pydantic_dataclass.py
  • tests/data/expected/main/input_model/model_with_python_types_dataclass.py
🧰 Additional context used
🧬 Code graph analysis (25)
tests/data/expected/main/input_model/pydantic_basemodel.py (4)
tests/data/expected/main/input_model/path_format.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format_filename_only.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_to_typeddict.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_with_jsonschema.py (1)
  • User (10-12)
tests/data/expected/main/input_model/model_with_python_types_typeddict.py (4)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/expected/main/input_model/model_with_python_types.py (2)
  • Tag (12-13)
  • ModelWithPythonTypes (16-26)
tests/data/expected/main/input_model/model_with_python_types_dataclass.py (2)
  • Tag (12-13)
  • ModelWithPythonTypes (17-27)
tests/data/expected/main/input_model/ref_strategy_dataclass_reuse_foreign.py (1)
  • Tag (13-15)
tests/data/expected/main/input_model/dataclass_with_python_types.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/input_model/optional_only_child_no_required.py (2)
tests/data/expected/main/input_model/empty_child_no_properties.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/expected/main/input_model/multiple_with_pydantic_output.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/expected/main/input_model/custom_generic_type_import.py (2)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/python/input_model/pydantic_models.py (1)
  • CustomGenericDict (16-19)
tests/data/expected/main/input_model/multiple_same_module.py (1)
src/datamodel_code_generator/model/type_alias.py (1)
  • TypeAlias (37-42)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_msgspec_output.py (4)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_different_family.py (2)
  • NestedPydantic (12-14)
  • ModelWithPydantic (17-19)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_mixed_types.py (1)
  • NestedPydantic (17-19)
src/datamodel_code_generator/model/base.py (1)
  • name (827-829)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_pydantic_output.py (1)
  • ModelWithPydantic (11-13)
tests/data/expected/main/input_model/multiple_with_dataclass_output.py (1)
src/datamodel_code_generator/model/type_alias.py (1)
  • TypeAlias (37-42)
tests/data/expected/main/input_model/custom_class.py (2)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/python/input_model/pydantic_models.py (1)
  • CustomClass (82-85)
tests/data/expected/main/input_model/std_dataclass.py (3)
tests/data/expected/main/input_model/dict_openapi.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format_filename_only.py (1)
  • User (10-12)
tests/data/expected/main/input_model/model_with_python_types.py (3)
tests/data/expected/main/input_model/model_with_python_types_dataclass.py (2)
  • Tag (12-13)
  • ModelWithPythonTypes (17-27)
tests/data/expected/main/input_model/model_with_python_types_typeddict.py (2)
  • Tag (11-12)
  • ModelWithPythonTypes (15-25)
tests/data/expected/main/input_model/ref_strategy_dataclass_reuse_foreign.py (1)
  • Tag (13-15)
tests/data/expected/main/input_model/ref_strategy_dataclass_reuse_foreign.py (2)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
src/datamodel_code_generator/model/base.py (1)
  • name (827-829)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_dataclass.py (1)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_mixed_types.py (1)
  • NestedDataclass (12-14)
tests/data/expected/main/input_model/default_put_dict_import.py (1)
src/datamodel_code_generator/parser/__init__.py (1)
  • DefaultPutDict (28-47)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_typeddict.py (2)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/python/input_model/mixed_nested.py (1)
  • NestedTypedDict (19-23)
tests/data/expected/main/input_model/empty_child_no_properties.py (2)
tests/data/expected/main/input_model/multiple_with_pydantic_output.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/expected/main/input_model/optional_only_child_no_required.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/expected/main/input_model/ref_strategy_regenerate_all.py (4)
src/datamodel_code_generator/model/type_alias.py (1)
  • TypeAlias (37-42)
src/datamodel_code_generator/model/typed_dict.py (2)
  • TypedDict (49-114)
  • key (131-135)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign.py (3)
  • Address (13-15)
  • Metadata (18-20)
  • User (23-27)
tests/data/expected/main/input_model/ref_strategy_reuse_all.py (1)
  • User (13-17)
tests/data/expected/main/input_model/pydantic_to_typeddict.py (1)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
tests/data/expected/main/input_model/path_format.py (10)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/input_model/dict_openapi.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format_filename_only.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_basemodel.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_dataclass.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_to_typeddict.py (1)
  • User (10-12)
tests/data/expected/main/input_model/pydantic_with_jsonschema.py (1)
  • User (10-12)
tests/data/expected/main/input_model/ref_strategy_no_nested_types.py (1)
  • User (11-13)
tests/data/expected/main/input_model/std_dataclass.py (1)
  • User (10-12)
tests/data/expected/main/input_model/typeddict.py (1)
  • User (10-12)
tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_foreign.py (2)
tests/data/python/input_model/typeddict_nested.py (1)
  • Role (11-15)
tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_all.py (1)
  • Member (13-16)
tests/data/expected/main/input_model/ref_strategy_reuse_foreign.py (2)
src/datamodel_code_generator/model/typed_dict.py (2)
  • TypedDict (49-114)
  • key (131-135)
tests/data/expected/main/input_model/ref_strategy_reuse_all.py (1)
  • User (13-17)
tests/data/expected/main/input_model/typeddict.py (2)
tests/data/expected/main/input_model/path_format.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format_filename_only.py (1)
  • User (10-12)
tests/data/expected/main/input_model/model_with_callable_types.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/input_model/path_format_filename_only.py (2)
tests/data/expected/main/input_model/dict_openapi.py (1)
  • User (10-12)
tests/data/expected/main/input_model/path_format.py (1)
  • User (10-12)
tests/data/expected/main/input_model/multiple_with_pydantic_output.py (3)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/input_model/empty_child_no_properties.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
tests/data/expected/main/input_model/optional_only_child_no_required.py (2)
  • GrandParent (10-11)
  • Parent (14-15)
🪛 Ruff (0.14.10)
tests/data/expected/main/input_model/model_with_callable_types.py

11-11: Redefinition of unused BaseModel from line 10

(F811)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: py312-black23 on Ubuntu
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.13 on Windows
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (56)
tests/data/expected/main/input_model/ref_strategy_no_nested_types.py (1)

1-13: LGTM! Test expected output is correct.

The generated dataclass is syntactically valid and appropriate for testing the "ref_strategy_no_nested_types" scenario. The fixed timestamp ensures test reproducibility, and the simple two-field dataclass correctly represents the expected output format.

tests/data/expected/main/input_model/dataclass_with_python_types.py (3)

1-4: LGTM!

The generated file header follows the expected format for datamodel-codegen output.


5-9: LGTM!

The imports are correct and follow Python best practices:

  • Future annotations enable modern type syntax
  • Abstract types from collections.abc are preferred for immutable/read-only interfaces

15-16: LGTM!

Using Mapping and Sequence from collections.abc is excellent practice for fields that should be immutable or read-only interfaces.

tests/data/expected/main/input_model/custom_generic_type_import.py (3)

1-8: LGTM: File header and imports are correct.

The generated file header and imports are properly structured. The import of CustomGenericDict from the test data module correctly references the custom generic type defined in the codebase.


11-12: LGTM: Model and first field definition are correct.

The model class and custom_dict field properly demonstrate the use of a custom generic type with type parameters.


13-15: Verify the field semantics match the input schema.

The field definition is syntactically correct. The type CustomGenericDict[str, str] | None with Field(...) means the field is required (must be provided) but can accept None as a value. The name optional_custom_dict might suggest the field can be omitted, but Pydantic will require it to be explicitly passed.

Please confirm this behavior matches the input schema's intent (nullable but required field).

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_msgspec_output.py (3)

1-5: LGTM!

The generated file header and future annotations import are correct.


13-15: LGTM!

The NestedPydantic class correctly uses msgspec Struct with properly annotated fields. The field titles are appropriately specified using Meta.


18-20: LGTM!

The ModelWithPydantic class correctly demonstrates the ref_strategy reuse_foreign behavior by combining a locally defined nested model with an imported foreign type (Category).

tests/data/expected/main/input_model/config_class.py (4)

1-11: LGTM! Clean imports.

The imports are appropriate for this type definition file. The from __future__ import annotations import enables modern union syntax (|) for the rest of the file.


13-107: LGTM! Comprehensive type aliases for configuration.

The TypeAlias definitions provide strong typing for all configuration options. The literal values are appropriate and well-structured for each option.


40-50: LGTM! Correct dataclass parameter mapping.

The DataclassArguments TypedDict correctly models all standard dataclass decorator parameters with appropriate optional boolean types.


109-232: Comprehensive configuration schema with extensive options.

The GenerateConfig TypedDict provides a thorough type definition for all configuration options. The structure is well-organized and uses appropriate types for each field.

tests/data/expected/main/input_model/ref_strategy_dataclass_reuse_foreign.py (2)

21-21: The NotRequired[Tag | None] pattern is correct. When converting from the input dataclass tag: Optional[Tag] = None, the TypedDict representation NotRequired[Tag | None] accurately preserves both the optionality (field can be absent) and nullability (value can be None) semantics. Using NotRequired[Tag] instead would incorrectly exclude None as a valid value, contradicting the input model's explicit Optional[Tag] type.


9-9: The Priority import path is correct and accessible.

The Priority enum is properly defined in tests/data/python/input_model/dataclass_nested.py and the import statement on line 9 correctly references it. No issues identified.

tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_foreign.py (2)

18-22: Member definition correctly matches reuse-all strategy.

The Member dataclass structure is identical to the one in ref_strategy_typeddict_reuse_all.py, which is correct. The only difference is that this file defines Profile locally while the reuse-all version imports it, demonstrating the two different reuse strategies as intended.


12-15: Profile definition correctly matches source module.

The locally-defined Profile class in the expected file has the same fields as the source Profile in tests.data.python.input_model.typeddict_nested.py: bio: str and website: str.

tests/data/expected/main/input_model/ref_strategy_typeddict_reuse_all.py (1)

1-16: Test expected output looks correct for reuse-all strategy.

The file correctly demonstrates the "reuse all" behavior by importing both Profile and Role from tests.data.python.input_model.typeddict_nested. The Member dataclass properly uses these imported types.

tests/data/expected/main/input_model/model_with_python_types_typeddict.py (3)

5-8: LGTM: Imports are correct.

The imports are appropriate for TypedDict-based models with modern Python typing features.


11-12: LGTM: Tag definition is correct.

The Tag TypedDict is properly defined and consistent with other model variants in the test suite.


23-25: No changes needed. The TypedDict semantics are correct.

The fields optional_set, nullable_frozenset, and optional_mapping are required fields in the source Pydantic model (none have default values). In Pydantic, Optional[T] or T | None means the value can be None, not that the field can be missing. The TypedDict representation field: T | None correctly captures this: the key must be present, but the value can be None. Using NotRequired would contradict the source model's semantics and make the code incorrect.

Likely an incorrect or invalid review comment.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_typeddict.py (1)

1-14: LGTM! Test fixture correctly demonstrates TypedDict with foreign type references.

This generated test output correctly demonstrates the reuse of foreign types (Category and NestedTypedDict) from another module within a TypedDict definition, aligning with the PR's multi-model input support objectives.

tests/data/expected/main/input_model/dict_openapi.py (1)

1-12: LGTM! Clean test fixture for basic Pydantic model generation.

This expected output correctly represents a simple Pydantic BaseModel with scalar fields, suitable for validating dict/OpenAPI input model scenarios.

tests/data/expected/main/input_model/dict_with_jsonschema.py (1)

1-12: LGTM! Test fixture validates JSON Schema input model generation.

This expected output is syntactically correct and appropriately demonstrates model generation from JSON Schema input.

tests/data/expected/main/input_model/pydantic_to_typeddict.py (1)

1-12: LGTM! Test fixture demonstrates Pydantic-to-TypedDict transformation.

This expected output correctly validates the code generator's ability to transform Pydantic input models to TypedDict output format.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_same_family_dataclass.py (1)

1-15: LGTM! Test fixture validates dataclass output with foreign type reuse.

This expected output correctly demonstrates dataclass generation with foreign type references from mixed_nested, complementing the TypedDict variant and validating ref-strategy reuse across different output formats.

tests/data/expected/main/input_model/ref_strategy_reuse_all.py (1)

1-17: LGTM! Test fixture demonstrates optional fields and comprehensive ref-reuse.

This expected output correctly validates TypedDict generation with NotRequired optional fields and demonstrates comprehensive reference reuse with imported types from nested_models.

tests/data/expected/main/input_model/path_format_filename_only.py (1)

1-12: LGTM! Test fixture validates Field metadata generation.

This expected output correctly demonstrates Pydantic model generation with Field metadata, including title attributes for each field.

tests/data/expected/main/input_model/empty_child_no_properties.py (1)

1-19: LGTM! Test fixture demonstrates multi-level inheritance with empty child.

This expected output correctly validates inheritance preservation across three levels (GrandParent → Parent → EmptyChild), with the empty child class containing no additional properties—a key scenario for testing the PR's inheritance preservation functionality.

tests/data/expected/main/input_model/path_format.py (1)

1-12: LGTM!

This test fixture correctly defines a Pydantic User model with the expected Field(...) syntax for required fields with title metadata. The structure is consistent with other similar expected output files in the test suite.

tests/data/expected/main/input_model/union_callable.py (1)

1-14: LGTM!

The test fixture correctly demonstrates union types with Callable and uses the modern collections.abc.Callable import. The field definitions with union types (Callable[[str], str] | int) are valid Python 3.10+ syntax.

tests/data/expected/main/input_model/pydantic_basemodel.py (1)

1-12: LGTM!

This test fixture is consistent with the expected Pydantic BaseModel output pattern and matches similar expected output files in the test suite (e.g., path_format.py, path_format_filename_only.py).

tests/data/expected/main/input_model/nested_model_with_callable.py (1)

12-18: LGTM!

The nested model structure is correct. The nested: NestedCallableModel field appropriately omits Field(...) since it doesn't require additional metadata, while fields with title metadata properly use the Field(...) pattern.

tests/data/expected/main/input_model/ref_strategy_regenerate_all.py (1)

1-29: LGTM!

The test fixture correctly demonstrates:

  • TypedDict with optional fields using NotRequired
  • TypeAlias for literal union types
  • Proper import organization (stdlib typing then typing_extensions)

The structure is consistent with related ref_strategy expected output files.

tests/data/expected/main/input_model/model_with_python_types.py (1)

12-26: LGTM!

The test fixture comprehensively covers various Python collection types (set, frozenset, Mapping, Sequence, nested generics) and demonstrates proper handling of:

  • Required fields with and without Field() metadata
  • Nullable/optional union types (... | None)
  • Nested model references (tag_obj: Tag)

The structure aligns with related TypedDict and dataclass variants in the test suite.

tests/data/expected/main/input_model/pydantic_with_jsonschema.py (1)

1-12: LGTM!

This test fixture correctly represents the expected Pydantic output when processing input models with JSON Schema metadata. The structure matches other similar expected output files in the suite.

tests/data/expected/main/input_model/recursive_model_types.py (1)

10-19: LGTM - recursive model pattern is correctly structured for Pydantic v1.

This test fixture demonstrates proper handling of recursive/self-referential models using Pydantic v1 patterns. The __root__ wrapper and update_forward_refs() call are intentional for v1 compatibility, as evidenced by the test not being marked with the v2-only @SKIP_PYDANTIC_V1 decorator.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_pydantic_output.py (1)

1-13: LGTM! Clean Pydantic model with foreign type reuse.

The generated output correctly demonstrates Pydantic model generation with imported types from external modules. The structure is clean and follows standard Pydantic conventions.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_different_family.py (1)

1-19: LGTM! Proper TypedDict generation with foreign type reuse.

The output correctly demonstrates conversion to TypedDict format while preserving references to external types. Both local and imported types are properly declared.

tests/data/expected/main/input_model/default_put_dict_import.py (1)

1-13: LGTM! Correct usage of internal generic type.

The generated output properly imports and uses DefaultPutDict from the internal parser module, demonstrating support for internal generic types in generated models. Both required and optional field variants are correctly represented.

tests/data/expected/main/input_model/custom_class.py (1)

1-12: LGTM! Proper handling of custom class references.

The generated output correctly imports and uses a custom class type as a field, demonstrating support for non-standard Pydantic field types. The structure follows expected patterns.

tests/data/expected/main/input_model/optional_only_child_no_required.py (1)

1-19: LGTM! Clean three-level inheritance with optional fields.

The generated output correctly demonstrates multi-level inheritance with a mix of required and optional fields. The child class properly inherits from its parent while adding its own optional field, following standard Pydantic patterns.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign_mixed_types.py (1)

1-26: LGTM! Correct mixed-type reference handling.

The generated output properly demonstrates TypedDict generation with a mix of local and imported type references. The structure correctly handles multiple nested types with different origins.

Note: Class names like NestedDataclass and NestedPydantic might be slightly misleading since they're all TypedDicts in this output, but this likely reflects the original model types being converted to a common format.

tests/data/expected/main/input_model/ref_strategy_reuse_foreign.py (1)

1-27: LGTM! Proper TypedDict with foreign imports and optional fields.

The generated output correctly demonstrates:

  • Foreign type import (Status) from external module
  • Local TypedDict definitions (Address, Metadata)
  • Proper use of NotRequired for optional nullable fields
  • Clean structure following TypedDict conventions
tests/data/expected/main/input_model/typeddict.py (1)

1-12: The filename is correct. The naming convention in the test suite describes the input model type or transformation scenario being tested, not the output format. typeddict.py correctly indicates that a TypedDict input model is being converted to the default output format (Pydantic BaseModel). This is consistent with other files like pydantic_basemodel.py (Pydantic input to default output) and pydantic_to_typeddict.py (Pydantic to TypedDict output). No changes are needed.

tests/data/expected/main/input_model/multiple_same_module.py (1)

1-34: LGTM!

The expected output file correctly demonstrates the multi-level inheritance hierarchy with TypedDict classes and generates a proper TypeAlias union for the leaf types. The structure aligns with the test case test_input_model_multiple_same_module.

tests/data/expected/main/input_model/model_with_callable_types.py (1)

14-25: LGTM!

The Pydantic model correctly demonstrates various Callable field types and Type[BaseModel] for comprehensive testing of unserializable type preservation.

tests/test_input_model.py (5)

20-21: LGTM!

The constants are well-defined. EXPECTED_INPUT_MODEL_PATH uses proper path construction relative to the test file, and TIMESTAMP provides a consistent frozen time for reproducible test outputs.


50-67: LGTM!

The helper function properly uses freeze_time for reproducible test outputs and assert_output for clean assertion of expected file contents.


1034-1054: LGTM!

The helper function properly handles multiple --input-model arguments and follows the same pattern as the single model helper with freeze_time and assert_output.


1598-1609: LGTM!

The test correctly calls load_model_schema directly without needing a temporary path. The past review concern about unused tmp_path has been addressed - the parameter is no longer present.


1538-1572: LGTM!

Good edge case test for loading .py files without path separators. The inline assertions appropriately verify both the inheritance chain from the module path and the locally created TempModel.

tests/data/expected/main/input_model/multiple_with_dataclass_output.py (1)

1-31: LGTM! Clean dataclass implementation with proper inheritance.

The inheritance chain (GrandParent → Parent → ChildA/ChildB) is correctly structured, and the type alias pattern for the union type is appropriate for this use case.

tests/data/expected/main/input_model/multiple_with_pydantic_output.py (2)

10-23: LGTM! Proper Pydantic inheritance hierarchy.

The inheritance chain is correctly implemented with appropriate Field metadata for each field.


26-27: The __root__ pattern here is appropriate—the test explicitly targets v1 output via --output-model-type pydantic.BaseModel, not v2. While Pydantic v2 replaced __root__ with RootModel, this generator is intentionally producing v1-compatible output for backward compatibility. This test validates that the code generator can read Pydantic v2 input models and generate older v1-compatible code when requested.

Comment thread tests/data/expected/main/input_model/model_with_callable_types.py Outdated
Comment thread tests/data/expected/main/input_model/std_dataclass.py
Comment thread tests/test_input_model.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
tests/test_input_model.py (1)

1245-1255: Remove or differentiate this duplicate test.

This test (test_input_model_multiple_only_not_contains) is identical to test_input_model_multiple_with_pydantic_output (lines 1217-1227) — same input models, same expected file, and same extra args. The test name and docstring also don't match each other, suggesting this is leftover from refactoring.

🔎 Proposed fix - remove the duplicate
-@SKIP_PYDANTIC_V1
-def test_input_model_multiple_only_not_contains(tmp_path: Path) -> None:
-    """Test multiple with Pydantic output."""
-    run_multiple_input_models_and_assert(
-        input_models=[
-            "tests.data.python.input_model.inheritance_models:ChildA",
-            "tests.data.python.input_model.inheritance_models:ChildB",
-        ],
-        output_path=tmp_path / "output.py",
-        expected_file=EXPECTED_INPUT_MODEL_PATH / "multiple_with_pydantic_output.py",
-        extra_args=["--output-model-type", "pydantic.BaseModel"],
-    )
-
-
tests/data/expected/main/input_model/model_with_python_types_dataclass.py (1)

27-27: Verify duplicate type in union is intentional.

The optional_mapping field has the same redundant duplicate type as in the TypedDict variant: Mapping[str, str] | Mapping[str, str] | None. This pattern appears consistently across both dataclass and TypedDict expected outputs, which suggests it may be a systematic issue in the code generation logic.

Please verify whether this duplication is intentional for testing purposes or represents a bug that should be fixed in the generator before these expected outputs are finalized.

🧹 Nitpick comments (3)
tests/test_input_model.py (3)

810-854: Consider consolidating near-duplicate test functions.

test_input_model_ref_strategy_reuse_foreign_no_regeneration (lines 810-822) and test_input_model_ref_strategy_reuse_all_no_regeneration (lines 842-854) have nearly identical implementations to their respective base tests (test_input_model_ref_strategy_reuse_foreign and test_input_model_ref_strategy_reuse_all). The docstrings suggest different intents but the test code is identical. Either the tests should verify something different, or they should be removed as duplicates.


1475-1502: Unused call_count variable.

The call_count variable is incremented but never used for any assertion or logic control. If it was intended for debugging or future verification, consider either using it in an assertion or removing it.

🔎 Proposed fix - remove unused variable
     original_find_spec = importlib.util.find_spec
     original_import_module = importlib.import_module
-    call_count = 0

     def fake_find_spec(name: str, *args: object, **kwargs: object) -> FakeSpec | None:
-        nonlocal call_count
-        call_count += 1
         if "nonexistent_import_module" in name:
             return FakeSpec()
         return original_find_spec(name, *args, **kwargs)

1529-1552: Minor: Redundant Path import aliasing.

Path is already imported at the module level, but here it's imported again as _Path. While functional, this is unusual. Consider using the already-imported Path directly.

🔎 Proposed fix
 def test_input_model_cwd_already_in_path(
     tmp_path: Path,
 ) -> None:
     """Test that cwd is not duplicated in sys.path when already present."""
     import sys
-    from pathlib import Path as _Path

-    cwd = str(_Path.cwd())
+    cwd = str(Path.cwd())
     initial_count = sys.path.count(cwd)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23eee28 and f39a2d1.

📒 Files selected for processing (5)
  • tests/data/expected/main/input_model/config_class.py
  • tests/data/expected/main/input_model/model_with_python_types.py
  • tests/data/expected/main/input_model/model_with_python_types_dataclass.py
  • tests/data/expected/main/input_model/model_with_python_types_typeddict.py
  • tests/test_input_model.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/data/expected/main/input_model/model_with_python_types.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: combine coverage
  • GitHub Check: benchmarks
🔇 Additional comments (10)
tests/test_input_model.py (4)

1-33: LGTM on imports and constants setup.

The new imports, constants (EXPECTED_INPUT_MODEL_PATH, TIMESTAMP), and the SKIP_PYTHON_314 marker are well-structured. The Python 3.14 skip marker is appropriately documented with a clear reason about different type annotations in model_json_schema output.


56-73: Well-designed test helper with time freezing.

The updated run_input_model_and_assert helper properly integrates freeze_time for deterministic timestamp generation and uses assert_output for comparing against expected files. This is a good pattern for snapshot-style testing.


1052-1093: Clean implementation of multiple input model helpers.

The run_multiple_input_models_and_assert and run_multiple_input_models_error_and_assert helpers follow the same pattern as their single-model counterparts, maintaining consistency. Good use of __tracebackhide__ for cleaner pytest output.


1615-1628: LGTM on test_input_model_output_model_type_default.

Good unit test that directly exercises load_model_schema without needing filesystem fixtures. The tmp_path parameter was correctly removed as indicated by the past review.

tests/data/expected/main/input_model/config_class.py (4)

1-11: LGTM on file header and imports.

The generated header with timestamp, and the imports structure look correct for a generated TypedDict configuration file.


109-238: Overall structure of GenerateConfig is well-formed.

The GenerateConfig TypedDict comprehensively covers all configuration options with appropriate NotRequired wrappers. The TypeAlias definitions for constrained literal types (like DataModelType, PythonVersion, etc.) provide good type safety.


121-121: The type hint correctly reflects the actual runtime type.

The defaultdict[str, dict[str, Any]] type hint is intentional and accurate. The codebase explicitly creates defaultdict objects (see src/datamodel_code_generator/__init__.py:501 and src/datamodel_code_generator/__main__.py:1131 where JSON is loaded with object_hook=lambda d: defaultdict(dict, **d)). The code also relies on defaultdict's default_factory behavior—accessing nested keys directly without checking existence (e.g., src/datamodel_code_generator/parser/jsonschema.py:1107). Since the actual runtime values are defaultdict objects, the type hint should accurately reflect that.

Likely an incorrect or invalid review comment.


126-128: The redundant union types in this file are confirmed, but this is generated test expected output.

The types Mapping[str, str | list[str]] | Mapping[str, str | list[str]], Sequence[StrictTypes] | Sequence[StrictTypes], and similar patterns at lines 163, 166, 168-170, 181-183, 211-213 are indeed redundant—they duplicate the same type in the union.

However, comparing the source (src/datamodel_code_generator/config.py line 94: aliases: Mapping[str, str | list[str]] | None) to this generated output reveals the duplication originates from the code generator itself during Pydantic-to-TypedDict conversion, not from hand-written code. This test file correctly captures what the generator currently produces, but the underlying issue is a bug in the generation logic that merges or duplicates types during the JSON schema conversion pipeline. The generator should be fixed to eliminate these redundant union members before writing output.

tests/data/expected/main/input_model/model_with_python_types_typeddict.py (1)

1-24: LGTM! Standard TypedDict model definitions.

The imports and TypedDict definitions follow expected patterns for generated code. The use of Python collection types (set, frozenset, Mapping, Sequence) is appropriate.

tests/data/expected/main/input_model/model_with_python_types_dataclass.py (1)

1-26: LGTM! Standard dataclass model definitions.

The imports and dataclass definitions follow expected patterns for generated code. The use of Python collection types is appropriate and mirrors the TypedDict variant.

Comment thread tests/data/expected/main/input_model/model_with_python_types_typeddict.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/datamodel_code_generator/parser/jsonschema.py (1)

1880-1888: Use self.SCHEMA_OBJECT_TYPE.__extra_key__ instead of hardcoding the extras alias.

The hardcoded string "#-datamodel-code-generator-#-extras-#-special-#" duplicates the value defined by JsonSchemaObject.__extra_key__ on line 235. If the format changes, this code would break silently.

🔎 Proposed fix
         # Don't propagate x-python-type from parent to children if it's a union type,
         # as this causes duplicate types (e.g., "Mapping[str, str] | None" becoming
         # "Mapping[str, str] | Mapping[str, str] | None")
         x_python_type = obj.extras.get("x-python-type", "")
         if " | " in x_python_type:
             # Remove from the extras dict in base_object (uses alias key)
-            extras_alias = "#-datamodel-code-generator-#-extras-#-special-#"
+            extras_alias = self.SCHEMA_OBJECT_TYPE.__extra_key__
             if extras_alias in base_object:
                 base_object[extras_alias].pop("x-python-type", None)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4afe19e and ded2d40.

📒 Files selected for processing (3)
  • src/datamodel_code_generator/parser/jsonschema.py
  • tests/data/expected/main/input_model/config_class.py
  • tests/test_input_model.py
🧰 Additional context used
🧬 Code graph analysis (3)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/imports.py (2)
  • Import (20-38)
  • from_full_path (35-38)
tests/test_input_model.py (3)
tests/conftest.py (1)
  • assert_output (545-560)
src/datamodel_code_generator/__init__.py (1)
  • chdir (246-256)
src/datamodel_code_generator/input_model.py (1)
  • load_model_schema (625-765)
tests/data/expected/main/input_model/config_class.py (2)
src/datamodel_code_generator/enums.py (17)
  • AllExportsCollisionStrategy (92-102)
  • AllExportsScope (81-89)
  • AllOfClassHierarchy (155-159)
  • AllOfMergeMode (142-152)
  • CollapseRootModelsNameStrategy (131-139)
  • DataModelType (48-56)
  • FieldTypeCollisionStrategy (105-113)
  • GraphQLScope (162-165)
  • InputFileType (35-45)
  • ModuleSplitMode (179-185)
  • NamingStrategy (116-128)
  • OpenAPIScope (70-78)
  • ReadOnlyWriteOnlyModelType (168-176)
  • ReuseScope (59-67)
  • StrictTypes (220-227)
  • TargetPydanticVersion (188-196)
  • UnionMode (199-203)
src/datamodel_code_generator/format.py (4)
  • DateClassType (60-65)
  • DatetimeClassType (50-57)
  • Formatter (171-177)
  • PythonVersion (68-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: py312-black23 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: 3.11 on Ubuntu
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (9)
tests/test_input_model.py (5)

5-32: LGTM! Well-organized imports and constants.

The addition of centralized constants (EXPECTED_INPUT_MODEL_PATH, TIMESTAMP) and the SKIP_PYTHON_314 marker improves test maintainability and clarity. The reasoning for skipping Python 3.14+ tests is clear and appropriate.


56-73: Excellent refactoring to golden file approach.

The shift from expected_output_contains substring matching to expected_file comparison with assert_output improves test reliability and makes test expectations explicit and reviewable. Using freeze_time ensures deterministic timestamps in generated output.


1052-1094: Well-designed helper functions.

The new run_multiple_input_models_and_assert and run_multiple_input_models_error_and_assert helpers follow the established pattern from single-model helpers and eliminate code duplication across the multiple-model test suite. Proper use of __tracebackhide__ improves pytest output clarity.


1096-1220: Comprehensive inheritance test coverage.

This test suite thoroughly exercises inheritance scenarios:

  • Single vs. multiple model loading
  • Multi-level inheritance chains
  • Forked inheritance (shared parents)
  • Mixed inheritance depths
  • Various output types (TypedDict, Pydantic, dataclass)

The consistent use of freeze_time and golden files ensures deterministic, reviewable expectations.


1224-1467: Thorough error handling test coverage.

The error tests comprehensively cover failure modes:

  • Invalid input types and formats
  • Missing files, modules, and attributes
  • Version compatibility issues
  • Import failures

All tests properly verify error messages and use appropriate mocking techniques. This ensures robust error reporting for users.

src/datamodel_code_generator/parser/jsonschema.py (3)

616-618: LGTM!

from pydantic import BaseModel is the standard way to define models that "inherit from BaseModel and define fields as annotated attributes." This addition correctly uses the public API path.


1201-1204: LGTM!

The logic correctly preserves union x-python-type values when schema_type is None (typically from anyOf/oneOf constructs). Returning False ensures the override path is taken, preventing the union type from being dropped.


1279-1281: LGTM!

The priority lookup via _resolve_type_import(class_name) ensures well-known types (like BaseModel) use their canonical public import paths, with a sensible fallback to the fully-qualified path for unknown types.

tests/data/expected/main/input_model/config_class.py (1)

1-232: LGTM!

This generated test expected output file correctly defines TypeAlias declarations matching the enum values from src/datamodel_code_generator/enums.py and src/datamodel_code_generator/format.py. The GenerateConfig TypedDict comprehensively covers the configuration options with appropriate NotRequired wrappers.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/datamodel_code_generator/parser/jsonschema.py (1)

1876-1880: Use the class constant instead of hardcoding the extras alias.

The hardcoded string "#-datamodel-code-generator-#-extras-#-special-#" duplicates the value defined in JsonSchemaObject.__extra_key__ (line 235). Using the constant would be more maintainable.

🔎 Proposed fix
         base_object = model_dump(obj, exclude={target_attribute_name}, exclude_unset=True, by_alias=True)
         x_python_type = obj.extras.get("x-python-type", "")
         if " | " in x_python_type:
-            extras_alias = "#-datamodel-code-generator-#-extras-#-special-#"
+            extras_alias = self.SCHEMA_OBJECT_TYPE.__extra_key__
             if extras_alias in base_object:
                 base_object[extras_alias].pop("x-python-type", None)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 853a02d and fae1e1d.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/parser/jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/imports.py (2)
  • Import (20-38)
  • from_full_path (35-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: benchmarks
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.13 on Windows
🔇 Additional comments (3)
src/datamodel_code_generator/parser/jsonschema.py (3)

616-616: LGTM!

Adding BaseModel to PYTHON_TYPE_IMPORTS enables proper public import path resolution for pydantic.BaseModel when used in x-python-type overrides.


1200-1203: LGTM - Union type handling for x-python-type overrides.

The new condition correctly identifies union types (| syntax) with no schema type as requiring a type override. This ensures unions in x-python-type are processed rather than passed through as compatible.


1276-1278: LGTM - Prefer public import paths for nested types.

The change to first try _resolve_type_import(class_name) before falling back to Import.from_full_path(qualified_name) enables reuse of well-known public import paths from PYTHON_TYPE_IMPORTS. This is especially useful for types like BaseModel that should be imported from their public API path.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/datamodel_code_generator/input_model.py (3)

429-473: Ref-strategy implementation matches tests but diverges from InputModelRefStrategy docstring

Functionally, _get_output_family, _get_type_family, _should_reuse_type, and _filter_defs_by_strategy correctly implement:

  • Always reusing enums via x-python-import.
  • Under ReuseForeign, reusing same-family types (e.g., Pydantic when output is Pydantic, TypedDict when output is TypedDict) plus enums, while regenerating other families into the target output type.
  • Under ReuseAll, reusing all nested types via x-python-import.

This aligns with the new test docstrings like test_input_model_ref_strategy_reuse_foreign_*, which explicitly describe “imports enum (always) and same-family types”.

However, the InputModelRefStrategy docstring in src/datamodel_code_generator/enums.py still states that ReuseForeign reuses “types from different model families,” which is now the opposite of what the code and tests do. It would be good to update that docstring (and any CLI help that mirrors it) to avoid confusion.

Also applies to: 482-523


562-621: Inheritance transformation handles single BaseModel parent chains but ignores multiple BaseModel bases

_transform_single_model_to_inheritance correctly reshapes flat Pydantic schemas into:

  • $defs entries for each BaseModel ancestor, and
  • a child schema with allOf: [{"$ref": "#/$defs/Parent"}] and only the child-specific properties/required fields,

recursing up the chain and merging $defs. This matches the new single- and multi-level inheritance tests.

One limitation is that when a model has multiple BaseModel parents (multiple inheritance), only direct_parents[0] is honored; fields from other BaseModel bases stay on the child and those bases never appear in allOf. That’s probably acceptable for now given the focus on simple single-inheritance hierarchies, but you may want to:

  • either document that only the first BaseModel parent is used, or
  • eventually extend this to support multiple BaseModel parents or raise a clear error in that case.

623-764: load_model_schema multi-model loader logic is solid and matches the new tests

Highlights:

  • Correctly defaults output_model_type to DataModelType.PydanticBaseModel when omitted, which your new test_input_model_output_model_type_default verifies for the single-model path.
  • For len(input_models) > 1, it:
    • Ensures CWD is on sys.path once.
    • Distinguishes module-vs-path imports (including .py filename heuristics), shares loaded modules via loaded_modules, and raises clear Error messages for bad formats, missing files/modules, or import failures.
    • Enforces that all objects are Pydantic BaseModel subclasses with model_json_schema (v2 runtime) and that input_file_type is Auto or JsonSchema for multi-Pydantic scenarios.
    • Generates per-model schemas through the custom generator, augments them with x-python-type / unserializable handling and inheritance transformation, merges $defs, and returns an anyOf root of $refs into those defs.
    • Applies ref_strategy via _filter_defs_by_strategy when requested, using _collect_nested_models across all roots.

Behavior lines up with the matrix of multiple --input-model success/error tests, and the module-loading duplication vs _load_single_model_schema is acceptable, though you could factor a shared “load object from input-model string” helper later if you want to DRY it up.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fae1e1d and 963cac6.

⛔ Files ignored due to path filters (1)
  • tests/data/jsonschema/x_python_type_union_anyof.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (5)
  • src/datamodel_code_generator/input_model.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • tests/data/expected/main/jsonschema/x_python_type_union_anyof.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/test_input_model.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🧬 Code graph analysis (3)
tests/data/expected/main/jsonschema/x_python_type_union_anyof.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
src/datamodel_code_generator/input_model.py (1)
src/datamodel_code_generator/enums.py (3)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/imports.py (2)
  • Import (20-38)
  • from_full_path (35-38)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


83-83: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


127-127: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


143-143: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


158-158: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


185-185: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


233-233: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


234-234: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


235-235: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


236-236: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


237-237: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


238-238: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


239-239: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


260-260: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


266-266: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


268-268: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


269-269: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


284-284: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


314-314: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


351-351: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


352-352: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


353-353: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


373-373: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


429-429: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


431-431: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


432-432: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


457-457: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


489-489: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


532-532: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


533-533: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


534-534: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


545-545: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


546-546: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


623-623: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


640-640: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


641-641: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


643-643: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


647-647: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


766-766: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


786-786: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


787-787: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


789-789: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


790-790: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


870-870: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


882-882: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: py312-black24 on Ubuntu
  • GitHub Check: 3.10 on Windows
  • GitHub Check: py312-isort7 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (9)
tests/data/expected/main/jsonschema/x_python_type_union_anyof.py (1)

1-13: Generated model fixture looks consistent and correct

Header, imports, and the ModelWithUnionType definition (nullable Mapping[str, str] with Field(..., title='Optional Mapping')) align with existing expected JSON Schema outputs and union-operator style in this test suite.

tests/main/jsonschema/test_main_jsonschema.py (1)

7209-7216: New x-python-type anyOf union test is well-integrated

The test_x_python_type_union_anyof wiring (JSON_SCHEMA_DATA_PATH input, input_file_type=None, assert_file_content, no extra args) matches the surrounding x_python_type_* tests and will exercise the new expected fixture without introducing new behavior in the helper.

src/datamodel_code_generator/parser/jsonschema.py (2)

554-617: BaseModel entry in PYTHON_TYPE_IMPORTS looks correct and consistent

Adding "BaseModel": Import.from_full_path("pydantic.BaseModel") aligns with how other common types are handled and lets _resolve_type_import canonicalize BaseModel coming from x-python-type without ad-hoc imports. No issues here.


1192-1203: Union guard and nested import resolution for x-python-type are sound

  • The extra check in _is_compatible_python_type to return False when python_type contains " | " and schema_type is None ensures union-style x-python-type (PEP 604) is treated as an override instead of being silently considered "compatible". That matches the new union/Mapping[...] | None preservation tests and avoids losing override semantics when the schema has no explicit type.
  • In _get_python_type_override, preferring self._resolve_type_import(class_name) over Import.from_full_path(qualified_name) for nested/qualified names reuses the canonical import mapping (e.g., BaseModel, collections.abc types) and only falls back to a full-path import when needed, which should reduce duplicate/odd imports.

Behavior is coherent with the new tests and doesn’t introduce obvious regressions.

Also applies to: 1273-1277

tests/test_input_model.py (3)

16-23: Shared expected path, timestamp freezing, and golden-file assertion are well factored

Using EXPECTED_INPUT_MODEL_PATH plus TIMESTAMP with freeze_time in run_input_model_and_assert and delegating to assert_output gives stable, golden-file based tests and eliminates per-test duplication. The helper argument order (input_model, output_path, expected_file, extra_args) is clear and matches how it’s used throughout the file.

Also applies to: 56-74


29-33: Python 3.14 skip marker is appropriate for schema-string-sensitive tests

SKIP_PYTHON_314 plus its use on the x-python-type preservation tests and the config-class test cleanly isolates cases that depend on model_json_schema’s exact annotation formatting. This keeps the suite green on Python 3.14+ without weakening coverage on earlier versions.

Also applies to: 475-552, 575-621, 1036-1043


1052-1591: Multiple --input-model helpers and inheritance/error-matrix tests give strong coverage

  • run_multiple_input_models_and_assert / run_multiple_input_models_error_and_assert mirror the single-model helpers cleanly, construct the CLI args correctly (repeating --input-model and appending --output / extras), and consistently assert on exit codes, file existence, and stderr.
  • The new tests cover:
    • Single-model inheritance (flat, multi-level, no-inheritance) and forked/mixed multi-model inheritance against golden files.
    • Output-model-type variants (Pydantic, dataclass, TypedDict) and ref-strategy combinations.
    • All major error surfaces for multiple --input-model (non-BaseModel, Pydantic v1 runtime, bad format, missing file/module/attribute, non-jsonschema input-file-type, module load/import failures).
    • sys.path behavior (cwd added vs already present), path-based module loading (with and without separators), and config coercion of input-model from string to list.
    • Direct load_model_schema defaulting of output_model_type via test_input_model_output_model_type_default.

The scenarios align with the new loader’s behavior and look comprehensive without obvious gaps.

src/datamodel_code_generator/input_model.py (2)

19-24: Type-serialization and unserializable handling pipeline is coherent

The combination of:

  • _get_input_model_json_schema_class marking all invalid/callable types with _UNSERIALIZABLE_MARKER,
  • _add_python_type_for_unserializable / _process_unserializable_property / _set_python_type_for_unserializable using full serialization (_serialize_python_type_full) to attach x-python-type at the right level (including anyOf/oneOf/items branches and Type[X]),
  • _init_preserved_type_origins + _serialize_python_type / _simple_type_name to add x-python-type for container types that are JSON-schema-serializable (set/frozenset/mappings/sequences, both typing and collections.abc origins), and
  • _collect_nested_models / _add_python_type_info / _add_python_type_info_generic for BaseModel vs dataclass/TypedDict,

forms a consistent story that matches the new tests around ModelWithPythonTypes, callables, unions, and nested definitions. The use of lazy imports with # noqa: PLC0415 is also aligned with prior project guidance to keep these defensive directives.

Also applies to: 34-107, 125-229, 231-427


766-901: Single-model loader _load_single_model_schema covers all supported types and validation paths

The single-model path:

  • Validates the module:Object / path/to/file.py:Object format and uses a CWD-injecting import strategy symmetric with load_model_schema.
  • Distinguishes:
    • dict → returns the dict directly, requiring a non-Auto InputFileType, which the dict tests exercise.
    • Pydantic BaseModel subclasses → enforces Auto/JsonSchema input_file_type, requires model_json_schema (v2 runtime), runs _try_rebuild_model, applies the same schema generator + x-python-type + inheritance transform as in the multi-case, and then ref-strategy filtering if requested.
    • dataclass / TypedDict types → enforces Auto/JsonSchema, uses TypeAdapter(obj).json_schema(), augments with _add_python_type_info_generic, and applies ref-strategy filtering, with clear error messaging if TypeAdapter is unavailable (v1 runtime).
    • Everything else → raises a concise “not a supported type” Error.

This matches the various error tests (*_non_jsonschema_error, Pydantic v1 runtime, dataclass/TypedDict Pydantic v1, unsupported type) and the positive dataclass/TypedDict and config-class cases. The loader’s responsibilities are clear and the branching looks correct.

Comment thread tests/test_input_model.py Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_input_model.py (1)

1-1574: Fix formatting to resolve pipeline failure.

The pipeline reports that ruff-format made formatting changes to this file. Run ruff format tests/test_input_model.py to fix the formatting issues and commit the changes.

🧹 Nitpick comments (1)
tests/test_input_model.py (1)

365-365: Remove redundant local sys imports.

The module-level import sys on line 5 is available throughout the file. The local imports on lines 365 and 1483 inside test functions are redundant and trigger the Ruff F811 warning.

🔎 Proposed fix

Remove the local imports on lines 365 and 1483:

Line 365 (inside test_input_model_adds_cwd_to_sys_path):

 def test_input_model_adds_cwd_to_sys_path(
     tmp_path: Path,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     """Test that --input-model adds cwd to sys.path if not present."""
-    import sys
-
     cwd = str(tmp_path)

Line 1483 (inside test_input_model_cwd_already_in_path):

 def test_input_model_cwd_already_in_path(
     tmp_path: Path,
 ) -> None:
     """Test that cwd is not duplicated in sys.path when already present."""
-    import sys
     from pathlib import Path as _Path

Also applies to: 1483-1483

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 963cac6 and 6804416.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/input_model.py
  • tests/test_input_model.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🧬 Code graph analysis (1)
src/datamodel_code_generator/input_model.py (2)
src/datamodel_code_generator/enums.py (3)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
src/datamodel_code_generator/model/base.py (2)
  • name (827-829)
  • DataModel (621-930)
🪛 GitHub Actions: Lint
tests/test_input_model.py

[error] 1-1: ruff-format: 1 file reformatted by hook; some formatting changes were made.

🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


83-83: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


127-127: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


132-132: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


143-143: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


158-158: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


185-185: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


233-233: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


234-234: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


235-235: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


236-236: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


237-237: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


238-238: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


239-239: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


260-260: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


266-266: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


268-268: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


269-269: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


285-285: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


315-315: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


352-352: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


353-353: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


354-354: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


374-374: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


430-430: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


432-432: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


433-433: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


458-458: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


490-490: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


533-533: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


534-534: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


535-535: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


546-546: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


547-547: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


624-624: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


641-641: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


642-642: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


644-644: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


648-648: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


767-767: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


787-787: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


788-788: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


790-790: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


791-791: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


871-871: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


883-883: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

tests/test_input_model.py

1483-1483: Redefinition of unused sys from line 5

Remove definition: sys

(F811)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: py312-isort5 on Ubuntu
  • GitHub Check: py312-pydantic1 on Ubuntu
  • GitHub Check: py312-black22 on Ubuntu
  • GitHub Check: 3.13 on Ubuntu
  • GitHub Check: py312-isort6 on Ubuntu
  • GitHub Check: 3.10 on Ubuntu
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.14 on Windows
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (1)
src/datamodel_code_generator/input_model.py (1)

1-902: LGTM! Solid implementation of input model loading and schema transformation.

The new input model loading infrastructure is well-designed and comprehensive. The public API (load_model_schema and Error) provides clean interfaces for loading and merging schemas from Python import paths with inheritance support. The implementation properly handles:

  • Type preservation via x-python-type annotations for types lost during JSON Schema conversion
  • Inheritance transformation using allOf structures
  • Multiple input model families (Pydantic v2, dataclass, TypedDict, msgspec)
  • Ref-strategy options for controlling type reuse vs regeneration
  • Robust error handling with descriptive messages

All previously flagged issues (import aliases, noqa directive placement) have been addressed in prior commits.

Based on learnings, defensive # noqa: PLC0415 and # noqa: PLR6301 directives should be kept even when Ruff reports them as unused via RUF100, as they prepare for potential future configuration changes.

- Add _resolve_type_import_from_defs method to resolve imports from
  $defs entries with x-python-import metadata
- Handle Annotated types in _serialize_python_type_full to prevent
  invalid type serialization with FieldInfo
@koxudaxi koxudaxi force-pushed the feature/multiple-input-model-inheritance branch from 16ba6c0 to 325ed3c Compare December 31, 2025 16:59
Add _full_type_name helper function that generates fully qualified names
for type arguments while keeping outer types as short names. This ensures
proper import resolution for custom types like StrictTypes.

- Add _full_type_name function with Union type handling (| syntax)
- Update _serialize_python_type to use _full_type_name for type args
- Update expected test file for config_class output
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/datamodel_code_generator/input_model.py (1)

876-881: Consider renaming variable for clarity.

The variable module is reused at line 877 (assigned ModuleSpec | None) and line 881 (reassigned to actual module). While not incorrect, using distinct names like module_spec and module would improve readability.

🔎 Suggested refactor for clarity
     else:
         try:
-            module = importlib.util.find_spec(modname)
-            if module is None:
+            module_spec = importlib.util.find_spec(modname)
+            if module_spec is None:
                 msg = f"Cannot find module {modname!r}"
                 raise Error(msg)
             module = importlib.import_module(modname)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 325ed3c and c0c7633.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/input_model.py
  • tests/data/expected/main/input_model/config_class.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🧬 Code graph analysis (2)
src/datamodel_code_generator/input_model.py (2)
src/datamodel_code_generator/enums.py (3)
  • DataModelType (48-56)
  • InputFileType (35-45)
  • InputModelRefStrategy (206-217)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • model_rebuild (218-220)
tests/data/expected/main/input_model/config_class.py (4)
src/datamodel_code_generator/model/type_alias.py (1)
  • TypeAlias (37-42)
src/datamodel_code_generator/model/typed_dict.py (1)
  • TypedDict (49-114)
src/datamodel_code_generator/enums.py (4)
  • StrictTypes (220-227)
  • AllExportsCollisionStrategy (92-102)
  • AllExportsScope (81-89)
  • AllOfClassHierarchy (155-159)
src/datamodel_code_generator/format.py (4)
  • DateClassType (60-65)
  • DatetimeClassType (50-57)
  • Formatter (171-177)
  • PythonVersion (68-140)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


66-66: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


90-90: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


134-134: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


139-139: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


150-150: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


165-165: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


192-192: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


240-240: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


241-241: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


242-242: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


243-243: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


244-244: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


245-245: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


246-246: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


267-267: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


273-273: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


276-276: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


282-282: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


292-292: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


322-322: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


333-333: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


339-339: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


340-340: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


408-408: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


409-409: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


410-410: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


430-430: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


486-486: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


488-488: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


489-489: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


514-514: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


546-546: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


589-589: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


590-590: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


591-591: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


602-602: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


603-603: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


680-680: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


697-697: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


698-698: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


700-700: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


704-704: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


823-823: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


843-843: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


844-844: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


846-846: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


847-847: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


927-927: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


939-939: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: 3.11 on Windows
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.10 on Windows
  • GitHub Check: 3.13 on macOS
  • GitHub Check: 3.12 on macOS
  • GitHub Check: 3.11 on macOS
  • GitHub Check: 3.14 on Windows
  • GitHub Check: Analyze (python)
  • GitHub Check: benchmarks
🔇 Additional comments (2)
src/datamodel_code_generator/input_model.py (1)

34-958: LGTM! The implementation is solid.

This file introduces comprehensive input model loading and schema transformation capabilities. The code handles complex type serialization, inheritance preservation, and various edge cases appropriately.

Regarding static analysis warnings: The RUF100 warnings about "unused noqa directives" should be ignored. Based on learnings, defensive # noqa: PLC0415 directives should be kept on lazy imports even when Ruff reports them as unused, and # noqa: PLR6301 directives are necessary for methods in Ruff preview mode.

The complexity suppressions (PLR0911, PLR0912, PLR0914, PLR0915) are appropriate given the nature of type serialization and multi-model loading logic.

Note on learnings: Based on learnings, defensive # noqa: PLC0415 directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule. Similarly, noqa: PLR6301 directives are necessary and should not be removed even if RUF100 suggests they are unused, as Ruff preview mode is enabled via lint.preview = true in pyproject.toml.

tests/data/expected/main/input_model/config_class.py (1)

1-233: LGTM! Generated test file is well-formed.

This generated test file correctly defines comprehensive type aliases and TypedDict structures for configuration. The fixed timestamp (1985-10-26T08:21:00+00:00) is appropriate for deterministic test output. All type definitions are consistent with the source enums and structures.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0c7633 and e45af85.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/input_model.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

  • src/datamodel_code_generator/input_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

Applied to files:

  • src/datamodel_code_generator/input_model.py
🪛 Ruff (0.14.10)
src/datamodel_code_generator/input_model.py

34-34: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


36-36: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


37-37: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


66-66: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


90-90: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


134-134: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


139-139: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


150-150: Unused noqa directive (non-enabled: PLR6301)

Remove unused noqa directive

(RUF100)


165-165: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


192-192: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


240-240: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


241-241: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


242-242: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


243-243: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


244-244: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


245-245: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


246-246: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


267-267: Unused noqa directive (non-enabled: PLW0603)

Remove unused noqa directive

(RUF100)


273-273: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


275-275: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


276-276: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


282-282: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


292-292: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


322-322: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


333-333: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


339-339: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


340-340: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


408-408: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


409-409: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


410-410: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


430-430: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


486-486: Unused noqa directive (non-enabled: PLR0911)

Remove unused noqa directive

(RUF100)


488-488: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


489-489: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


514-514: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


546-546: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


589-589: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


590-590: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


591-591: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


602-602: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


603-603: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


680-680: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


697-697: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


698-698: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


700-700: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


704-704: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


823-823: Unused noqa directive (non-enabled: PLR0912, PLR0914, PLR0915)

Remove unused noqa directive

(RUF100)


843-843: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


844-844: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


846-846: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


847-847: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


927-927: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


939-939: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 3.10 on Windows
  • GitHub Check: benchmarks
🔇 Additional comments (10)
src/datamodel_code_generator/input_model.py (10)

1-130: LGTM! Solid foundation with type serialization utilities.

The module structure, constants, and initial type serialization utilities are well-designed. The _serialize_python_type_full() function comprehensively handles various Python type representations including Union, Annotated, Callable, and generic types.


132-161: LGTM! Custom schema generator correctly handles unserializable types.

The lazy-loaded InputModelJsonSchema class properly catches types that Pydantic cannot serialize to JSON Schema, including Callable types. The noqa: PLR6301 directives on Lines 139 and 150 should be kept despite Ruff's RUF100 warnings, as they prepare for potential future configuration changes.

Based on learnings, these defensive directives are intentional for preview rules.


163-236: LGTM! Robust handling of unserializable types with proper recursion guards.

The functions correctly process properties marked as unserializable, handle complex structures (anyOf/oneOf/items), and use visited_defs tracking to prevent infinite recursion when processing nested models.


238-380: LGTM! Comprehensive type serialization with preserved type origins.

The lazy initialization of preserved type origins and the type serialization functions handle complex scenarios including Union types (with modern | syntax), Annotated types, and fully qualified names. The logic correctly preserves important type information that would be lost in JSON Schema conversion.


382-484: LGTM! Well-designed nested model discovery and type annotation.

The nested model collection uses proper recursion guards with visited sets, supports multiple type families (BaseModel, Enum, dataclass, TypedDict, msgspec), and safely handles type hints with appropriate fallback. The x-python-type annotation functions correctly preserve type information across different input formats.


486-537: LGTM! Type family classification and reuse logic are correct.

The type family determination correctly handles the precedence of different type categories, the output family mapping properly handles all DataModelType cases, and the reuse logic appropriately treats Enums as always reusable while requiring family matching for other types.


539-612: LGTM! Strategy-based filtering and model rebuilding are well-implemented.

The _filter_defs_by_strategy() function correctly implements the three strategies (RegenerateAll, ReuseAll, ReuseForeign) and properly annotates reused types with x-python-import metadata. The _try_rebuild_model() function appropriately handles special config classes with their required type namespaces.


614-678: LGTM! Inheritance transformation correctly implements allOf structure.

The _transform_single_model_to_inheritance() function properly handles multi-level inheritance by recursively processing parent schemas, using a cache (processed_parents) to avoid reprocessing, and correctly separating parent and child properties/requirements. The resulting allOf structure with parent references preserves the inheritance hierarchy in JSON Schema.


680-821: LGTM! Main API function correctly handles multiple input models with comprehensive validation.

The load_model_schema() function properly:

  • Validates input format and loads modules/classes
  • Enforces Pydantic v2 BaseModel requirement for multiple inputs
  • Validates input_file_type compatibility
  • Applies inheritance transformation to each model
  • Merges schemas into an anyOf structure with aggregated $defs
  • Applies reference strategy filtering

The error messages are clear and helpful, and the function handles both module import paths and file paths correctly.


823-876: LGTM! Single model loading correctly handles multiple input types (after bug fix).

The _load_single_model_schema() function (aside from the Lines 877-881 bug) properly:

  • Parses input format and distinguishes module vs file paths
  • Handles dict inputs (raw schema)
  • Handles Pydantic v2 BaseModel with inheritance transformation
  • Handles dataclass/TypedDict via Pydantic v2 TypeAdapter
  • Validates input_file_type compatibility for each case
  • Applies appropriate schema transformations and ref strategy filtering

The error messages provide clear guidance, and the function correctly uses the various helper functions for schema generation and transformation.

Also applies to: 882-958

Comment thread src/datamodel_code_generator/input_model.py
- Add tests for _simple_type_name edge cases (NoneType, generic types)
- Add tests for _full_type_name (string annotation, ForwardRef, typing specials)
- Add tests for _serialize_python_type_full with Annotated type
- Add tests for _resolve_type_import_from_defs (found, not found, no x-python-import, exception handling)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Use list.__class_getitem__(()) to create a GenericAlias with origin
but no args, avoiding the ruff UP035 auto-fix that converted typing.List to list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Renamed misleading variable from 'module' to 'spec' when storing
the result of importlib.util.find_spec(), as it returns a ModuleSpec
not a module object.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Test builtin type (module='builtins') returns short name
- Test collections.abc type returns short name

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@koxudaxi koxudaxi merged commit 9dc9dd4 into main Dec 31, 2025
39 checks passed
@koxudaxi koxudaxi deleted the feature/multiple-input-model-inheritance branch December 31, 2025 18:41
@github-actions
Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: Breaking changes detected

Reasoning: The PR changes Config.input_model from Optional[str] to Optional[list[str]]. While a field validator ensures backward compatibility when setting the value (single strings are coerced to lists), code that reads this field will now receive a list instead of a string. This affects users who programmatically interact with the Config class. The CLI remains backward compatible since --input-model can still be used once as before, or multiple times for the new functionality.

Content for Release Notes

API/CLI Changes

  • Config.input_model type changed from str to list[str] - The input_model field in the Config class now stores a list of strings instead of a single string. While backward compatibility is maintained when setting the value (single strings are automatically coerced to lists), code that reads config.input_model will now receive a list[str] instead of str | None. Users who programmatically access this field should update their code to handle the list type. (Add multiple --input-model support with inheritance preservation #2881)
    # Before
    if config.input_model:
        process_model(config.input_model)  # config.input_model was str
    
    # After
    if config.input_model:
        for model in config.input_model:  # config.input_model is now list[str]
            process_model(model)

This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 1, 2026

🎉 Released in 0.51.0

This PR is now available in the latest release. See the release notes for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants