Skip to content

Add Pydantic missing sentinel option#3510

Merged
koxudaxi merged 1 commit into
mainfrom
pydantic-missing-sentinel
Jul 1, 2026
Merged

Add Pydantic missing sentinel option#3510
koxudaxi merged 1 commit into
mainfrom
pydantic-missing-sentinel

Conversation

@koxudaxi

@koxudaxi koxudaxi commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Fixes: #437

Summary by CodeRabbit

  • New Features
    • Added --use-missing-sentinel to generate Pydantic v2 models using Pydantic’s MISSING sentinel for optional fields without defaults.
    • Extended --target-pydantic-version to support 2.12+, including compatibility updates for newer Pydantic behaviors.
  • Bug Fixes
    • Improved generated defaults, type hints, and nullable handling for missing-sentinel output (including correct aliasing behavior).
    • Added validation to reject unsupported option combinations unless using the Pydantic v2 base-model output.
  • Documentation
    • Updated CLI reference/quick reference content with the new option and 2.12 guidance.

@coderabbitai

coderabbitai Bot commented Jun 30, 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

Adds --use-missing-sentinel support for Pydantic v2.12, including config validation, parser propagation, field rendering changes, fixtures/tests, and updated CLI reference docs.

Changes

--use-missing-sentinel feature

Layer / File(s) Summary
Config contracts and CLI metadata
src/datamodel_code_generator/enums.py, src/datamodel_code_generator/_types/generate_config_dict.py, src/datamodel_code_generator/_types/parser_config_dicts.py, src/datamodel_code_generator/base_config.py, src/datamodel_code_generator/config.py, src/datamodel_code_generator/model/base.py, src/datamodel_code_generator/arguments.py, src/datamodel_code_generator/cli_options.py, src/datamodel_code_generator/prompt_data.py, docs/architecture.md, docs/cli-reference/index.md, docs/cli-reference/quick-reference.md, docs/cli-reference/model-customization.md, tests/test_main_kr.py
Adds TargetPydanticVersion.V2_12, use_missing_sentinel config fields, the new CLI flag, option metadata, prompt/docs entries, and updated configuration field counts.
generate() validation and parser wiring
src/datamodel_code_generator/__init__.py, src/datamodel_code_generator/parser/base.py, src/datamodel_code_generator/parser/openapi.py, src/datamodel_code_generator/parser/jsonschema.py, src/datamodel_code_generator/parser/graphql.py
Adds missing-sentinel validation in generate(), threads the flag into parser construction, and spreads shared field kwargs through parser field-building paths.
Pydantic v2 missing-sentinel rendering
src/datamodel_code_generator/model/pydantic_v2/imports.py, src/datamodel_code_generator/model/pydantic_v2/base_model.py, src/datamodel_code_generator/model/pydantic_base.py, src/datamodel_code_generator/model/pydantic_v2/_config.py
Defines the missing-sentinel import and updates Pydantic v2 field rendering, imports, type hints, defaults, and config attribute selection for V2_12.
Tests and generated fixtures
tests/data/expected/main/input_model/config_class.py, tests/data/expected/main/jsonschema/missing_sentinel*.py, tests/main/jsonschema/test_main_jsonschema.py, tests/main/test_public_api_signature_baseline.py, tests/model/test_base.py, tests/model/pydantic_v2/test_config.py, tests/test_enums.py
Adds golden fixtures, CLI/runtime tests, signature baseline updates, and version-comparison coverage for the new option and V2_12.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gaborbernat

Poem

🐇 I hopped where MISSING fields appear,
And v2.12 came bright and clear.
The defaults now know when to stay,
While absent bits can dance away.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main addition: a Pydantic missing sentinel option.
Linked Issues check ✅ Passed The PR adds the CLI option, generator support, docs, and tests needed for missing-sentinel handling.
Out of Scope Changes check ✅ Passed The changes are focused on the new missing-sentinel feature and its supporting docs, config, and tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pydantic-missing-sentinel

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.

@github-actions

Copy link
Copy Markdown
Contributor

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

Comment thread src/datamodel_code_generator/__init__.py Fixed
@codspeed-hq

codspeed-hq Bot commented Jun 30, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by ×2.2

⚠️ 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_graphql_style_pydantic_v2 820.4 ms 363.2 ms ×2.3
WallTime test_perf_aws_style_openapi_pydantic_v2 1,957.7 ms 873.6 ms ×2.2
WallTime test_perf_all_options_enabled 5.4 s 2.4 s ×2.2
WallTime test_perf_duplicate_names 1,036.5 ms 468.5 ms ×2.2
WallTime test_perf_kubernetes_style_pydantic_v2 2.7 s 1.2 s ×2.2
WallTime test_perf_complex_refs 2,015.1 ms 916.1 ms ×2.2
WallTime test_perf_large_models_pydantic_v2 3.5 s 1.6 s ×2.2
WallTime test_perf_openapi_large 3 s 1.4 s ×2.2
WallTime test_perf_deep_nested 5.1 s 2.4 s ×2.2
WallTime test_perf_multiple_files_input 3.5 s 1.6 s ×2.2
WallTime test_perf_stripe_style_pydantic_v2 1,949.7 ms 915.4 ms ×2.1

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 pydantic-missing-sentinel (e76a8c3) with main (1870d87)

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.

@koxudaxi koxudaxi force-pushed the pydantic-missing-sentinel branch from 5589d32 to 1de6600 Compare June 30, 2026 18:39
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1870d87) to head (e76a8c3).

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #3510    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files          153       154     +1     
  Lines        33896     34018   +122     
  Branches      3967      3984    +17     
==========================================
+ Hits         33896     34018   +122     
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 force-pushed the pydantic-missing-sentinel branch 3 times, most recently from 36ccde0 to 938d291 Compare July 1, 2026 01:06

@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_config.py (1)

129-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid deprecated .dict() in new test code.

.dict() on a Pydantic v2 model is deprecated in favor of model_dump(). The repo already has an open issue tracking migration off deprecated Pydantic v2 methods; adding new usages works against that effort and will emit PydanticDeprecatedSince20 warnings during the test run.

♻️ Suggested fix
-    config = model.extra_template_data["config"].dict(exclude_unset=True)
+    config = model.extra_template_data["config"].model_dump(exclude_unset=True)
🤖 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_config.py` at line 129, The test is still
calling the deprecated Pydantic v2 .dict() method on
model.extra_template_data["config"], which adds a new deprecated usage in test
code. Update this assertion/setup in the pydantic_v2 config test to use
model_dump() with the same exclude_unset behavior instead, and keep the change
localized to the config-related test path.
🤖 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_config.py`:
- Line 129: The test is still calling the deprecated Pydantic v2 .dict() method
on model.extra_template_data["config"], which adds a new deprecated usage in
test code. Update this assertion/setup in the pydantic_v2 config test to use
model_dump() with the same exclude_unset behavior instead, and keep the change
localized to the config-related test path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 15ea86c4-8c7f-4fdf-a3dd-4704ab9a4128

📥 Commits

Reviewing files that changed from the base of the PR and between 36ccde0 and 938d291.

⛔ Files ignored due to path filters (12)
  • docs/llms-full.txt is excluded by none and included by none
  • skills/datamodel-code-generator/references/cli-options.md is excluded by none and included by none
  • tests/data/expected/main/help/color.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main/help/no_color.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/basic.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/json_output.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_list_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_question.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/help_shows_new_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/prompt/default_parser_text.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/jsonschema/missing_sentinel.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (31)
  • docs/architecture.md
  • docs/cli-reference/index.md
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/quick-reference.md
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/_types/parser_config_dicts.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/base_config.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/config.py
  • src/datamodel_code_generator/enums.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/pydantic_base.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/imports.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/data/expected/main/input_model/config_class.py
  • tests/data/expected/main/jsonschema/missing_sentinel.py
  • tests/data/expected/main/jsonschema/missing_sentinel_no_union_operator.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/main/test_public_api_signature_baseline.py
  • tests/model/pydantic_v2/test_config.py
  • tests/model/test_base.py
  • tests/test_enums.py
  • tests/test_main_kr.py
✅ Files skipped from review due to trivial changes (7)
  • src/datamodel_code_generator/model/pydantic_base.py
  • docs/cli-reference/quick-reference.md
  • tests/test_main_kr.py
  • docs/architecture.md
  • tests/data/expected/main/jsonschema/missing_sentinel.py
  • docs/cli-reference/index.md
  • docs/cli-reference/model-customization.md
🚧 Files skipped from review as they are similar to previous changes (22)
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/config.py
  • src/datamodel_code_generator/model/pydantic_v2/imports.py
  • tests/data/expected/main/jsonschema/missing_sentinel_no_union_operator.py
  • src/datamodel_code_generator/_types/parser_config_dicts.py
  • src/datamodel_code_generator/model/pydantic_v2/_config.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/model/test_base.py
  • tests/main/test_public_api_signature_baseline.py
  • src/datamodel_code_generator/parser/base.py
  • tests/data/expected/main/input_model/config_class.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • src/datamodel_code_generator/init.py
  • src/datamodel_code_generator/enums.py
  • src/datamodel_code_generator/base_config.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py

@koxudaxi koxudaxi force-pushed the pydantic-missing-sentinel branch from 938d291 to 8eaaa14 Compare July 1, 2026 01:12

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

Actionable comments posted: 1

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

Inline comments:
In `@src/datamodel_code_generator/model/pydantic_v2/base_model.py`:
- Around line 253-258: The fall-back logic in the `fall_back_to_nullable`
property on `BaseModel` is dropping schema-level nullability when
`use_missing_sentinel_default` is enabled. Update this override so it still
honors `field.nullable` from `JsonSchemaParser.get_object_field()` (not just
`type_has_null`), preserving `None` in annotations for OpenAPI 3.0 nullable
fields while keeping the required-vs-optional distinction intact; use
`type_has_null` only as part of the nullable decision, not the sole condition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 649afd50-1512-4c21-bd78-a5fd89822b59

📥 Commits

Reviewing files that changed from the base of the PR and between 938d291 and 8eaaa14.

⛔ Files ignored due to path filters (14)
  • docs/llms-full.txt is excluded by none and included by none
  • skills/datamodel-code-generator/references/cli-options.md is excluded by none and included by none
  • tests/data/expected/enums/pydantic_version_at_least.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main/help/color.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main/help/no_color.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/basic.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/json_output.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_list_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/generate_prompt/with_question.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/main_kr/help_shows_new_options.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/model/pydantic_v2/config_target_pydantic_future_version.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/expected/prompt/default_parser_text.txt is excluded by !tests/data/**/*.txt and included by none
  • tests/data/jsonschema/missing_sentinel.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (31)
  • docs/architecture.md
  • docs/cli-reference/index.md
  • docs/cli-reference/model-customization.md
  • docs/cli-reference/quick-reference.md
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/_types/parser_config_dicts.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/base_config.py
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/config.py
  • src/datamodel_code_generator/enums.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/pydantic_base.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/imports.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/data/expected/main/input_model/config_class.py
  • tests/data/expected/main/jsonschema/missing_sentinel.py
  • tests/data/expected/main/jsonschema/missing_sentinel_no_union_operator.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • tests/main/test_public_api_signature_baseline.py
  • tests/model/pydantic_v2/test_config.py
  • tests/model/test_base.py
  • tests/test_enums.py
  • tests/test_main_kr.py
✅ Files skipped from review due to trivial changes (11)
  • tests/data/expected/main/jsonschema/missing_sentinel.py
  • tests/test_main_kr.py
  • src/datamodel_code_generator/prompt_data.py
  • tests/data/expected/main/jsonschema/missing_sentinel_no_union_operator.py
  • src/datamodel_code_generator/_types/parser_config_dicts.py
  • src/datamodel_code_generator/base_config.py
  • docs/cli-reference/index.md
  • docs/cli-reference/quick-reference.md
  • tests/data/expected/main/input_model/config_class.py
  • docs/cli-reference/model-customization.md
  • docs/architecture.md
🚧 Files skipped from review as they are similar to previous changes (17)
  • src/datamodel_code_generator/cli_options.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • src/datamodel_code_generator/_types/generate_config_dict.py
  • src/datamodel_code_generator/model/pydantic_v2/imports.py
  • src/datamodel_code_generator/config.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/enums.py
  • tests/model/test_base.py
  • src/datamodel_code_generator/model/pydantic_v2/_config.py
  • src/datamodel_code_generator/parser/graphql.py
  • tests/main/jsonschema/test_main_jsonschema.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/arguments.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/init.py
  • tests/main/test_public_api_signature_baseline.py
  • src/datamodel_code_generator/parser/jsonschema.py

Comment thread src/datamodel_code_generator/model/pydantic_v2/base_model.py Outdated
@koxudaxi koxudaxi force-pushed the pydantic-missing-sentinel branch from 8eaaa14 to 5253417 Compare July 1, 2026 01:23
@koxudaxi koxudaxi force-pushed the pydantic-missing-sentinel branch from 5253417 to e76a8c3 Compare July 1, 2026 02:12
@koxudaxi koxudaxi merged commit 0e02198 into main Jul 1, 2026
62 checks passed
@koxudaxi koxudaxi deleted the pydantic-missing-sentinel branch July 1, 2026 02:23
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR is purely additive. It introduces a new opt-in CLI flag --use-missing-sentinel (default off, action="store_true" with default=None) and a new 2.12 value for the TargetPydanticVersion enum. Every behavioral change is gated behind the new flag: the parser use_missing_sentinel attribute defaults to False, so nullable-handling changes in jsonschema.py/openapi.py (self.strict_nullable or self.use_missing_sentinel) are no-ops when the flag is unused. The one shared-code refactor (represent_python_value(self.default)self.represented_default in pydantic_base.py) is behavior-preserving because the base-class represented_default property returns exactly represent_python_value(self.default); the pydantic_v2 override only differs when use_missing_sentinel_default is true, which requires the flag. The get_config_attributes refactor to _is_pydantic_version_at_least preserves the identical config mapping for the existing 2 and 2.11 values. No CLI options, Python API parameters, enum values, classes, or functions appear as removed lines in the diff, and no defaults were changed. Per the ground rules, newly added opt-in options with behavior-preserving defaults are not breaking changes.


This analysis was performed by Claude Code Action

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🎉 Released in 0.66.2

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.

Differentiate "required with default", "required nullable", and "optional" properties

2 participants