Skip to content

Add --collapse-root-models-name-strategy option#2791

Merged
koxudaxi merged 19 commits intomainfrom
feature/collapse-root-models-name-strategy
Dec 25, 2025
Merged

Add --collapse-root-models-name-strategy option#2791
koxudaxi merged 19 commits intomainfrom
feature/collapse-root-models-name-strategy

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Dec 24, 2025

Fixes: #1418

Summary by CodeRabbit

  • New Features

    • Added --collapse-root-models-name-strategy, --http-timeout and --use-field-description-example CLI options.
    • generate() can return generated code as a string or a mapping of multiple modules; new exported return type and error type for schema parse reporting.
    • Schema extensions (x-*) and propertyNames support exposed to templates and generation.
  • Documentation

    • Updated CLI reference, general options, field customization, custom templates, and module-usage docs to cover new options and behaviors.
  • Tests

    • Expanded test suite for naming strategies, propertyNames, schema extensions, HTTP timeout, and field-example handling.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 24, 2025

📝 Walkthrough

Walkthrough

Adds three CLI options (--collapse-root-models-name-strategy, --http-timeout, --use-field-description-example), threads them through CLI, config, and parser layers, implements collapse-root-models naming strategies (Child/Parent) and schema-extension/propertyNames support, adds HTTP timeout handling, updates generate() return behavior, and expands tests and docs accordingly.

Changes

Cohort / File(s) Summary
CLI & Options
src/datamodel_code_generator/arguments.py, src/datamodel_code_generator/cli_options.py
New CLI flags: --collapse-root-models-name-strategy, --http-timeout, --use-field-description-example; metadata entries and help text added; CLI validation for strategy dependency.
Main / Config / Entrypoint
src/datamodel_code_generator/__main__.py, src/datamodel_code_generator/__init__.py
New Config fields and public API surface: collapse_root_models_name_strategy, use_field_description_example, http_timeout; added GeneratedModules alias, SchemaParseError, _build_module_content; generate() signature/return updated to support string/dict/None.
Parser Core
src/datamodel_code_generator/parser/base.py, src/datamodel_code_generator/parser/jsonschema.py, src/datamodel_code_generator/parser/openapi.py, src/datamodel_code_generator/parser/graphql.py
Parser constructors accept and store new flags; base parser implements Parent/Child rename logic for collapsing root models with conflict checks and warnings; JSON Schema parser: x-* extensions, propertyNames handling, schema validation helper, and SchemaParseError integration; propagation of http_timeout and use_field_description_example.
HTTP Layer
src/datamodel_code_generator/http.py
Added DEFAULT_HTTP_TIMEOUT and timeout parameter to get_body, passed to httpx.get.
Model / Field Docstrings
src/datamodel_code_generator/model/base.py
New use_field_description_example flag in Config and DataModelFieldBase; docstring generation updated to optionally include example content.
Prompt / CLI Docs
src/datamodel_code_generator/prompt_data.py, docs/cli-reference/*.md, docs/custom_template.md, docs/using_as_module.md, docs/cli-reference/*
Added option descriptions and docs for new flags; docs updated for generate() return semantics and schema extensions; some duplicated sections present in docs diffs.
Tests (additions/expectations)
tests/main/*, tests/parser/*, tests/data/expected/main/jsonschema/*, tests/data/expected/main/openapi/*, tests/data/expected/main_kr/*
Large set of new/updated tests and expected outputs covering collapse-root-models name strategies (child/parent), propertyNames/x-extensions, field-description examples, HTTP timeout assertions, additional_imports merging, and generate() return behavior; some duplicated test definitions and duplicated doc sections in test docs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as CLI
  participant Main as datamodel_code_generator.__main__
  participant Gen as generate()
  participant Parser as Parser (JsonSchema/OpenAPI/GraphQL)
  participant HTTP as http.get
  note right of CLI `#a3be8c`: user supplies flags (--http-timeout,\n--collapse-root-models-name-strategy,\n--use-field-description-example)
  CLI->>Main: parse args -> Config
  Main->>Gen: run_generate_from_config(config, extra_template_data)
  Gen->>Parser: init(parser_settings including collapse_root_models_name_strategy,\nuse_field_description_example, http_timeout)
  Parser->>HTTP: fetch external refs (timeout param)
  HTTP-->>Parser: schema content
  Parser->>Parser: parse schemas, apply collapse-root-models logic (Parent/Child), set schema extensions/propertyNames
  Parser-->>Gen: module bodies
  Gen->>Gen: _build_module_content(header, body, custom_file_header)
  Gen-->>CLI: return string | GeneratedModules | write files
  note left of Gen `#efe1b3`: warnings emitted for conflicts (multiple wrappers, direct refs)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

safe-to-fix

Suggested reviewers

  • ilovelinux

Poem

🐰 I hopped through flags and docs today,

Parent or Child, you choose the way.
Timeouts set, examples in sight,
Schemas merged and names made right.
A tiny rabbit cheers—codegen's bright!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The PR includes some changes beyond the core feature: new propertyNames support in JSON Schema parsing, http_timeout feature, use_field_description_example feature, and schema extensions documentation. While these are tangential enhancements, they appear intentional infrastructure additions rather than unrelated work. Verify whether propertyNames support, http_timeout, use_field_description_example, and schema extensions were intentionally bundled with this feature or should be separated into distinct PRs for cleaner scope management.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding a new CLI option --collapse-root-models-name-strategy, which is the primary feature introduced across documentation and code.
Linked Issues check ✅ Passed The PR fully addresses issue #1418 by implementing the requested feature to control which name is preserved when collapsing root models, with both 'child' and 'parent' strategy options, comprehensive documentation updates, and extensive test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 97.62% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/collapse-root-models-name-strategy

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.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 24, 2025

CodSpeed Performance Report

Merging #2791 will not alter performance

Comparing feature/collapse-root-models-name-strategy (105619f) with main (75dca9d)

Summary

✅ 73 untouched
⏩ 10 skipped1

Footnotes

  1. 10 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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 24, 2025

Codecov Report

❌ Patch coverage is 98.36066% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 99.46%. Comparing base (75dca9d) to head (105619f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/datamodel_code_generator/parser/base.py 96.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2791      +/-   ##
==========================================
- Coverage   99.46%   99.46%   -0.01%     
==========================================
  Files          88       88              
  Lines       13504    13564      +60     
  Branches     1594     1600       +6     
==========================================
+ Hits        13432    13491      +59     
  Misses         36       36              
- Partials       36       37       +1     
Flag Coverage Δ
unittests 99.46% <98.36%> (-0.01%) ⬇️

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.

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/arguments.py (1)

17-33: Clarify or enforce the --collapse-root-models dependency

The new argument is wired correctly (Enum-backed choices, default None, good help text), but the help currently states:

“Requires --collapse-root-models to be set.”

There’s no enforcement in arguments.py itself; if a user passes only --collapse-root-models-name-strategy, it will effectively be a no-op rather than an error.

Consider either:

  • Enforcing this constraint in config/generation code (raise if name-strategy is set while collapse_root_models is falsey), or
  • Softening the wording to “Only has effect when --collapse-root-models is set.”

Also applies to: 180-187

docs/cli-reference/model-customization.md (1)

14-15: Docs clearly describe the option; consider highlighting the parent use case

The table entry and dedicated section for --collapse-root-models-name-strategy are accurate and link correctly, and the JSON Schema example for the child strategy matches the expected output.

Since the primary new capability is the parent behavior, you might optionally:

  • Add a short contrasting example (or diff) for --collapse-root-models-name-strategy parent, or
  • Explicitly note that omitting the option behaves like child, while parent switches to preserving the wrapper name.

This would make the motivation from issue #1418 more obvious to readers skimming the docs.

Also applies to: 1510-1577

src/datamodel_code_generator/parser/base.py (1)

1683-1683: Remove unused noqa directive.

The complexity-related noqa codes (PLR0912, PLR0914, PLR0915) are not enabled in your Ruff configuration, making this directive unnecessary.

🔎 Proposed fix
-    def __collapse_root_models(  # noqa: PLR0912, PLR0914, PLR0915
+    def __collapse_root_models(
         self,
         models: list[DataModel],
         unused_models: list[DataModel],
📜 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 f59786f and 49e4303.

⛔ Files ignored due to path filters (5)
  • tests/data/jsonschema/collapse_root_models_name_strategy_child.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/collapse_root_models_name_strategy_direct_refs.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/collapse_root_models_name_strategy_multiple_wrappers.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/collapse_root_models_name_strategy_parent.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/collapse_root_models_name_strategy_with_inheritance.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (18)
  • docs/cli-reference/index.md
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/quick-reference.md
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_child.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_direct_refs.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_multiple_wrappers.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_parent.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_with_inheritance.py
  • tests/main/jsonschema/test_main_jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (10)
src/datamodel_code_generator/parser/openapi.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_parent.py (3)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_child.py (1)
  • Model (14-15)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_direct_refs.py (1)
  • Model (22-24)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_multiple_wrappers.py (1)
  • Model (22-24)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_multiple_wrappers.py (3)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_child.py (1)
  • Model (14-15)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_direct_refs.py (1)
  • Model (22-24)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_parent.py (1)
  • Model (14-15)
src/datamodel_code_generator/parser/graphql.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
src/datamodel_code_generator/arguments.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_child.py (3)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_direct_refs.py (1)
  • Model (22-24)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_multiple_wrappers.py (1)
  • Model (22-24)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_parent.py (1)
  • Model (14-15)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
tests/main/jsonschema/test_main_jsonschema.py (2)
tests/main/conftest.py (2)
  • output_file (98-100)
  • run_main_and_assert (244-408)
src/datamodel_code_generator/__main__.py (1)
  • Exit (95-101)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/__main__.py

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

Remove unused noqa directive

(RUF100)


960-960: Unused noqa directive (non-enabled: T201)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (20)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_multiple_wrappers.py (1)

10-24: The expected output correctly preserves all three classes (SharedInner, Wrapper1, Wrapper2) without collapsing. The test function at line 4999 confirms this is intentional: when the parent strategy is applied to a schema with multiple wrappers referencing the same inner model, the code issues a UserWarning ("Cannot apply 'parent' strategy when inner model has multiple root models") and skips the collapse operation to avoid ambiguity. This design correctly prevents semantic loss—collapsing would either lose the Wrapper1/Wrapper2 distinction (child strategy) or create ambiguity about which wrapper name to preserve (parent strategy). The expected output is correct.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_with_inheritance.py (1)

1-20: Expected inheritance fixture looks consistent with feature intent

The generated Wrapper/Derived/Model layout is clean, uses the modern from __future__ import annotations + | None style, and matches the intended “parent strategy with inheritance” scenario for the new option.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_parent.py (1)

1-15: Parent-strategy expected output matches requested behavior

Preserving ISectionBlockMetadata as the inner model name and wiring Model.metadata to it aligns with the “keep wrapper name” requirement from the linked issue, and the style matches other expected fixtures.

src/datamodel_code_generator/parser/graphql.py (1)

16-31: New name-strategy parameter is correctly plumbed through GraphQLParser

Importing CollapseRootModelsNameStrategy, adding an optional collapse_root_models_name_strategy argument, and forwarding it into super().__init__ keeps the API backward compatible while enabling the new behavior for GraphQL inputs as well. The parameter is grouped next to collapse_root_models, which keeps the signature coherent.

Also applies to: 175-176, 285-287

docs/cli-reference/quick-reference.md (1)

80-90: Quick-reference entries for --collapse-root-models-name-strategy are accurate and consistent

The new option is correctly listed under Model Customization and in the alphabetical index, with a concise description that matches the test docstring and points to the model-customization.md#collapse-root-models-name-strategy anchor.

Also applies to: 186-202

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

4951-5031: New tests give solid coverage for --collapse-root-models-name-strategy behavior

The added tests exercise:

  • Happy-path behavior for both child and parent strategies (including CLI-doc coverage for child),
  • The argument validation error when the strategy is used without --collapse-root-models, and
  • Warning paths where parent cannot be safely applied (multiple wrappers, direct refs), plus an inheritance case.

The use of run_main_and_assert, pytest.warns, and function-name–derived expected filenames is consistent with the rest of the suite and should keep the new option well guarded against regressions.

docs/cli-reference/index.md (1)

14-18: Category count and index entry stay consistent with new option

The Model Customization count bump to 34 and the new --collapse-root-models-name-strategy entry under C are consistent and correctly linked to the detailed docs section.

Also applies to: 48-50

src/datamodel_code_generator/prompt_data.py (1)

23-31: New prompt description matches option semantics

The OPTION_DESCRIPTIONS entry for --collapse-root-models-name-strategy is concise and aligned with the intended behavior of choosing the kept name when collapsing root models.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_child.py (1)

1-15: Expected output correctly reflects child naming strategy

The generated models keep the inner FieldType2 name and reference it from Model.metadata, which matches the documented child behavior for --collapse-root-models-name-strategy.

src/datamodel_code_generator/cli_options.py (1)

83-88: CLI metadata wiring for new option is correct

CLI_OPTION_META now includes --collapse-root-models-name-strategy in the Model category, matching the argparse option and keeping docs sync infrastructure intact.

src/datamodel_code_generator/parser/openapi.py (1)

20-36: OpenAPI parser correctly threads the new naming strategy to the base parser

The added import, constructor parameter, and super().__init__ forwarding for collapse_root_models_name_strategy are consistent with existing configuration plumbing and ensure OpenAPI benefits from the same collapse-root-models naming behavior as JSON Schema.

Also applies to: 259-262, 299-372

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

27-27: LGTM! Parameter threading is clean and consistent.

The new collapse_root_models_name_strategy parameter is properly imported, typed, and forwarded to the parent Parser class, following the same pattern as the existing collapse_root_models parameter.

Also applies to: 601-601, 711-711

src/datamodel_code_generator/__init__.py (2)

302-310: Excellent enum documentation.

The CollapseRootModelsNameStrategy enum is well-documented with clear explanations of both the Child and Parent strategies, making it easy for users to understand the behavior.


529-529: Parameter correctly integrated into generate() API.

The new parameter is properly added to the function signature and forwarded to the parser initialization.

Also applies to: 803-803

src/datamodel_code_generator/__main__.py (3)

28-28: Configuration field properly integrated.

The new collapse_root_models_name_strategy field is correctly added to the Config model with appropriate typing.

Note: The static analysis hint about line 491's noqa: UP045 directive is a false positive—the directive is necessary to maintain consistency with other Optional type annotations in the Config class.

Also applies to: 491-491


959-964: Validation logic is correct.

The validation appropriately enforces that --collapse-root-models-name-strategy can only be used when --collapse-root-models is enabled, with a clear error message.


808-808: Parameter correctly forwarded to generate function.

The collapse_root_models_name_strategy is properly passed through from the config to the generate() call.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_direct_refs.py (1)

1-24: Test data file for direct-refs scenario.

This is an expected output file used for test validation. No review needed as it represents generated output that will be compared against actual generation results during testing.

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

30-30: LGTM! Proper parameter threading for the new strategy.

The new collapse_root_models_name_strategy parameter is correctly imported, added to __init__, and stored as an instance variable. The None default ensures backward compatibility.

Also applies to: 754-754, 944-944


1714-1784: Excellent implementation with thorough conflict detection.

The strategy implementation is well-designed:

  • Backward compatibility: The None check (lines 1716-1718) preserves existing behavior when no strategy is specified.
  • Defensive checks: Proper validation prevents edge cases:
    • Multiple wrappers referencing the same inner model (lines 1735-1742)
    • Direct non-wrapper references to the inner model (lines 1754-1760)
  • Clear warnings: Users are informed when the strategy cannot be applied, helping them understand why their configuration isn't taking effect.
  • Correct reference management: The code properly rewires references (lines 1770-1778) and cleans up unused wrappers (lines 1781-1783).

The Parent strategy implementation (line 1763) correctly renames the inner model to adopt the wrapper's name, addressing the issue described in #1418.

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 49e4303 and 572afef.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/parser/base.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

🔇 Additional comments (4)
src/datamodel_code_generator/parser/base.py (4)

30-30: LGTM!

Import addition is correct and properly placed.


754-754: LGTM!

Parameter addition maintains backward compatibility with None default.


944-944: LGTM!

Instance variable assignment follows existing patterns.


1683-1683: Remove unused noqa directive.

The complexity checks (PLR0912, PLR0914, PLR0915) are not triggered for this method, so the noqa directive is unnecessary.

🔎 Proposed fix
-    def __collapse_root_models(  # noqa: PLR0912, PLR0914, PLR0915
+    def __collapse_root_models(
⛔ Skipped due to learnings
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.

Comment thread src/datamodel_code_generator/parser/base.py
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

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

1683-1683: Optional: Remove unused noqa directive.

The static analysis tool reports that the noqa directive for PLR0912, PLR0914, PLR0915 is unnecessary because these rules are not enabled in your configuration.

🔎 Proposed fix
-    def __collapse_root_models(  # noqa: PLR0912, PLR0914, PLR0915
+    def __collapse_root_models(
📜 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 572afef and a23b4e0.

⛔ Files ignored due to path filters (2)
  • tests/data/jsonschema/collapse_root_models_name_strategy_complex.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/collapse_root_models_name_strategy_nested_wrappers.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (9)
  • src/datamodel_code_generator/parser/base.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child_v2.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent_v2.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_child.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py
  • tests/main/jsonschema/test_main_jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (9)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent.py (4)
src/datamodel_code_generator/model/base.py (1)
  • name (741-743)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_child.py (1)
  • Model (14-15)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent.py (1)
  • Model (18-19)
tests/main/jsonschema/test_main_jsonschema.py (3)
tests/test_main_kr.py (1)
  • output_file (44-46)
tests/main/conftest.py (2)
  • output_file (98-100)
  • run_main_and_assert (244-408)
src/datamodel_code_generator/__main__.py (1)
  • Exit (95-101)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (302-310)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child_v2.py (3)
src/datamodel_code_generator/model/base.py (1)
  • name (741-743)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent_v2.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py (1)
  • Model (18-19)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_child.py (3)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent.py (1)
  • Model (18-19)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py (2)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child_v2.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent_v2.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child.py (1)
src/datamodel_code_generator/model/base.py (1)
  • name (741-743)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent.py (3)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_child.py (1)
  • Model (14-15)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent_v2.py (3)
src/datamodel_code_generator/model/base.py (1)
  • name (741-743)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_child_v2.py (1)
  • Model (24-27)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py (1)
  • Model (18-19)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

🔇 Additional comments (9)
tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_child.py (1)

1-15: LGTM! Test fixture correctly demonstrates the "child" naming strategy.

The generated output appropriately shows the inner model name (Inner) being preserved when using the "child" collapse strategy, which aligns with the PR objectives.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent.py (1)

1-19: LGTM! Test fixture correctly demonstrates the "parent" naming strategy for Pydantic v1.

The generated output appropriately shows wrapper names (MiddleWrapper, OuterWrapper) being preserved when using the "parent" collapse strategy with Pydantic v1's __root__ pattern.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py (1)

1-19: LGTM! Test fixture correctly demonstrates the "parent" naming strategy for Pydantic v2.

The generated output appropriately shows wrapper names being preserved when using the "parent" collapse strategy with Pydantic v2's RootModel pattern.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent.py (1)

1-27: LGTM! Test fixture correctly demonstrates the "parent" naming strategy with inheritance.

The generated output appropriately shows wrapper names (UserWrapper, AdminWrapper, SettingsWrapper) being preserved, including proper inheritance (AdminWrapper(UserWrapper)), when using the "parent" collapse strategy in complex scenarios.

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

30-30: LGTM! Import addition is correct.

The CollapseRootModelsNameStrategy enum import is properly placed alongside other strategy/enum imports.


754-754: LGTM! Parameter properly threaded through Parser initialization.

The collapse_root_models_name_strategy parameter is correctly typed with CollapseRootModelsNameStrategy | None, has an appropriate default of None, and is properly stored as an instance variable.

Also applies to: 944-944


1714-1774: LGTM! Parent strategy implementation is well-designed and addresses previous feedback.

The collapse logic for the new CollapseRootModelsNameStrategy.Parent strategy is robust:

  1. Validation checks prevent incorrect collapsing:

    • Lines 1722-1737: Warns and skips when multiple wrappers reference the same inner model
    • Lines 1739-1754: Warns and skips when the inner model is directly referenced by non-wrapper models
  2. Proper metadata synchronization (lines 1756-1758):

    • Updates inner_model.class_name
    • Updates inner_model.reference.name
    • Calls inner_model.set_reference_path()

    This addresses the past review comment about keeping reference metadata consistent after renaming.

  3. Clean reference management (lines 1762-1774):

    • Correctly updates reference children
    • Removes unused imports
    • Marks wrapper for removal when no longer referenced
tests/main/jsonschema/test_main_jsonschema.py (1)

4951-5076: Comprehensive test coverage for the new feature.

The test suite thoroughly covers the new --collapse-root-models-name-strategy option with tests for:

  • Both child and parent strategies
  • Error validation when --collapse-root-models is missing
  • Warning scenarios (multiple wrappers, direct references)
  • Edge cases (inheritance, nested wrappers, complex schemas)

The tests follow existing patterns and properly use pytest.warns for warning validation.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_complex_parent_v2.py (1)

1-27: Expected output correctly demonstrates parent naming strategy.

The file shows wrapper names (UserWrapper, AdminWrapper, SettingsWrapper) are preserved in the parent strategy, as opposed to the child strategy which uses inner names (Person, Admin, Config). This correctly validates the intended behavior of the --collapse-root-models-name-strategy parent option.

@koxudaxi koxudaxi force-pushed the feature/collapse-root-models-name-strategy branch 2 times, most recently from e3fb365 to ffd73c9 Compare December 25, 2025 00:54
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 a23b4e0 and ffd73c9.

📒 Files selected for processing (1)
  • tests/main/jsonschema/test_main_jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/main/jsonschema/test_main_jsonschema.py (3)
tests/test_main_kr.py (1)
  • output_file (44-46)
tests/main/conftest.py (1)
  • output_file (98-100)
src/datamodel_code_generator/__main__.py (1)
  • Exit (95-101)

Comment thread tests/main/jsonschema/test_main_jsonschema.py
koxudaxi and others added 11 commits December 25, 2025 01:20
* Add --http-timeout CLI option for configurable HTTP request timeout

* docs: update CLI reference documentation and prompt data

🤖 Generated by GitHub Actions

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Pass schema extensions to templates

* Move model_base import to top of file

* Add schema extensions documentation

Document how x-* schema extensions are passed to custom templates
via the extensions variable, with examples for database model
configuration and other use cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
* Add propertyNames and x-propertyNames support

* Fix Pydantic v1 compatibility for x-propertyNames

Use the model_validate utility function from util module instead of
calling JsonSchemaObject.model_validate() directly, which only
exists in Pydantic v2.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* Add test for x-propertyNames non-dict branch coverage

Test that x-propertyNames with non-dict value (e.g., boolean) is
correctly ignored, achieving 100% diff coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
* Add --use-field-description-example option

* docs: update CLI reference documentation and prompt data

🤖 Generated by GitHub Actions

* Add tests for complete branch coverage of docstring property

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@koxudaxi koxudaxi force-pushed the feature/collapse-root-models-name-strategy branch from 2489c00 to f4283e2 Compare December 25, 2025 01:20
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 (1)
tests/main/jsonschema/test_main_jsonschema.py (1)

5075-5244: Collapse-root-models name-strategy tests cover the key behaviors; just verify the fixtures and v1/v2 outputs.

The new --collapse-root-models-name-strategy tests look well thought out:

  • Happy paths for both child and parent strategies, including inheritance and nested/complex wrapper scenarios.
  • Negative path enforcing --collapse-root-models as a prerequisite.
  • Guardrails for ambiguous cases (multiple_wrappers, direct_refs) via pytest.warns(UserWarning, match=...).
  • Parametrization over pydantic.BaseModel/pydantic_v2.BaseModel where relevant.

Two small things to verify:

  1. JSON/expected fixtures exist and are wired correctly for all of these inputs/outputs, since they live outside this diff (collapse_root_models_name_strategy_*.json and the corresponding jsonschema_collapse_root_models_name_strategy_*.py files).

  2. For test_main_jsonschema_collapse_root_models_name_strategy_complex_parent, a single expected_file is used for both pydantic v1 and v2. If the generator truly produces identical code there, that’s fine; otherwise, you may want to mirror the split used in nested_wrappers_parent and add a separate v2 golden file.

#!/bin/bash
# Verify that all collapse_root_models_name_strategy fixtures exist and inspect any v1/v2 diffs.

echo "== Checking JSON schema fixtures =="
names=(
  child
  parent
  multiple_wrappers
  direct_refs
  with_inheritance
  nested_wrappers
  complex
)
for n in "${names[@]}"; do
  echo "- collapse_root_models_name_strategy_${n}.json"
  find tests -type f -name "collapse_root_models_name_strategy_${n}.json" || echo "  MISSING"
done

echo ""
echo "== Checking expected Python outputs for complex/nested wrappers =="
find tests -type f -name "jsonschema_collapse_root_models_name_strategy_nested_wrappers_*.py" -o \
           -name "jsonschema_collapse_root_models_name_strategy_complex_*.py"

echo ""
echo "If both pydantic v1/v2 complex-parent runs are intended to share one golden file, you should see only:"
echo "  jsonschema_collapse_root_models_name_strategy_complex_parent.py"
echo "Otherwise, consider adding a separate *_v2.py and updating the test accordingly."
🧹 Nitpick comments (13)
tests/main/graphql/test_main_graphql.py (1)

368-424: Consider parametrizing these similar tests.

All three tests share nearly identical structure (same input, output, skip condition) and differ only in the extra arguments and JSON files used. Consider parametrizing them to reduce duplication:

🔎 Example parametrization approach
+@pytest.mark.parametrize(
+    ("scenario", "extra_args", "expected_file"),
+    [
+        (
+            "extra_template_data_string",
+            ["--extra-template-data", str(GRAPHQL_DATA_PATH / "additional-imports-in-extra-template-data.json")],
+            "additional_imports.py",
+        ),
+        (
+            "extra_template_data_list",
+            ["--extra-template-data", str(GRAPHQL_DATA_PATH / "additional-imports-list-format.json")],
+            "additional_imports.py",
+        ),
+        (
+            "merged",
+            [
+                "--extra-template-data", str(GRAPHQL_DATA_PATH / "additional-imports-partial.json"),
+                "--additional-imports", "datetime.date,mymodule.myclass.MyCustomPythonClass",
+            ],
+            "additional_imports.py",
+        ),
+    ],
+)
 @pytest.mark.skipif(
     black.__version__.split(".")[0] == "19",
     reason="Installed black doesn't support the old style",
 )
-def test_main_graphql_additional_imports_in_extra_template_data(output_file: Path) -> None:
-    """Test additional_imports specified in extra-template-data JSON file (string format)."""
+def test_main_graphql_additional_imports_scenarios(scenario: str, extra_args: list, expected_file: str, output_file: Path) -> None:
+    """Test additional_imports with various input scenarios."""
     run_main_and_assert(
         input_path=GRAPHQL_DATA_PATH / "additional-imports.graphql",
         output_path=output_file,
         input_file_type="graphql",
         assert_func=assert_file_content,
-        expected_file="additional_imports.py",
-        extra_args=[
-            "--extra-template-data",
-            str(GRAPHQL_DATA_PATH / "additional-imports-in-extra-template-data.json"),
-        ],
+        expected_file=expected_file,
+        extra_args=extra_args,
     )
-
-
-@pytest.mark.skipif(
-    black.__version__.split(".")[0] == "19",
-    reason="Installed black doesn't support the old style",
-)
-def test_main_graphql_additional_imports_in_extra_template_data_list_format(output_file: Path) -> None:
-    """Test additional_imports specified in extra-template-data JSON file (list format)."""
-    run_main_and_assert(
-        input_path=GRAPHQL_DATA_PATH / "additional-imports.graphql",
-        output_path=output_file,
-        input_file_type="graphql",
-        assert_func=assert_file_content,
-        expected_file="additional_imports.py",
-        extra_args=[
-            "--extra-template-data",
-            str(GRAPHQL_DATA_PATH / "additional-imports-list-format.json"),
-        ],
-    )
-
-
-@pytest.mark.skipif(
-    black.__version__.split(".")[0] == "19",
-    reason="Installed black doesn't support the old style",
-)
-def test_main_graphql_additional_imports_merged(output_file: Path) -> None:
-    """Test merging additional_imports from CLI and extra-template-data JSON."""
-    run_main_and_assert(
-        input_path=GRAPHQL_DATA_PATH / "additional-imports.graphql",
-        output_path=output_file,
-        input_file_type="graphql",
-        assert_func=assert_file_content,
-        expected_file="additional_imports.py",
-        extra_args=[
-            "--extra-template-data",
-            str(GRAPHQL_DATA_PATH / "additional-imports-partial.json"),
-            "--additional-imports",
-            "datetime.date,mymodule.myclass.MyCustomPythonClass",
-        ],
-    )
src/datamodel_code_generator/http.py (1)

32-32: Remove unused noqa directive.

Static analysis indicates that FBT001 and FBT002 rules are not enabled, making this noqa directive unnecessary.

🔎 Proposed fix
-    ignore_tls: bool = False,  # noqa: FBT001, FBT002
+    ignore_tls: bool = False,
tests/main/openapi/test_main_openapi.py (1)

32-32: Cache clearing for schema extension tests is appropriate but couples to private helpers

Importing datamodel_code_generator.model.base and calling _get_environment.cache_clear() / _get_template_with_custom_dir.cache_clear() before test_main_openapi_schema_extensions is a reasonable way to avoid cross-test interference from the new Jinja2 environment/template caching. The test itself cleanly exercises schema extensions via a custom template dir. The only minor downside is coupling to underscored helpers; if their implementation stops using lru_cache, this test will need updating, but that’s acceptable for a targeted regression test.

Also applies to: 613-634

src/datamodel_code_generator/__init__.py (1)

1001-1043: Consider extracting shared logic to reduce duplication.

The future import extraction logic in lines 1008-1035 duplicates the logic in _build_module_content (lines 493-519). While the file-writing case handles future_imports slightly differently (using pre-extracted result.future_imports), the core extraction and header manipulation logic is identical.

This is a minor maintainability concern - if the header/future-import handling logic needs to change, it must be updated in both places.

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

682-793: Parser.init wiring for new options looks correct; watch default behavior for name strategy

  • use_field_description_example, http_timeout, and collapse_root_models_name_strategy are added to the constructor and stored on self, and they’re consistently threaded to where they’re used.
  • One thing to double‑check: with collapse_root_models=True and collapse_root_models_name_strategy=None (the default in this signature), the behavior in __collapse_root_models changes for root models that wrap another model via reference (they’re now skipped entirely in that branch). If historical behavior was to always keep the child name, you may want to ensure that the CLI (and any programmatic entry points) explicitly pass CollapseRootModelsNameStrategy.Child when collapse_root_models is enabled, or default None to Child inside the parser to avoid silent behavior changes for library users.

Also applies to: 835-836, 938-939, 947-949


1059-1068: HTTP timeout integration into _get_text_from_url is sound; trim unused noqa

  • Using self.http_timeout with fallback to DEFAULT_HTTP_TIMEOUT and passing it through to get_body cleanly introduces per‑parser HTTP timeouts without changing existing call sites.
  • Ruff reports # noqa: PLC0415 as unused under current settings; you can safely drop this directive to avoid RUF100 noise.
Suggested cleanup
-    def _get_text_from_url(self, url: str) -> str:
-        from datamodel_code_generator.http import DEFAULT_HTTP_TIMEOUT, get_body  # noqa: PLC0415
+    def _get_text_from_url(self, url: str) -> str:
+        from datamodel_code_generator.http import DEFAULT_HTTP_TIMEOUT, get_body

1688-1780: Collapse-root-models naming strategy logic is mostly solid; confirm defaults and consider minor robustness tweaks

  • The new CollapseRootModelsNameStrategy handling looks careful:
    • For Parent, you guard against:
      • Multiple distinct root wrappers for the same inner model (root_model_wrappers length check).
      • Direct references to the inner model from non‑wrapper models (direct_refs).
    • On success, you correctly:
      • Rename inner_model.class_name.
      • Update inner_model.reference.name.
      • Call inner_model.set_reference_path(root_type_model.reference.path) so path and related metadata stay consistent (matches the pattern used in __rename_and_relocate_scc_models).
      • Rebind data_type.reference and clean up root_type_model.reference.children, imports, and unused wrappers.
  • Two points to consider:
    1. Default strategy behavior: when root_type_field.data_type.reference is set and collapse_root_models_name_strategy is None, the code now skips collapsing entirely (continue). If “child” naming was the previous default, this changes behavior for callers who enable collapse_root_models but don’t pass a strategy (especially programmatic uses of Parser/JsonSchemaParser). It might be safer to:
      • Treat None as CollapseRootModelsNameStrategy.Child inside this method, or
      • Normalize None to Child in __init__ when collapse_root_models is True.
        Please verify this against existing behavior and tests.
    2. Duplicate wrapper detection: root_model_wrappers is a list and can contain the same wrapper multiple times if the inner model is referenced multiple times within a single root wrapper (unlikely but possible with complex schemas). Converting this to a set before the len(...) > 1 check would make the “multiple root models” warning more precise.

Also, Ruff flags the # noqa: PLR0912, PLR0914, PLR0915 on the function definition as unused under current rules; you can drop it if you’re not enabling those checks.

Optional robustness tweak for wrapper collection
-                        root_model_wrappers = [
-                            parent_model
-                            for child in inner_reference.children
-                            if isinstance(child, DataType)
-                            and (parent_model := get_most_of_parent(child, DataModel))
-                            and isinstance(parent_model, self.data_model_root_type)
-                        ]
+                        root_model_wrappers = {
+                            parent_model
+                            for child in inner_reference.children
+                            if isinstance(child, DataType)
+                            and (parent_model := get_most_of_parent(child, DataModel))
+                            and isinstance(parent_model, self.data_model_root_type)
+                        }
...
-                        if len(root_model_wrappers) > 1:
+                        if len(root_model_wrappers) > 1:
                             warn(
-                                    f"Cannot apply 'parent' strategy for '{inner_model.class_name}' - "
-                                    f"it is referenced by multiple root models: "
-                                    f"{[m.class_name for m in root_model_wrappers]}. Skipping collapse.",
+                                    f"Cannot apply 'parent' strategy for '{inner_model.class_name}' - "
+                                    f"it is referenced by multiple root models: "
+                                    f"{[m.class_name for m in root_model_wrappers]}. Skipping collapse.",
src/datamodel_code_generator/parser/jsonschema.py (5)

329-347: JsonSchemaObject: propertyNames support and x-propertyNames shim look correct; clean up unused noqa

  • Adding propertyNames: Optional[JsonSchemaObject] aligns with JSON Schema and your later parse_property_names implementation.
  • The __init__ logic to support the OpenAPI 3.0 x-propertyNames extension (parsing it into a JsonSchemaObject via model_validate and storing in propertyNames) is sensible, and popping it from extras avoids duplication.
  • Ruff notes the # noqa: N815, UP045 on the propertyNames field as unused under current configuration; you can safely remove that directive.
Suggested minor cleanup
-    propertyNames: Optional[JsonSchemaObject] = None  # noqa: N815, UP045
+    propertyNames: Optional[JsonSchemaObject] = None

Also applies to: 383-399


1605-1623: _is_root_model_schema correctly treats propertyNames as non-root; unused noqa hint

  • Adding obj.propertyNames to the list of disqualifiers for “root model schema” is correct: if propertyNames is present, the schema is better treated via the object/property‑names path instead of primitive root‑type handling.
  • Ruff reports the # noqa: PLR0911 here as unused under current rules; removing it would avoid RUF100 noise.
Suggested cleanup
-    def _is_root_model_schema(self, obj: JsonSchemaObject) -> bool:  # noqa: PLR0911
+    def _is_root_model_schema(self, obj: JsonSchemaObject) -> bool:

1730-1784: propertyNames support is a solid first pass; note interaction with patternProperties

  • parse_property_names builds a dict type where:
    • The value type is taken from additionalProperties (falling back to Any), which matches JSON Schema’s semantics for map values.
    • The key type is:
      • A Literal over string enums if propertyNames.enum contains strings.
      • A constrained string type when pattern, minLength, or maxLength are present.
      • A plain string otherwise.
  • Routing is added in:
    • parse_item: if item.is_object or item.patternProperties or item.propertyNames: ... then falls through to patternProperties or propertyNames handling when there are no explicit properties.
    • parse_root_type: chooses patternProperties first, then propertyNames, then other branches.
    • parse_obj: elif obj.patternProperties or obj.propertyNames: now sends such schemas through parse_root_type.
  • This is a clear improvement over the previous lack of propertyNames support. One nuance: when both patternProperties and propertyNames are present, patternProperties “wins” and propertyNames is ignored. That diverges slightly from the JSON Schema spec (where both constraints can apply), but it still represents strictly better behavior than before. If you want full fidelity later, you might consider combining both constraints into a single dict type instead of using elif.

Also applies to: 2443-2531, 2688-2744, 3283-3284


2222-2237: _validate_schema_object improves error reporting; consider extending to ref loading

  • _validate_schema_object wraps model_validate in a SchemaParseError that includes the schema path, which is very helpful for debugging malformed schemas.
  • It’s correctly used at key entry points:
    • parse_raw_obj (generic raw handling),
    • File‑level parsing in _parse_file (root object, definitions, targeted object paths, and reserved refs).
  • As a result, most top‑level and definition‑level validation errors will report a clear path.
  • Note that _load_ref_schema_object still calls model_validate directly, so validation errors resolved via $ref can still surface as raw Pydantic exceptions. If you want completely uniform error handling, you could optionally move _validate_schema_object into _load_ref_schema_object too by threading through an appropriate path, but that’s an enhancement rather than a blocker.

Also applies to: 3246-3247, 3392-3412, 3431-3432


2816-2837: _parse_multiple_types_with_properties: extensions are now propagated to the root wrapper

  • For schemas with multiple primitive types plus object properties, the root wrapper created here now also receives schema extensions via set_schema_extensions, and the new use_field_description_example flag is passed to its root field.
  • This keeps behavior consistent with parse_root_type and parse_object.

Also note: Ruff reports # noqa: FBT001 on the additional_properties parameter in parse_property_names (line ~2382) and # noqa: PLR0912, PLR0915 on parse_root_type (line ~2688) as unused under current rules, so you can remove them if you want a clean static‑analysis run.

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

684-693: HTTP timeout expectations are consistent; consider adding a non-default timeout case.

The added timeout=30.0 assertions on all httpx.get calls keep the tests aligned with the new default HTTP timeout and look correct across the various URL/headers/TLS scenarios. If you want slightly stronger coverage of --http-timeout, a follow-up test that passes a non-default value (e.g., --http-timeout 5) and asserts that value is forwarded to httpx.get would close the loop, but this is not strictly required for this PR.

Also applies to: 730-739, 759-768, 1629-1697, 1781-1850, 5518-5525

📜 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 ffd73c9 and f4283e2.

⛔ Files ignored due to path filters (19)
  • tests/data/graphql/additional-imports-in-extra-template-data.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/graphql/additional-imports-list-format.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/graphql/additional-imports-partial.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/extras.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/multiline_description_with_example.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/multiple_examples.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_allof_ref.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_enum.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_enum_integers.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_min_max_length.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_nested.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_no_additional.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/property_names_pattern.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/single_line_description_with_example.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/openapi/schema_extensions.yaml is excluded by !tests/data/**/*.yaml and included by none
  • tests/data/openapi/x_property_names.yaml is excluded by !tests/data/**/*.yaml and included by none
  • tests/data/openapi/x_property_names_non_dict.yaml is excluded by !tests/data/**/*.yaml and included by none
  • tests/data/templates_extensions/pydantic_v2/BaseModel.jinja2 is excluded by none and included by none
  • uv.lock is excluded by !**/*.lock, !**/*.lock and included by none
