Fix array RootModel default value handling in parser#2963
Conversation
|
Warning Rate limit exceeded@koxudaxi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 20 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughRefines default and nullable handling across root models, arrays, and synthetic enums; updates specific Field defaults from ellipsis to concrete values; and adjusts CI to always upload coverage artifacts and force Codecov upload while surfacing coverage failures. Changes
Sequence Diagram(s)(omitted) Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
|
📚 Docs Preview: https://pr-2963.datamodel-code-generator.pages.dev |
Merging this PR will not alter performance
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2963 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 94 94
Lines 17761 17778 +17
Branches 2039 2043 +4
=========================================
+ Hits 17761 17778 +17
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:
|
7f45fa3 to
e9cd243
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
.github/workflows/test.yaml (1)
122-150: Make the “uncovered lines” diagnostic step non-fatal to avoid masking logs.If
grep -v "100%"matches nothing, it exits non-zero and can mark the diagnostic step as failed (even though you already plan to fail at the final step). Consider ensuring this step never fails so the log is consistently printed and the following upload steps remain clean.Proposed tweak
- name: Show uncovered lines on failure if: steps.coverage.outcome == 'failure' run: | echo "::error::Coverage check failed. Lines not covered:" - .tox/coverage/bin/coverage report --show-missing --fail-under=0 | grep -v "100%" + .tox/coverage/bin/coverage report --show-missing --fail-under=0 | grep -v "100%" || truesrc/datamodel_code_generator/parser/jsonschema.py (1)
3271-3334:parse_root_type: double-check default_factory-only cases don’t becomedefault=None.Similar to
parse_array_fields,obj.has_defaultcan be true because ofdefault_factoryinobj.extras. In multiple branches,default_valuebecomesobj.default(commonlyNone) whenobj.has_defaultis true, which can unintentionally force aNonedefault even when the intended behavior is “usedefault_factory”.This is especially worth verifying because this method now also sets
default=ondata_model_root_type(...)(Line 3333-3334), so the model-level default could also be skewed.If you confirm the renderer doesn’t strictly prioritize
default_factory, consider the same “explicit default vs default_factory” guard here (computehas_explicit_default = "default" in get_fields_set(obj)and only useobj.defaultwhen that’s true; otherwise keepUNDEFINEDand rely ondefault_factoryin extras).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
docs/llms-full.txtis excluded by none and included by nonesrc/datamodel_code_generator/model/template/pydantic/BaseModel_root.jinja2is excluded by none and included by nonesrc/datamodel_code_generator/model/template/pydantic_v2/RootModel.jinja2is excluded by none and included by none
📒 Files selected for processing (5)
.github/workflows/test.yamldocs/cli-reference/field-customization.mdsrc/datamodel_code_generator/model/pydantic/base_model.pysrc/datamodel_code_generator/parser/jsonschema.pytests/data/expected/main/jsonschema/titles_use_title_as_name.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
📚 Learning: 2026-01-02T08:25:22.111Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
Applied to files:
tests/data/expected/main/jsonschema/titles_use_title_as_name.pydocs/cli-reference/field-customization.mdsrc/datamodel_code_generator/parser/jsonschema.pysrc/datamodel_code_generator/model/pydantic/base_model.py
🧬 Code graph analysis (1)
src/datamodel_code_generator/parser/jsonschema.py (2)
src/datamodel_code_generator/model/base.py (1)
nullable(915-917)src/datamodel_code_generator/types.py (1)
nullable(396-398)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/jsonschema.py
3059-3059: Unused noqa directive (non-enabled: PLR0912, PLR0915)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: 3.13 on Ubuntu
- GitHub Check: 3.11 on Windows
- GitHub Check: 3.13 on macOS
- GitHub Check: 3.10 on macOS
- GitHub Check: 3.12 on Windows
- GitHub Check: 3.10 on Windows
- GitHub Check: 3.13 on Windows
- GitHub Check: 3.12 on macOS
- GitHub Check: 3.11 on macOS
- GitHub Check: 3.14 on Windows
- GitHub Check: Analyze (python)
- GitHub Check: benchmarks
🔇 Additional comments (6)
docs/cli-reference/field-customization.md (1)
3912-3923: Doc example default now matches generated output (COMPLETED).tests/data/expected/main/jsonschema/titles_use_title_as_name.py (1)
47-58: Expected output updated to concrete default for the union root.src/datamodel_code_generator/parser/jsonschema.py (2)
888-909: Good: avoid accidentally marking synthetic enums as having a default.
Conditionally addingdefaultonly whenoriginal.has_defaultprevents downstream “required vs optional” logic from being skewed by synthetic objects.
3059-3152: No action needed. The# noqa: PLR0912, PLR0915suppressions are appropriate for this method's complexity (22 conditional branches) and match the pattern used elsewhere in the codebase. The rendering layer already prevents outputting bothdefaultanddefault_factorytogether through explicit conditional checks (not field.has_default_factory_in_fieldin templates anddata.pop("default_factory", None)in field rendering), so the suggested refactor is unnecessary.Likely an incorrect or invalid review comment.
src/datamodel_code_generator/model/pydantic/base_model.py (2)
223-228: LGTM! Simplified condition ensures consistent behavior for required fields.The change from checking
self.required and not self.has_defaultto justself.requiredensures that required fields always havedefault_factory = None, which aligns with Pydantic semantics where required fields should not have factory-generated defaults.This is consistent with the downstream logic at lines 230-236 that already gates optional field processing with
not self.required.
252-256: Consistent simplification of the required field condition.The change from
elif self.required and not default_factorytoelif self.requiredis safe because the logic at line 223 guarantees thatdefault_factorywill always beNonefor required fields. Whenself.requiredis True, line 223 setsdefault_factory = None, and the only subsequent potential modification at lines 230-237 explicitly excludes required fields with itsnot self.requiredcondition. Therefore, the additionaland not default_factorycheck is redundant.Test coverage for this edge case exists (e.g.,
test_base_model_nullable_requiredandtest_base_model_strict_non_nullable_required), which verify that required fields with defaults are handled correctly.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/datamodel_code_generator/parser/jsonschema.py (1)
3059-3059: Remove unusednoqadirective.Static analysis indicates that
PLR0912andPLR0915are not enabled in your linter configuration, making this directive unnecessary.Suggested fix
- def parse_array_fields( # noqa: PLR0912, PLR0915 + def parse_array_fields(
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/test.yamlsrc/datamodel_code_generator/parser/jsonschema.py
🚧 Files skipped from review as they are similar to previous changes (1)
- .github/workflows/test.yaml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
📚 Learning: 2026-01-02T08:25:22.111Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:22.111Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.
Applied to files:
src/datamodel_code_generator/parser/jsonschema.py
🧬 Code graph analysis (1)
src/datamodel_code_generator/parser/jsonschema.py (2)
src/datamodel_code_generator/model/base.py (1)
nullable(915-917)src/datamodel_code_generator/types.py (1)
nullable(396-398)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/parser/jsonschema.py
3059-3059: Unused noqa directive (non-enabled: PLR0912, PLR0915)
Remove unused noqa directive
(RUF100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: 3.12 on Windows
- GitHub Check: py312-black24 on Ubuntu
- GitHub Check: py312-pydantic1 on Ubuntu
- GitHub Check: 3.10 on Windows
- GitHub Check: py312-black22 on Ubuntu
- GitHub Check: benchmarks
- GitHub Check: 3.13 on Windows
- GitHub Check: py312-isort7 on Ubuntu
- GitHub Check: 3.12 on Ubuntu
- GitHub Check: 3.14 on Windows
- GitHub Check: 3.11 on Windows
- GitHub Check: Analyze (python)
🔇 Additional comments (4)
src/datamodel_code_generator/parser/jsonschema.py (4)
900-909: LGTM!Good fix for synthetic enum default propagation. The conditional unpacking
**({"default": original.default} if original.has_default else {})ensures that thedefaultkeyword is only passed when the original schema explicitly defines one, preventinghas_defaultfrom being incorrectly set toTrueon the synthetic object.
3070-3084: LGTM - Improved nullable/required logic for array fields.The refined logic correctly handles the interplay between defaults and nullability:
requiredis now derived solely fromhas_default(excludingforce_optional_for_required_fieldspath)- The explicit
nullable = Falsewhenobj.has_default(line 3082) prevents unintendedOptionalwrapping for arrays with default valuesThis aligns well with the PR objective of fixing array RootModel default value handling.
3298-3324: LGTM - Consistent default propagation to field and root type.The computed
default_valueandhas_default_overrideare correctly applied to both:
- The
data_model_field_type(lines 3303, 3314)- The
data_model_root_type(line 3323)The conditional on line 3323
default=default_value if has_default_override else UNDEFINEDensures the root type's default is only set when the override logic determined one should exist.
3269-3289: Well-structured default/nullable handling for root types.The
is_type_aliascheck (line 3269) correctly differentiates between type aliases and root models with defaults. The four-branch logic cleanly handles:
force_optional_for_required_fields- always optionalnullableschemas - not required, explicitly nullable- Schemas with defaults (non-alias) - not required, explicitly non-nullable
- Required schemas - uses
UNDEFINEDsentinel for missing defaultsThe explicit
nullable = Falseon line 3282 ensures root models with defaults aren't incorrectly wrapped inOptional. TheIS_ALIASattribute is a standard ClassVar defined on all model types (base.py:631, overridden in type_alias.py:24) and correctly distinguishes type aliases from root models.
Breaking Change AnalysisResult: No breaking changes detected Reasoning: This PR is a bug fix that corrects handling of default values for RootModel/union types. Previously, default values specified in JSON schemas for union root types were being ignored (generating This analysis was performed by Claude Code Action |
|
🎉 Released in 0.53.0 This PR is now available in the latest release. See the release notes for details. |
Summary by CodeRabbit
Bug Fixes
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.