Skip to content

Use semantic None default checks#3479

Merged
koxudaxi merged 1 commit into
mainfrom
fix/semantic-none-checks
Jun 23, 2026
Merged

Use semantic None default checks#3479
koxudaxi merged 1 commit into
mainfrom
fix/semantic-none-checks

Conversation

@koxudaxi

@koxudaxi koxudaxi commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Bug Fixes

    • Improved --strip-default-none behavior so “default None” is stripped based on semantic equality to None, not on falsy values or string literals like 'None'.
    • Ensures consistent field rendering and ordering across supported model types, including optional fields with None-like defaults.
  • Tests

    • Added a new regression test and golden fixtures covering dataclass and msgspec outputs with --target-python-version 3.10.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 62c04e36-acc8-4070-84fc-1f14b3b9dbfa

📥 Commits

Reviewing files that changed from the base of the PR and between 00cde07 and 03126ae.

⛔ Files ignored due to path filters (1)
  • tests/data/openapi/strip_default_none_semantic.yaml is excluded by !tests/data/**/*.yaml and included by none
📒 Files selected for processing (7)
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/dataclass.py
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_dataclass.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_msgspec.py
  • tests/main/openapi/test_main_openapi.py
✅ Files skipped from review due to trivial changes (2)
  • tests/data/expected/main/openapi/strip_default_none_semantic_dataclass.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_msgspec.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/datamodel_code_generator/model/pydantic_base.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/dataclass.py
  • tests/main/openapi/test_main_openapi.py

📝 Walkthrough

Walkthrough

Introduces DataModelFieldBase.should_strip_default_none(keep_optional=False) as a shared method in base.py that centralizes the logic for deciding whether a None default should be omitted. Three model-type implementations (dataclass.py, msgspec.py, pydantic_base.py) are updated to call this method instead of their previous inline conditions, and the now-redundant private _should_strip_default_none_from_field helper in pydantic_base.py is removed. New golden fixtures and a parametrized E2E test validate the semantic behavior.

Changes

strip_default_none refactor and test coverage

Layer / File(s) Summary
Base helper: should_strip_default_none
src/datamodel_code_generator/model/base.py
Adds DataModelFieldBase.should_strip_default_none(keep_optional=False) — the canonical decision method gating on strip_default_none, the keep_optional flag combined with data_type.is_optional, and self.default is None.
Caller migration: dataclass, msgspec, pydantic_base
src/datamodel_code_generator/model/dataclass.py, src/datamodel_code_generator/model/msgspec.py, src/datamodel_code_generator/model/pydantic_base.py
Replaces represented_default == "None" and strip_default_none inline checks in dataclass.py and msgspec.py with field.should_strip_default_none(). In pydantic_base.py, removes _should_strip_default_none_from_field() and updates __str__ to call self.should_strip_default_none(keep_optional=True).
Golden fixtures and E2E tests
tests/data/expected/main/openapi/strip_default_none_semantic_dataclass.py, tests/data/expected/main/openapi/strip_default_none_semantic_msgspec.py, tests/main/openapi/test_main_openapi.py
Adds dataclass and msgspec golden output fixtures for SemanticNoneModel showing that string 'None' defaults are preserved while actual None defaults are stripped. Adds a parametrized test test_main_openapi_strip_default_none_semantic_default covering both dataclasses.dataclass and msgspec.Struct.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • koxudaxi/datamodel-code-generator#3292: Both PRs change src/datamodel_code_generator/model/msgspec.py's has_field_assignment logic used for msgspec.Struct field ordering (switching the assignment/default predicate to the new should_strip_default_none-based behavior in the main PR vs adjusting the sort-key predicate in the retrieved PR).
  • koxudaxi/datamodel-code-generator#3476: Both PRs modify src/datamodel_code_generator/model/pydantic_base.py to change how Field(..., ...) rendering strips a None default (especially when Field metadata is present), updating the exact predicate used for None default omission.

Suggested labels

breaking-change-analyzed, breaking-change

Poem

🐇 A helper appeared in the base,
Where None-stripping logic found its place.
No more scattered checks in three —
One method sets all defaults free!
🥕 String 'None' lives on, true None is gone.

🚥 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 clearly and concisely summarizes the main change: introducing semantic None default checks as the basis for the strip-default-none feature.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/semantic-none-checks

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

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

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

@codspeed-hq

codspeed-hq Bot commented Jun 23, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 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.

✅ 11 untouched benchmarks
⏩ 98 skipped benchmarks1


Comparing fix/semantic-none-checks (03126ae) with main (db0fe49)

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 fix/semantic-none-checks branch from 383842a to 35e7c4d Compare June 23, 2026 01:04
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (9745f5b) to head (03126ae).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #3479   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          151       151           
  Lines        32494     32498    +4     
  Branches      3783      3783           
=========================================
+ Hits         32494     32498    +4     
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 fix/semantic-none-checks branch from 35e7c4d to 00cde07 Compare June 23, 2026 02:04

@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/main/openapi/test_main_openapi.py (1)

1322-1336: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding explicit test IDs for clearer test output.

The parametrization would benefit from explicit ids to make pytest output more readable.

📝 Suggested enhancement
 `@pytest.mark.parametrize`(
     ("output_model_type", "target_python_version", "expected_file"),
     [
         (
             "dataclasses.dataclass",
             "3.10",
             "strip_default_none_semantic_dataclass.py",
         ),
         (
             "msgspec.Struct",
             "3.10",
             "strip_default_none_semantic_msgspec.py",
         ),
     ],
+    ids=["dataclass", "msgspec"],
 )
🤖 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/main/openapi/test_main_openapi.py` around lines 1322 - 1336, Add
explicit test IDs to the `@pytest.mark.parametrize` decorator to improve pytest
output readability. Include an ids parameter as the fourth argument to the
parametrize call with descriptive string identifiers for each test case (one ID
for the dataclasses.dataclass case and one for the msgspec.Struct case), so that
pytest reports use these meaningful names instead of the default parameter
indices.
🤖 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/main/openapi/test_main_openapi.py`:
- Around line 1322-1336: Add explicit test IDs to the `@pytest.mark.parametrize`
decorator to improve pytest output readability. Include an ids parameter as the
fourth argument to the parametrize call with descriptive string identifiers for
each test case (one ID for the dataclasses.dataclass case and one for the
msgspec.Struct case), so that pytest reports use these meaningful names instead
of the default parameter indices.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: efd76873-2a14-4b49-b24f-7ab5b7061e2a

📥 Commits

Reviewing files that changed from the base of the PR and between 35e7c4d and 00cde07.

⛔ Files ignored due to path filters (1)
  • tests/data/openapi/strip_default_none_semantic.yaml is excluded by !tests/data/**/*.yaml and included by none
📒 Files selected for processing (7)
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/dataclass.py
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic_base.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_dataclass.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_msgspec.py
  • tests/main/openapi/test_main_openapi.py
✅ Files skipped from review due to trivial changes (2)
  • tests/data/expected/main/openapi/strip_default_none_semantic_dataclass.py
  • tests/data/expected/main/openapi/strip_default_none_semantic_msgspec.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/datamodel_code_generator/model/dataclass.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic_base.py

@koxudaxi koxudaxi force-pushed the fix/semantic-none-checks branch from 00cde07 to 03126ae Compare June 23, 2026 02:11
@koxudaxi koxudaxi merged commit ea7a3aa into main Jun 23, 2026
62 checks passed
@koxudaxi koxudaxi deleted the fix/semantic-none-checks branch June 23, 2026 02:24
@github-actions

Copy link
Copy Markdown
Contributor

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: PR #3479 is an internal, behavior-preserving refactor of the --strip-default-none logic. It consolidates duplicated checks into a new should_strip_default_none() method on the field base class. The old dataclass/msgspec condition represented_default == "None" (where represented_default is repr(self.default)) is functionally equivalent to the new self.default is None check, since a string default "None" reprs to 'None' and never matched. The removed _should_strip_default_none_from_field was a private method (not public API, not used in any Jinja2 template) replaced by equivalent logic via should_strip_default_none(keep_optional=True). No CLI options, public Python API, default values, generated-output behavior, Python version support, or templates change in a user-observable way. The added tests only document existing behavior. Therefore there are no breaking changes.


This analysis was performed by Claude Code Action

@github-actions

Copy link
Copy Markdown
Contributor

🎉 Released in 0.65.1

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