📒 Files selected for processing (48)
  • docs/cli-reference/field-customization.md
  • docs/cli-reference/general-options.md
  • docs/cli-reference/index.md
  • docs/cli-reference/quick-reference.md
  • docs/custom_template.md
  • docs/using_as_module.md
  • pyproject.toml
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/http.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/data/expected/main/jsonschema/field_extras.py
  • tests/data/expected/main/jsonschema/field_extras_field_extra_keys.py
  • tests/data/expected/main/jsonschema/field_extras_field_extra_keys_v2.py
  • tests/data/expected/main/jsonschema/field_extras_field_include_all_keys.py
  • tests/data/expected/main/jsonschema/field_extras_field_include_all_keys_v2.py
  • tests/data/expected/main/jsonschema/field_extras_v2.py
  • tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py
  • tests/data/expected/main/jsonschema/property_names_allof_ref.py
  • tests/data/expected/main/jsonschema/property_names_enum.py
  • tests/data/expected/main/jsonschema/property_names_enum_integers.py
  • tests/data/expected/main/jsonschema/property_names_min_max_length.py
  • tests/data/expected/main/jsonschema/property_names_nested.py
  • tests/data/expected/main/jsonschema/property_names_no_additional.py
  • tests/data/expected/main/jsonschema/property_names_pattern.py
  • tests/data/expected/main/jsonschema/string_dict.py
  • tests/data/expected/main/openapi/schema_extensions.py
  • tests/data/expected/main/openapi/x_property_names.py
  • tests/data/expected/main/openapi/x_property_names_non_dict.py
  • tests/data/expected/main_kr/main_use_field_description_example/output.py
  • tests/data/expected/main_kr/main_use_field_description_example_multiple/output.py
  • tests/data/expected/main_kr/main_use_field_description_with_example/output.py
  • tests/data/expected/main_kr/main_use_inline_field_description_example_only/output.py
  • tests/data/expected/main_kr/main_use_inline_field_description_with_example/output.py
  • tests/main/graphql/test_main_graphql.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/main/openapi/test_main_openapi.py
  • tests/main/test_main_general.py
  • tests/main/test_main_json.py
  • tests/parser/test_jsonschema.py
  • tests/test_main_kr.py
✅ Files skipped from review due to trivial changes (1)
  • tests/data/expected/main_kr/main_use_field_description_example/output.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/prompt_data.py
  • docs/cli-reference/index.md
  • src/datamodel_code_generator/parser/graphql.py
🧰 Additional context used
🧬 Code graph analysis (18)
tests/data/expected/main/jsonschema/field_extras_field_include_all_keys.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/jsonschema/property_names_pattern.py (1)
tests/data/expected/main/openapi/x_property_names.py (1)
  • PatternKeys (10-11)
tests/data/expected/main/openapi/x_property_names.py (1)
tests/data/expected/main/jsonschema/property_names_pattern.py (1)
  • PatternKeys (10-11)
tests/data/expected/main/jsonschema/property_names_enum_integers.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main/jsonschema/field_extras_field_extra_keys.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/main/test_main_general.py (1)
src/datamodel_code_generator/__init__.py (4)
  • Error (370-379)
  • InputFileType (204-214)
  • SchemaParseError (411-431)
  • generate (523-1059)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (309-317)
tests/test_main_kr.py (3)
tests/main/conftest.py (3)
  • run_main_with_args (215-241)
  • output_file (98-100)
  • run_main_and_assert (244-408)
src/datamodel_code_generator/__main__.py (3)
  • Exit (95-101)
  • main (878-1141)
  • get (118-120)
