Skip to content

Add --type-overrides option to replace schema types with custom Python types#2758

Merged
koxudaxi merged 1 commit intomainfrom
feature/type-overrides
Dec 23, 2025
Merged

Add --type-overrides option to replace schema types with custom Python types#2758
koxudaxi merged 1 commit intomainfrom
feature/type-overrides

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Dec 23, 2025

Fixes: #2243

Summary by CodeRabbit

  • New Features
    • Added --type-overrides CLI option to customize type mappings during code generation, supporting both model-level and scoped field-level overrides. Users can now map types to custom implementations via JSON-formatted configuration.

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.47%. Comparing base (4579ce8) to head (f9885ea).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2758      +/-   ##
==========================================
+ Coverage   99.37%   99.47%   +0.10%     
==========================================
  Files          83       83              
  Lines       12233    12537     +304     
  Branches     1467     1498      +31     
==========================================
+ Hits        12156    12471     +315     
+ Misses         45       35      -10     
+ Partials       32       31       -1     
Flag Coverage Δ
unittests 99.47% <100.00%> (+0.10%) ⬆️

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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 23, 2025

Warning

Rate limit exceeded

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 6916fd7 and f9885ea.

⛔ Files ignored due to path filters (3)
  • tests/data/jsonschema/type_overrides_nested.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/type_overrides_scoped.json is excluded by !tests/data/**/*.json and included by none
  • tests/data/jsonschema/type_overrides_test.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (15)
  • docs/cli-reference/index.md
  • docs/cli-reference/quick-reference.md
  • docs/cli-reference/typing-customization.md
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • tests/data/expected/main/type_overrides_model_level.py
  • tests/data/expected/main/type_overrides_nested_types.py
  • tests/data/expected/main/type_overrides_scoped.py
  • tests/main/test_main_general.py

Walkthrough

A new type_overrides feature allows mapping generated model types to custom Python imports via CLI, config, and code API. The parameter flows from CLI argument parsing through configuration and parser initialization, then applies transformations to model fields during code generation. Implementation spans CLI options, parser base class logic, and parser subclass signatures.

Changes

Cohort / File(s) Summary
CLI & Argument Parsing
src/datamodel_code_generator/arguments.py, src/datamodel_code_generator/cli_options.py
Added --type-overrides CLI option with JSON parsing validator (_type_overrides function) and CLI metadata entry for documentation generation.
Public API & Configuration
src/datamodel_code_generator/__init__.py, src/datamodel_code_generator/__main__.py
Extended generate() function signature and Config dataclass with new type_overrides: dict[str, str] | None parameter; threaded through to parser invocation.
Parser Implementation
src/datamodel_code_generator/parser/base.py
Core logic: added type_overrides parameter to Parser.__init__, built _type_override_imports mapping, and introduced override processing methods (__remove_overridden_models, __apply_type_overrides, _apply_override_to_field, _apply_override_to_data_type). Integrated override application into _process_single_module pipeline.
Parser Subclass Signatures
src/datamodel_code_generator/parser/{graphql,jsonschema,openapi}.py
Updated __init__ signatures to accept type_overrides parameter and forward to parent class via super().__init__().
Test Coverage
tests/main/test_main_general.py
Added comprehensive tests for _type_overrides validator, CLI integration, model-level and scoped override scenarios, nested types, and error handling.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as CLI/Arguments
    participant Config as Config
    participant Parser as Parser.\_\_init\_\_
    participant ProcessModule as Parser.\_process_single_module
    participant Override as Override Logic

    User->>CLI: --type-overrides '{"ModelA": "pkg.Type"}'
    CLI->>CLI: _type_overrides() validates JSON
    CLI->>Config: type_overrides stored in Config
    Config->>Parser: type_overrides passed to Parser.__init__
    Parser->>Parser: _type_override_imports = Import.from_full_path()
    Parser->>ProcessModule: Process models normally
    ProcessModule->>Override: Call __remove_overridden_models()
    Override->>Override: Filter models by override keys
    ProcessModule->>Override: Call __apply_type_overrides()
    Override->>Override: Apply scoped (ClassName.field) overrides to fields
    Override->>Override: Apply model-level overrides to DataType
    Override->>Override: Set Import references and clear nested types
    ProcessModule->>Parser: Return processed models with custom types
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and clearly summarizes the main feature: adding a --type-overrides CLI option to replace schema types with custom Python types.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #2243: JSON-based type override mappings, CLI option support, and automatic type replacement in generated models.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the type-overrides feature across the codebase with no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

