feat: Add --module-split-mode option to generate one file per model (#1170)#2685
feat: Add --module-split-mode option to generate one file per model (#1170)#2685
Conversation
WalkthroughThis PR introduces a Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
🤖 Generated by GitHub Actions
…' into feature/module-split-mode-single
CodSpeed Performance ReportMerging #2685 will not alter performanceComparing Summary
Footnotes
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2685 +/- ##
=======================================
Coverage 99.53% 99.53%
=======================================
Files 81 81
Lines 11334 11358 +24
Branches 1354 1357 +3
=======================================
+ Hits 11281 11305 +24
Misses 32 32
Partials 21 21
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5797470 to
456dab0
Compare
…rity and consistency
🤖 Generated by GitHub Actions
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
docs/cli-reference/general-options.md (1)
17-17: Clarify--module-split-modedefaults and constraintsThe new docs read well and align with the generated example. Two small clarifications would help advanced users:
- Explicitly state that omitting
--module-split-modekeeps the existing behavior (no per-model splitting) and thatsingleis currently the only supported value.- Call out that
--module-split-mode=singlerequires an output directory (not a single file path), since modular output is assumed downstream.These are documentation-only tweaks; the implementation looks fine.
Also applies to: 1676-1784
src/datamodel_code_generator/util.py (1)
10-11: Sharedcamel_to_snakehelper looks correct; consider explicit test coverageThe regex-based implementation and caching are sound and appropriate for reuse across the codebase. To guard against regressions, especially for acronyms (
HTTPResponse→http_response) and mixed alphanumerics, consider adding a small focused test forcamel_to_snakeif one doesn’t already exist via the module-splitting tests.Also applies to: 147-155
src/datamodel_code_generator/__main__.py (1)
24-35: Config/CLI wiring formodule_split_modeis correct; drop the newnoqaThe new
ModuleSplitModeimport,Config.module_split_modefield, and propagation intogenerate()viarun_generate_from_configare consistent with how other enum-style options are handled and look correct.Ruff is flagging the new
# noqa: UP045onmodule_split_modeas an unused directive; unlike the legacy ones, this line is newly introduced in this PR, so it’s easy to keep it clean.Suggested diff to remove the redundant
noqaon the new field- module_split_mode: Optional[ModuleSplitMode] = None # noqa: UP045 + module_split_mode: Optional[ModuleSplitMode] = NoneAlso applies to: 468-468, 663-673, 770-771
src/datamodel_code_generator/parser/base.py (2)
26-35: Module-splitSinglegrouping and import resolution are consistent with existing designThe parser changes for
ModuleSplitMode.Singleare internally coherent:
- Grouping models per file via
module_key()usingcamel_to_snake(data_model.class_name)yields one logical module per class while preserving directory structure (*data_model.module_path).model_path_to_module_namecorrectly decouples a model’s logical module name from its originalmodule_name, so__change_from_import()computes relative imports against the per-class module when split mode is enabled, and falls back to the original behavior otherwise.- The use of
target_full_nameand the existingrelative()/exact_import()helpers ensures that withuse_exact_imports=Trueyou get clean imports likefrom .user import User, matching the new documentation example.- Interaction with circular-import handling and tree-scope reuse appears safe: when models are relocated into
_internal/shared modules, the absence of a mapping causes the code to fall back todata_type.full_name, which is updated with the new path, so imports still resolve correctly.Given the complexity, it’s worth making sure there is test coverage for:
--module-split-mode=singleboth with and without--use-exact-imports, and- combinations with
--reuse-model --reuse-scope=treeand scenarios that previously triggered circular-import resolution.But the current implementation itself looks sound.
Also applies to: 72-72, 1066-1150, 2411-2421, 2454-2463, 2475-2482, 2546-2553
1066-1066: Remove unusednoqacodes on__change_from_importRuff reports the
# noqa: PLR0913, PLR0914on__change_from_importas unused because those rules aren’t enabled. Since this function is already long and complex, keeping the signature as-is is fine, but thenoqaitself is now just noise.Suggested diff to drop the unused
noqa- def __change_from_import( # noqa: PLR0913, PLR0914 + def __change_from_import( self, models: list[DataModel], imports: Imports, scoped_model_resolver: ModelResolver, *, init: bool, internal_modules: set[tuple[str, ...]] | None = None, model_path_to_module_name: dict[str, str] | None = None, ) -> None:
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
docs/cli-reference/general-options.md(2 hunks)docs/cli-reference/index.md(2 hunks)docs/cli-reference/quick-reference.md(2 hunks)src/datamodel_code_generator/__init__.py(4 hunks)src/datamodel_code_generator/__main__.py(3 hunks)src/datamodel_code_generator/arguments.py(2 hunks)src/datamodel_code_generator/cli_options.py(1 hunks)src/datamodel_code_generator/parser/base.py(8 hunks)src/datamodel_code_generator/reference.py(1 hunks)src/datamodel_code_generator/util.py(2 hunks)tests/data/expected/main/jsonschema/module_split_single/__init__.py(1 hunks)tests/data/expected/main/jsonschema/module_split_single/model.py(1 hunks)tests/data/expected/main/jsonschema/module_split_single/order.py(1 hunks)tests/data/expected/main/jsonschema/module_split_single/user.py(1 hunks)tests/data/jsonschema/module_split_single/input.json(1 hunks)tests/main/test_main_general.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
tests/data/expected/main/jsonschema/module_split_single/order.py (2)
src/datamodel_code_generator/util.py (1)
BaseModel(140-144)tests/data/expected/main/jsonschema/module_split_single/user.py (1)
User(11-13)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/__init__.py (1)
ModuleSplitMode(303-309)
src/datamodel_code_generator/arguments.py (1)
src/datamodel_code_generator/__init__.py (1)
ModuleSplitMode(303-309)
tests/data/expected/main/jsonschema/module_split_single/__init__.py (3)
tests/data/expected/main/jsonschema/module_split_single/model.py (1)
Model(11-12)tests/data/expected/main/jsonschema/module_split_single/order.py (1)
Order(13-15)tests/data/expected/main/jsonschema/module_split_single/user.py (1)
User(11-13)
tests/data/expected/main/jsonschema/module_split_single/user.py (1)
src/datamodel_code_generator/model/base.py (1)
name(599-601)
src/datamodel_code_generator/parser/base.py (2)
src/datamodel_code_generator/__init__.py (1)
ModuleSplitMode(303-309)src/datamodel_code_generator/util.py (1)
camel_to_snake(152-155)
src/datamodel_code_generator/reference.py (1)
src/datamodel_code_generator/util.py (1)
camel_to_snake(152-155)
src/datamodel_code_generator/__init__.py (1)
src/datamodel_code_generator/model/enum.py (1)
Enum(39-120)
🪛 Ruff (0.14.8)
src/datamodel_code_generator/__main__.py
468-468: Unused noqa directive (non-enabled: UP045)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/parser/base.py
1066-1066: Unused noqa directive (non-enabled: PLR0913, PLR0914)
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). (4)
- GitHub Check: 3.14 on Windows
- GitHub Check: dev
- GitHub Check: benchmarks
- GitHub Check: Analyze (python)
🔇 Additional comments (10)
src/datamodel_code_generator/reference.py (1)
39-39: LGTM! Good refactoring to centralize utility function.Moving
camel_to_snaketoutil.pyeliminates code duplication and provides caching benefits via@lru_cacheas shown in the relevant code snippets.Also applies to: 284-284
src/datamodel_code_generator/arguments.py (2)
25-25: LGTM! Import follows established pattern.The
ModuleSplitModeimport aligns with other enum imports in this module.
327-332: LGTM! CLI argument definition follows established patterns.The argument structure is consistent with other enum-based options like
--all-exports-collision-strategy. Help text clearly describes the functionality.tests/main/test_main_general.py (1)
1032-1059: LGTM! Test follows established patterns.The test structure is consistent with other CLI tests in this file. The
cli_docmarker provides proper documentation metadata, and the test correctly exercises the module-split functionality with appropriate flag combinations.src/datamodel_code_generator/cli_options.py (1)
209-209: LGTM! Metadata entry correctly structured.The CLI option metadata follows the established pattern and is appropriately categorized as
OptionCategory.GENERAL.docs/cli-reference/quick-reference.md (1)
147-147: LGTM! Documentation entries are clear and correctly formatted.The option is properly documented in both the categorized list and alphabetical index with consistent formatting and appropriate descriptions.
Also applies to: 218-218
tests/data/expected/main/jsonschema/module_split_single/order.py (1)
1-15: Order fixture matches per-model split expectationsThe model definition, type hints, and relative import from
.userall look correct for themodule_split_singlelayout; nothing to change here.docs/cli-reference/index.md (1)
17-18: Index entry for--module-split-modeis consistentThe category count, jump index, and
Msection entry all correctly reference the new option and its anchor. No further changes needed.Also applies to: 22-22, 105-108
tests/data/expected/main/jsonschema/module_split_single/__init__.py (1)
1-14: Re-exporting models from per-file modules looks correctThe generated
__init__.pycleanly re-exportsModel,Order, andUserfrom their respective modules and defines__all__accordingly; this matches the documented split-mode example.src/datamodel_code_generator/__init__.py (1)
303-310:ModuleSplitModeintegration into the public API looks solidDefining
ModuleSplitModehere, threading it throughgenerate()intoparser.parse(), and exporting it via__all__is consistent with the existing enum-based configuration pattern and should be fully backwards-compatible for callers that don’t use the new option.Also applies to: 382-483, 725-732, 860-875
Summary by CodeRabbit
Release Notes
New Features
--module-split-modeCLI option to control how generated model classes are organized in output (single file or separate files per model).Documentation
--module-split-modeoption, including configuration examples and usage guidance.✏️ Tip: You can customize this high-level summary in your review settings.