tests/conftest.py (1)
  • freeze_time (285-288)
tests/data/expected/main/jsonschema/property_names_enum.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
src/datamodel_code_generator/parser/base.py (2)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (309-317)
src/datamodel_code_generator/http.py (1)
  • get_body (29-45)
src/datamodel_code_generator/parser/jsonschema.py (2)
src/datamodel_code_generator/__init__.py (2)
  • CollapseRootModelsNameStrategy (309-317)
  • SchemaParseError (411-431)
src/datamodel_code_generator/util.py (1)
  • model_validate (201-205)
src/datamodel_code_generator/parser/openapi.py (1)
src/datamodel_code_generator/__init__.py (1)
  • CollapseRootModelsNameStrategy (309-317)
tests/data/expected/main/jsonschema/string_dict.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/data/expected/main_kr/main_use_field_description_example_multiple/output.py (1)
tests/data/expected/parser/openapi/openapi_parser_parse_modular/bar.py (1)
  • Field (6-7)
tests/main/jsonschema/test_main_jsonschema.py (1)
tests/main/conftest.py (2)
  • output_file (98-100)
  • run_main_and_assert (244-408)
tests/data/expected/main/openapi/schema_extensions.py (1)
src/datamodel_code_generator/model/base.py (1)
  • name (764-766)
tests/main/openapi/test_main_openapi.py (1)
src/datamodel_code_generator/model/base.py (2)
  • _get_environment (428-443)
  • _get_template_with_custom_dir (447-459)
src/datamodel_code_generator/__init__.py (1)
src/datamodel_code_generator/http.py (1)
  • get_body (29-45)
🪛 markdownlint-cli2 (0.18.1)
docs/custom_template.md

86-86: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


111-111: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


122-122: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


129-129: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

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

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)


985-985: Unused noqa directive (non-enabled: T201)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/http.py

32-32: Unused noqa directive (non-enabled: FBT001, FBT002)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/jsonschema.py

346-346: Unused noqa directive (non-enabled: N815, UP045)

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)


2382-2382: Unused noqa directive (non-enabled: FBT001)

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/__init__.py

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

Remove unused noqa directive

(RUF100)

🔇 Additional comments (85)
tests/data/expected/main/jsonschema/field_extras_field_extra_keys_v2.py (1)

19-19: Code is technically correct; verify relevance to PR scope.

The addition of the status field is syntactically correct and follows standard Pydantic patterns. However, this test file appears to validate field extras functionality, while the PR objectives focus on adding --collapse-root-models-name-strategy.

Please confirm this change is intentional and related to the PR scope, or if it's a side effect of test fixture regeneration.

tests/data/expected/main/jsonschema/string_dict.py (2)

7-7: LGTM!

The Field import is correctly added to support the title parameter on the __root__ field below.


11-11: LGTM! Consider verifying alignment with the new naming strategy.

The addition of Field(..., title='MyStringDict') correctly preserves the model name as a title on the root field. This aligns with the PR's objective to add a naming strategy for collapsed root models.

Since this is a test expectation file, please verify that this output correctly demonstrates the intended behavior of the new --collapse-root-models-name-strategy option when preserving parent/root model names.

tests/data/expected/main_kr/main_use_inline_field_description_with_example/output.py (1)

1-21: LGTM - Valid test fixture for inline field description with example.

This test fixture correctly demonstrates the expected output when using the --use-field-description-example option with multiline descriptions. The attribute docstring pattern following the field definition is a recognized convention for documentation tools.

tests/data/expected/main_kr/main_use_inline_field_description_example_only/output.py (1)

1-14: LGTM - Valid test fixture for example-only inline documentation.

The fixture correctly shows the expected output when a field has a single-line description with an example, where the attribute docstring displays only the example value.

tests/data/expected/main_kr/main_use_field_description_example_multiple/output.py (1)

1-18: LGTM - Valid test fixture for multiple examples.

The fixture correctly demonstrates the expected output when a field has multiple examples, using the examples (plural) parameter in Field() and formatting them as a bullet list in the attribute docstring.

tests/data/expected/main_kr/main_use_field_description_with_example/output.py (1)

1-26: LGTM - Valid test fixture demonstrating various example patterns.

The fixture correctly demonstrates multiple field example scenarios:

  • Single example with description
  • Both example and examples parameters (line 17 shows the expected behavior when both are present)
  • examples only with array value

The attribute docstrings are consistently formatted for each pattern.

pyproject.toml (1)

107-107: Version 0.0.15 is the latest available version and is safe to use.

The zensical version is current and valid. There are no newer releases available, and the bump from 0.0.13 to 0.0.15 aligns with recent development activity in the library.

tests/main/graphql/test_main_graphql.py (4)

368-384: LGTM! Test follows established patterns.

The test correctly verifies that additional_imports can be specified via the --extra-template-data JSON file in string format. The structure, skip condition, and assertion approach are consistent with existing tests in this file.


387-403: LGTM! List format test is well-structured.

This test properly verifies that additional_imports can be specified in list format within the extra-template-data JSON, complementing the string format test above.


406-424: LGTM! Merge test covers an important scenario.

This test correctly verifies that additional_imports from both CLI (--additional-imports) and extra-template-data are properly merged, which is essential functionality.


368-424: Clarify relationship to PR objectives.

The PR title is "Add --collapse-root-models-name-strategy" and references issue #1418 about preserving root model names during collapse operations. However, these tests are for additional_imports functionality in GraphQL code generation, which appears unrelated to the stated PR objectives.

Please clarify:

  • Are these tests part of a separate feature that was bundled into this PR?
  • Is the AI summary incomplete and missing context about additional_imports changes?
  • Should these tests be in a different PR?
tests/data/expected/main/jsonschema/property_names_min_max_length.py (1)

7-13: No changes needed. This is a test fixture showing the expected generator output when --field-constraints is not specified. The generator deliberately produces constr by default and supports modern Annotated[str, StringConstraints(...)] patterns via the --field-constraints flag. The test explicitly validates that constr is generated for this scenario.

Likely an incorrect or invalid review comment.

tests/main/test_main_json.py (1)

182-182: LGTM! HTTP timeout assertion updated correctly.

The test now correctly asserts that timeout=30.0 is passed to httpx.get, aligning with the new HTTP timeout functionality introduced in this PR.

tests/parser/test_jsonschema.py (1)

90-90: LGTM! HTTP timeout assertions updated consistently.

All test assertions now correctly verify that timeout=30.0 is passed to httpx.get calls, ensuring the new HTTP timeout functionality is properly tested across JSON Schema parser scenarios.

Also applies to: 120-120, 158-158, 193-193

tests/data/expected/main/jsonschema/field_extras_field_include_all_keys_v2.py (1)

32-32: LGTM! Expected test output updated correctly.

The addition of the status field with an example demonstrates correct handling of field examples in the generated code.

tests/data/expected/main/openapi/x_property_names_non_dict.py (1)

1-11: LGTM! Expected output for x-propertyNames test case.

The generated code correctly uses RootModel[dict[str, str]] to represent a non-dict property names scenario for OpenAPI schema extensions.

tests/data/expected/main/jsonschema/property_names_no_additional.py (1)

1-13: LGTM! Expected output for propertyNames constraint.

The generated code correctly applies the pattern constraint ^[a-z]+$ to dictionary keys using constr, demonstrating proper handling of JSON Schema propertyNames validation.

tests/data/expected/main/jsonschema/property_names_enum.py (1)

1-13: LGTM! Expected output for propertyNames enum constraint.

The generated code correctly uses Literal['name', 'age', 'email'] to constrain dictionary keys to specific values, demonstrating proper handling of JSON Schema propertyNames with enum validation.

tests/data/expected/main/jsonschema/property_names_enum_integers.py (1)

1-11: LGTM! Expected output for integer enum propertyNames.

The generated code correctly falls back to dict[str, str] for integer enum constraints on property names, which is appropriate since JSON object keys must be strings.

tests/data/expected/main/openapi/x_property_names.py (1)

1-11: LGTM! Expected output for OpenAPI x-propertyNames extension.

The generated code correctly handles the x-propertyNames extension by applying pattern constraints to dictionary keys using constr(pattern=r'^[a-z]+$'), consistent with JSON Schema propertyNames handling.

tests/data/expected/main/jsonschema/property_names_pattern.py (1)

1-11: LGTM!

The generated test file correctly demonstrates the new propertyNames pattern constraint support. The RootModel with dict[constr(pattern=...), str] is the appropriate pattern for constraining dictionary keys, and the structure aligns with the similar file in tests/data/expected/main/openapi/x_property_names.py.

src/datamodel_code_generator/model/base.py (3)

322-332: Edge case: empty examples list may cause unexpected behavior.

The condition on line 326 if examples and isinstance(examples, list) and len(examples) > 1 correctly guards against empty lists for the multi-example case. However, line 331 elif examples and isinstance(examples, list) and len(examples) == 1 handles single-item lists.

If examples is an empty list [], both conditions are false, so no example is added—which is correct. The logic handles this edge case properly.


317-320: Verify the interaction between use_inline_field_description and use_field_description_example.

This block only adds the description to parts when using inline field description with examples AND the description contains a newline. This seems intentional to prevent duplicating single-line descriptions (which would appear both inline and in docstring).

However, the condition elif self.use_inline_field_description and self.use_field_description_example means this block is only reached when use_field_description is False (due to the preceding if). Ensure this interaction is the intended behavior for the combined flags.


152-152: LGTM!

The new use_field_description_example field follows the established pattern for boolean configuration flags in DataModelFieldBase.

tests/data/expected/main/openapi/schema_extensions.py (1)

1-19: LGTM!

The generated test file correctly demonstrates the new schema extensions (x-*) support, with the __collection__ class attribute being populated from OpenAPI schema extensions. This validates that extensions are properly passed through to custom templates.

tests/data/expected/main/jsonschema/property_names_nested.py (1)

1-11: LGTM!

The generated test file correctly demonstrates propertyNames constraints on nested dictionary properties. The Parent model with an optional child field of type dict[constr(pattern=...), str] properly shows how key constraints propagate to nested object properties.

tests/data/expected/main/jsonschema/jsonschema_collapse_root_models_name_strategy_nested_wrappers_parent_v2.py (1)

1-19: LGTM!