❤️ Share

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

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Dec 23, 2025

CodSpeed Performance Report

Merging #2758 will not alter performance

Comparing feature/type-overrides (f9885ea) with main (7b5f08f)1

Summary

✅ 73 untouched
⏩ 10 skipped2

Footnotes

  1. No successful run was found on main (525166f) during the generation of this report, so 7b5f08f was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

67-86: _type_overrides validation looks solid; consider clarifying CLI semantics and input form

The JSON parsing and validation for _type_overrides are correct and consistent with _dataclass_arguments (good error messages and strict str -> str mapping). The --type-overrides option wiring also looks fine.

Two points to double‑check:

  1. The option currently expects an inline JSON string (e.g. --type-overrides '{"Foo": "pkg.Type"}'), whereas the original issue text suggested a JSON file similar to --aliases. If file-based usage is still desired, you may want to add a Path-based variant or explicit docs that this is inline JSON only.
  2. The help text mentions scoped keys like "User.field", but the implementation later uses model.class_name and field.name. If users are expected to key by the Python field name (after snake-casing / renaming) rather than the original schema property name, it’s worth calling that out explicitly in the help to avoid confusion.

Also applies to: 533-541

src/datamodel_code_generator/__main__.py (1)

473-474: Config plumbing for type_overrides is correct; consider adding TOML support for dict-valued options

The new Config.type_overrides: Optional[dict[str, str]] field and its propagation via run_generate_from_config(..., type_overrides=config.type_overrides, ...) into generate() look correct and align with the argparse _type_overrides parser and pyproject key normalization (type-overridestype_overrides).

One follow‑up improvement: generate_pyproject_config() currently formats values via _format_toml_value, which only handles bool, str, and list/tuple types. For type_overrides (and similarly dataclass_arguments), this will serialize the mapping as a TOML array of keys rather than a proper inline table, leading to unusable config if the user runs:

datamodel-codegen --type-overrides '{"Foo": "pkg.Type"}' --generate-pyproject-config

It would be worth extending _format_toml_value to support dict[str, TomlValue] (emitting { key = value, ... }) and updating TomlValue accordingly, so that type_overrides round-trips cleanly into pyproject.toml.

Also applies to: 784-785

📜 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 d8e2fef and 6916fd7.

⛔ Files ignored due to path filters (1)
  • tests/data/jsonschema/type_overrides_test.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (9)
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • tests/main/test_main_general.py
🔇 Additional comments (10)
src/datamodel_code_generator/parser/jsonschema.py (1)

616-616: LGTM! Clean parameter propagation for type overrides feature.

The type_overrides parameter is correctly added to the signature and passed through to the base parser. The implementation follows the established pattern used by other configuration parameters like type_mappings.

Also applies to: 718-718

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

195-195: LGTM! Consistent implementation across parsers.

The type_overrides parameter addition mirrors the implementation in JsonSchemaParser, maintaining consistency across parser subclasses. Both parsers correctly delegate the actual override logic to the base Parser class.

Also applies to: 296-296

tests/main/test_main_general.py (4)

21-21: LGTM! Consistent import addition for test validation.

The _type_overrides import follows the same pattern as _dataclass_arguments, enabling validation function testing for the new feature.


439-471: Excellent validation test coverage.

The test cases thoroughly validate the _type_overrides argument parser, covering:

  • Model-level overrides: {"CustomType": "my_app.types.MyType"}
  • Scoped overrides: {"User.field": "my_app.Type"}
  • Multiple mappings and edge cases (empty dict)
  • Comprehensive error scenarios with specific error messages

The test structure mirrors the existing _dataclass_arguments tests, maintaining consistency.


