Skip to content

Add config parameter to Parser classes#2863

Closed
koxudaxi wants to merge 9 commits intomainfrom
feature/parser-config-pattern
Closed

Add config parameter to Parser classes#2863
koxudaxi wants to merge 9 commits intomainfrom
feature/parser-config-pattern

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Dec 30, 2025

Summary by CodeRabbit

  • New Features

    • New OpenAPI and GraphQL parser configuration models; global parsing options expanded (imports, formatting, module split, export strategies). Aliases now accept single or multiple values.
  • Refactor

    • Parsers updated to use unified config+options initialization, simplifying constructors and centralizing settings.
  • Tests

    • Test suite updated for config-driven parser initialization and extended alias handling.
  • Documentation

    • CLI docs updated to use literal-typed fields.

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Parsers were refactored to use config-driven initialization: new ParserConfig variants (OpenAPIParserConfig, GraphQLParserConfig) and ParseConfig extensions; Parser and parser subclasses accept config + **options, merge options into config (options take precedence), and derive internal attributes from the resolved config.

Changes

Cohort / File(s) Summary
Configuration models
src/datamodel_code_generator/config.py
ParseConfig extended with with_import, format_, settings_path, disable_future_imports, all_exports_scope, all_exports_collision_strategy, module_split_mode; aliases now `Mapping[str, str
Parser core
src/datamodel_code_generator/parser/base.py
Replaced long explicit Parser.__init__ signature with `config: ParserConfig
GraphQL parser
src/datamodel_code_generator/parser/graphql.py
GraphQLParser.__init__ now `config: GraphQLParserConfig
JSON Schema parser
src/datamodel_code_generator/parser/jsonschema.py
JsonSchemaParser.__init__ now `config: ParserConfig
OpenAPI parser
src/datamodel_code_generator/parser/openapi.py
OpenAPIParser.__init__ now `config: OpenAPIParserConfig
Public API / types
src/datamodel_code_generator/__init__.py, src/datamodel_code_generator/_types/*, src/datamodel_code_generator/reference.py
aliases type widened across generate, GenerateConfigDict, ParserConfigDict, and ModelResolver to accept `str
Tests
tests/main/test_public_api_signature_baseline.py
Baseline updated: _BaselineParser.__init__ and _baseline_generate accept `config: ParserConfig
Docs snippets
docs/cli-reference/*.md
Example snippets updated to use Literal[...] for constant fields and added from typing import Literal imports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller as Caller
  participant ParserSub as ParserSubclass
  participant ParserBase as ParserBase
  participant Config as ParserConfig

  Note over Caller,Config: Caller may pass a config object and/or explicit options

  Caller->>ParserSub: instantiate(source, config?, **options)
  alt config provided
    ParserSub->>Config: validate/rebuild config (model_validate/model_rebuild)
    alt options present
      ParserSub->>Config: merge options into config (options take precedence)
    end
  else no config
    ParserSub->>Config: Config.from_options(options)
  end
  ParserSub->>ParserBase: super().__init__(source, config=config)
  ParserBase->>Config: read resolved fields (types, flags, templates, imports)
  ParserBase->>ParserBase: set internal attributes from config
  ParserBase-->>ParserSub: initialized state
  ParserSub-->>Caller: ready parser instance
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

breaking-change-analyzed

Suggested reviewers

  • ilovelinux

Poem

🐰 I hopped through configs, stitch by stitch,
Merged options clean, made defaults rich.
Aliases grew leaves — a list or string,
Parsers now heed the config I bring.
A tiny hop, a tidy spring! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: introducing a config parameter to Parser classes, replacing numerous explicit constructor parameters with a centralized config-driven approach.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

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

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 30, 2025

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

Comment thread src/datamodel_code_generator/parser/base.py Fixed
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 30, 2025

CodSpeed Performance Report

Merging #2863 will not alter performance

Comparing feature/parser-config-pattern (507c33f) with main (c3e159c)

⚠️ Unknown Walltime execution environment detected

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

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

Summary

✅ 11 untouched
⏩ 98 skipped1

Footnotes

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

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

57-63: Config-based target_datetime_class will be ignored due to premature default assignment

The config merging for openapi_scopes, include_path_parameters, and use_status_code_in_response_name is correct. However, there's a critical issue with target_datetime_class handling:

At line 312:

target_datetime_class = target_datetime_class or DatetimeClassType.Awaredatetime

This default is applied before calling super().__init__(). The parent Parser.__init__ (in base.py) attempts to merge config.target_datetime_class, but only when the parameter is None:

target_datetime_class = (
    config.target_datetime_class if target_datetime_class is None else target_datetime_class
)

Since line 312 already set target_datetime_class to Awaredatetime when it was None, the parent's config check fails, and any config.target_datetime_class value is silently ignored.

To fix this, read from config before applying the default:

if config is not None and target_datetime_class is None:
    target_datetime_class = config.target_datetime_class
target_datetime_class = target_datetime_class or DatetimeClassType.Awaredatetime

This same issue exists in JsonSchemaParser.

🧹 Nitpick comments (2)
tests/main/test_public_api_signature_baseline.py (1)

609-737: Config-backed parser tests give good coverage; consider de-duplicating the setup

The three new tests for JsonSchemaParser, OpenAPIParser, and GraphQLParser nicely verify that:

  • ParserConfig / OpenAPIParserConfig / GraphQLParserConfig can be rebuilt with a _types_namespace and instantiated.
  • Passing config=... wires values into parser attributes (including extra_template_data and parser‑specific options).

The repeated model_rebuild(_types_namespace=...) blocks are very similar to the existing GenerateConfig test. If this pattern keeps growing, you might want a tiny shared helper to rebuild any config model with the common namespace, but that’s optional.

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

56-61: GraphQL-specific config precedence is sensible and preserves explicit overrides

The new config: GraphQLParserConfig | None = None parameter plus:

if config is not None:
    if config.data_model_scalar_type is not None and data_model_scalar_type is DataTypeScalar:
        data_model_scalar_type = config.data_model_scalar_type
    if config.data_model_union_type is not None and data_model_union_type is DataTypeUnion:
        data_model_union_type = config.data_model_union_type

give a clear precedence model:

  • If callers stick with the default DataTypeScalar / DataTypeUnion, a GraphQLParserConfig can override both types.
  • If they pass custom data_model_scalar_type / data_model_union_type explicitly, those are respected (no override because the is DataTypeScalar/DataTypeUnion checks fail).

Passing config=config into super().__init__ and then assigning self.data_model_scalar_type / self.data_model_union_type after the base initializer keeps GraphQL-specific concerns isolated from the base Parser while still exposing the chosen types to parsing methods. This looks correct.

One minor edge case: if someone explicitly passes data_model_scalar_type=DataTypeScalar and sets a different data_model_scalar_type in GraphQLParserConfig, the config will win. That seems acceptable, but it might be worth documenting that “config beats default-like explicit args” if this pattern is used elsewhere.

Also applies to: 104-232

📜 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 83776ca and ef6f61c.

📒 Files selected for processing (6)
  • src/datamodel_code_generator/config.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
  • tests/main/test_public_api_signature_baseline.py
🧰 Additional context used
🧬 Code graph analysis (4)
src/datamodel_code_generator/parser/graphql.py (3)
src/datamodel_code_generator/config.py (1)
  • GraphQLParserConfig (325-329)
src/datamodel_code_generator/model/scalar.py (1)
  • DataTypeScalar (94-99)
src/datamodel_code_generator/model/union.py (1)
  • DataTypeUnion (66-74)
src/datamodel_code_generator/parser/openapi.py (2)
src/datamodel_code_generator/config.py (1)
  • OpenAPIParserConfig (317-322)
src/datamodel_code_generator/format.py (1)
  • DatetimeClassType (50-57)
src/datamodel_code_generator/parser/base.py (1)
src/datamodel_code_generator/config.py (1)
  • ParserConfig (192-314)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/config.py (1)
  • ParserConfig (192-314)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

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

85-85: LGTM! Clean addition of config parameter.

The TYPE_CHECKING import and optional config parameter are well-designed:

  • Avoids circular imports by using TYPE_CHECKING
  • Maintains backward compatibility with optional parameter
  • Allows dependency injection of configuration

Also applies to: 694-694


690-690: Remove unused noqa directive.

Ruff reports that the complexity suppressions on line 690 are not needed:

def __init__(  # noqa: PLR0012, PLR0013, PLR0014, PLR0915

These Pylint codes (too many branches/arguments/locals/statements) are either not enabled in your configuration or not triggered at the current thresholds.

🔎 Proposed fix

Remove the unused noqa directive:

-    def __init__(  # noqa: PLR0912, PLR0913, PLR0914, PLR0915
+    def __init__(
         self,
         source: str | Path | list[Path] | ParseResult | dict[str, YamlValue],
         *,

If these suppressions are needed in the future, Ruff will warn you to add them back.

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

181-187: Baseline parser now models config parameter correctly

Adding config: ParserConfig | None = None to _BaselineParser.__init__ keeps the test harness aligned with the real Parser.__init__ signature and with ParserConfig’s field set/defaults used in the config‑vs‑signature tests below. No issues here.

src/datamodel_code_generator/config.py (2)

317-323: OpenAPIParserConfig cleanly scopes OpenAPI-only parser options

Defining OpenAPIParserConfig(ParserConfig) with:

openapi_scopes: list[OpenAPIScope] | None = None
include_path_parameters: bool = False
use_status_code_in_response_name: bool = False

matches the corresponding OpenAPIParser.__init__ kwargs and keeps these options out of the generic ParserConfig. This gives a nice separation between shared parser configuration and OpenAPI-specific behavior without changing validation rules.


325-330: GraphQLParserConfig correctly mirrors GraphQL-specific type hooks

GraphQLParserConfig(ParserConfig) adds:

data_model_scalar_type: type[DataModel] | None = None
data_model_union_type: type[DataModel] | None = None

which align with GraphQLParser.__init__’s data_model_scalar_type / data_model_union_type kwargs and with the new tests that rebuild the model with a _types_namespace. These fields stay out of the base ParserConfig, keeping specialization clear while still allowing a single config object to be passed into GraphQLParser.

Comment thread src/datamodel_code_generator/parser/base.py Outdated
Comment thread src/datamodel_code_generator/parser/base.py Outdated
Comment thread src/datamodel_code_generator/parser/jsonschema.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 30, 2025

Codecov Report

❌ Patch coverage is 99.17127% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.34%. Comparing base (c3e159c) to head (507c33f).

Files with missing lines Patch % Lines
tests/main/test_public_api_signature_baseline.py 98.83% 0 Missing and 2 partials ⚠️
src/datamodel_code_generator/parser/graphql.py 95.45% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2863      +/-   ##
==========================================
- Coverage   99.38%   99.34%   -0.05%     
==========================================
  Files          91       91              
  Lines       15555    15773     +218     
  Branches     1827     1855      +28     
==========================================
+ Hits        15460    15670     +210     
- Misses         50       55       +5     
- Partials       45       48       +3     
Flag Coverage Δ
unittests 99.34% <99.17%> (-0.05%) ⬇️

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.

Comment thread src/datamodel_code_generator/parser/base.py Dismissed
Comment thread src/datamodel_code_generator/parser/base.py Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/main/test_public_api_signature_baseline.py (1)

403-426: test_parser_config_defaults_match_parser_signature has become effectively a no-op

With _BaselineParser.__init__ now only exposing config as a kw-only parameter, expected_params in this test contains just "config", while ParserConfig.model_fields has no field by that name. The for loop therefore never performs any assertions, despite the docstring claiming it checks defaults against Parser.__init__.

Consider either:

  • Updating the test to assert some concrete property of how ParserConfig defaults are applied (e.g., instantiating Parser with an empty ParserConfig and checking selected attributes), or
  • Removing/renaming this test and its docstring so it doesn’t suggest behavior that no longer occurs.
src/datamodel_code_generator/parser/base.py (1)

700-829: Excellent refactoring to config-driven initialization.

This refactoring significantly improves the maintainability and usability of the Parser class:

  • Cleaner API: Single config parameter replaces ~90 individual keyword arguments
  • Consistent precedence: The opt() wrapper ensures explicit options > config > defaults throughout
  • Backward compatible: Still accepts **options for per-call overrides
  • Reduced duplication: Centralized option resolution eliminates repetitive conditional logic

The extra_template_data handling (lines 827-829) correctly ensures it's converted to a defaultdict when needed, maintaining the expected behavior.

Based on learnings, the previous repetitive config override logic has been successfully replaced with this cleaner pattern as noted in past review comments.

📜 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 ef6f61c and a6e64fc.

📒 Files selected for processing (8)
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/template-customization.md
  • src/datamodel_code_generator/config.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
  • tests/main/test_public_api_signature_baseline.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/datamodel_code_generator/config.py
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-25T09:23:08.506Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

Applied to files:

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

Applied to files:

  • src/datamodel_code_generator/parser/base.py
🧬 Code graph analysis (3)
tests/main/test_public_api_signature_baseline.py (2)
src/datamodel_code_generator/config.py (3)
  • ParserConfig (194-316)
  • OpenAPIParserConfig (319-324)
  • GraphQLParserConfig (327-331)
src/datamodel_code_generator/parser/__init__.py (1)
  • LiteralType (20-25)
src/datamodel_code_generator/parser/graphql.py (4)
src/datamodel_code_generator/format.py (1)
  • DatetimeClassType (50-57)
src/datamodel_code_generator/parser/__init__.py (1)
  • LiteralType (20-25)
src/datamodel_code_generator/parser/base.py (2)
  • Parser (693-3201)
  • _get_option (83-95)
src/datamodel_code_generator/config.py (1)
  • GraphQLParserConfig (327-331)
src/datamodel_code_generator/parser/openapi.py (3)
src/datamodel_code_generator/format.py (1)
  • DatetimeClassType (50-57)
src/datamodel_code_generator/parser/base.py (3)
  • _get_option (83-95)
  • get_special_path (295-297)
  • parse (3121-3201)
src/datamodel_code_generator/config.py (1)
  • OpenAPIParserConfig (319-324)
🪛 GitHub Check: CodeQL
src/datamodel_code_generator/parser/base.py

[notice] 74-74: Cyclic import
Import of module datamodel_code_generator.types begins an import cycle.


[notice] 80-80: Cyclic import
Import of module datamodel_code_generator.config begins an import cycle.

🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: combine coverage
🔇 Additional comments (16)
docs/cli-reference/model-customization.md (1)

1371-1379: Literal-typed Api.version example is consistent and correct

Importing Literal and annotating version as Literal['v1'] while keeping Field('v1', const=True, ...) accurately documents a constant string field and stays consistent with the surrounding Pydantic v2 and TypedDict/msgspec examples.

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

2556-2557: MapState Literal types correctly mirror schema consts and double-quote behavior

Switching map_view_mode, map_split_mode, and is_split to Literal[...] with matching const defaults and aliases is consistent with the input schema and clearly demonstrates --use-double-quotes output; no issues spotted.

Also applies to: 2562-2578

tests/main/test_public_api_signature_baseline.py (4)

30-46: Baseline parser stub now correctly models the config-centric public API

Importing Parser/YamlValue and updating _BaselineParser.__init__ to accept source plus a kw-only config: ParserConfig | None = None (with **options: Any) matches the new constructor shape for Parser.__init__. The existing signature test will keep the config kw-only parameter in sync without over-constraining the internal options surface.

Also applies to: 177-185


497-537: JsonSchemaParser–ParserConfig integration test is well targeted

The test_jsonschema_parser_with_config case:

  • Rebuilds ParserConfig with the appropriate _types_namespace for forward refs.
  • Passes key options (validation, snake_case_field, use_schema_description, reuse_model, and extra_template_data) via ParserConfig.
  • Asserts that the resulting JsonSchemaParser instance exposes these attributes and that extra_template_data is preserved.

This provides a solid sanity check that the new config-based initialization path behaves as expected for the JSON Schema parser.


540-582: OpenAPIParserConfig test exercises OpenAPI-specific options correctly

test_openapi_parser_with_config:

  • Rebuilds OpenAPIParserConfig with the necessary type namespace.
  • Verifies that validation, snake_case_field, openapi_scopes, include_path_parameters, and use_status_code_in_response_name set on the config are reflected on the OpenAPIParser instance.

This is a good focused check that the OpenAPI-specific configuration surface is correctly wired through the new config parameter.


585-624: GraphQLParserConfig test cleanly validates scalar/union type overrides

test_graphql_parser_with_config:

  • Rebuilds GraphQLParserConfig for forward refs.
  • Asserts that validation and snake_case_field propagate, and that data_model_scalar_type / data_model_union_type on GraphQLParser are exactly the configured DataTypeScalar and DataTypeUnion classes.

This gives good coverage that GraphQL-specific parser behavior is correctly driven by the new config model.

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

84-86: LGTM!

The TYPE_CHECKING imports are correctly updated to include ParserConfig for type hints without runtime impact.


627-638: LGTM! The config-driven initialization is correctly implemented.

The target_datetime_class default handling now properly respects the precedence: explicit options > config > default. The condition at lines 635-636 correctly checks both sources before applying the fallback.

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

27-28: LGTM!

Import changes correctly add DatetimeClassType, _get_option, and get_special_path for the config-driven initialization and existing functionality.

Also applies to: 35-35


41-42: LGTM!

TYPE_CHECKING imports correctly updated to include OpenAPIParserConfig and DataModelFieldBase for type hints.


165-188: LGTM! The OpenAPI config-driven initialization is well-structured.

The implementation correctly:

  1. Uses _get_option to resolve OpenAPI-specific options with proper precedence (options > config > default)
  2. Applies the same target_datetime_class default pattern as JsonSchemaParser
  3. Forwards config to the base class via super().__init__
  4. Sets instance attributes after parent initialization
src/datamodel_code_generator/parser/graphql.py (3)

16-17: LGTM!

Import reorganization correctly adds necessary imports for the config-driven initialization pattern: DatetimeClassType, LiteralType, _get_option, and Types.

Also applies to: 23-23, 28-28, 32-32


42-45: LGTM!

TYPE_CHECKING imports correctly updated to include GraphQLParserConfig and related types for type hints.


89-111: LGTM! The GraphQL config-driven initialization follows the established pattern.

The implementation correctly uses _get_option to resolve GraphQL-specific options with proper precedence. Note that DatetimeClassType.Datetime is used as the default here, whereas JSON Schema and OpenAPI parsers use DatetimeClassType.Awaredatetime - this appears intentional for GraphQL's different datetime handling conventions.

Please confirm the intentional difference in default target_datetime_class between parsers:

  • GraphQLParser: DatetimeClassType.Datetime
  • JsonSchemaParser/OpenAPIParser: DatetimeClassType.Awaredatetime
src/datamodel_code_generator/parser/base.py (2)

77-81: Cyclic import warnings are false positives.

The static analysis tool flags cyclic imports on lines 74 and 80, but these are not problematic at runtime because:

  • The ParserConfig import on line 80 is guarded by TYPE_CHECKING, which is False at runtime
  • This is the standard pattern for avoiding runtime import cycles while preserving type hints for static analysis

The warnings can be safely ignored.


83-95: LGTM! Clean option resolution with correct precedence.

The _get_option helper correctly implements the documented precedence (options > config > default) and provides a clean abstraction for resolving configuration values throughout the __init__ method.

Comment thread src/datamodel_code_generator/parser/base.py Outdated
- Remove _get_option helper function from base.py
- Use ParserConfig.model_validate(options) to convert options to config
- Update all instance variable references to use config.xxx pattern
- Apply same pattern to JsonSchemaParser, OpenAPIParser, GraphQLParser
- Fix aliases type from Mapping[str, str] to Mapping[str, str | list[str]]
  to support multiple aliases per field
- Add model_rebuild() calls to resolve forward references before validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comment thread src/datamodel_code_generator/parser/base.py Fixed
Comment thread src/datamodel_code_generator/parser/base.py Fixed
Comment thread src/datamodel_code_generator/parser/base.py Fixed
Comment thread src/datamodel_code_generator/parser/base.py Fixed
Comment thread src/datamodel_code_generator/parser/graphql.py Fixed
Comment thread src/datamodel_code_generator/parser/graphql.py Fixed
Comment thread src/datamodel_code_generator/parser/jsonschema.py Fixed
Comment thread src/datamodel_code_generator/parser/jsonschema.py Fixed
Comment thread src/datamodel_code_generator/parser/openapi.py Fixed
Comment thread src/datamodel_code_generator/parser/openapi.py Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/datamodel_code_generator/parser/base.py (1)

685-685: Remove unused noqa directive.

The noqa directive for PLR0912, PLR0914, PLR0915 is no longer needed as these complexity rules are not enabled in the linting configuration.

🔎 Proposed fix
-    def __init__(  # noqa: PLR0912, PLR0915
+    def __init__(
🧹 Nitpick comments (4)
src/datamodel_code_generator/parser/graphql.py (1)

110-118: Optional refactor: clarify intent when clearing options.

Lines 112 and 118 set options = {} after merging them into config, but the variable is then unused (flagged by static analysis). Consider using del options or assigning to _ to make the intent clearer:

🔎 Proposed refactor
 if config is None:
     config = GraphQLParserConfigClass.model_validate(options)
-    options = {}  # Clear options since they're now in config
 elif options:
     # Merge options into config (options take precedence)
     config_dict = config.model_dump()
     config_dict.update(options)
     config = GraphQLParserConfigClass.model_validate(config_dict)
-    options = {}  # Clear options since they're now in config

Or if you want to be explicit:

     config = GraphQLParserConfigClass.model_validate(config_dict)
-    options = {}  # Clear options since they're now in config
+    del options  # Options have been merged into config
src/datamodel_code_generator/parser/openapi.py (1)

187-195: Optional refactor: clarify intent when clearing options.

Lines 189 and 195 set options = {} after processing, but the variable is unused afterward (flagged by static analysis). Same issue as in graphql.py. Consider using del options or removing the assignments to clarify intent.

🔎 Proposed refactor
 if config is None:
     config = OpenAPIParserConfigClass.model_validate(options)
-    options = {}  # Clear options since they're now in config
 elif options:
     # Merge options into config (options take precedence)
     config_dict = config.model_dump()
     config_dict.update(options)
     config = OpenAPIParserConfigClass.model_validate(config_dict)
-    options = {}  # Clear options since they're now in config
src/datamodel_code_generator/parser/jsonschema.py (2)

635-647: Local imports are intentional but flagged by linting.

The imports inside __init__ are placed here to avoid circular imports and to provide the type namespace for model_rebuild(). This is a valid pattern, but the pipeline is failing with PLC0415 (imports should be at top-level).

Consider suppressing the lint rule for these specific lines if this pattern is intentional:

🔎 Suggested inline suppression
-        from datamodel_code_generator.config import ParserConfig as ParserConfigClass
-        from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
-        from datamodel_code_generator.types import DataTypeManager, StrictTypes
+        from datamodel_code_generator.config import ParserConfig as ParserConfigClass  # noqa: PLC0415
+        from datamodel_code_generator.model.base import DataModel, DataModelFieldBase  # noqa: PLC0415
+        from datamodel_code_generator.types import DataTypeManager, StrictTypes  # noqa: PLC0415

649-657: Remove unused options = {} assignments.

The static analysis correctly identifies that options is assigned to {} on lines 651 and 657 but never used afterward. These assignments are dead code and can be safely removed.

🔎 Proposed fix
         if config is None:
             config = ParserConfigClass.model_validate(options)
-            options = {}  # Clear options since they're now in config
         elif options:
             # Merge options into config (options take precedence)
             config_dict = config.model_dump()
             config_dict.update(options)
             config = ParserConfigClass.model_validate(config_dict)
-            options = {}  # Clear options since they're now in config
📜 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 a6e64fc and 97bf992.

📒 Files selected for processing (9)
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/_types/parser_config_dict.py
  • src/datamodel_code_generator/config.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/reference.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/datamodel_code_generator/config.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-25T09:22:22.481Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.

Applied to files:

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

Applied to files:

  • src/datamodel_code_generator/parser/base.py
📚 Learning: 2025-12-25T09:23:08.506Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

Applied to files:

  • src/datamodel_code_generator/parser/base.py
🧬 Code graph analysis (2)
src/datamodel_code_generator/parser/graphql.py (4)
src/datamodel_code_generator/format.py (1)
  • DatetimeClassType (50-57)
src/datamodel_code_generator/parser/__init__.py (1)
  • LiteralType (20-25)
src/datamodel_code_generator/config.py (1)
  • GraphQLParserConfig (327-331)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • model_rebuild (215-217)
src/datamodel_code_generator/parser/base.py (3)
src/datamodel_code_generator/util.py (1)
  • camel_to_snake (248-251)
src/datamodel_code_generator/config.py (1)
  • ParserConfig (194-316)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • model_rebuild (215-217)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/parser/graphql.py

[error] 96-98: PL C0415 import should be at the top-level of a file

src/datamodel_code_generator/parser/openapi.py

[error] 173-175: PL C0415 import should be at the top-level of a file


[error] 173-175: PLC0415 import should be at the top-level of a file

src/datamodel_code_generator/parser/jsonschema.py

[error] 635-637: PL C0415 import should be at the top-level of a file


[error] 635-637: PLC0415 import should be at the top-level of a file


[error] 636-638: PLC0415 import should be at the top-level of a file

src/datamodel_code_generator/parser/base.py

[error] 690-692: PL C0415 import should be at the top-level of a file


[error] 688-692: PLC0415 import should be at the top-level of a file


[error] 689-691: PLC0415 import should be at the top-level of a file


[error] 690-692: PLC0415 import should be at the top-level of a file

🪛 GitHub Check: CodeQL
src/datamodel_code_generator/parser/graphql.py

[notice] 112-112: Unused local variable
Variable options is not used.


[notice] 118-118: Unused local variable
Variable options is not used.

src/datamodel_code_generator/parser/openapi.py

[notice] 189-189: Unused local variable
Variable options is not used.


[notice] 195-195: Unused local variable
Variable options is not used.

src/datamodel_code_generator/parser/jsonschema.py

[notice] 651-651: Unused local variable
Variable options is not used.


[notice] 657-657: Unused local variable
Variable options is not used.

src/datamodel_code_generator/parser/base.py

[notice] 74-74: Cyclic import
Import of module datamodel_code_generator.types begins an import cycle.


[notice] 74-74: Unused import
Import of 'DataTypeManager' is not used.


[notice] 693-693: Cyclic import
Import of module datamodel_code_generator.config begins an import cycle.


[notice] 694-694: Cyclic import
Import of module datamodel_code_generator.model.base begins an import cycle.


[notice] 695-695: Cyclic import
Import of module datamodel_code_generator.types begins an import cycle.

🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)

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

55-55: LGTM: Alias type broadened to support multiple values.

The type change from Mapping[str, str] | None to Mapping[str, str | list[str]] | None is backward compatible and aligns with the PR's objective to support multiple aliases per field (useful for Pydantic v2 AliasChoices).

src/datamodel_code_generator/__init__.py (1)

472-472: LGTM: Public API updated to support multiple aliases.

The parameter type change is consistent with the broader alias handling updates across the codebase and maintains backward compatibility.

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

49-49: LGTM: Config type updated for multiple alias support.

The type widening is consistent with the broader changes and maintains backward compatibility.

src/datamodel_code_generator/reference.py (1)

228-228: LGTM: Constructor signatures updated for multiple alias support.

The type changes to Mapping[str, str | list[str]] | None in both FieldNameResolver.__init__ and ModelResolver.__init__ are consistent with the broader alias handling updates across the codebase.

Also applies to: 529-529

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

110-124: Approve config-driven initialization pattern with note on merge semantics.

The config initialization and merging logic (lines 110-124) correctly implements:

  1. Config creation from options when config is None
  2. Option merging when both are provided (options take precedence)
  3. Default datetime class for GraphQL

Note: When both config and options are provided, options take precedence via config_dict.update(options). Ensure this behavior is documented and tested, especially for nested dict fields like base_class_map where shallow merging could cause unexpected behavior.

Consider documenting the merge precedence in the docstring:

"""Initialize the GraphQL parser with configuration options.

When both config and options are provided, individual option values
take precedence over config values (shallow merge).
"""
src/datamodel_code_generator/parser/openapi.py (1)

187-207: Approve config-driven initialization pattern.

The config initialization logic correctly implements:

  1. Config creation from options when config is None
  2. Option merging when both are provided (options take precedence)
  3. Default DatetimeClassType.Awaredatetime for OpenAPI (different from GraphQL's Datetime)
  4. Initialization of OpenAPI-specific fields from config

The implementation is consistent with the pattern used in graphql.py. Same note applies: when both config and options are provided, shallow merge semantics could affect nested dict fields.

Consider adding the same merge precedence documentation mentioned in the graphql.py review.

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

83-86: LGTM!

Clean addition of type-only imports under the TYPE_CHECKING block, supporting the new config-driven initialization pattern.


659-665: Config-driven default for target_datetime_class looks correct.

The logic properly applies the default DatetimeClassType.Awaredatetime only when config.target_datetime_class is None, which addresses the previous review concern about silently overriding config values.

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

697-713: LGTM! Clean config-driven initialization.

The model_rebuild call correctly resolves forward references in ParserConfig before validation. The config merging logic properly handles three cases:

  1. No config provided → create from options
  2. Config provided, no options → use config as-is
  3. Config provided with options → merge with options taking precedence

This is a solid implementation of the config-driven pattern.


715-898: Well-executed config-driven refactoring.

The conversion from explicit parameters to config-driven initialization is clean and consistent. All attributes are now derived from the ParserConfig object with a uniform pattern. The previous issues with inconsistent boolean handling have been addressed.

The special cases are handled appropriately:

  • extra_template_data correctly converts to defaultdict if needed (lines 715-717)
  • treat_dot_as_module uses or False for None-safe boolean conversion (line 734)
  • ModelResolver initialization cleanly pulls config values (lines 842-859)

Comment thread src/datamodel_code_generator/parser/base.py
Comment on lines +693 to +695
from datamodel_code_generator.config import ParserConfig
from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
from datamodel_code_generator.types import DataTypeManager, StrictTypes
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add # noqa: PLC0415 directives to fix pipeline failures.

These runtime imports are intentionally placed inside __init__ to avoid circular import cycles. Per project conventions, lazy imports should have # noqa: PLC0415 directives to suppress the linter warning.

🔎 Proposed fix
-        from datamodel_code_generator.config import ParserConfig
-        from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
-        from datamodel_code_generator.types import DataTypeManager, StrictTypes
+        from datamodel_code_generator.config import ParserConfig  # noqa: PLC0415
+        from datamodel_code_generator.model.base import DataModel, DataModelFieldBase  # noqa: PLC0415
+        from datamodel_code_generator.types import DataTypeManager, StrictTypes  # noqa: PLC0415

Based on learnings, defensive # noqa: PLC0415 directives should be kept on lazy imports even when Ruff might report them as unused, to prepare for potential future configuration changes.

🧰 Tools
🪛 GitHub Check: CodeQL

[notice] 693-693: Cyclic import
Import of module datamodel_code_generator.config begins an import cycle.


[notice] 694-694: Cyclic import
Import of module datamodel_code_generator.model.base begins an import cycle.


[notice] 695-695: Cyclic import
Import of module datamodel_code_generator.types begins an import cycle.

🤖 Prompt for AI Agents
In src/datamodel_code_generator/parser/base.py around lines 693 to 695, the
three runtime/lazy imports (ParserConfig, DataModel/DataModelFieldBase,
DataTypeManager/StrictTypes) need defensive linter suppression; add "# noqa:
PLC0415" to each lazy import line to silence the PLC0415 warning and prevent
future pipeline failures while keeping the imports inside the function to avoid
circular import cycles.

Comment on lines 96 to 108
from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
from datamodel_code_generator.types import DataTypeManager, StrictTypes

# Rebuild the model to resolve forward references before validation
GraphQLParserConfigClass.model_rebuild(
_types_namespace={
"DataModel": DataModel,
"DataModelFieldBase": DataModelFieldBase,
"DataTypeManager": DataTypeManager,
"StrictTypes": StrictTypes,
}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix linting error: move imports to top-level or use TYPE_CHECKING.

The local imports at lines 96-98 violate PLC0415 (imports should be at module level). Consider:

  1. Move imports to the top of the file under a TYPE_CHECKING guard if they're only used for type checking
  2. If imports are needed at runtime to avoid circular dependencies, suppress the linting error with a comment explaining why

The model_rebuild call (lines 101-108) appears correct for resolving forward references before validation.

🔎 Proposed fix: Use TYPE_CHECKING guard

If the imports are only for type annotations during rebuild:

+if TYPE_CHECKING:
+    from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
+
 def __init__(
     self,
     source: str | Path | ParseResult,
     *,
     config: GraphQLParserConfig | None = None,
     **options: Any,
 ) -> None:
     """Initialize the GraphQL parser with configuration options."""
-    from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
     from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
     from datamodel_code_generator.types import DataTypeManager, StrictTypes

Otherwise, add a suppression comment:

+    # Import here to avoid circular dependency  # noqa: PLC0415
     from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
from datamodel_code_generator.types import DataTypeManager, StrictTypes
# Rebuild the model to resolve forward references before validation
GraphQLParserConfigClass.model_rebuild(
_types_namespace={
"DataModel": DataModel,
"DataModelFieldBase": DataModelFieldBase,
"DataTypeManager": DataTypeManager,
"StrictTypes": StrictTypes,
}
)
# noqa: PLC0415
from datamodel_code_generator.config import GraphQLParserConfig as GraphQLParserConfigClass
# noqa: PLC0415
from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
# noqa: PLC0415
from datamodel_code_generator.types import DataTypeManager, StrictTypes
# Rebuild the model to resolve forward references before validation
GraphQLParserConfigClass.model_rebuild(
_types_namespace={
"DataModel": DataModel,
"DataModelFieldBase": DataModelFieldBase,
"DataTypeManager": DataTypeManager,
"StrictTypes": StrictTypes,
}
)
🧰 Tools
🪛 GitHub Actions: Lint

[error] 96-98: PL C0415 import should be at the top-level of a file

🤖 Prompt for AI Agents
In src/datamodel_code_generator/parser/graphql.py around lines 96 to 108, local
imports of GraphQLParserConfigClass, DataModel, DataModelFieldBase,
DataTypeManager, and StrictTypes cause PLC0415; move these imports to the module
top and wrap them in a TYPE_CHECKING guard if they are only used for typing
(from typing import TYPE_CHECKING) so they don't execute at runtime, or if they
must remain local to avoid circular imports keep them local but add a comment
explaining the circular-import reason and suppress the lint error with # noqa:
PLC0415.

Comment on lines 173 to 185
from datamodel_code_generator.config import OpenAPIParserConfig as OpenAPIParserConfigClass
from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
from datamodel_code_generator.types import DataTypeManager, StrictTypes

# Rebuild the model to resolve forward references before validation
OpenAPIParserConfigClass.model_rebuild(
_types_namespace={
"DataModel": DataModel,
"DataModelFieldBase": DataModelFieldBase,
"DataTypeManager": DataTypeManager,
"StrictTypes": StrictTypes,
}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix linting error: move imports to top-level or use TYPE_CHECKING.

The local imports at lines 173-175 violate PLC0415 (imports should be at module level), same issue as in graphql.py. Consider:

  1. Move imports to the top under a TYPE_CHECKING guard if only used for type checking
  2. Add a suppression comment if needed at runtime to avoid circular dependencies

The model_rebuild call (lines 178-185) appears correct for resolving forward references.

🔎 Proposed fix: Use TYPE_CHECKING guard or add suppression

If imports are only for type annotations:

+if TYPE_CHECKING:
+    from datamodel_code_generator.config import OpenAPIParserConfig as OpenAPIParserConfigClass
+
 def __init__(
     self,
     source: str | Path | list[Path] | ParseResult,
     *,
     config: OpenAPIParserConfig | None = None,
     **options: Any,
 ) -> None:
     """Initialize the OpenAPI parser with configuration options."""
-    from datamodel_code_generator.config import OpenAPIParserConfig as OpenAPIParserConfigClass
     from datamodel_code_generator.model.base import DataModel, DataModelFieldBase
     from datamodel_code_generator.types import DataTypeManager, StrictTypes

Otherwise:

+    # Import here to avoid circular dependency  # noqa: PLC0415
     from datamodel_code_generator.config import OpenAPIParserConfig as OpenAPIParserConfigClass

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Actions: Lint

[error] 173-175: PL C0415 import should be at the top-level of a file


[error] 173-175: PLC0415 import should be at the top-level of a file

🤖 Prompt for AI Agents
In src/datamodel_code_generator/parser/openapi.py around lines 173 to 185, local
imports of OpenAPIParserConfigClass, DataModel, DataModelFieldBase,
DataTypeManager and StrictTypes trigger PLC0415; move these imports to the
module top and guard them with typing.TYPE_CHECKING (from typing import
TYPE_CHECKING) if they are only used for type checking, e.g. import inside a
TYPE_CHECKING block, or if they must remain runtime imports keep them at top and
add a linter suppression comment for PLC0415 to avoid the violation and prevent
circular import issues; leave the model_rebuild call as-is.

Comment thread src/datamodel_code_generator/parser/base.py Fixed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
src/datamodel_code_generator/parser/openapi.py (1)

173-177: Keep # noqa: PLC0415 on lazy imports despite RUF100

The local imports of OpenAPIParserConfigClass and model_dump in __init__ are deliberate lazy imports guarded with # noqa: PLC0415. Even though Ruff flags these as “unused noqa” (RUF100) today, the directive is useful if PLC0415 is enabled in future, and matches the project’s established pattern for lazy imports. I’d keep them as-is and ignore the RUF100 hint.

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

96-99: Retain lazy-import # noqa: PLC0415 even with RUF100

As with openapi.py, these imports are intentionally local to __init__ and annotated with # noqa: PLC0415 to tolerate an import-at-top-level rule if it’s enabled later. I’d ignore Ruff’s RUF100 suggestion here and keep the directives.

src/datamodel_code_generator/config.py (1)

212-217: Keep lazy-import # noqa: PLC0415 directives despite RUF100

The imports of DataModel, DataModelFieldBase, DataTypeManager, and StrictTypes inside from_options are intentionally deferred to avoid import cycles between config, model.base, and types. The # noqa: PLC0415 comments are defensive for when PLC0415 is enabled, and should be kept even though Ruff currently marks them as unused (RUF100).

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

83-87: JsonSchemaParser now correctly delegates all options via ParserConfig

The new __init__ signature and body:

  • Build/merge a ParserConfig via ParserConfigClass.from_options (with options overriding any existing config values).
  • Only apply the DatetimeClassType.Awaredatetime default when config.target_datetime_class is still None.
  • Call super().__init__(source=source, config=config) so the base Parser handles all common wiring.

This fixes the earlier issue where a local target_datetime_class default could override a value set in ParserConfig, and aligns JsonSchemaParser with the same config-driven pattern as the other parsers.

Also applies to: 627-651


635-636: RUF100 on # noqa: PLC0415 can be ignored here

The local imports of ParserConfigClass and model_dump in JsonSchemaParser.__init__ are intentional lazy imports with # noqa: PLC0415, mirroring the approach elsewhere to avoid potential circular imports and future PLC0415 issues. I’d keep these directives despite Ruff’s unused-noqa hint.

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

192-195: Consider keeping a backward-compatible alias for open_api_scopes

OpenAPIParser now exposes self.open_api_scopes (note the extra underscore) instead of something like openapi_scopes. If external callers were reading a previous attribute (e.g. parser.openapi_scopes), this change could be a silent breaking change. Consider adding an alias assignment or property (e.g. self.openapi_scopes = self.open_api_scopes) to ease transition.

Also applies to: 685-704

tests/main/test_public_api_signature_baseline.py (1)

233-315: Union-string normalization is correct but subtle; consider adding direct tests

The new _normalize_union_str and _split_generic_args functions correctly handle:

  • Nested generics like Mapping[str, list[int | None]]
  • Top-level unions like A | B | C
  • ForwardRef/string annotations where unions appear inside generics

by tracking bracket depth and recursively normalizing inner segments before sorting top-level union parts. Given the string-wrangling is non-trivial, it might be worth adding a small set of focused tests (e.g. parametrized over representative type strings) to catch regressions independent of the config/TypedDict comparison tests.

src/datamodel_code_generator/config.py (1)

208-237: ParserConfig.from_options correctly handles v2/v1 differences, but repeats rebuild

The from_options classmethod:

  • For v2: lazily imports DataModel, DataModelFieldBase, DataTypeManager, StrictTypes, calls cls.model_rebuild(_types_namespace=...), then validates options via model_validate.
  • For v1: synthesizes a defaults dict from cls.__fields__, overlays options, and uses construct() to bypass forward-ref resolution.

This should give the right behavior for both versions and support subclasses like OpenAPIParserConfig/GraphQLParserConfig via Self. The only minor concern is that model_rebuild will be invoked on every call in v2; if this becomes hot, you might later consider a small guard (e.g., a class-level flag) to avoid redundant rebuilds, but it’s not critical right now.

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

71-71: Move DataTypeManager import to TYPE_CHECKING block to avoid circular imports.

DataTypeManager is only used as a type annotation (line 707) and isn't needed at runtime. Moving it to the TYPE_CHECKING block will prevent potential circular import issues.

🔎 Proposed refactor
-from datamodel_code_generator.types import DataType, DataTypeManager
+from datamodel_code_generator.types import DataType

And in the TYPE_CHECKING block:

 if TYPE_CHECKING:
     from collections.abc import Iterable, Iterator, Sequence
 
     from datamodel_code_generator.config import ParserConfig
+    from datamodel_code_generator.types import DataTypeManager

682-682: Remove unused noqa directive.

The noqa directive for PLR0912, PLR0915 is no longer needed as these complexity rules are not enabled in your linting configuration.

🔎 Proposed fix
-    def __init__(  # noqa: PLR0912, PLR0915
+    def __init__(
         self,
         source: str | Path | list[Path] | ParseResult | dict[str, YamlValue],
         *,
📜 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 97bf992 and b0bacf7.

📒 Files selected for processing (6)
  • src/datamodel_code_generator/config.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
  • tests/main/test_public_api_signature_baseline.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-25T09:23:08.506Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

Applied to files:

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

Applied to files:

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

Applied to files:

  • src/datamodel_code_generator/parser/base.py
🧬 Code graph analysis (4)
src/datamodel_code_generator/parser/openapi.py (3)
src/datamodel_code_generator/util.py (1)
  • model_dump (254-258)
src/datamodel_code_generator/config.py (2)
  • OpenAPIParserConfig (351-356)
  • from_options (209-237)
src/datamodel_code_generator/enums.py (1)
  • OpenAPIScope (70-78)
src/datamodel_code_generator/config.py (4)
src/datamodel_code_generator/util.py (1)
  • is_pydantic_v2 (52-57)
src/datamodel_code_generator/model/base.py (2)
  • DataModel (621-930)
  • DataModelFieldBase (151-469)
src/datamodel_code_generator/enums.py (2)
  • StrictTypes (213-220)
  • OpenAPIScope (70-78)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • model_rebuild (215-217)
src/datamodel_code_generator/parser/graphql.py (3)
src/datamodel_code_generator/parser/__init__.py (1)
  • LiteralType (20-25)
src/datamodel_code_generator/parser/base.py (1)
  • Parser (675-3078)
src/datamodel_code_generator/config.py (2)
  • GraphQLParserConfig (359-363)
  • from_options (209-237)
src/datamodel_code_generator/parser/jsonschema.py (3)
src/datamodel_code_generator/config.py (2)
  • ParserConfig (195-348)
  • from_options (209-237)
src/datamodel_code_generator/util.py (1)
  • model_dump (254-258)
src/datamodel_code_generator/format.py (1)
  • DatetimeClassType (50-57)
🪛 Ruff (0.14.10)
tests/main/test_public_api_signature_baseline.py

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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/openapi.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/config.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/graphql.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/jsonschema.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

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

165-194: Config-driven __init__ wiring and datetime default look correct

The config resolution logic (config is None, merge with options, then fill target_datetime_class only when still None) keeps options precedence and avoids clobbering an explicit config.target_datetime_class, while still defaulting to Awaredatetime. Delegating to super().__init__(source=source, config=config) matches the new Parser.__init__ contract, and deriving open_api_scopes, include_path_parameters, and use_status_code_in_response_name directly from the finalized OpenAPIParserConfig is consistent with the rest of the PR. I don’t see functional issues here.

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

88-120: GraphQL parser config handling and overrides are consistent with base Parser

The GraphQLParser.__init__ follows the same pattern as JsonSchema/OpenAPI: options merged into GraphQLParserConfig, default target_datetime_class only when missing (here to Datetime), then delegating to Parser.__init__. Overriding data_model_scalar_type, data_model_union_type, and rebinding use_standard_collections / use_union_operator from the resolved config keeps the GraphQL-specific behaviors explicit without fighting the base class. This looks sound.

tests/main/test_public_api_signature_baseline.py (2)

30-49: Baseline signatures correctly track new config/aliases surface

The baseline imports and signatures (_BaselineParser with config: ParserConfig | None = None, **options and _baseline_generate’s aliases: Mapping[str, str | list[str]] | None) now mirror the updated public API (Parser.init and GenerateConfig/ParserConfig.aliases). This should keep the signature-compat tests meaningful as the config types evolve.

Also applies to: 69-73


529-700: Config-based parser tests nicely cover the new initialization path

The new test_jsonschema_parser_with_config, test_openapi_parser_with_config, and test_graphql_parser_with_config:

  • Rebuild the relevant config models with a proper _types_namespace for Pydantic v2.
  • Instantiate each parser using a config instance instead of kwargs.
  • Assert that key flags and parser-specific attributes (e.g., extra_template_data, open_api_scopes, data_model_scalar_type) are propagated.

This gives good coverage for the core PR behavior (config-driven parser initialization) without overfitting to internal details.

src/datamodel_code_generator/config.py (3)

10-12: Aliases type expansion is consistent across config and baselines

Updating GenerateConfig.aliases and ParserConfig.aliases to Mapping[str, str | list[str]] | None matches the new baseline signature and allows multiple aliases per key in Pydantic v2 (AliasChoices-style). This keeps the configuration models and tests aligned and gives users more flexibility without breaking the existing str-only case.

Also applies to: 89-90, 255-256


195-207: Pydantic v1 extra="allow" for ParserConfig is reasonable under from_options

Changing the v1 Config.extra to "allow" makes sense given that the v1 path in from_options uses construct() and may receive option dicts that include fields not explicitly modeled yet. This avoids unexpected ValidationErrors while you still control which fields are actually consumed by the parser.


351-364: OpenAPI/GraphQL-specific config classes cleanly separate parser concerns

OpenAPIParserConfig and GraphQLParserConfig inherit from ParserConfig and add only their respective specialization fields (openapi_scopes / include_path_parameters / use_status_code_in_response_name and data_model_scalar_type / data_model_union_type). This keeps parser-specific options structured without polluting the base config and works well with the shared from_options logic.

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

690-691: Keep # noqa: PLC0415 directives despite Ruff warnings.

While Ruff reports these noqa directives as unused, they should be retained per project conventions. These defensive directives protect against potential future Ruff configuration changes that might enable the import-outside-top-level rule (PLC0415).

Based on learnings, defensive # noqa: PLC0415 directives should be kept on lazy imports even when Ruff reports them as unused via RUF100.


682-887: LGTM! Excellent config-driven refactoring.

The conversion to config-driven initialization is well-implemented:

  • Clean signature accepting config + **options maintains backward compatibility
  • Proper merge logic when both config and options are provided (options take precedence)
  • Consistent attribute derivation from config throughout
  • Lazy imports properly used to avoid circular dependencies

This refactoring significantly improves maintainability by centralizing configuration management while preserving the existing API surface through **options.

target_date_class=target_date_class,
treat_dot_as_module=treat_dot_as_module or False,
use_serialize_as_any=use_serialize_as_any,
from datamodel_code_generator.config import ParserConfig # noqa: PLC0415

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
datamodel_code_generator.config
begins an import cycle.

Copilot Autofix

AI 4 months ago

In general, to fix a cyclic import between parser.base and config, you remove or invert the runtime import dependency so that neither module needs to import the other during execution. Type-only imports under if TYPE_CHECKING: are fine, because they don’t run at import time.

The cleanest fix here is to stop constructing ParserConfig inside Parser.__init__. Instead, require callers to pass in a proper ParserConfig instance (or at least avoid referencing ParserConfig in this method) so that parser.base no longer imports datamodel_code_generator.config at runtime. That breaks the cycle while preserving functionality by delegating configuration construction to the caller or by only using duck-typed attributes. We already have a TYPE_CHECKING import of ParserConfig (for type hints), so we can safely remove the runtime import and the call to ParserConfig.from_options, and treat config as an object with the required attributes.

Concretely, in Parser.__init__:

  • Remove the inline import from datamodel_code_generator.config import ParserConfig # noqa: PLC0415.
  • Remove the branch that uses ParserConfig.from_options(options) when config is None.
  • Replace it with a simpler requirement that config must be provided (or at least that config is not None at runtime) and raise an explicit error if not provided. This keeps the existing behavior for valid calls that already pass a ParserConfig, and avoids any need to import the config module here.
  • The rest of the method can stay as-is, since it only accesses attributes on config and doesn’t need to know its concrete class.

This way, parser.base depends only on a protocol-like interface of config (its attributes), the type hint for ParserConfig remains under if TYPE_CHECKING: for static analysis, and the runtime cycle is removed.

Suggested changeset 1
src/datamodel_code_generator/parser/base.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/datamodel_code_generator/parser/base.py b/src/datamodel_code_generator/parser/base.py
--- a/src/datamodel_code_generator/parser/base.py
+++ b/src/datamodel_code_generator/parser/base.py
@@ -686,13 +686,19 @@
         config: ParserConfig | None = None,
         **options: Any,  # NOTE: Subclasses handle config+options merging, so options is typically empty here
     ) -> None:
-        """Initialize the Parser with configuration options."""
-        from datamodel_code_generator.config import ParserConfig  # noqa: PLC0415
+        """Initialize the Parser with configuration options.
 
-        # Subclasses are expected to merge config+options before calling super().__init__()
-        # This branch handles the case when Parser is used directly (e.g., for testing)
+        Subclasses are expected to construct and merge configuration before calling
+        this initializer. The `options` argument is kept for backwards
+        compatibility but is not used here.
+        """
         if config is None:
-            config = ParserConfig.from_options(options)
+            msg = (
+                "Parser.__init__ requires a non-None config instance. "
+                "Subclasses should construct the appropriate ParserConfig "
+                "before calling super().__init__."
+            )
+            raise ValueError(msg)
 
         extra_template_data: defaultdict[str, Any] | None = config.extra_template_data
         if extra_template_data is not None and not isinstance(extra_template_data, defaultdict):
EOF
@@ -686,13 +686,19 @@
config: ParserConfig | None = None,
**options: Any, # NOTE: Subclasses handle config+options merging, so options is typically empty here
) -> None:
"""Initialize the Parser with configuration options."""
from datamodel_code_generator.config import ParserConfig # noqa: PLC0415
"""Initialize the Parser with configuration options.

# Subclasses are expected to merge config+options before calling super().__init__()
# This branch handles the case when Parser is used directly (e.g., for testing)
Subclasses are expected to construct and merge configuration before calling
this initializer. The `options` argument is kept for backwards
compatibility but is not used here.
"""
if config is None:
config = ParserConfig.from_options(options)
msg = (
"Parser.__init__ requires a non-None config instance. "
"Subclasses should construct the appropriate ParserConfig "
"before calling super().__init__."
)
raise ValueError(msg)

extra_template_data: defaultdict[str, Any] | None = config.extra_template_data
if extra_template_data is not None and not isinstance(extra_template_data, defaultdict):
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 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 b0bacf7 and a497759.

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

Applied to files:

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

Applied to files:

  • src/datamodel_code_generator/parser/base.py
📚 Learning: 2025-12-25T09:23:08.506Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

Applied to files:

  • src/datamodel_code_generator/parser/base.py
  • tests/main/test_public_api_signature_baseline.py
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/base.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

tests/main/test_public_api_signature_baseline.py

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

Remove unused noqa directive

(RUF100)

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

64-64: LGTM! Baseline signatures correctly updated.

The baseline signatures properly reflect the config-driven parser refactor:

  • extra_template_data now uses defaultdict for auto-vivification
  • aliases supports both single strings and lists
  • Parser constructor accepts config and **options

Also applies to: 69-69, 182-183


572-701: Excellent test coverage for config-driven parser initialization.

These tests comprehensively verify that parser subclasses correctly accept and apply ParserConfig variants:

  • JsonSchemaParser with ParserConfig
  • OpenAPIParser with OpenAPIParserConfig
  • GraphQLParser with GraphQLParserConfig

The proper use of model_rebuild with type namespace ensures tests work correctly with Pydantic v2.


703-865: Strong validation of config+options merge semantics.

These tests ensure the critical behavior where options override config values:

  • Config sets baseline values
  • Options parameters take precedence
  • extra_template_data automatically converts from dict to defaultdict
  • Defaultdict behavior works correctly (returns empty dict for missing keys)

This validates backward compatibility while supporting the new config-driven API.

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

74-77: LGTM! Proper circular import avoidance.

The TYPE_CHECKING import of ParserConfig combined with runtime import at line 690 correctly avoids circular import issues while maintaining type safety.


690-690: Note: The noqa: PLC0415 directive is intentional.

Despite Ruff's RUF100 warning, this defensive # noqa: PLC0415 should be retained. Based on learnings, defensive noqa directives on lazy imports are kept even when reported as unused, to prepare for potential future Ruff configuration changes that might enable the import-outside-toplevel rule.


690-699: Well-designed config initialization logic.

The initialization correctly handles multiple scenarios:

  • Runtime import breaks circular dependencies
  • Falls back to creating config from options when Parser is used directly
  • Converts regular dict to defaultdict for backward compatibility
  • Subclasses handle config+options merging before calling super()

This provides a clean migration path while maintaining backward compatibility.


701-883: Comprehensive config-driven initialization.

The attribute initialization is well-structured:

  • All ~90 parser attributes correctly initialized from config
  • DataTypeManager instantiated with appropriate config parameters
  • Imports properly handle use_exact_imports and additional_imports
  • ModelResolver receives all necessary config options
  • Generic base class and template data configurations applied correctly

The migration from explicit keyword parameters to config-driven initialization is complete and correct.

"""

def __init__( # noqa: PLR0912, PLR0913, PLR0915
def __init__( # noqa: PLR0912, PLR0915
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directives.

The noqa directives for PLR0912 (too-many-branches) and PLR0915 (too-many-statements) are unnecessary as these rules are not enabled in your linting configuration. This was previously marked as addressed but the directives remain.

🔎 Proposed fix
-    def __init__(  # noqa: PLR0912, PLR0915
+    def __init__(
         self,
         source: str | Path | list[Path] | ParseResult | dict[str, YamlValue],
🧰 Tools
🪛 Ruff (0.14.10)

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

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In src/datamodel_code_generator/parser/base.py around line 682, the __init__
definition contains an unused "  # noqa: PLR0912, PLR0915" directive; remove the
noqa comment from the def __init__ line so the signature reads normally without
the trailing noqa, and commit the change (no other code changes required).

parts = [p.strip() for p in type_str.split(" | ")]
return " | ".join(sorted(parts))
return type_str
def _normalize_union_str(type_str: str) -> str: # noqa: PLR0912
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directive.

The noqa: PLR0912 directive is unnecessary as the PLR0912 rule (too-many-branches) is not enabled in your linting configuration.

🔎 Proposed fix
-def _normalize_union_str(type_str: str) -> str:  # noqa: PLR0912
+def _normalize_union_str(type_str: str) -> str:
     """Normalize a union type string by sorting its components recursively.
🧰 Tools
🪛 Ruff (0.14.10)

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

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In tests/main/test_public_api_signature_baseline.py around line 233, the
function definition contains an unnecessary noqa directive ("# noqa: PLR0912");
remove the unused directive from the function signature so it becomes a normal
def _normalize_union_str(type_str: str) -> str: and run tests/lint to confirm no
warnings remain.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/datamodel_code_generator/config.py (1)

251-251: Verify backward compatibility of the aliases type extension.

Same type extension as GenerateConfig.aliases (line 89). This verification comment applies to both fields.

📜 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 a497759 and 507c33f.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/config.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/config.py (3)
src/datamodel_code_generator/util.py (1)
  • is_pydantic_v2 (52-57)
src/datamodel_code_generator/enums.py (1)
  • StrictTypes (213-220)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • model_rebuild (215-217)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/config.py

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

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

11-11: LGTM!

The Self import is correctly used for the self-referential return type in the from_options classmethod.


347-353: LGTM!

The OpenAPIParserConfig class is well-structured with appropriate OpenAPI-specific fields. The inheritance from ParserConfig follows the config-driven pattern described in the PR.


355-360: LGTM!

The GraphQLParserConfig class follows the same pattern as OpenAPIParserConfig with appropriate GraphQL-specific fields for data model types.

snake_case_field: bool = False
strip_default_none: bool = False
aliases: Mapping[str, str] | None = None
aliases: Mapping[str, str | list[str]] | None = None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find all usages of the aliases field and its access patterns
rg -nP --type=py 'aliases' -C3 | head -200

Repository: koxudaxi/datamodel-code-generator

Length of output: 17256


🏁 Script executed:

# Check the watch.py function signature and how it uses aliases
cat -n src/datamodel_code_generator/watch.py | head -60

Repository: koxudaxi/datamodel-code-generator

Length of output: 2632


🏁 Script executed:

# Check the generate function signature that watch.py calls
rg -n 'def generate' src/datamodel_code_generator/__init__.py -A 20 | head -80

Repository: koxudaxi/datamodel-code-generator

Length of output: 1141


Fix type signature mismatch in watch.py for aliases parameter.

The watch_and_regenerate function declares aliases: dict[str, str] | None (line 26) but should be dict[str, str | list[str]] | None to match the updated signature in generate() and the configuration classes. The type must support both str and list[str] values to maintain consistency with the extended type in GenerateConfig and ParserConfig.

🤖 Prompt for AI Agents
In src/datamodel_code_generator/config.py around line 89 and in
src/datamodel_code_generator/watch.py around line 26, the type for aliases must
allow both string and list[string]; update the watch.py parameter and any
related annotations from dict[str, str] | None to dict[str, str | list[str]] |
None (or the equivalent Mapping/typing form used across the project) so it
matches GenerateConfig/ParserConfig; ensure any usages or type checks that
assume a simple str are adjusted to handle list[str] as well and update imports
(typing.Mapping or list union) if required.

"""Pydantic v1 model config."""

extra = "forbid"
extra = "allow"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Inconsistent extra field handling between Pydantic v1 and v2.

Pydantic v2 config (line 199) has extra="forbid", but this change sets extra="allow" for v1 (line 205). This creates version-dependent validation behavior where:

  • v1 silently accepts extra/invalid fields
  • v2 rejects them with a validation error

This inconsistency undermines cross-version compatibility and could mask typos or incorrect field names when running on v1.

🔎 Proposed fix
-            extra = "allow"
+            extra = "forbid"

If extra fields are needed for the config-merging pattern described in the PR, consider handling them explicitly in from_options rather than changing the model's validation behavior.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extra = "allow"
extra = "forbid"
🤖 Prompt for AI Agents
In src/datamodel_code_generator/config.py around line 205, the change sets
Pydantic v1 model config extra="allow" which conflicts with the v2 config
extra="forbid" at ~line 199 and creates version-dependent behavior; revert the
v1 config to extra="forbid" to match v2, and if the codebase needs to accept or
merge extra fields implement explicit handling inside the classmethod
from_options (collect/populate allowed extras there) rather than changing the
model's validation behavior so both Pydantic versions reject unknown fields by
default.

Comment on lines +212 to +216
from datamodel_code_generator.model.base import ( # noqa: PLC0415
DataModel,
DataModelFieldBase,
)
from datamodel_code_generator.types import DataTypeManager, StrictTypes # noqa: PLC0415
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused noqa directives.

The noqa: PLC0415 directives on lines 212 and 216 are not needed, as indicated by static analysis.

🔎 Proposed fix
-            from datamodel_code_generator.model.base import (  # noqa: PLC0415
+            from datamodel_code_generator.model.base import (
                 DataModel,
                 DataModelFieldBase,
             )
-            from datamodel_code_generator.types import DataTypeManager, StrictTypes  # noqa: PLC0415
+            from datamodel_code_generator.types import DataTypeManager, StrictTypes
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from datamodel_code_generator.model.base import ( # noqa: PLC0415
DataModel,
DataModelFieldBase,
)
from datamodel_code_generator.types import DataTypeManager, StrictTypes # noqa: PLC0415
from datamodel_code_generator.model.base import (
DataModel,
DataModelFieldBase,
)
from datamodel_code_generator.types import DataTypeManager, StrictTypes
🧰 Tools
🪛 Ruff (0.14.10)

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

Remove unused noqa directive

(RUF100)


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

Remove unused noqa directive

(RUF100)

🤖 Prompt for AI Agents
In src/datamodel_code_generator/config.py around lines 212 to 216, the trailing
"noqa: PLC0415" directives on the two import lines are unnecessary; remove the "
# noqa: PLC0415" suffixes from the import statements so the imports remain the
same but without the unused noqa directives, keeping the lines otherwise
unchanged.

Comment on lines +227 to +233
# For Pydantic v1, use construct() to skip validation (forward refs not resolved)
defaults: dict[str, Any] = {}
for field_name, field in cls.__fields__.items(): # type: ignore[attr-defined]
# Note: ParserConfig fields don't use default_factory, so we only handle static defaults
defaults[field_name] = field.default
defaults.update(options)
return cls.construct(**defaults) # type: ignore[return-value]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pydantic v1 path bypasses validation entirely using construct().

The Pydantic v1 code path uses construct() to skip validation due to unresolved forward references. However, this bypasses ALL validation, not just forward-ref resolution, which could allow invalid or inconsistent configuration objects to be created.

Consider alternative approaches:

  • Use parse_obj() or model_validate() equivalents that perform validation
  • Pre-resolve the required types before validation (similar to the v2 approach)
  • Document why validation must be skipped and the risks involved
🤖 Prompt for AI Agents
In src/datamodel_code_generator/config.py around lines 227 to 233, the Pydantic
v1 path currently uses cls.construct(...) which skips all validation; instead,
call cls.update_forward_refs() to resolve forward references and then
instantiate with validation (e.g., use cls.parse_obj(defaults) or
cls(**defaults)) so validation runs; also catch and re-raise or log
pydantic.ValidationError with a clear message to preserve existing error
behavior.

@koxudaxi koxudaxi closed this Dec 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants