Fix for #3045#3046
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds correct handling for dict-typed field defaults whose values are Pydantic BaseModel instances: empty dict defaults now use Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/datamodel_code_generator/model/pydantic_base.py (1)
158-162: Consider updating the TODO in the loop to handle nested dict types.The new code at lines 144-156 handles direct dict types, but the loop still has a TODO for dict handling. For complex union types where the dict is nested within
data_types, the loop would skip it without processing.This creates asymmetry - direct dicts are handled, but dicts within unions may not be. Consider whether this TODO should be addressed for completeness, or document why it's intentionally left as-is.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/datamodel_code_generator/model/pydantic_base.py` around lines 158 - 162, The loop over self.data_type.data_types currently skips entries where data_type.is_dict (TODO) causing nested dicts inside unions to be ignored; update the loop in the method using self.data_type.data_types to handle nested dicts the same way as the direct dict branch (the logic implemented earlier at lines ~144-156) by parsing the dict model to compute the default and not simply continue when data_type.is_dict is true, using the same helper/parse routine used for direct dict handling so union-member dict types are correctly processed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/datamodel_code_generator/model/pydantic_base.py`:
- Around line 158-162: The loop over self.data_type.data_types currently skips
entries where data_type.is_dict (TODO) causing nested dicts inside unions to be
ignored; update the loop in the method using self.data_type.data_types to handle
nested dicts the same way as the direct dict branch (the logic implemented
earlier at lines ~144-156) by parsing the dict model to compute the default and
not simply continue when data_type.is_dict is true, using the same helper/parse
routine used for direct dict handling so union-member dict types are correctly
processed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3c7cf1df-abf3-44ec-ad64-6fd7a417104b
⛔ Files ignored due to path filters (2)
tests/data/jsonschema/pydantic_v2_model_default_dict_empty.jsonis excluded by!tests/data/**/*.jsonand included by nonetests/data/jsonschema/pydantic_v2_model_default_dict_non_empty.jsonis excluded by!tests/data/**/*.jsonand included by none
📒 Files selected for processing (4)
src/datamodel_code_generator/model/pydantic_base.pytests/data/expected/main/jsonschema/pydantic_v2_model_default_dict_empty.pytests/data/expected/main/jsonschema/pydantic_v2_model_default_dict_non_empty.pytests/main/jsonschema/test_main_jsonschema.py
Merging this PR will improve performance by 22.66%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | WallTime | test_perf_kubernetes_style_pydantic_v2 |
2.8 s | 2.4 s | +19.14% |
| ⚡ | WallTime | test_perf_openapi_large |
3.4 s | 2.8 s | +22.66% |
| ⚡ | WallTime | test_perf_aws_style_openapi_pydantic_v2 |
1.9 s | 1.8 s | +10.29% |
| ⚡ | WallTime | test_perf_graphql_style_pydantic_v2 |
823.2 ms | 740.7 ms | +11.15% |
| ⚡ | WallTime | test_perf_all_options_enabled |
6.6 s | 5.9 s | +12.46% |
| ⚡ | WallTime | test_perf_duplicate_names |
1,093 ms | 951.3 ms | +14.9% |
| ⚡ | WallTime | test_perf_deep_nested |
6.2 s | 5.5 s | +12.77% |
| ⚡ | WallTime | test_perf_large_models_pydantic_v2 |
3.7 s | 3.3 s | +12.79% |
| ⚡ | WallTime | test_perf_complex_refs |
2.3 s | 1.9 s | +18.41% |
| ⚡ | WallTime | test_perf_multiple_files_input |
3.7 s | 3.3 s | +11.04% |
Comparing ashipilov:fix-dict-default (9ef313c) with main (f3f3912)
Footnotes
-
98 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/datamodel_code_generator/model/pydantic_base.py (1)
162-174: Consider extracting shared dict-handling logic.This block is nearly identical to lines 145-157. While the duplication is consistent with how list handling is structured (direct at 132-144, nested at 176-188), a helper method could reduce this repetition.
Since refactoring would also affect the existing list handling, this can be deferred.
♻️ Optional: Extract helper method
def _get_dict_default_factory(self, data_type_value) -> str | None: """Get default factory for dict field with BaseModel values.""" if ( data_type_value.reference and isinstance(data_type_value.reference.source, BaseModelBase) and isinstance(self.default, dict) ): if not self.default: return STANDARD_DICT class_name = data_type_value.alias or data_type_value.reference.source.class_name return ( f"lambda :{{k: {class_name}." f"{self._PARSE_METHOD}(v) for k, v in {self.default!r}.items()}}" ) return NoneThen call this helper in both locations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/datamodel_code_generator/model/pydantic_base.py` around lines 162 - 174, Extract the duplicated dict default-factory logic into a helper method (e.g., _get_dict_default_factory) that accepts data_type_value and returns either STANDARD_DICT, the generated lambda string, or None; the helper should check the same conditions (data_type_value.reference, isinstance(data_type_value.reference.source, BaseModelBase), isinstance(self.default, dict)) and use data_type_value.alias or data_type_value.reference.source.class_name and self._PARSE_METHOD when building the lambda, then replace the two nearly identical blocks that reference data_type/data_type_value/self.default with calls to this helper and return its result when non-None.tests/main/jsonschema/test_main_jsonschema.py (1)
1938-1950: Add one runtime assertion for the generated defaults.These tests currently only snapshot the emitted source. Since the regression is about
default_factorybehavior, I'd also instantiate the generatedParentModeland assert the empty case yields{}and the populated case yieldsItemModelvalues. That makes the regression harder to reintroduce if the expected fixtures are updated alongside the generator.Example direction
def test_main_generate_pydantic_v2_model_default_dict_non_empty(tmp_path: Path) -> None: ... assert_file_content(output_file, "pydantic_v2_model_default_dict_non_empty.py") + namespace: dict[str, object] = {} + exec(compile(output_file.read_text(encoding="utf-8"), str(output_file), "exec"), namespace) + parent = namespace["ParentModel"]() + item_model = namespace["ItemModel"] + assert parent.dict_with_defaults == { + "jedi": item_model(name="Yoda", description="The wise old jedi") + }Also applies to: 1953-1965, 1968-1980, 1983-1995
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/main/jsonschema/test_main_jsonschema.py` around lines 1938 - 1950, Add runtime assertions to the test_main_generate_pydantic_v2_model_default_dict_empty test (and the sibling tests at ranges 1953-1965, 1968-1980, 1983-1995): after generate(...) and assert_file_content(...), import or load the generated module and instantiate ParentModel to verify default behavior (e.g., ParentModel() yields items == {}), and for the populated-case tests instantiate ParentModel with the provided data and assert the items contain the expected ItemModel instances/values; use the generated class names ParentModel and ItemModel and keep these assertions alongside the existing snapshot checks to prevent regressions in default_factory handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/datamodel_code_generator/model/pydantic_base.py`:
- Around line 162-174: Extract the duplicated dict default-factory logic into a
helper method (e.g., _get_dict_default_factory) that accepts data_type_value and
returns either STANDARD_DICT, the generated lambda string, or None; the helper
should check the same conditions (data_type_value.reference,
isinstance(data_type_value.reference.source, BaseModelBase),
isinstance(self.default, dict)) and use data_type_value.alias or
data_type_value.reference.source.class_name and self._PARSE_METHOD when building
the lambda, then replace the two nearly identical blocks that reference
data_type/data_type_value/self.default with calls to this helper and return its
result when non-None.
In `@tests/main/jsonschema/test_main_jsonschema.py`:
- Around line 1938-1950: Add runtime assertions to the
test_main_generate_pydantic_v2_model_default_dict_empty test (and the sibling
tests at ranges 1953-1965, 1968-1980, 1983-1995): after generate(...) and
assert_file_content(...), import or load the generated module and instantiate
ParentModel to verify default behavior (e.g., ParentModel() yields items == {}),
and for the populated-case tests instantiate ParentModel with the provided data
and assert the items contain the expected ItemModel instances/values; use the
generated class names ParentModel and ItemModel and keep these assertions
alongside the existing snapshot checks to prevent regressions in default_factory
handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b3622713-6cd0-446a-aa9c-c524d0af2ece
⛔ Files ignored due to path filters (2)
tests/data/jsonschema/pydantic_v2_model_default_nullable_dict_empty.jsonis excluded by!tests/data/**/*.jsonand included by nonetests/data/jsonschema/pydantic_v2_model_default_nullable_dict_non_empty.jsonis excluded by!tests/data/**/*.jsonand included by none
📒 Files selected for processing (4)
src/datamodel_code_generator/model/pydantic_base.pytests/data/expected/main/jsonschema/pydantic_v2_model_default_nullable_dict_empty.pytests/data/expected/main/jsonschema/pydantic_v2_model_default_nullable_dict_non_empty.pytests/main/jsonschema/test_main_jsonschema.py
Breaking Change AnalysisResult: No breaking changes detected Reasoning: This PR is a bug fix that adds proper handling for dict fields with model value defaults in Pydantic v2. The previous code had a TODO comment ("TODO: Parse dict model for default") and would skip proper handling, producing incorrect output. The new code produces correct, working code with proper model_validate calls. While generated output will differ for affected schemas, this corrects broken behavior rather than breaking working functionality. The change follows the same pattern already established for list handling. This analysis was performed by Claude Code Action |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3046 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 86 86
Lines 18071 18091 +20
Branches 2101 2108 +7
=========================================
+ Hits 18071 18091 +20
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:
|
|
🎉 Released in 0.55.0 This PR is now available in the latest release. See the release notes for details. |
Fixes #3045 by handling dictionaries in the same way as the lists
Summary by CodeRabbit
New Features
Tests