473-546: Well-documented tests highlighting important behavioral distinctions.

The integration tests effectively demonstrate and validate two key scenarios:

  1. Model-level overrides (lines 473-512): Correctly verify that the overridden type model is NOT generated (assert "class CustomType" not in result) while the import IS added. The @pytest.mark.cli_doc with primary=True makes this the canonical documentation example.

  2. Scoped overrides (lines 515-546): The comment at line 542 documents critical behavior: "Address model SHOULD still be generated (scoped override doesn't remove models)". This distinction—that User.address overrides only the field reference, not the model definition—is essential for users to understand.

These tests serve as both validation and documentation of the feature's nuanced behavior.


548-583: Comprehensive coverage of nested type scenarios.

This test validates that type overrides work correctly with container types (List[Tag]), ensuring:

  • Overridden types aren't generated as models (assert "class Tag" not in result)
  • Imports are correctly added (assert "from my_app import Tag" in result)
  • The overridden type is properly referenced in nested contexts (array items)

This completes the test coverage for common override scenarios (model-level, scoped, and nested).

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

279-285: OpenAPIParser correctly forwards type_overrides to the base parser

The new type_overrides parameter is threaded through the OpenAPI parser’s constructor and passed to super().__init__ in the correct position alongside type_mappings, preserving existing behavior when it’s None.

Also applies to: 382-387

src/datamodel_code_generator/__init__.py (1)

497-504: Public generate() API extension for type_overrides is consistent and non-breaking

Adding type_overrides to generate() and forwarding it into the parser constructor is done cleanly and maintains backward compatibility (optional arg, default None). This aligns with the CLI/config plumbing and the base parser’s new parameter.

Also applies to: 749-751

src/datamodel_code_generator/cli_options.py (1)

171-172: CLI metadata for --type-overrides is correctly categorized

--type-overrides is registered under the Typing customization category, matching the argparse group and keeping the docs metadata in sync.

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

776-937: The current implementation correctly registers override imports through the property chain; the proposed fix is unnecessary.

The review's concern appears to be based on incomplete analysis of the import collection flow. When __apply_type_overrides sets data_type.import_ = override_import, these imports are automatically collected through the existing property chain:

  1. data_type.imports property yields self.import_ if set
  2. data_type.all_imports recursively yields from self.imports
  3. field.imports collects from self.data_type.all_imports
  4. model.imports property collects from all field imports
  5. _finalize_modules appends model.imports to the module's imports

Crucially, model.imports is a non-cached property evaluated after __apply_type_overrides has modified the DataTypes in _process_single_module, so the override imports are properly included. Existing tests (test_type_overrides_model_level, test_type_overrides_scoped, test_type_overrides_nested_types) confirm this behavior by expecting the override imports to be present in the generated output.

No changes to the import registration logic are needed. The review's proposed diff, while well-intentioned, introduces unnecessary complexity that the current design already handles correctly through its property-based import collection system.

@koxudaxi koxudaxi force-pushed the feature/type-overrides branch from 6916fd7 to faaa923 Compare December 23, 2025 13:56
@koxudaxi koxudaxi force-pushed the feature/type-overrides branch from faaa923 to f9885ea Compare December 23, 2025 14:15
@koxudaxi koxudaxi enabled auto-merge (squash) December 23, 2025 14:18
@koxudaxi koxudaxi merged commit 10fb940 into main Dec 23, 2025
35 checks passed
@koxudaxi koxudaxi deleted the feature/type-overrides branch December 23, 2025 14:20
@github-actions
Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR adds a new --type-overrides CLI option and corresponding type_overrides parameter to the Python API. All changes are purely additive: (1) The new CLI option is optional and has no effect unless explicitly used. (2) The new type_overrides parameter in generate(), Parser classes, and Config all have default values of None, preserving backward compatibility. (3) No existing options, defaults, or behaviors are modified. (4) No template changes are introduced. (5) Generated code output is unchanged unless users explicitly use the new option. This is a feature addition that does not break any existing functionality.


This analysis was performed by Claude Code Action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type overrides

1 participant