Skip to content

Deduplicate Pydantic v2 config helpers#3350

Merged
koxudaxi merged 9 commits into
mainfrom
refactor/pydantic-v2-config-dedup
Jun 12, 2026
Merged

Deduplicate Pydantic v2 config helpers#3350
koxudaxi merged 9 commits into
mainfrom
refactor/pydantic-v2-config-dedup

Conversation

@koxudaxi

@koxudaxi koxudaxi commented Jun 12, 2026

Copy link
Copy Markdown
Owner

No description provided.

@codspeed-hq

codspeed-hq Bot commented Jun 12, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 45.11%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 11 improved benchmarks
⏩ 98 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime test_perf_openapi_large 3.1 s 2.1 s +49.28%
WallTime test_perf_multiple_files_input 3.8 s 2.6 s +49.15%
WallTime test_perf_graphql_style_pydantic_v2 844 ms 575 ms +46.79%
WallTime test_perf_complex_refs 2.1 s 1.4 s +46.06%
WallTime test_perf_aws_style_openapi_pydantic_v2 2 s 1.3 s +46.02%
WallTime test_perf_kubernetes_style_pydantic_v2 2.8 s 1.9 s +45.24%
WallTime test_perf_duplicate_names 1,112.3 ms 772.8 ms +43.93%
WallTime test_perf_stripe_style_pydantic_v2 2.1 s 1.5 s +43.28%
WallTime test_perf_deep_nested 5.4 s 3.8 s +43.28%
WallTime test_perf_all_options_enabled 5.6 s 3.9 s +42.46%
WallTime test_perf_large_models_pydantic_v2 3.8 s 2.7 s +40.96%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing refactor/pydantic-v2-config-dedup (7f5fd40) with main (46a9e67)

Open in CodSpeed

Footnotes

  1. 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.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Extracts a shared PatternConstraints, centralizes Pydantic v2 config-building into model/pydantic_v2/_config.py, refactors BaseModel and DataClass to use the shared builder, updates msgspec to inherit PatternConstraints, and adds tests for config rendering and constraints.

Changes

Config Generation Refactoring