The generated test file correctly demonstrates the "Parent" naming strategy for --collapse-root-models-name-strategy. With the parent strategy, OuterWrapper retains its name (the outer/root model's name) rather than being replaced by the inner model's name. This aligns with the PR objective from issue #1418.

src/datamodel_code_generator/http.py (2)

26-27: LGTM!

The DEFAULT_HTTP_TIMEOUT = 30.0 constant is a sensible default and is properly used as the parameter default value.


34-45: LGTM!

The timeout parameter is correctly added and properly passed through to httpx.get(). The implementation follows the existing pattern for other HTTP configuration parameters.

tests/test_main_kr.py (5)

334-356: LGTM!

Good test coverage for the new --use-field-description-example flag. The test is properly decorated with @pytest.mark.cli_doc for documentation generation and uses the established run_main_and_assert pattern.


359-404: Good coverage of combined flag scenarios.

These tests cover important combinations:

  • --use-field-description + --use-field-description-example
  • --use-inline-field-description + --use-field-description-example

This ensures the interaction between description-related flags works correctly.


407-437: Good edge case coverage.

Tests for:

  • Single-line description with inline + example flags (only example in docstring)
  • Multiple examples formatting as bulleted list

These align with the logic implemented in DataModelFieldBase.docstring.


1538-1570: LGTM!

The HTTP timeout test properly:

  1. Mocks httpx.get to avoid actual network calls
  2. Passes --http-timeout 60 via CLI
  3. Asserts the timeout value is correctly passed to httpx.get as 60.0

104-109: Behavior is correct and intentional.

When no --output file is specified, the tool correctly outputs generated code to stdout. The test properly verifies that running with modular input and no output file produces the expected classes in stdout.

tests/data/expected/main/jsonschema/field_extras_field_include_all_keys.py (1)

27-28: LGTM!

The new status field follows the established pattern for field extras, using Field(None, examples=['active']) which aligns with the existing age field's use of the examples parameter.

tests/data/expected/main/jsonschema/field_extras_field_extra_keys.py (1)

20-20: LGTM!

The new status field follows the same pattern as existing fields and correctly uses the examples parameter.

tests/data/expected/main/jsonschema/field_extras.py (1)

13-13: LGTM!

The status field addition is consistent with the test expectations for field examples support.

tests/data/expected/main/jsonschema/field_extras_v2.py (1)

13-13: LGTM!

Consistent addition of the status field with examples support.

docs/custom_template.md (1)

78-159: LGTM! Well-documented feature addition.

The Schema Extensions documentation is clear and includes helpful examples showing how to use x- prefixed fields in custom templates. The static analysis warnings about "emphasis as heading" (MD036) are false positives—the bold text labels are appropriate for code example annotations, not misused headings.

docs/cli-reference/quick-reference.md (1)

74-74: LGTM! Clean documentation additions.

The three new CLI options are properly documented in both the categorized tables and alphabetical index with correct links and descriptions.

Also applies to: 90-90, 164-164, 203-203, 239-239, 291-291

src/datamodel_code_generator/cli_options.py (1)

84-86: LGTM! Properly categorized CLI option metadata.

The three new CLI options are correctly registered with appropriate categories (MODEL, FIELD, GENERAL) and follow the established pattern.

Also applies to: 137-139, 239-239

docs/cli-reference/general-options.md (1)

1605-1662: The --http-timeout documentation is well-structured with no duplication issues.

The search confirms only one --http-timeout section exists in the file (line 1605). No duplication was found. The documentation includes clear descriptions, usage examples, and concrete JSON/Python code samples.

tests/data/expected/main/jsonschema/property_names_allof_ref.py (1)

14-15: Input schema file not found; unable to verify expected output.

The expected output file references property_names_allof_ref.json as input, but this file could not be located in the repository despite thorough searching. Without access to the input schema, the correctness of the empty Model class cannot be verified.

docs/cli-reference/field-customization.md (1)

25-26: New field-description example option and examples look coherent

The added --use-field-description-example row, updated extras/status examples, and the new sections for both --use-field-description-example and --use-inline-field-description are internally consistent: inputs and outputs line up, anchors match option names, and behavior is described clearly (single vs multiple examples, interaction with descriptions). I don’t see any issues here.

Also applies to: 1556-1566, 1575-1596, 1600-1620, 1670-1720, 2879-2970, 2974-3306

tests/main/openapi/test_main_openapi.py (2)

1681-1697: HTTP timeout assertions correctly cover new default behavior

The updated httpx_get_mock.assert_has_calls expectations now include timeout=30.0 for both the primary OpenAPI fetch and nested refs, and similarly for the body/parameters remote ref test. This accurately pins the new default timeout wiring without over-specifying other parameters. Looks good.

Also applies to: 1758-1765


4466-4487: x-propertyNames tests nicely cover both happy path and invalid-input behavior

The new tests for x-propertyNames verify both correct translation to propertyNames and the non-dict case being ignored, following the existing OpenAPI test patterns (input path, expected file, pydantic_v2 output). This gives solid coverage for the extension handling with clear, focused assertions.

docs/using_as_module.md (1)

7-8: Updated generate() usage and return-value docs are clear and consistent

The new sections for getting generated code as a string, handling multi-module output via GeneratedModules, and the return-value summary table accurately describe the different behaviors based on output. The examples are coherent and the note about raising datamodel_code_generator.Error when multiple modules target a single file path sets expectations well.

Also applies to: 15-62, 190-199

src/datamodel_code_generator/parser/openapi.py (4)

20-38: LGTM!

The CollapseRootModelsNameStrategy import is correctly added alongside related imports from datamodel_code_generator.


184-298: LGTM!

The three new parameters (use_field_description_example, http_timeout, collapse_root_models_name_strategy) are properly added to the __init__ signature with correct type annotations and default values that match their usage patterns.


301-410: LGTM!

All three new parameters are correctly propagated to the super().__init__() call, maintaining consistency with the parameter order in the parent JsonSchemaParser class.


748-779: LGTM!

The use_field_description_example parameter is correctly propagated to the data_model_field_type constructor call within parse_all_parameters, enabling field description examples to be included when parsing operation parameters.

tests/main/test_main_general.py (6)

10-22: LGTM!

New imports for inline_snapshot, GeneratedModules, and SchemaParseError are correctly added to support the new test cases.


479-554: LGTM!

Comprehensive test coverage for SchemaParseError:

  • test_schema_parse_error_includes_path: Validates error message contains schema path context
  • test_schema_parse_error_includes_nested_path: Validates nested path references (e.g., $defs/MyModel)
  • test_schema_parse_error_original_error: Verifies original_error and path attributes are preserved
  • test_schema_parse_error_without_path: Tests error formatting when no path is provided

1517-1591: LGTM!

Good test coverage for the generate() return value behavior:

  • Returns str when output=None for single file
  • Returns str for Pydantic v2 and dataclass model types
  • Returns None when an output path is provided

1593-1630: LGTM!

test_generate_file_content_matches_return_value properly validates that the string returned by generate() with output=None matches the content written to a file when output is provided. This is important for API consistency.


1632-1723: Consider adding cleanup for temporary files.

The test creates two schema files in tmp_path but relies solely on pytest's tmp_path fixture for cleanup. While this works, the test could be more robust by verifying the returned dict structure more explicitly.

Also, the snapshot at line 1673-1676 shows test_generate_returns_dict_for0 which appears truncated - this is expected behavior from the temp directory name, but the assertion validates the structure correctly.


1725-1792: LGTM!

Good coverage of custom_file_header behavior with generate():

  • Basic custom header
  • Header containing code after docstring (validates future import extraction)
  • Header with disable_future_imports=True
src/datamodel_code_generator/__main__.py (7)

23-43: LGTM!

The CollapseRootModelsNameStrategy import is correctly added to the imports from datamodel_code_generator.


449-449: LGTM!

New use_field_description_example config field correctly added with bool type and False default.


485-493: LGTM!

New config fields http_timeout and collapse_root_models_name_strategy are correctly added with appropriate types and defaults.


551-563: LGTM!

The _extract_additional_imports helper function cleanly extracts additional_imports from extra_template_data entries, handling both string and list formats. The use of pop() ensures imports aren't duplicated in the template data.


741-876: LGTM!

run_generate_from_config properly:

  1. Passes through the three new parameters (use_field_description_example, http_timeout, collapse_root_models_name_strategy)
  2. Captures the return value from generate()
  3. Handles both str and dict return types when output is None, writing to stdout appropriately

984-990: LGTM!

Proper validation that --collapse-root-models-name-strategy requires --collapse-root-models to be set. This prevents user confusion from setting a naming strategy without enabling the collapse feature.


1016-1024: LGTM!

The logic to extract and merge additional_imports from extra_template_data into config.additional_imports is correct. The assert on line 1017 is safe since we're inside the else branch where extra_template_data is assigned from json.load().

src/datamodel_code_generator/__init__.py (9)

85-91: LGTM!

The GeneratedModules type alias is well-defined and documented. Using tuple[str, ...] as the key type allows for flexible module path representation (e.g., ("models", "user.py")).


309-318: LGTM!

The CollapseRootModelsNameStrategy enum is correctly defined with clear documentation explaining the Child and Parent strategies for naming when collapsing root models.


411-432: LGTM!

The SchemaParseError exception class properly:

  • Extends the base Error class
  • Stores path and original_error for debugging
  • Formats the message with path context when available
  • Falls back to plain message when no path is provided

482-521: LGTM!

The _build_module_content helper function correctly handles:

  • Future import extraction and repositioning after custom headers
  • Header insertion point calculation via _find_future_import_insertion_point
  • Edge cases when body is empty or no custom header is provided

523-654: LGTM!

The generate() function signature is properly updated with:

  • New parameters: use_field_description_example, http_timeout, collapse_root_models_name_strategy
  • Updated return type annotation: str | GeneratedModules | None
  • Updated docstring documenting the three return scenarios

656-668: LGTM!

The HTTP timeout handling correctly:

  • Imports DEFAULT_HTTP_TIMEOUT from the http module
  • Uses user-provided http_timeout if not None, otherwise falls back to default
  • Passes the timeout to get_body() call

841-888: LGTM!

All three new parameters are correctly passed to the parser constructor:

  • use_field_description_example (line 841)
  • http_timeout (line 878)
  • collapse_root_models_name_strategy (line 887)

948-979: LGTM!

The new return value handling for output=None case properly:

  • Returns str for single-module output (using _build_module_content)
  • Returns GeneratedModules dict for multi-module output
  • Correctly sanitizes filenames and applies custom headers

1086-1107: LGTM!

The __all__ list is correctly updated to export GeneratedModules and SchemaParseError.

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

25-38: New CollapseRootModelsNameStrategy import is consistent with downstream usage

The enum import is correctly added and matches the new collapse_root_models_name_strategy type annotations below. No issues here.


2911-2923: Updated __collapse_root_models call signature is consistent

Passing imports and scoped_model_resolver into __collapse_root_models matches the updated method signature and enables it to manage imports when collapsing/root‑model rewrites are performed. No functional concerns here.

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

24-41: New imports (CollapseRootModelsNameStrategy, SchemaParseError) are appropriate

Both CollapseRootModelsNameStrategy and SchemaParseError are used meaningfully later (JsonSchemaParser.__init__ and _validate_schema_object respectively). The import block remains tidy and consistent with existing style.


534-757: JsonSchemaParser.init: new options are correctly threaded to the base Parser

  • The new parameters use_field_description_example, http_timeout, and collapse_root_models_name_strategy are added to JsonSchemaParser.__init__ and passed straight through to super().__init__, matching the base Parser signature.
  • This keeps the JSON Schema parser aligned with the core configuration surface. Just ensure the CLI/options layer is updated to map the new flags correctly (especially defaulting the collapse name strategy as intended).

1105-1107: use_field_description_example is consistently passed to DataModelField construction

The new use_field_description_example flag is threaded through all relevant data_model_field_type call sites (object fields, bool properties, arrays, root types, enums, etc.), matching the parser’s self.use_field_description_example attribute. This wiring looks internally consistent; behavior will be driven by the DataModelField implementation.

Also applies to: 2197-2212, 2629-2633, 2667-2669, 2770-2772, 2833-2837, 2923-2926, 2955-2958, 3017-3018, 3040-3041


1165-1171: Schema extensions (x-*) are now exposed to templates in a uniform way

  • set_schema_extensions correctly filters obj.extras for keys starting with "x-" and attaches them under extra_template_data[path]["extensions"].
  • Call sites cover object models, root models (arrays, primitives, combined allOf), and enum root wrappers, so templates can consistently access schema extensions regardless of construct.
  • No functional issues spotted; this is a straightforward, useful addition.

Also applies to: 1172-1177, 1920-1921, 2072-2073, 2126-2127, 2297-2299, 2647-2649, 2751-2753, 2817-2819, 2943-2944, 3017-3018


2330-2377: Traversal and patternProperties integration remain consistent after adding propertyNames

  • parse_pattern_properties is unchanged and still aggregates patterns by value type, which is fine.
  • _traverse_schema_objects and _add_id_callback are extended to descend into propertyNames, ensuring $id and $ref resolution covers constraints defined there as well.
  • No functional regressions apparent; recursion remains well‑bounded (no new cycles introduced).

Also applies to: 3153-3158


2589-2637: Array parsing + extensions/description-example integration look consistent

  • parse_array_fields and parse_array now:
    • Preserve the previous constraints/nullable/required logic,
    • Attach schema extensions via set_schema_extensions,
    • And pass use_field_description_example through to the constructed field(s).
  • The self‑reference handling in parse_array is unchanged aside from threading the new flag.
  • Overall, array behavior should stay backward compatible while enabling the new features.

Also applies to: 2647-2669


2923-3041: Enum parsing: extensions and description-example flag are wired consistently

  • For both nullable and non‑nullable enum schemas, the code now:
    • Attaches schema extensions to the appropriate reference path before building root wrappers.
    • Passes use_field_description_example into the enum field(s), ensuring this feature applies uniformly.
  • Control flow and nullability handling are otherwise unchanged, so existing behavior should be preserved.

3132-3213: Traversal and id-resolution updates for propertyNames are correct

  • _traverse_schema_objects and _add_id_callback now also walk propertyNames, ensuring both $ref resolution and $id registration work for property‑name constraint schemas.
  • This mirrors existing handling for patternProperties and additionalProperties, keeping traversal semantics coherent.
tests/main/jsonschema/test_main_jsonschema.py (1)

3117-3220: PropertyNames test matrix looks solid and idiomatic.

The new property_names_* tests cleanly exercise the key propertyNames behaviors (pattern, enum, length, no additionalProperties, nesting, allOf+$ref) against pydantic_v2.BaseModel and integrate correctly with run_main_and_assert/golden files. No issues from the test harness side.

Comment thread tests/main/graphql/test_main_graphql.py
@koxudaxi koxudaxi merged commit 0ee5ea3 into main Dec 25, 2025
37 of 38 checks passed
@koxudaxi koxudaxi deleted the feature/collapse-root-models-name-strategy branch December 25, 2025 02:00
@github-actions
Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR adds a new optional CLI option (--collapse-root-models-name-strategy) and corresponding Python API parameter (collapse_root_models_name_strategy) with default value None. When not specified (the default), the existing behavior of --collapse-root-models is preserved exactly - it continues to skip collapsing root models that reference other models. The new functionality is purely opt-in and requires explicitly passing either 'child' or 'parent' as a value. All existing workflows and generated code remain unchanged unless users explicitly adopt the new option. This is a backwards-compatible additive feature.


This analysis was performed by Claude Code Action

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.

[Request] ability to reverse --collapse-root-models collapse order

1 participant