Improve CLI startup time with lazy imports#2799
Conversation
📝 WalkthroughWalkthroughReplaces module-level PYDANTIC_V2 with runtime Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant CLI as CLI (__main__.py)
participant Init as Top-level (__init__.py)
participant Enums as enums.py
participant Util as util (is_pydantic_v2/get_safe_loader)
participant Pyd as Pydantic (BaseModel/model_dump)
CLI->>CLI: parse args (--version/--help/--generate-prompt)
alt fast-path requested
CLI-->>CLI: print info & exit (no heavy imports)
note right of CLI `#D1F2D6`: lightweight startup path
else normal startup
CLI->>Init: import package
Init->>Enums: import enums (fast, no pydantic)
Init->>Util: call is_pydantic_v2() (lazy)
Util->>Pyd: lazily import pydantic if needed
Util-->>Init: provide ConfigDict/BaseModel proxies
Init->>Pyd: instantiate models / call model_dump (v2 path uses model_dump)
note right of Pyd `#F2E5D1`: model_dump/model_validate invoked conditionally
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used🧠 Learnings (4)📓 Common learnings📚 Learning: 2025-12-25T09:22:57.664ZApplied to files:
📚 Learning: 2025-12-25T09:22:14.661ZApplied to files:
📚 Learning: 2025-12-18T13:43:16.235ZApplied to files:
🧬 Code graph analysis (4)src/datamodel_code_generator/util.py (1)
src/datamodel_code_generator/__init__.py (2)
src/datamodel_code_generator/model/base.py (2)
src/datamodel_code_generator/types.py (3)
🪛 GitHub Check: CodeQLsrc/datamodel_code_generator/__init__.py[notice] 158-158: Unused global variable 🪛 Ruff (0.14.10)src/datamodel_code_generator/util.py36-36: Unused Remove unused (RUF100) 40-40: Unused Remove unused (RUF100) 41-41: Unused Remove unused (RUF100) 54-54: Unused Remove unused (RUF100) 80-80: Unused Remove unused (RUF100) 82-82: Unused Remove unused (RUF100) 197-197: Unused Remove unused (RUF100) 215-215: Unused Remove unused (RUF100) 218-218: Unused Remove unused (RUF100) 232-232: Unused Remove unused (RUF100) src/datamodel_code_generator/__init__.py72-72: Unused Remove unused (RUF100) 95-95: Unused Remove unused (RUF100) 97-97: Unused Remove unused (RUF100) 128-128: Unused Remove unused (RUF100) 130-130: Unused Remove unused (RUF100) 149-149: Unused Remove unused (RUF100) 151-151: Unused Remove unused (RUF100) 918-918: Unused Remove unused (RUF100) src/datamodel_code_generator/model/base.py446-446: Unused Remove unused (RUF100) 483-483: Unused Remove unused (RUF100) 720-720: Unused Remove unused (RUF100) src/datamodel_code_generator/types.py93-93: Unused Remove unused (RUF100) 185-185: Unused Remove unused (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)
🔇 Additional comments (27)
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 |
CodSpeed Performance ReportMerging #2799 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 #2799 +/- ##
==========================================
+ Coverage 99.46% 99.51% +0.04%
==========================================
Files 88 89 +1
Lines 13564 13715 +151
Branches 1600 1613 +13
==========================================
+ Hits 13491 13648 +157
Misses 36 36
+ Partials 37 31 -6
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:
|
There was a problem hiding this comment.
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/model/base.py (1)
428-447: Lazy Jinja2 imports are fine; clean up unusednoqaand consider deduplicating the rebuild block
- Moving
ChoiceLoader,Environment,FileSystemLoader, andselect_autoescapeimports into_get_environmentand_get_environment_with_absolute_pathachieves the lazy-import goal without changing behavior.- Ruff’s RUF100 flags the
# noqa: PLC0415comments on these import lines; since PLC0415 isn’t enabled anymore, those suppressions can be dropped.- The final
if is_pydantic_v2(): ... else: ...block runs the samemodel_rebuildcalls in both branches, so the conditional is redundant and can be simplified to a single branch.Suggested diff for RUF100 and redundant if/else
- from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape # noqa: PLC0415 + from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape @@ - from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape # noqa: PLC0415 + from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape @@ -if is_pydantic_v2(): - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) -else: - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) +_rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} +DataType.model_rebuild(_types_namespace=_rebuild_namespace) +BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) +DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel})Also applies to: 466-477, 493-497
🧹 Nitpick comments (4)
src/datamodel_code_generator/__init__.py (1)
94-94: Clean up unused noqa directives.Ruff reports that the
# noqa: PLC0415directives on these lines are unused because the PLC0415 rule (import outside top-level) is not enabled in your configuration. These can be safely removed.🔎 Remove unused noqa directives
- import yaml # noqa: PLC0415 + import yaml - from datamodel_code_generator.util import SafeLoader # noqa: PLC0415 + from datamodel_code_generator.util import SafeLoaderApply similar changes to lines 129, 150, and 1074.
Also applies to: 96-96, 129-129, 150-150, 1074-1074
src/datamodel_code_generator/__main__.py (1)
7-7: Clean up unused noqa directives.Ruff reports that the
# noqa: PLR2004directive on line 7 and# noqa: PLC0415on line 888 are unused. These can be safely removed since the corresponding rules are not enabled.Also applies to: 888-888
src/datamodel_code_generator/util.py (2)
36-36: Clean up unused noqa directives.Ruff reports that multiple
# noqa: PLC0415and# noqa: PLW0603directives are unused because the corresponding rules are not enabled in your configuration. These can be safely removed.Also applies to: 40-41, 78-78, 81-81, 83-83, 199-199, 221-221, 224-224, 238-238
32-56: Consider documenting the lazy loading strategy.The lazy loading approach with
@lru_cacheand on-demand imports is excellent for performance, but it introduces subtle complexity. Consider adding a brief module-level docstring or comment explaining the lazy loading strategy to help future maintainers understand why imports are scattered and why@lru_cacheis used.Example:
"""Utility functions and Pydantic version compatibility helpers. This module uses lazy loading extensively to minimize CLI startup time: - Version detection is cached with @lru_cache and only happens on first use - Pydantic imports are deferred until needed - Module attributes (BaseModel, SafeLoader) are loaded via __getattr__ """
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/model/base.pysrc/datamodel_code_generator/model/pydantic/__init__.pysrc/datamodel_code_generator/model/pydantic_v2/__init__.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/pydantic_patch.pysrc/datamodel_code_generator/reference.pysrc/datamodel_code_generator/types.pysrc/datamodel_code_generator/util.pytests/main/conftest.pytests/main/test_exec_validation.pytests/main/test_main_general.py
🧰 Additional context used
🧬 Code graph analysis (11)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/util.py (8)
is_pydantic_v2(49-51)model_copy(281-285)model_dump(260-264)model_validate(267-271)model_validator(116-121)model_validator(125-131)model_validator(135-139)model_validator(142-173)
src/datamodel_code_generator/__init__.py (1)
src/datamodel_code_generator/util.py (6)
is_pydantic_v2(49-51)inner(152-152)inner(155-155)inner(158-158)inner(160-171)inner(183-190)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
src/datamodel_code_generator/model/base.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(27-52)src/datamodel_code_generator/util.py (4)
is_pydantic_v2(49-51)model_copy(281-285)model_dump(260-264)model_validate(267-271)
tests/main/test_main_general.py (1)
tests/main/conftest.py (1)
run_main_with_args(215-241)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
src/datamodel_code_generator/reference.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(27-52)src/datamodel_code_generator/util.py (6)
camel_to_snake(254-257)is_pydantic_v2(49-51)model_validator(116-121)model_validator(125-131)model_validator(135-139)model_validator(142-173)
src/datamodel_code_generator/model/pydantic/__init__.py (1)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
tests/main/test_exec_validation.py (1)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
src/datamodel_code_generator/types.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(27-52)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
tests/main/conftest.py (2)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)src/datamodel_code_generator/__init__.py (1)
DataModelType(236-244)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/__main__.py
[error] 1-1: ruff formatting check failed. 2 files were modified by hooks; process exited with code 1. See diff for file changes. The failing step was 'prek run --all-files'.
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__init__.py
94-94: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
96-96: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
127-127: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
129-129: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
148-148: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
150-150: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
1074-1074: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/__main__.py
7-7: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
888-888: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/base.py
431-431: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
468-468: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/pydantic_v2/__init__.py
48-48: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/pydantic/__init__.py
43-43: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/pydantic_patch.py
27-27: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/util.py
36-36: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
40-40: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
41-41: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
78-78: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
81-81: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
83-83: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
199-199: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
221-221: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
224-224: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
238-238: Unused noqa directive (non-enabled: PLW0603)
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). (20)
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.13 on Windows
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.11 on macOS
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (27)
src/datamodel_code_generator/pydantic_patch.py (2)
24-29: Guarded patching logic looks correct and idempotentThe Python ≥3.12 guard and local import of
pydantic.typingmake the patch safe and only applied when needed; repeated calls are harmless.
27-27: Remove unused# noqa: PLC0415to satisfy RuffRuff reports RUF100 here because
PLC0415is not an enabled rule. You can drop the# noqa: PLC0415comment without changing behavior.Suggested diff
- import pydantic.typing # noqa: PLC0415 + import pydantic.typing⛔ Skipped due to learnings
Learnt from: koxudaxi Repo: koxudaxi/datamodel-code-generator PR: 2681 File: tests/cli_doc/test_cli_doc_coverage.py:82-82 Timestamp: 2025-12-18T13:43:16.235Z Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.tests/main/conftest.py (1)
22-22: Importingis_pydantic_v2()keeps test skip logic aligned with runtime Pydantic versionUsing the shared helper instead of a constant matches the new version-detection strategy across the codebase.
tests/main/test_main_general.py (1)
52-56: Updated pysnooper-missing simulation matches lazy import approachSwitching to
mocker.patch.dict("sys.modules", {"pysnooper": None})is consistent with a design that checks forpysnooperpresence via imports orsys.modulesrather than a fixed module attribute, and keeps the tests aligned with the new lazy-import behavior.Also applies to: 58-64
src/datamodel_code_generator/model/pydantic_v2/__init__.py (2)
48-48: Remove unused# noqa: PLC0415to fix Ruff warningSame as in the v1 module, the
PLC0415suppression is not needed and triggers RUF100.Suggested diff
- from datamodel_code_generator.util import is_pydantic_v2 # noqa: PLC0415 + from datamodel_code_generator.util import is_pydantic_v2⛔ Skipped due to learnings
Learnt from: koxudaxi Repo: koxudaxi/datamodel-code-generator PR: 2681 File: tests/cli_doc/test_cli_doc_coverage.py:82-82 Timestamp: 2025-12-18T13:43:16.235Z Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.
46-52: ConfigDictdict()implementation correctly uses Pydantic v2model_dump()The version-agnostic pattern is sound. Pydantic v2 recommends
model_dump()over the deprecateddict()method for dict representation. The# noqa: PLC0415is necessary because the import inside the method is indeed a local import and would trigger the rule without suppression.src/datamodel_code_generator/parser/jsonschema.py (1)
80-89: Pydantic v1/v2 switching in JsonSchemaObject and ConfigDict usage is correctly implemented
- Importing
is_pydantic_v2and gating theConfigDictimport on it avoids importing v2-only symbols under Pydantic v1.- The
JsonSchemaObjecthelpers split cleanly:
- v2 path:
get_fields()returnsmodel_fields- v1 path:
get_fields()returns__fields__andmodel_rebuild()delegates toupdate_forward_refs()- The
model_configvs innerConfigsplit allowsEXCLUDE_FIELD_KEYSto build fromJsonSchemaObject.get_fields()for both versions, and the constant is properly used in__init__to filter extras.This is a reasonable and internally consistent way to centralize Pydantic-version-specific behavior in the parser.
tests/main/test_exec_validation.py (1)
16-18: Runtime Pydantic v2 check for exec-validation tests is correctUsing
is_pydantic_v2()in_SKIP_PYDANTICproperly checks the currently installed Pydantic major version at runtime through dynamic import withinget_pydantic_version(), ensuring tests are automatically skipped on environments without Pydantic v2.src/datamodel_code_generator/model/pydantic/__init__.py (1)
39-47: Version-awaredict()implementation is soundThe lazy import of
is_pydantic_v2inside the method and conditional delegation tomodel_dump(**kwargs)for Pydantic v2 keeps this helper working across versions. The# noqa: PLC0415is correct and necessary—it suppresses the lazy import style violation, which is acceptable here to avoid hard top-level version dependencies.src/datamodel_code_generator/model/base.py (1)
39-40: Pydantic-compatibility and deep-copy changes follow the recommended compatibility layer
- Importing
ConfigDict,is_pydantic_v2, and themodel_*helpers centralizes version-aware behavior and matches Pydantic's recommended approach for dual v1/v2 support.ConstraintsBaseandDataModelFieldBasecorrectly choose betweenmodel_config = ConfigDict(...)(v2) and an innerConfigclass (v1), following Pydantic's migration patterns.- The v1-only
DataModelFieldBase.model_rebuild()override that delegates toupdate_forward_refs()keeps forward-ref behavior compatible with Pydantic v1.has_constraints,merge_constraints, andcopy_deep()all funnel through helper functions (model_dump,model_copy,model_validate) that correctly call v2 methods when available and fall back to v1 equivalents, avoiding direct.dict()/.copy()calls.- The helper functions in
util.pyimplement the recommended shim pattern: calling v2 names (model_dump,model_validate,model_copy) if present, else falling back to v1 names (dict,parse_obj,copy).This approach aligns with Pydantic's official guidance for supporting both major versions without a full migration.
src/datamodel_code_generator/types.py (3)
55-55: LGTM! Runtime version detection enables lazy loading.The switch from importing a static
PYDANTIC_V2constant to the runtime functionis_pydantic_v2()aligns with the PR objective of improving CLI startup time through lazy imports. This change allows the Pydantic version check to be deferred until actually needed.
97-99: LGTM! Conditional import properly gates v2-only code.The runtime check correctly guards the import of Pydantic v2-specific modules (
GetCoreSchemaHandlerandcore_schema), preventing import errors when running with Pydantic v1.
289-295: LGTM! Config selection uses runtime version check.The model configuration now properly uses
is_pydantic_v2()to select between Pydantic v2'smodel_configand v1'sConfigclass at runtime.src/datamodel_code_generator/__init__.py (3)
94-98: LGTM! YAML lazy loading improves startup time.The local import of
yamlandSafeLoaderdefers the YAML library loading untilload_yaml()is actually called, reducing initial import overhead.
125-135: LGTM! Proper error handling for optional pysnooper dependency.The lazy import with a clear error message guides users to install the debug extra when needed, while the exception chaining preserves the original error context.
1074-1074: LGTM! yaml.parser lazy import defers dependency loading.The local import of
yaml.parserreduces initial startup overhead by loading it only when needed for input type inference.src/datamodel_code_generator/__main__.py (3)
7-11: Excellent! Early version check minimizes import overhead.Handling
--version/-Vbefore other imports ensures the fastest possible response for version queries, avoiding the overhead of loading the entire application. This is a best practice for CLI tools.
887-890: LGTM! Conditional argcomplete import reduces startup time.The environment-based guard ensures argcomplete is only imported when tab completion is active (
_ARGCOMPLETEenvironment variable), avoiding unnecessary imports during normal CLI execution.
122-123: LGTM! Runtime version check for Config model.The conditional model configuration properly uses
is_pydantic_v2()to select the appropriate Pydantic version at runtime.src/datamodel_code_generator/reference.py (3)
38-38: LGTM! Import updated for runtime version detection.The addition of
is_pydantic_v2to the import statement supports the transition from static to runtime version checking.
96-115: LGTM! Version-specific dict method selection.The runtime check properly gates Pydantic v2's
model_dumpvs v1'ssuper().dict()implementation, ensuring correct behavior across versions.
168-175: LGTM! Config selection uses runtime check.The model configuration correctly uses
is_pydantic_v2()to choose between v2'smodel_configand v1'sConfigclass.src/datamodel_code_generator/util.py (5)
32-56: LGTM! Version detection with pydantic patch integration.The lazy version detection with
@lru_cacheensures the version is checked only once, and applying the pydantic patch before importing pydantic is the correct order of operations.
75-109: LGTM! Lazy SafeLoader construction with YAML 1.2 compatibility.The lazy loader construction with deep copying prevents mutation of the base loader, and the YAML 1.2 boolean pattern with deprecation warnings provides a smooth migration path.
111-111: LGTM! Forward reference enables lazy BaseModel import.Using a string forward reference
"_BaseModel"in the TypeVar avoids importing BaseModel at module load time, supporting the lazy loading strategy.
195-216: LGTM! ConfigDict proxy enables lazy loading with proper interface.The proxy class correctly delegates both
__call__for instantiation and__class_getitem__for subscripting to the lazily-loaded ConfigDict, maintaining the full interface.
236-246: LGTM! getattr provides clean lazy loading interface.The
__getattr__pattern properly exposesBaseModelandSafeLoaderon-demand while raisingAttributeErrorfor unknown attributes, maintaining expected module behavior.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/datamodel_code_generator/__init__.py (1)
142-158: The_pysnooper_initializedflag name could be clearer.As noted in a previous review, this flag tracks whether the default disabled state has been set, not whether pysnooper is truly initialized. Consider renaming to
_pysnooper_default_state_setfor clarity in a follow-up.The lazy import pattern itself is correct for the startup optimization goal.
🧹 Nitpick comments (1)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
48-54: Consider removing the unused noqa directive.The lazy import of
is_pydantic_v2inside the method supports the startup optimization goal. However, static analysis indicates thenoqa: PLC0415directive on line 50 is unused (the rule isn't enabled).🔎 Suggested fix
def dict(self, **kwargs: Any) -> dict[str, Any]: # type: ignore[override] """Version-compatible dict method for templates.""" - from datamodel_code_generator.util import is_pydantic_v2 # noqa: PLC0415 + from datamodel_code_generator.util import is_pydantic_v2 if is_pydantic_v2(): return self.model_dump(**kwargs) return super().dict(**kwargs)
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/arguments.pysrc/datamodel_code_generator/enums.pysrc/datamodel_code_generator/model/pydantic_v2/__init__.pysrc/datamodel_code_generator/model/pydantic_v2/base_model.pysrc/datamodel_code_generator/types.py
💤 Files with no reviewable changes (1)
- src/datamodel_code_generator/model/pydantic_v2/base_model.py
🧰 Additional context used
🧬 Code graph analysis (4)
src/datamodel_code_generator/__init__.py (3)
src/datamodel_code_generator/enums.py (3)
AllExportsCollisionStrategy(92-102)AllExportsScope(81-89)InputFileType(35-45)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)src/datamodel_code_generator/model/type_alias.py (1)
TypeAlias(37-42)
src/datamodel_code_generator/arguments.py (1)
src/datamodel_code_generator/enums.py (3)
StrictTypes(199-206)TargetPydanticVersion(181-189)UnionMode(192-196)
src/datamodel_code_generator/__main__.py (1)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
src/datamodel_code_generator/types.py (4)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)src/datamodel_code_generator/model/base.py (2)
name(769-771)DataModelFieldBase(124-425)src/datamodel_code_generator/enums.py (1)
StrictTypes(199-206)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__init__.py
72-72: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
95-95: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
97-97: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
128-128: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
130-130: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
149-149: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
151-151: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
918-918: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/pydantic_v2/__init__.py
50-50: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/__main__.py
7-7: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
10-10: Unused noqa directive (non-enabled: T201)
Remove unused noqa directive
(RUF100)
13-13: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
894-894: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/types.py
93-93: 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). (19)
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: 3.12 on macOS
- GitHub Check: 3.11 on macOS
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (16)
src/datamodel_code_generator/arguments.py (1)
17-35: LGTM - Import reorganization to centralized enums module.The imports of
StrictTypesandUnionModenow come from the centralizeddatamodel_code_generator.enumsmodule, which aligns with the PR's goal of improving CLI startup time by separating enums from heavy pydantic imports.src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
13-15: LGTM - Import reorganization for centralized enums.The
UnionModeimport is now sourced from the centralized enums module, whileBaseModelandDataModelFieldremain from the local.base_modelmodule.src/datamodel_code_generator/__main__.py (3)
5-17: Effective early exit optimization for CLI startup.The early exit handling for
--versionand--helpbefore importing heavy dependencies is an effective optimization. This avoids loading pydantic and other dependencies when users just want version info or help text.Note: The GitHub security scan flagged the print statement at module level, but this is intentional behavior for the early exit path.
128-129: Verify class-level conditional works correctly at import time.The
is_pydantic_v2()check at class body level is evaluated once when the module is imported. This is correct for defining different class structures based on Pydantic version, but ensure the utility function is stable and doesn't cause import-time side effects.The same pattern is used at line 316 for validator definitions.
893-896: LGTM - Environment-guarded argcomplete loading.The argcomplete import and autocomplete call are now guarded by checking for
_ARGCOMPLETEenvironment variable, which is set by argcomplete during shell completion. This avoids unnecessary imports during normal CLI execution.src/datamodel_code_generator/types.py (4)
55-55: LGTM - Import updated to use runtime check function.The import now uses
is_pydantic_v2function instead of the staticPYDANTIC_V2constant, enabling runtime version detection.
89-98: Good backwards compatibility mechanism via__getattr__.The module-level
__getattr__provides lazy access toStrictTypesfor code that imports it from this module, maintaining backwards compatibility while the enum has been moved to the centralized enums module. This avoids breaking existing imports likefrom datamodel_code_generator.types import StrictTypes.
100-111: LGTM - Conditional imports based on Pydantic version.The
StrictTypesimport underTYPE_CHECKINGsupports type hints without runtime import, and the conditional import ofGetCoreSchemaHandlerandcore_schemaonly occurs when Pydantic v2 is installed.
291-315: LGTM - DataType class configuration conditional.The
is_pydantic_v2()check correctly configuresmodel_configfor Pydantic v2 or provides a fallbackConfigclass for Pydantic v1. This maintains compatibility with both versions.src/datamodel_code_generator/enums.py (3)
1-12: Well-structured centralized enums module.This new module effectively separates all enum definitions from pydantic-heavy modules, which is the core of the startup time optimization. The minimal imports (only
enum,typing, andtyping_extensions) ensure this module loads quickly.
192-206: Note: Naming convention differs forUnionModeandStrictTypes.These two enums use lowercase member names (
smart,left_to_right,str,bytes, etc.) while other enums in this file use PascalCase (Schemas,Module,Error). This appears intentional to match the Pydantic v2 API values and CLI argument values respectively.
209-230: LGTM - Comprehensive and well-organized__all__export.The export list is alphabetically sorted and includes all public entities (constants, TypedDict, and Enums).
src/datamodel_code_generator/__init__.py (4)
26-47: LGTM - Centralized enum imports.All enum classes are now imported from
datamodel_code_generator.enums, which is the core of the startup optimization. This avoids loading pydantic when only enum definitions are needed.
71-80: LGTM - Runtime Pydantic version check for YamlValue type.The conditional type definition handles the difference between Pydantic v1 and v2's handling of
TypeAliasType. Pydantic v1 requires the fallback toAnyfor recursive type parts.
93-99: LGTM - Lazy imports inload_yaml.Moving the
yamlandSafeLoaderimports inside the function reduces top-level import time. This is called on-demand rather than at module import.
916-918: LGTM - Lazy import for yaml.parser ininfer_input_type.The
yaml.parserimport is deferred untilinfer_input_typeis actually called, supporting the startup optimization.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/datamodel_code_generator/format.py (1)
177-177: Clean up unusednoqadirective.The static analysis tool indicates that the suppressed codes (
PLR0912,PLR0913,PLR0915,PLR0917) are not enabled in your Ruff configuration, making the directive unnecessary.🔎 Proposed fix
- def __init__( # noqa: PLR0912, PLR0913, PLR0915, PLR0917 + def __init__(src/datamodel_code_generator/types.py (1)
93-93: Consider removing unused noqa directives.The static analysis indicates these
noqadirectives are for rules that aren't enabled in your Ruff configuration:
- Line 93:
noqa: PLC0415(import-outside-toplevel)- Line 190:
noqa: PLR2004(magic-value-comparison)While the directives don't cause harm, removing them would clean up the code slightly.
Proposed cleanup
def __getattr__(name: str) -> Any: """Provide lazy access to StrictTypes for backwards compatibility.""" if name == "StrictTypes": - from datamodel_code_generator.enums import StrictTypes # noqa: PLC0415 + from datamodel_code_generator.enums import StrictTypes return StrictTypesif n == 1: return tuple(iterables[0]) - if n == 2: # noqa: PLR2004 + if n == 2: return (*iterables[0], *iterables[1])Also applies to: 190-190
src/datamodel_code_generator/__main__.py (1)
7-7: Clean up unused noqa directives.Static analysis indicates that the
noqadirectives on these lines suppress rules that are not enabled in your ruff configuration:
- Lines 7, 13:
PLR2004(magic value comparison)- Lines 10, 28:
T201(print statement)- Line 906:
PLC0415(import outside top-level)Removing these unused directives would improve code clarity.
🔎 Proposed cleanup
-if len(sys.argv) == 2 and sys.argv[1] in {"--version", "-V"}: # noqa: PLR2004 +if len(sys.argv) == 2 and sys.argv[1] in {"--version", "-V"}: from importlib.metadata import version - print(f"datamodel-codegen {version('datamodel-code-generator')}") # noqa: T201 + print(f"datamodel-codegen {version('datamodel-code-generator')}") sys.exit(0) -if len(sys.argv) == 2 and sys.argv[1] in {"--help", "-h"}: # noqa: PLR2004 +if len(sys.argv) == 2 and sys.argv[1] in {"--help", "-h"}: from datamodel_code_generator.arguments import arg_parser arg_parser.print_help() sys.exit(0) - print(prompt_output) # noqa: T201 + print(prompt_output) sys.exit(0) - import argcomplete # noqa: PLC0415 + import argcompleteAlso applies to: 10-10, 13-13, 28-28, 906-906
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/format.pysrc/datamodel_code_generator/types.py
🧰 Additional context used
🧬 Code graph analysis (3)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/prompt.py (1)
generate_prompt(76-128)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)
src/datamodel_code_generator/types.py (4)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(49-51)src/datamodel_code_generator/model/base.py (1)
name(769-771)src/datamodel_code_generator/enums.py (1)
StrictTypes(199-206)
src/datamodel_code_generator/format.py (1)
src/datamodel_code_generator/util.py (1)
load_toml(26-29)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
7-7: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
10-10: Unused noqa directive (non-enabled: T201)
Remove unused noqa directive
(RUF100)
13-13: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
28-28: Unused noqa directive (non-enabled: T201)
Remove unused noqa directive
(RUF100)
906-906: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/types.py
93-93: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
190-190: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/format.py
177-177: Unused noqa directive (non-enabled: PLR0912, PLR0913, PLR0915, PLR0917)
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). (20)
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: Analyze (python)
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.14 on macOS
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.14 on Ubuntu
- GitHub Check: 3.12 on macOS
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: benchmarks
🔇 Additional comments (11)
src/datamodel_code_generator/format.py (4)
203-206: LGTM: Early instance variable assignment enables lazy loading.Storing these configuration values upfront allows the conditional formatter initialization logic to reference them cleanly.
208-209: LGTM: Conditional formatter enablement supports startup optimization.The
use_blackanduse_isortflags cleanly gate the initialization of formatter configurations, aligning with the PR's goal of reducing startup time by avoiding unnecessary imports and configuration loading.
211-254: LGTM: Conditional Black initialization improves startup performance.The lazy loading of Black configuration (including deferred
pyproject.tomlparsing) directly supports the PR objective. The initialization is properly gated by theuse_blackflag, andformat_codeensuresapply_blackis only invoked whenFormatter.BLACKis in the active formatters list.Note: Line 254's
type: ignore[assignment]suggestsself.black_modemay be typed as non-optional. While the runtime safety is ensured by the formatter checks, consider whether the type annotation should explicitly allowNonefor clarity.
256-268: LGTM: Conditional Isort initialization mirrors Black's lazy loading pattern.The symmetric approach for Isort configuration loading is clean and consistent. The
apply_isortmethod already handles theNonecase forself.isort_config(line 389), ensuring backward compatibility with isort 4.x while supporting the lazy loading optimization.src/datamodel_code_generator/types.py (5)
55-55: LGTM: Import updated to use runtime version check.The change from a static
PYDANTIC_V2constant to theis_pydantic_v2()function aligns with the PR's goal of improving startup time through lazy imports.
89-98: Well-implemented lazy import mechanism.The module-level
__getattr__follows PEP 562 and correctly provides lazy access toStrictTypes, deferring its import until actually needed. This directly supports the PR's startup performance goals.
100-106: Correct use of TYPE_CHECKING for type hints.The TYPE_CHECKING import ensures type checkers can resolve
StrictTypesreferences without triggering a runtime import, properly complementing the lazy__getattr__mechanism.
109-111: Runtime check correctly replaces static constant.The runtime
is_pydantic_v2()check correctly replaces the previous staticPYDANTIC_V2constant. While this is evaluated during class definition at import time, it enables the lazy import pattern used elsewhere in the PR.
180-193: Good micro-optimization for common cases.The
chain_as_tupleoptimization avoids the overhead ofitertools.chain()for the common cases of 0-2 iterables by using direct tuple construction and unpacking. This is a sensible performance improvement.src/datamodel_code_generator/__main__.py (2)
87-87: LGTM! Runtime pydantic version detection supports lazy imports.The replacement of the static
PYDANTIC_V2constant with the runtimeis_pydantic_v2()function enables lazy evaluation and avoids eager imports at module load time, which aligns well with the PR's objective to improve CLI startup performance.Also applies to: 140-140, 328-328
905-908: LGTM! Environment-guarded argcomplete import improves startup time.The conditional import of
argcompletebased on the_ARGCOMPLETEenvironment variable ensures the module is only loaded during shell completion, avoiding unnecessary overhead during normal CLI execution. This is an effective lazy-loading pattern that directly supports the PR's performance goals.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/datamodel_code_generator/model/base.py (2)
437-438: LGTM: Lazy imports improve startup time.Moving jinja2 imports inside cached functions defers the import until template rendering is needed. Since these functions use
@lru_cache, the import overhead is only paid once.Remove unused noqa directives
The static analysis correctly identifies that
PLC0415is not enabled, so the noqa comments are unnecessary:- from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape # noqa: PLC0415 + from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescapeApply the same change at lines 474-475 and 711.
Also applies to: 474-475
892-901: Consider unifying identical conditional branches.Both the
if is_pydantic_v2():andelse:branches execute identical code. Unless this is anticipating future divergence, the conditional could be simplified:Simplify by removing redundant conditional
-if is_pydantic_v2(): - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) -else: - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) +_rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} +DataType.model_rebuild(_types_namespace=_rebuild_namespace) +BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) +DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel})However, if this conditional is preparing for version-specific namespace requirements, keep it as-is.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/datamodel_code_generator/model/base.pysrc/datamodel_code_generator/model/pydantic/base_model.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/parser/openapi.py
🚧 Files skipped from review as they are similar to previous changes (1)
- src/datamodel_code_generator/parser/jsonschema.py
🧰 Additional context used
🧬 Code graph analysis (3)
src/datamodel_code_generator/parser/openapi.py (1)
src/datamodel_code_generator/util.py (1)
model_dump(260-264)
src/datamodel_code_generator/model/pydantic/base_model.py (7)
src/datamodel_code_generator/model/base.py (3)
nullable(863-865)imports(288-313)imports(771-776)src/datamodel_code_generator/types.py (2)
nullable(293-295)imports(481-526)src/datamodel_code_generator/model/dataclass.py (1)
imports(139-144)src/datamodel_code_generator/model/pydantic_v2/types.py (1)
imports(62-67)src/datamodel_code_generator/model/enum.py (1)
imports(119-121)src/datamodel_code_generator/model/typed_dict.py (1)
imports(150-155)src/datamodel_code_generator/model/type_alias.py (1)
imports(28-34)
src/datamodel_code_generator/model/base.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (4)
is_pydantic_v2(49-51)model_copy(281-285)model_dump(260-264)model_validate(267-271)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/model/pydantic/base_model.py
[error] 267-273: ruff-format: formatting changes were made by the hook. The pre-commit hook reformatted the file and CI failed due to the formatting change. Re-run the command to apply fixes: 'prek run --all-files --show-diff-on-failure --skip readme'.
🪛 Ruff (0.14.10)
src/datamodel_code_generator/model/base.py
437-437: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
474-474: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
711-711: 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). (20)
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.12 on macOS
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.14 on Ubuntu
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.14 on macOS
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (7)
src/datamodel_code_generator/model/base.py (5)
287-313: LGTM: Fast-path optimization improves performance.The early return when no special typing imports are needed (Union, Optional, Annotated) avoids the overhead of import filtering and iterator operations. The logic correctly identifies the common case where fields only need their data type's imports.
674-674: LGTM: Caching prevents redundant expensive operations.The per-instance cache for
get_dedup_keyis well-designed:
- Cache key
(class_name, use_default)correctly captures all inputs- Caches the result of expensive
render()andimportscalls- No invalidation needed since models are effectively immutable after creation
- Updated docstring clearly documents the caching behavior
Also applies to: 700-716
79-89: LGTM: Runtime version checks enable lazy pydantic imports.Replacing static
PYDANTIC_V2with runtimeis_pydantic_v2()checks allows pydantic imports to be deferred. The conditional logic maintains backward compatibility with both v1 and v2 configurations.Also applies to: 127-137
169-179: LGTM: Provides consistent model_rebuild API across pydantic versions.The compatibility shim for v1's
update_forward_refsasmodel_rebuildis well-implemented:
- Only defined when not using v2 (avoids overriding native method)
- Correctly translates
_types_namespaceto v1'slocalns- Maintains API consistency across the codebase
46-47: LGTM: TYPE_CHECKING guard reduces runtime imports.Moving jinja2 type imports under
TYPE_CHECKINGis the correct pattern. These types are only needed for static analysis and are not required at runtime, contributing to faster startup.src/datamodel_code_generator/parser/openapi.py (1)
755-755: The patternmodel_dump(object_schema, exclude_none=True)is already established throughout the codebase and presents no risk. This identical approach is used in jsonschema.py at lines 1084, 2617, 2753, and 2820. Constraint fields are all optional with None defaults; downstream code accesses them via attribute checks or dictionary assignment after serialization, neither of which depends on explicit None values in the output dictionary. No breaking changes are expected.src/datamodel_code_generator/model/pydantic/base_model.py (1)
269-277: The fast-path conditions are correct and properly cover all scenarios where Field() is not needed.The logic is sound: when a field is required, non-nullable, has no alias, no constraints, and no extras, the
__str__method returns an empty string, makingself.fieldfalsy and thus avoiding the unnecessaryField()import. The concerns aboutnullablebeingFalsevsNoneare unfounded—both evaluate as True innot self.nullable, andField("...")is only generated whennullable=TrueANDrequired=True(line 240-241). For required fields with the fast-path conditions,_computed_default_factoryis alwaysNone(set explicitly at line 220-221), so it cannot introduce unexpectedField()generation.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/datamodel_code_generator/util.py (2)
228-257: Consider simplifying BaseModel caching and documenting for type checkers.The
__getattr__pattern for lazy module-level attributes works correctly but has some considerations:
- The manual caching of
_BaseModel(lines 243, 248, 250-252) could be simplified by relying on the@lru_cacheon_get_base_model_class().- Type checkers may not recognize
BaseModelandSafeLoaderas module exports without explicit__all__or a.pyistub file.🔎 Optional simplification
-_BaseModel: type | None = None - - def __getattr__(name: str) -> Any: """Provide lazy access to BaseModel and SafeLoader.""" - global _BaseModel # noqa: PLW0603 if name == "BaseModel": - if _BaseModel is None: - _BaseModel = _get_base_model_class() - return _BaseModel + return _get_base_model_class() if name == "SafeLoader": return get_safe_loader() msg = f"module {__name__!r} has no attribute {name!r}" raise AttributeError(msg)The
lru_cacheon_get_base_model_class()already handles caching, making the manual global unnecessary.
36-41: Optional: Clean up unused noqa directives.Static analysis indicates multiple
noqadirectives that appear unnecessary because the corresponding rules (PLC0415, PLW0603) may not be enabled in your ruff configuration. Consider removing them to reduce noise:
- Lines 36, 40, 41, 88, 91, 93, 209, 231, 234:
# noqa: PLC0415- Line 248:
# noqa: PLW0603(will be removed if you apply the global statement fix)Also applies to: 88-93, 209-209, 231-231, 234-234, 248-248
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/datamodel_code_generator/util.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/datamodel_code_generator/util.py (2)
src/datamodel_code_generator/pydantic_patch.py (1)
apply_patch(24-29)src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)
🪛 GitHub Actions: Lint
src/datamodel_code_generator/util.py
[error] 55-55: ruff: PLW0603 Using the global statement to update _is_v2 is discouraged
[error] 63-63: ruff: PLW0603 Using the global statement to update _is_v2_11 is discouraged
🪛 Ruff (0.14.10)
src/datamodel_code_generator/util.py
36-36: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
40-40: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
41-41: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
88-88: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
91-91: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
93-93: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
209-209: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
231-231: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
234-234: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
248-248: Unused noqa directive (non-enabled: PLW0603)
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). (19)
- GitHub Check: Analyze (python)
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: 3.13 on macOS
- GitHub Check: benchmarks
🔇 Additional comments (4)
src/datamodel_code_generator/util.py (4)
121-121: Verify that type checkers handle the string-based TypeVar bound correctly.Using a string forward reference
"_BaseModel"for the TypeVar bound is unconventional. While this enables lazy loading, type checkers typically expect the bound to be resolvable at module load time. This may cause type inference issues in code using this TypeVar.Consider testing with mypy and pyright to confirm that type checking works correctly for functions using this
ModelTypeVar. If issues arise, you may need to useTYPE_CHECKINGblocks or adjust the approach.
152-202: LGTM: Version-aware validator decorators.The replacement of static
PYDANTIC_V2checks withis_pydantic_v2()calls is correct and enables lazy version detection. The logic properly handles v1/v2 API differences, including the classmethod wrapping for v2beforemode validators.
270-295: LGTM: Version-compatible utility functions.All utility functions correctly implement version-aware branching using
is_pydantic_v2(). The replacements properly map to the correct Pydantic v1 and v2 APIs.
215-226: No action required:ConfigDict[...]syntax is not used in the codebase.The
__class_getitem__method on line 222 would theoretically fail with dict (Pydantic v1) at runtime, but this code path is never invoked. The actual usage pattern only callsConfigDict(...)via__call__, which works correctly. Thepyright: ignore[reportIndexIssue]comment indicates this limitation is acknowledged. If subscript notation is needed in the future, the proxy would need to handle dict subscripting separately.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/datamodel_code_generator/model/base.py (1)
902-911: Redundant conditional: both branches are identical.The
if is_pydantic_v2()/elsebranches execute exactly the same code. Sincemodel_rebuildalready handles v1/v2 differences internally (via the custom wrapper defined at lines 171-179), this conditional can be removed.🔎 Proposed simplification
-if is_pydantic_v2(): - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) -else: - _rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} - DataType.model_rebuild(_types_namespace=_rebuild_namespace) - BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) - DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel}) +_rebuild_namespace = {"Union": Union, "DataModelFieldBase": DataModelFieldBase, "DataType": DataType} +DataType.model_rebuild(_types_namespace=_rebuild_namespace) +BaseClassDataType.model_rebuild(_types_namespace=_rebuild_namespace) +DataModelFieldBase.model_rebuild(_types_namespace={"DataModel": DataModel})
🧹 Nitpick comments (2)
src/datamodel_code_generator/model/base.py (2)
447-448: Consider removing unused noqa directives.Static analysis indicates
# noqa: PLC0415is unused in the current linter configuration. If pylint isn't used in CI, these can be removed for cleaner code.🔎 Proposed fix
- from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape # noqa: PLC0415 + from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape
710-726: LGTM!Good caching optimization for
get_dedup_key. The cache key tuple correctly captures both parameters that affect the result.Same minor note: the
# noqa: PLC0415on line 721 is flagged as unused by static analysis.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/datamodel_code_generator/model/base.py
🧰 Additional context used
🪛 Ruff (0.14.10)
src/datamodel_code_generator/model/base.py
447-447: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
484-484: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
721-721: 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). (20)
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.11 on macOS
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: 3.12 on Windows
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: 3.12 on macOS
- GitHub Check: 3.13 on macOS
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: 3.14 on macOS
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: benchmarks
- GitHub Check: Analyze (python)
🔇 Additional comments (5)
src/datamodel_code_generator/model/base.py (5)
39-48: LGTM!Clean import organization:
is_pydantic_v2is imported for runtime checks, while jinja2 types are deferred underTYPE_CHECKINGto support lazy loading. This aligns with the PR's goal of improving CLI startup time.
79-89: LGTM!Runtime version check at class definition time correctly configures pydantic v1/v2-specific model configuration.
208-222: LGTM!Good caching optimization with appropriate documentation. The docstring correctly notes that "parent is stable at render time", which justifies the cache-once design without invalidation.
303-307: Verify fast path consistency with slow path.The fast path returns
data_type.all_importsunfiltered, while the slow path filters outIMPORT_UNION/IMPORT_OPTIONALwhen not needed in the type hint. Ifdata_type.all_importscontains these imports but the final type hint doesn't use them, the fast path would include unnecessary imports that the slow path would exclude.This is likely an acceptable tradeoff for performance (extra imports don't cause errors), but worth confirming this behavioral difference is intentional.
684-684: LGTM!Cache initialization with correct type hint matches the usage pattern in
get_dedup_key.
6c54277 to
ae13c77
Compare
- Fix Pydantic v1 compatibility by using __dict__ for self_reference cache - Add noqa comments for PLW0603 in util.py - Fix type error: wrap all_imports iterator in tuple() - Rename _pysnooper_initialized to _pysnooper_default_state_set for clarity - Add PydanticV2Dataclass to skip condition in conftest.py
- Add # pragma: no cover for fast path CLI code (--version, --help, --generate-prompt) that runs at module import time - Add # pragma: no cover for stdout output and ARGCOMPLETE paths - Add # pragma: no branch for pydantic version caching checks 🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add # pragma: no cover for chain_as_tuple n==0 and n==1 cases - Add # pragma: no cover for deprecated YAML bool warning branch 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2fd794c to
bdfd2b7
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/datamodel_code_generator/util.py (1)
1-5: Update docstring to reflectis_pydantic_v2()instead ofPYDANTIC_V2The module docstring still refers to “Pydantic version detection (PYDANTIC_V2)”, but the code now exposes
is_pydantic_v2()andget_pydantic_version()instead of a constant. Updating the docstring would prevent confusion for readers.<suggested_change>
ReplacePYDANTIC_V2in the docstring withis_pydantic_v2()/get_pydantic_version().
</suggested_change>
♻️ Duplicate comments (2)
src/datamodel_code_generator/util.py (1)
60-74: YAML bool deprecation warning and customized SafeLoader behavior are coherentThe
_construct_yaml_bool_with_warninghelper correctly warns on deprecated capitalized YAML booleans and only treats lowercasetrue/falseas canonical, andget_safe_loader()constructs an isolated loader class that adjusts timestamp and bool constructors plus implicit resolvers. The use of@lru_cache(maxsize=1)ensures this setup cost is paid only once.Given earlier discussion about the SafeLoader customization, it’s fine to keep this implementation for now; if you revisit it in a dedicated refactor, you can switch to a subclass-based pattern without changing the public API.
Also applies to: 76-110
src/datamodel_code_generator/__init__.py (1)
126-137: pysnooper debug flag_pysnooper_default_state_setis used correctlyThe updated
enable_debug_message()andsnooper_to_methods()both respect_pysnooper_default_state_set: the former enables tracing and marks the flag, the latter lazily setsDISABLED = Trueonce across all decorated classes. This matches the intent to keep tracing off by default and only enable it via the--debugoption.Since CodeQL previously flagged this as unused, it’s good to keep in mind that this is a known false positive; behavior here looks correct as implemented.
Also applies to: 142-159
🧹 Nitpick comments (4)
src/datamodel_code_generator/model/base.py (2)
208-221: Self-reference caching via__dict__is safe and avoids Pydantic assignment quirksThe new
self_reference()implementation caches the boolean result in__dict__["_self_reference_cache"], which avoids Pydantic v1’s restrictions on field assignment while making repeated calls cheap. Given thatparentanddata_typeare stable post-construction, this caching is appropriate and should not change semantics.If you ever add mutation of
parentordata_typeafter construction, consider invalidating_self_reference_cachein those paths.
683-684: Per-instance dedup key caching is correct and should significantly reduce render costsInitializing
_dedup_key_cacheinDataModel.__init__and caching(render_output, imports)tuples per(class_name, use_default)key avoids repeatedrender()andimportscomputation in hot paths like model deduplication. The fallback class name"M"whenuse_defaultandclass_name is Nonematches the existing default-name behavior.If you later add knobs that affect rendered output (e.g., extra template data toggles), consider including them in the cache key or explicitly documenting that
get_dedup_keyassumes those inputs are stable for a given instance.Also applies to: 709-725
src/datamodel_code_generator/types.py (1)
180-188:chain_as_tuple2-iterable fast path is safe and matches existing semanticsThe special case for
len(iterables) == 2using(*iterables[0], *iterables[1])eagerly consumes both iterables in order, just liketuple(chain(*iterables)), and avoids the small overhead of constructing an intermediatechainobject in the common two-iterator case.If you later find three-way chains are also hot, you could extend this pattern to
len == 3while keeping the general fallback for larger counts.src/datamodel_code_generator/__main__.py (1)
769-897: run_generate_from_config now correctly handles stdout-only generationThe new block that prints
resultwhenoutput is Noneensures CLI calls that rely on stdout (no--output) still surface generated code, including the case wheregenerate()returns a dict of multiple modules (each printed with a trailing newline). This keeps behavior intuitive without affecting the file-output code paths.If multi-module-to-stdout output becomes more common, consider prefixing each chunk with a comment containing its module path to make the combined stream easier to interpret.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/model/base.pysrc/datamodel_code_generator/model/pydantic/base_model.pysrc/datamodel_code_generator/types.pysrc/datamodel_code_generator/util.pytests/main/conftest.py
🧰 Additional context used
🧠 Learnings (4)
📓 Common 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:14.661Z
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: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:22:57.664Z
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.
📚 Learning: 2025-12-25T09:22:57.664Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:22:57.664Z
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/conftest.pysrc/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/types.pysrc/datamodel_code_generator/util.pysrc/datamodel_code_generator/model/base.pysrc/datamodel_code_generator/__init__.py
📚 Learning: 2025-12-25T09:22:14.661Z
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:14.661Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.
Applied to files:
src/datamodel_code_generator/__main__.pysrc/datamodel_code_generator/util.pysrc/datamodel_code_generator/model/pydantic/base_model.py
📚 Learning: 2025-12-18T13:43:16.235Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.
Applied to files:
src/datamodel_code_generator/util.pysrc/datamodel_code_generator/model/pydantic/base_model.py
🧬 Code graph analysis (6)
tests/main/conftest.py (2)
src/datamodel_code_generator/util.py (1)
is_pydantic_v2(52-57)src/datamodel_code_generator/enums.py (1)
DataModelType(48-56)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/prompt.py (1)
generate_prompt(76-128)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(52-57)
src/datamodel_code_generator/types.py (3)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(52-57)src/datamodel_code_generator/enums.py (1)
StrictTypes(199-206)
src/datamodel_code_generator/util.py (4)
src/datamodel_code_generator/reference.py (1)
_BaseModel(81-138)src/datamodel_code_generator/pydantic_patch.py (1)
apply_patch(24-29)src/datamodel_code_generator/model/base.py (2)
method(380-382)name(796-798)src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)
src/datamodel_code_generator/model/base.py (6)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (2)
is_pydantic_v2(52-57)model_dump(258-262)src/datamodel_code_generator/parser/base.py (2)
parent(602-604)data_type(1079-1081)src/datamodel_code_generator/types.py (3)
all_data_types(432-438)all_imports(469-473)imports(476-521)src/datamodel_code_generator/model/msgspec.py (1)
needs_annotated_import(433-443)src/datamodel_code_generator/model/pydantic/base_model.py (1)
imports(267-275)
src/datamodel_code_generator/model/pydantic/base_model.py (6)
src/datamodel_code_generator/types.py (2)
nullable(288-290)imports(476-521)src/datamodel_code_generator/model/dataclass.py (1)
imports(139-144)src/datamodel_code_generator/model/pydantic_v2/types.py (1)
imports(62-67)src/datamodel_code_generator/model/enum.py (1)
imports(119-121)src/datamodel_code_generator/model/typed_dict.py (1)
imports(150-155)src/datamodel_code_generator/model/type_alias.py (1)
imports(28-34)
🪛 GitHub Check: CodeQL
src/datamodel_code_generator/__main__.py
[notice] 11-11: Use of a print statement at module level
Print statement may execute during import.
[notice] 31-31: Use of a print statement at module level
Print statement may execute during import.
src/datamodel_code_generator/__init__.py
[notice] 158-158: Unused global variable
The global variable '_pysnooper_default_state_set' is not used.
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__main__.py
8-8: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
11-11: Unused noqa directive (non-enabled: T201)
Remove unused noqa directive
(RUF100)
15-15: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
31-31: Unused noqa directive (non-enabled: T201)
Remove unused noqa directive
(RUF100)
909-909: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/types.py
93-93: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
185-185: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/util.py
36-36: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
40-40: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
41-41: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
54-54: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
79-79: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
82-82: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
84-84: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
200-200: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
219-219: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
222-222: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
236-236: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/base.py
446-446: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
483-483: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
720-720: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/__init__.py
72-72: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
95-95: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
97-97: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
128-128: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
130-130: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
149-149: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
151-151: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
918-918: 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). (20)
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.14 on macOS
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.14 on Windows
- GitHub Check: benchmarks
- GitHub Check: Analyze (python)
🔇 Additional comments (19)
src/datamodel_code_generator/model/pydantic/base_model.py (1)
267-275: DataModelField.imports fast path is safe and avoids unnecessary Field computationFor required, non-nullable fields with no alias, constraints, or extras,
__str__returns""soself.fieldis falsy andIMPORT_FIELDwas never added. The new early return tosuper().importspreserves behavior while skipping the expensiveself.field/__str__path for this common case.tests/main/conftest.py (1)
22-22: Exec-skip logic now correctly covers both Pydantic v1 and v2 model typesUsing
is_pydantic_v2()plus the set check for{PydanticV2BaseModel, PydanticV2Dataclass}gives the right behavior: exec validation is skipped when the generated model type and installed Pydantic major version are incompatible. This also fixes the earlier omission ofPydanticV2Dataclassin the skip condition.You may want to re-run
tests/main/test_exec_validation.pyunder environments with and without Pydantic v2 to confirm the skip behavior matches expectations.Also applies to: 448-452
src/datamodel_code_generator/model/base.py (3)
39-40: Pydantic v1/v2 branching and config setup look consistent and centralizedUsing
is_pydantic_v2()plusConfigDictfor v2 and innerConfigclasses for v1 in bothConstraintsBaseandDataModelFieldBase, and wiringmodel_rebuildonly for v1 insideif not TYPE_CHECKING, keeps behavior aligned across versions while routing through the new utility helpers (model_dump,model_validate). The finalmodel_rebuildblock forDataType,BaseClassDataType, andDataModelFieldBaseis symmetric for v1/v2 and looks correct.It’s worth running a small smoke test under both Pydantic v1 and v2 to ensure forward refs and constraint handling behave identically (e.g., the existing integration tests that exercise both versions).
Also applies to: 74-90, 124-139, 168-181, 901-910
297-323: Imports fast path correctly skips typing imports when unnecessaryThe
importsproperty’s fast path (not has_union and not has_optional and not needs_annotated) short-circuits totuple(self.data_type.all_imports), and the slow path still conditionally re-addsUnion,Optional, andAnnotatedas needed. This preserves previous behavior while avoiding extra iteration and filtering for the common simple-field case.Keep an eye on fields that use
Annotatedin subclasses (e.g. msgspec) to ensureuse_annotatedandneeds_annotated_importremain in sync with how templates consume these imports.
443-462: Lazy Jinja2 imports with cached environments/templates are well-structuredMoving
jinja2imports into_get_environmentand_get_environment_with_absolute_pathand cachingEnvironment/Templateinstances with@lru_cachegives the intended lazy-loading behavior while keeping template lookup order (custom dir first, then built-ins) intact. TheTemplateBase.templateoverride correctly routes absolute paths through_get_template_with_absolute_pathand relative ones through_get_template_with_custom_dir.Given Jinja2’s sensitivity to loader configuration, it’s worth re-running any tests that exercise custom template directories and absolute custom templates to confirm includes still resolve as expected.
Also applies to: 480-505, 508-566, 771-778
src/datamodel_code_generator/types.py (2)
55-56: LazyStrictTypesexposure via__getattr__preserves backwards compatibilityDefining
__getattr__to resolveStrictTypesfromdatamodel_code_generator.enumson first access ensures existingfrom datamodel_code_generator.types import StrictTypesimports continue to work while centralizing enum definitions. Pairing this with a TYPE_CHECKING-only import keeps type checkers happy without adding runtime cost.If there are external users importing
StrictTypesfrom this module, it’s worth running a quick import test (includingfrom ... import StrictTypesand attribute access) to confirm behavior under both Python 3.10 and 3.11+.Also applies to: 89-99, 100-108
55-56: DataType’s v1/v2 configuration and helpers look correctUsing
is_pydantic_v2()withConfigDictfor v2 and aConfigclass (plus v1-onlymodel_rebuild) for v1 keepsDataType’s behavior aligned with Pydantic expectations. The rest of the class—imports,type_hint, andbase_type_hint—remains unchanged in semantics, so the version split is purely in config and forward-ref handling.It’s a good idea to run the existing schema-to-type tests (including ones that exercise constrained types and unions) under both Pydantic v1 and v2 to confirm
DataTypestill produces the same type hints and imports.Also applies to: 96-106, 293-322, 523-551, 564-653, 669-776
src/datamodel_code_generator/__main__.py (5)
5-33: CLI fast paths for--version,--help, and--generate-promptare narrowly scopedThe early checks on
sys.argvonly trigger for the specific single-flag--version/-Vand--help/-hcases and for explicit--generate-promptusage, so programmatic calls tomain(args)are unaffected. This matches the performance goal of avoiding heavy imports for these quick operations.Given past static-analysis concerns about module-level
sys.exit, it’s worth confirming (e.g., via a tiny script) that importing this module from another program with unrelatedsys.argvvalues does not trigger the fast paths.
87-93: Config’s v1/v2 handling and validators remain coherent with the new utilitiesSwitching Config’s v2 path to use
ConfigDictandmodel_validate, and keeping the v1 path on innerConfigplus dict-based validators, lines up withis_pydantic_v2()and the new compatibility helpers fromutil. The split between v2 instance-based validators and v1 classmethod-style validators appears consistent and keeps behavior intact.Running the CLI with a mix of options that exercise these validators (e.g.,
--original-field-name-delimiter,--custom-file-header*,--keyword-only) under both Pydantic v1 and v2 would be a good sanity check.Also applies to: 140-177, 331-385, 388-443, 564-577
906-912: Argcomplete integration is correctly isolated and lazyChecking for
_ARGCOMPLETEinos.environbefore importingargcompleteand callingargcomplete.autocomplete(arg_parser)keeps tab-completion setup opt-in and avoids adding startup cost for normal invocations.If you ship shell completion scripts, it’d be good to verify they still work with this guarded integration (especially on shells where
_ARGCOMPLETEis set differently).
916-936: Main control flow remains clear and compatible with new featuresThe handling of
--version,--generate_pyproject_config,--generate_prompt, pyproject loading, and the--check/diff logic all remain unchanged in semantics, with the only meaningful addition being the reuse ofrun_generate_from_configand the new stdout printing. Error messages for bad combinations like--checkwithout--outputor--watchwith invalid inputs are still precise.Also applies to: 969-979, 987-1001, 1110-1119
916-924: No regression in URL / RAW_DATA_TYPES handling with new input-type autodetectionThe updated
infer_input_typestill routes text throughload_yaml, and theexcept yaml.parser.ParserErrorfallback toInputFileType.CSVpreserves the previous heuristic while avoiding a top-level YAML import. Combined with the reuse of already-read text for single-file inputs, this keeps autodetection behavior intact and efficient.If you have fixtures with ambiguous inputs (YAML-like CSV or vice versa), re-run those to confirm the inferred type and downstream parsing still work as before.
Also applies to: 648-654, 993-1001, 1110-1119
src/datamodel_code_generator/util.py (4)
1-5: Centralized Pydantic version detection with caching and patching is well-designed
get_pydantic_version()applies thepydantic_patchbefore importingpydantic, parses the version once under@lru_cache, and returns(version, is_v2, is_v2_11).is_pydantic_v2()then adds a cheap global cache on top, which is appropriate for very hot paths. ThePLW0603suppression is justified given this explicit design.If you maintain support for a wide range of Pydantic versions, a tiny harness that prints
get_pydantic_version()andis_pydantic_v2()across those versions can help ensure the boundary checks (>= 2.0b3,>= 2.11) remain correct.Also applies to: 32-47, 49-58
112-149: Version-awaremodel_validatorandfield_validatorwrappers correctly route to v1/v2 APIsBoth decorators now branch on
is_pydantic_v2()at decoration time: using Pydantic v2’smodel_validator/field_validatorin v2 and falling back toroot_validator/validatorin v1, with themode == "before"mapping topre=Truecorrectly. This keeps your call sites uniform while handling both major versions.As these wrappers abstract Pydantic’s public APIs, it’s worth re-running a representative subset of models using both v1 and v2 validators to ensure signatures and error messages are still as expected.
Also applies to: 177-193
196-214: LazyConfigDictand BaseModel exposure via proxies and__getattr__is clean
_get_config_dict()and_ConfigDictProxyprovide a simple way to callConfigDict(...)where available and fall back todictfor v1-only paths, and_get_base_model_class()plus module-level__getattr__expose a version-compatibleBaseModelthat setsstrict=Falseon v2. The use of a cached_BaseModelinstance avoids repeated imports and class generation.Consider a quick import test (
from datamodel_code_generator.util import BaseModel, ConfigDict) under both Pydantic v1 and v2 to verify that the resolved types behave as intended when subclassed.Also applies to: 216-245
247-256: Compatibility helpers for dump/validate/fields/copy align with v1/v2 semanticsThe
model_dump,model_validate,get_fields_set, andmodel_copyhelpers consistently branch onis_pydantic_v2()and delegate to the appropriate Pydantic methods (model_dumpvsdict,model_validatevsparse_obj,model_fields_setvs__fields_set__,model_copyvscopy). This centralization should make it much easier to keep templates and models version-agnostic.You might want to add or extend a small unit test module that exercises these helpers directly across both Pydantic versions for at least one simple
BaseModelsubclass.Also applies to: 258-283
src/datamodel_code_generator/__init__.py (3)
26-47: Enums and type aliases are cleanly centralized and exportedImporting all the enums and constants (
DataModelType,ReuseScope,TargetPydanticVersion, etc.) fromdatamodel_code_generator.enumssimplifies this module and keeps the public API driven by a single enums source. The__all__list still exposes the expected symbols, so external consumers shouldn’t see any breaking changes.Also applies to: 59-68, 71-81, 942-963
26-27: YamlValue typing and lazy YAML loading are version-aware and safeUsing
TypeAliasTypeforYamlValueunder TYPE_CHECKING and at runtime only whenis_pydantic_v2()is true, and falling back to a simplerdict[str, Any] | list[Any] | YamlScalaralias for v1, avoids Pydantic v1’s issues with recursive aliases while preserving richer typing for v2. Moving the YAML andSafeLoaderimports intoload_yaml(andyaml.parserintoinfer_input_type) keeps startup light and aligns with the new lazy-import strategy.Re-run the YAML-related tests (including those that rely on type inference via
infer_input_type) under both Pydantic v1 and v2 to confirm there are no regressions in parsing or typing.Also applies to: 59-68, 71-81, 93-115
93-100: YAML error handling ininfer_input_typealigns with the new loader
infer_input_typenow importsyaml.parserlocally and treats ayaml.parser.ParserErrorfromload_yamlas the CSV signal, leaving other errors to surface normally. Combined withload_yaml’s use of the customizedSafeLoader, this keeps the heuristic “YAML-parse failure => maybe CSV” intact without a top-level YAML dependency.If there are edge-case fixtures for CSV-vs-YAML detection, it would be useful to verify that they still infer as expected with the new loader and error handling.
Also applies to: 215-263, 916-935
af6a3c7 to
0df34d8
Compare
0df34d8 to
edc0fdb
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/datamodel_code_generator/model/base.py (2)
605-684: Per‑instance_dedup_key_cacheis a reasonable optimization; consider documenting mutation assumptions.Caching the
(render(...), imports)tuple per(class_name, use_default)key avoids recomputing expensive template rendering and import collection when deduplication checks are repeated for the same model. This assumes the model (fields, imports, decorators) is effectively immutable after first use, which matches how models are constructed today; if future code mutates models post‑render, we might need cache invalidation. A short comment noting that expectation near_dedup_key_cachewould make this clearer.Also applies to: 709-726
901-910: Bottommodel_rebuildbranch is duplicated and could be simplified.Both branches of the final
if is_pydantic_v2(): ... else:execute identicalmodel_rebuildcalls with the same_rebuild_namespace. Since the v1/v2 behavior has already been handled inside the classes, you can collapse this into a single branch to reduce noise.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/datamodel_code_generator/__init__.pysrc/datamodel_code_generator/model/base.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/reference.pysrc/datamodel_code_generator/types.pysrc/datamodel_code_generator/util.py
🧰 Additional context used
🧠 Learnings (4)
📓 Common 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:14.661Z
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: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:22:57.664Z
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.
📚 Learning: 2025-12-25T09:22:57.664Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:22:57.664Z
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/__init__.pysrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/util.pysrc/datamodel_code_generator/reference.pysrc/datamodel_code_generator/types.pysrc/datamodel_code_generator/model/base.py
📚 Learning: 2025-12-25T09:22:14.661Z
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:14.661Z
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/util.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/util.py
🧬 Code graph analysis (4)
src/datamodel_code_generator/parser/jsonschema.py (1)
src/datamodel_code_generator/util.py (2)
is_pydantic_v2(52-57)model_dump(256-260)
src/datamodel_code_generator/util.py (1)
src/datamodel_code_generator/pydantic_patch.py (1)
apply_patch(24-29)
src/datamodel_code_generator/reference.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (6)
camel_to_snake(250-253)is_pydantic_v2(52-57)model_validator(117-122)model_validator(126-132)model_validator(136-140)model_validator(143-174)
src/datamodel_code_generator/types.py (3)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
ConfigDict(29-54)src/datamodel_code_generator/util.py (1)
is_pydantic_v2(52-57)src/datamodel_code_generator/enums.py (1)
StrictTypes(199-206)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__init__.py
72-72: Unused noqa directive (non-enabled: E402)
Remove unused noqa directive
(RUF100)
95-95: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
97-97: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
128-128: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
130-130: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
149-149: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
151-151: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
918-918: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/util.py
36-36: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
40-40: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
41-41: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
54-54: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
79-79: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
82-82: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
84-84: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
199-199: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
217-217: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
220-220: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
234-234: Unused noqa directive (non-enabled: PLW0603)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/types.py
93-93: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
185-185: Unused noqa directive (non-enabled: PLR2004)
Remove unused noqa directive
(RUF100)
src/datamodel_code_generator/model/base.py
446-446: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
483-483: Unused noqa directive (non-enabled: PLC0415)
Remove unused noqa directive
(RUF100)
720-720: 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). (20)
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: py312-isort6 on Ubuntu
- GitHub Check: benchmarks
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.11 on Ubuntu
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.13 on macOS
- GitHub Check: py312-isort5 on Ubuntu
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.11 on macOS
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-black23 on Ubuntu
- GitHub Check: 3.10 on Ubuntu
- GitHub Check: Analyze (python)
🔇 Additional comments (23)
src/datamodel_code_generator/parser/jsonschema.py (2)
80-89: Pydantic v2 gating and model_config look correct and consistent with util helpers.Using
is_pydantic_v2()for both theConfigDictimport and theJsonSchemaObjectget_fields/model_rebuild/model_configsplit keeps the v1/v2 behavior centralized and aligned with the new util helpers. The runtime-only definitions underif not TYPE_CHECKINGalso avoid pyright noise while still supportingJsonSchemaObject.model_rebuild()via the inherited BaseModel method on v2. Based on learnings, the extra calls tois_pydantic_v2()are cheap due to the global cached flag on top ofget_pydantic_version(). Please rerun schema tests under both Pydantic v1 and v2 to confirm no behavioral drift.Also applies to: 91-93, 204-227, 369-383
1084-1100: Switch tomodel_dumpfor constraints/merging is version-safe and theexclude_noneusage is appropriate.Replacing direct
.dict()calls with the sharedmodel_dump()helper (plusexclude_unset=True/by_alias=Truewhere previously used) keeps all these code paths compatible with both Pydantic v1 and v2. Usingexclude_none=Truespecifically for constraint dictionaries (get_object_field, array/root/mixed-root constraints, and primitive/allOf merges) avoids emitting spuriousNoneentries while preserving falsy-but-meaningful values likeFalseand0. This looks semantically equivalent or stricter than before, but please ensure the JSON Schema constraint tests (especially around hostname pattern, allOf/anyOf merges, and root model constraints) are exercised on both Pydantic versions.Also applies to: 1131-1132, 1239-1244, 1253-1267, 1281-1287, 1363-1367, 1563-1573, 1716-1722, 1736-1761, 2617-2636, 2754-2756, 2821-2823
src/datamodel_code_generator/reference.py (2)
37-39: Version-aware_BaseModeldictoverride and config are sound.The split
dictimplementation based onis_pydantic_v2()preserves the existing exclusion semantics (always unioning_exclude_fieldsintoexclude) while delegating tomodel_dumpon v2 andBaseModel.dicton v1. UsingConfigDictvs an innerConfigclass to setarbitrary_types_allowedandkeep_untouched/ignored_typesmatches the expected Pydantic v2/v1 patterns and keeps_BaseModelusable across both. Since this depends on Pydantic internals, please confirm serialization ofReference/resolver-related models still matches snapshots on both v1 and v2.Also applies to: 81-90, 95-139
156-167:Referencemodel_config migration toConfigDictmaintains prior behavior.The new
model_config = ConfigDict(...)for v2 mirrors the v1Configattributes (arbitrary_types_allowed,keep_untouchedviaignored_types, and instance revalidation policy) and keepscached_propertyuntouched as before. This should be transparent to calling code while aligning with Pydantic v2’s config style. Please verify that constructing and serializingReferenceinstances behaves the same under both Pydantic versions (especially aroundcached_propertyusage and revalidation).Also applies to: 168-184
src/datamodel_code_generator/model/base.py (5)
39-40: ConstraintsBase v1/v2 config split is correct and uses shared helpers.Using
ConfigDictwhenis_pydantic_v2()is true and a v1Configclass otherwise mirrors the pattern in other modules and keepsarbitrary_types_allowed/keep_untouchedbehavior the same.has_constraintsnow goes throughmodel_dump, which is the right central hook for v1/v2 compatibility.Also applies to: 74-90
124-139: DataModelFieldBase Pydantic config and v1-onlymodel_rebuildare aligned with the new BaseModel abstraction.The
model_config = ConfigDict(...)for v2 and legacyConfigclass for v1 follow the same pattern as other models. Defining a custommodel_rebuild()only on v1 (usingupdate_forward_refs) while letting v2 inherit the default behavior is appropriate, and the bottom-of-file rebuild block correctly passes a types namespace for both versions.Also applies to: 168-180, 901-910
181-187:self_referencecaching is safe and avoids repeated graph walks.Caching the boolean result in
__dict__['_self_reference_cache']sidesteps Pydantic v1’s attribute restrictions and keeps subsequentself_reference()calls O(1). The computation itself still correctly checks for a parent reference and compares against all referenced data types. This looks good for both correctness and performance.Also applies to: 208-221
296-323: Fast path inDataModelFieldBase.importspreserves semantics while reducing work.Computing
has_union,has_optional, andneeds_annotatedfrom the already-builttype_hintand short‑circuiting totuple(self.data_type.all_imports)when none apply avoids the extra filtering/chain_as_tupleoverhead on the common path. When any of those flags are set, the logic still mirrors the previous behavior (dropping unused UNION/OPTIONAL imports and explicitly adding the needed ones, plusAnnotatedwhen required).
443-462: Lazy Jinja2 environment/template loading is correctly encapsulated and keeps PLC0415noqadirectives for future‑proofing.The new
_get_environment*and_get_template*helpers lazily import Jinja2, support custom template directories and absolute paths, and are safely cached bylru_cache. Using# noqa: PLC0415on the lazy imports matches the project’s convention (and should be kept despite RUF100) to guard against future “import at top level” rules. Please verify custom template override tests (including includes) still pass.Also applies to: 480-505, 761-778
src/datamodel_code_generator/types.py (3)
55-56: Lazy StrictTypes exposure via__getattr__maintains backward compatibility.Redirecting
StrictTypesaccesses through__getattr__todatamodel_code_generator.enums.StrictTypespreservesfrom datamodel_code_generator.types import StrictTypesfor callers while letting you centralize the enum inenums.py. Keeping the lazy import guarded with# noqa: PLC0415is consistent with the project’s convention for function‑scope imports despite RUF100.Also applies to: 90-97, 100-107
180-188:chain_as_tupletwo‑iterable fast path is safe and avoidsitertools.chainoverhead.The new special case for
len(iterables) == 2simply expands both iterables into a single tuple, which is equivalent to the previoustuple(chain(*iterables))behavior (including for generators) but slightly cheaper in the common 2‑way case. No functional concerns here.
109-112: DataType Pydantic config and init changes align with v1/v2 support and lazy imports.Conditionally importing
GetCoreSchemaHandler/core_schemaonly whenis_pydantic_v2()is true keeps v1 environments from requiringpydantic_coreat runtime. Themodel_configsplit (ConfigDict withextra="forbid"/revalidate_instances="never"vs v1Configwithextra="forbid"/copy_on_model_validation) matches expectations, and the v1‑onlymodel_rebuildoverride mirrors the pattern used in other models. Wrappingsuper().__init__inif not TYPE_CHECKINGkeeps type checkers happy without affecting runtime. Please re-run type-hint and generation tests under both Pydantic versions to confirm.Also applies to: 293-321, 523-552
src/datamodel_code_generator/util.py (5)
32-47: Version detection and global caching inis_pydantic_v2()match the agreed design.
get_pydantic_version()centralizes theapply_patch()call and version parsing behind anlru_cache(maxsize=1), andis_pydantic_v2()then adds a global_is_v2flag to eliminate repeated function/call‑site overhead on hot paths. This aligns with the prior decision to keep the PLW0603‑suppressed global caching for performance and should be kept as‑is despite lint nudges. Based on learnings, this is the intended pattern.Also applies to: 49-58
60-74: YAML bool deprecation warning and lazy SafeLoader are functionally sound.The new
_construct_yaml_bool_with_warning()cleanly warns on legacy uppercase YAML bools while still accepting them, andget_safe_loader()now lazily builds a customized loader (with timestamp and bool resolvers adjusted) under anlru_cache(maxsize=1). Exposing this via__getattr__asSafeLoaderkeeps the external API simple and avoids touching PyYAML until actually needed. Please ensure YAML parsing tests (especially around bools and timestamps) cover both old and new forms.Also applies to: 76-110, 232-241
196-212: ConfigDict proxy cleanly defers the Pydantic v2 import.
_ConfigDictProxyplus_get_config_dict()let callers writeConfigDict(...)without importing Pydantic at module import time, while still constructing a realpydantic.ConfigDictunder the hood when used. This is a neat way to share the v2 config object across modules while keeping the import lazy; just avoid usingisinstance(..., ConfigDict)anywhere, since it’s a proxy, not the actual type.
214-227: Lazy BaseModel selection and module__getattr__are well-structured.
_get_base_model_class()chooses between a v2 subclass withmodel_config = ConfigDict(strict=False)and the raw Pydantic BaseModel, and__getattr__uses it to exposeBaseModellazily while also delegatingSafeLoadertoget_safe_loader(). The use of global_BaseModelwith# noqa: PLW0603is consistent with the project’s performance/lint strategy. Please confirm that all internal imports ofBaseModelnow go throughdatamodel_code_generator.utilso this indirection is consistently applied.Also applies to: 229-241
116-175: Pydantic v1/v2 decorators and helper wrappers are correctly unified onis_pydantic_v2().
model_validatorandfield_validatornow dispatch topydantic.model_validator/field_validatoron v2 and toroot_validator/validatoron v1, which is exactly what you need to keep shared decorators in templates/models. Likewise,model_dump,model_validate,get_fields_set, andmodel_copywrap the v2model_*APIs and the v1dict/parse_obj/__fields_set__/copyAPIs behind a consistent interface. This should significantly simplify caller code; re-running tests that exercise validators, defaults, and field sets on both Pydantic versions will be important.Also applies to: 177-193, 256-281
src/datamodel_code_generator/__init__.py (6)
26-47: LGTM: Centralized enum imports improve modularity.The
TypeAliasTypeimport and centralized enum imports fromenums.pyalign well with the lazy loading objectives. This reduces duplication and improves maintainability.
95-98: LGTM: Lazy imports for YAML dependencies.The lazy imports of
yamlandSafeLoaderdefer loading untilload_yaml()is called, improving startup time. The# noqa: PLC0415directives should be retained despite RUF100 warnings—they're defensive annotations in case the import-outside-top-level rule is enabled in future Ruff configurations.Based on learnings, these defensive directives are intentional.
126-136: LGTM: Lazy loading with improved error handling.The lazy import of
pysnooperand the chained exception with installation guidance are well-implemented. The# noqa: PLW0603and# noqa: PLC0415directives are intentional defensive annotations per project learnings and should be retained.
142-159: LGTM: Lazy pysnooper initialization with state management.The lazy import pattern with early return (lines 150-153) and the
_pysnooper_default_state_setflag correctly prevent redundant initialization when the decorator is applied to multiple classes. The# noqadirectives are intentional defensive annotations per project learnings.
918-919: LGTM: Lazy import for YAML parser.The lazy import of
yaml.parserdefers loading untilinfer_input_type()is called. The# noqa: PLC0415directive should be retained as a defensive annotation per project learnings.
74-80: The module-levelis_pydantic_v2()call is acceptable—pydantic is a required dependency.Pydantic (v1.5+) is declared as a required dependency in pyproject.toml, not optional. The module-level version check at line 76 is appropriate for a required dependency, and the dual-layer caching (global variable + lru_cache) in
is_pydantic_v2()is an intentional performance optimization to eliminate both function call and cache lookup overhead.Likely an incorrect or invalid review comment.
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.