Layer / File(s) Summary
Pattern Constraint Extraction
src/datamodel_code_generator/model/pydantic_base.py, src/datamodel_code_generator/model/msgspec.py
Adds PatternConstraints (with regex and pattern) and updates msgspec to inherit it; removes local pydantic.Field import and regex/pattern overrides from Constraints.
Pydantic v2 Config Infrastructure
src/datamodel_code_generator/model/pydantic_v2/_config.py, src/datamodel_code_generator/model/pydantic_v2/__init__.py
Adds ConfigAttribute, get_config_extra, get_config_attributes, build_base_config_parameters, and ConfigDict model; re-exports ConfigDict from pydantic_v2.__init__.
BaseModel Config Generation Refactoring
src/datamodel_code_generator/model/pydantic_v2/base_model.py
Replaces inline config assembly with build_base_config_parameters(...), validates/stores config using ConfigDict, preserves regex_engine logic, and removes local config-attribute mapping and regex/pattern overrides.
DataClass Config Generation Refactoring
src/datamodel_code_generator/model/pydantic_v2/dataclass.py
Delegates DataClass config parameter construction to build_base_config_parameters(...) and removes private helpers for config attribute/extra resolution.
RootModel Config Flag
src/datamodel_code_generator/model/pydantic_v2/root_model.py
Declares RootModel.SUPPORTS_CONFIG_EXTRA = False and removes the _get_config_extra override.
Config Generation Test Coverage
tests/model/pydantic_v2/*
Adds tests verifying ConfigDict re-export, deterministic config key ordering and rendered snippets for BaseModel/DataClass, and PatternConstraints behavior across constraint implementations.

Sequence Diagram(s)

sequenceDiagram
  participant TemplateData
  participant BaseModel
  participant build_base_config_parameters
  participant ConfigDict
  TemplateData->>BaseModel: provide extra_template_data, all_data_types
  BaseModel->>build_base_config_parameters: build_base_config_parameters(extra_template_data, all_data_types, ...)
  build_base_config_parameters->>ConfigDict: return config parameters (if any)
  ConfigDict->>BaseModel: validated config (model_validate) stored in extra_template_data["config"]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

breaking-change-analyzed

Poem

I nibble at patterns, gentle and spry,
Regex and pattern now live by and by,
A builder hops in to gather the keys,
Configs line up tidy as leaves in the breeze,
🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Deduplicate Pydantic v2 config helpers' directly and accurately summarizes the main objective: extracting shared config helpers from multiple Pydantic v2 modules into a new _config.py module to eliminate code duplication.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/pydantic-v2-config-dedup

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

❤️ Share

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

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

📚 Docs Preview: https://pr-3350.datamodel-code-generator.pages.dev

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/datamodel_code_generator/model/pydantic_v2/dataclass.py (1)

13-16: 💤 Low value

Consider importing ConfigAttribute directly from _config module.

ConfigAttribute is defined in _config.py but imported here via base_model.py. Importing directly from the source would reduce indirection and make the dependency clearer.

-from datamodel_code_generator.model.pydantic_v2.base_model import (
-    ConfigAttribute,
-)
+from datamodel_code_generator.model.pydantic_v2._config import (
+    ConfigAttribute,
+    _ConfigGenMixin,
+)

And remove the duplicate _ConfigGenMixin import on line 13.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/datamodel_code_generator/model/pydantic_v2/dataclass.py` around lines 13
- 16, Replace the indirect import of ConfigAttribute from base_model by
importing ConfigAttribute directly from
datamodel_code_generator.model.pydantic_v2._config and remove the duplicated
_ConfigGenMixin import so that dataclass.py imports _ConfigGenMixin and
ConfigAttribute only from the _config module (eliminate the base_model import of
ConfigAttribute and the duplicate _ConfigGenMixin import).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/datamodel_code_generator/model/pydantic_v2/dataclass.py`:
- Around line 13-16: Replace the indirect import of ConfigAttribute from
base_model by importing ConfigAttribute directly from
datamodel_code_generator.model.pydantic_v2._config and remove the duplicated
_ConfigGenMixin import so that dataclass.py imports _ConfigGenMixin and
ConfigAttribute only from the _config module (eliminate the base_model import of
ConfigAttribute and the duplicate _ConfigGenMixin import).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bc7dc97e-a9ed-4297-8d07-d7a0728a2b4a

📥 Commits

Reviewing files that changed from the base of the PR and between a1252a2 and 2e0476b.

📒 Files selected for processing (7)
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • src/datamodel_code_generator/model/pydantic_v2/__init__.py
  • src/datamodel_code_generator/model/pydantic_v2/_config.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/model/pydantic_v2/dataclass.py
  • tests/model/pydantic_v2/test_config.py

@koxudaxi koxudaxi force-pushed the refactor/pydantic-v2-config-dedup branch 3 times, most recently from 68a27f0 to f565342 Compare June 12, 2026 09:17
@koxudaxi koxudaxi force-pushed the refactor/pydantic-v2-config-dedup branch from f565342 to bed2766 Compare June 12, 2026 09:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/model/pydantic_v2/test_root_model.py (1)

74-88: ⚡ Quick win

Cover the additionalProperties=False branch too.

This regression test only exercises the True -> extra='allow' path. The removed RootModel override suppressed config-extra unconditionally, so a bug in the False -> extra='forbid' mapping would still slip through. Please add a second case for additionalProperties=False or parametrize both values.

Suggested expansion
 def test_root_model_ignores_extra_config() -> None:
     """RootModel must not render ConfigDict(extra=...) because Pydantic rejects it."""
-    root_model = RootModel(
-        fields=[
-            DataModelFieldBase(
-                name="a",
-                data_type=DataType(type="str"),
-                required=True,
-            )
-        ],
-        reference=Reference(name="TestRootModel", path="test_root_model"),
-        extra_template_data=defaultdict(dict, {"test_root_model": {"additionalProperties": True}}),
-    )
-
-    assert "model_config" not in root_model.render()
+    for additional_properties in (True, False):
+        root_model = RootModel(
+            fields=[
+                DataModelFieldBase(
+                    name="a",
+                    data_type=DataType(type="str"),
+                    required=True,
+                )
+            ],
+            reference=Reference(name="TestRootModel", path="test_root_model"),
+            extra_template_data=defaultdict(
+                dict,
+                {"test_root_model": {"additionalProperties": additional_properties}},
+            ),
+        )
+
+        assert "model_config" not in root_model.render()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/model/pydantic_v2/test_root_model.py` around lines 74 - 88, Expand the
test_root_model_ignores_extra_config test to cover both values of
additionalProperties (True and False) by parametrizing or adding a second case:
create a RootModel (the same way using extra_template_data on RootModel and
DataModelFieldBase) with additionalProperties=False and assert the rendered
output from root_model.render() exercises the False -> extra='forbid' mapping
(e.g., check the rendered model_config or absence/contents reflect 'forbid'),
while keeping the existing True case that maps to 'allow'; ensure you reference
RootModel, extra_template_data, additionalProperties and render when locating
the test to modify.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/model/pydantic_v2/test_root_model.py`:
- Around line 74-88: Expand the test_root_model_ignores_extra_config test to
cover both values of additionalProperties (True and False) by parametrizing or
adding a second case: create a RootModel (the same way using extra_template_data
on RootModel and DataModelFieldBase) with additionalProperties=False and assert
the rendered output from root_model.render() exercises the False ->
extra='forbid' mapping (e.g., check the rendered model_config or
absence/contents reflect 'forbid'), while keeping the existing True case that
maps to 'allow'; ensure you reference RootModel, extra_template_data,
additionalProperties and render when locating the test to modify.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3e2d4a67-1860-4d9d-bf5a-541cdd2eb163

📥 Commits

Reviewing files that changed from the base of the PR and between f565342 and bed2766.

📒 Files selected for processing (9)
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • src/datamodel_code_generator/model/pydantic_v2/__init__.py
  • src/datamodel_code_generator/model/pydantic_v2/_config.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/model/pydantic_v2/dataclass.py
  • src/datamodel_code_generator/model/pydantic_v2/root_model.py
  • tests/model/pydantic_v2/test_config.py
  • tests/model/pydantic_v2/test_root_model.py
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/datamodel_code_generator/model/pydantic_v2/_config.py
  • src/datamodel_code_generator/model/pydantic_v2/init.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • tests/model/pydantic_v2/test_config.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/model/pydantic_v2/dataclass.py
  • src/datamodel_code_generator/model/msgspec.py

@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (46a9e67) to head (7f5fd40).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #3350   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          127       129    +2     
  Lines        28420     28450   +30     
  Branches      3452      3442   -10     
=========================================
+ Hits         28420     28450   +30     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@koxudaxi koxudaxi merged commit 276caac into main Jun 12, 2026
51 checks passed
@koxudaxi koxudaxi deleted the refactor/pydantic-v2-config-dedup branch June 12, 2026 11:56
@github-actions

Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: I could not access the prepared authoritative sources (analysis-context.md, pr.diff, deleted-lines.txt) — they live under /home/runner/work/_temp/ which is outside the session's allowed working directory, and every access method (Read tool permission, Bash cat/grep/python/cp/ls including sandbox-disabled, gh pr diff, and a subagent) was blocked. Per the ground rules, a breaking change (removed/changed CLI option, API symbol, default, template, etc.) may only be reported if it appears as a removed line in the prepared diff, which I cannot inspect. The only reliable in-repo evidence is PR #3350's merge metadata: title "Deduplicate Pydantic v2 config helpers" and commit messages ("Refactor pydantic v2 config generation", "Preserve Pydantic v2 ConfigDict surface", "Avoid copying Pydantic v2 config parameters", "Cover RootModel ignored extra modes", "Restore Pydantic v2 constraints surface", "Avoid RootModel config scans"). This describes an internal deduplication refactor that explicitly preserves/restores the generated config surface, indicating generated output is intended to be unchanged. With no evidenced removed lines and the refactor's stated goal of preserving behavior, I cannot assert any breaking change and therefore report none. Caveat: this conclusion is based on PR metadata, not the inaccessible diff.


This analysis was performed by Claude Code Action

@github-actions

Copy link
Copy Markdown
Contributor

🎉 Released in 0.64.0

This PR is now available in the latest release. See the release notes for details.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant