Skip to content

Support --use-annotated *and* --use-non-positive-negative-number-constrained-types#3015

Merged
koxudaxi merged 10 commits intokoxudaxi:mainfrom
torarvid:support-use-annotated-with-non-positive
Mar 3, 2026
Merged

Support --use-annotated *and* --use-non-positive-negative-number-constrained-types#3015
koxudaxi merged 10 commits intokoxudaxi:mainfrom
torarvid:support-use-annotated-with-non-positive

Conversation

@torarvid
Copy link
Copy Markdown
Contributor

@torarvid torarvid commented Mar 2, 2026

Fixes #3014

Summary by CodeRabbit

  • Bug Fixes

    • Avoids duplicate or conflicting numeric constraints in generated models when specialized non‑positive/non‑negative numeric types are used.
  • New Features

    • Generator now prefers specialized NonNegative/NonPositive numeric types where applicable, omitting zero-based Field() bounds.
    • Updated example output adds a model demonstrating mixed constrained fields, including a plain constrained int field.
  • Tests

    • Added tests covering annotated types with non‑positive/non‑negative constrained numbers and combinations with other numeric bounds.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 2, 2026

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
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds an internal mapping of specialized numeric constrained types to the JSON Schema keys they consume; removes those consumed keys from field constraints when emitting Annotated fields; refines how numeric constraints are collected and passed to data-type constructors; and adds tests plus expected output for annotated + constrained-type combinations.

Changes

Cohort / File(s) Summary
Constraint deduplication & data-type logic
src/datamodel_code_generator/parser/jsonschema.py
Introduce _CONSTRAINED_TYPE_CONSUMED_KEYS mapping; strip consumed numeric constraint keys for specialized constrained types in get_object_field; adjust get_data_type to selectively collect/pass numeric constraint kwargs when field_constraints is enabled and honor use_non_positive_negative_number_constrained_types.
Test coverage
tests/test_main_kr.py
Add tests verifying combination of --use-annotated with --use-non-positive-negative-number-constrained-types, including cases with additional numeric bounds; tests skip on older Pydantic versions.
Expected generated output
tests/data/expected/main_kr/use_non_positive_negative_with_other_constraints/output.py
Add expected Python output using Annotated with NonNegative*/NonPositive* types and preserved non-zero Field bounds for bounded examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

breaking-change-analyzed

Poem

🐇 I hopped through schema rows today,
Pulled out keys that got in the way.
Annotated types now wear their crown,
No duplicate Fields weighing them down,
Thump-thump — I leave, a carrot gown 🥕

🚥 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 accurately describes the main change: enabling --use-annotated and --use-non-positive-negative-number-constrained-types flags to work together.
Linked Issues check ✅ Passed The PR directly addresses issue #3014 by implementing logic to use specialized constrained types (NonNegativeInt, NonPositiveInt, etc.) within Annotated when both flags are enabled, matching the expected behavior.
Out of Scope Changes check ✅ Passed All changes focus on fixing the interaction between --use-annotated and --use-non-positive-negative-number-constrained-types flags. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Comment thread src/datamodel_code_generator/parser/jsonschema.py Fixed
Comment thread src/datamodel_code_generator/parser/jsonschema.py Fixed
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 2, 2026

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 torarvid:support-use-annotated-with-non-positive (25ed824) with main (bff6a30)

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/datamodel_code_generator/parser/jsonschema.py`:
- Around line 1309-1311: Remove the commented-out debug statement causing Ruff
ERA001: delete the line "sys.stderr.write(f\"yoyo {kwargs}\\n\\n\")" from the
block where kwargs = model_dump(obj) and the subsequent if
self.field_constraints check; if you still need runtime debug info, replace it
with a proper logger.debug call (e.g., use the module/class logger) rather than
a commented or inline sys.stderr write so linting and CI pass for jsonschema.py
around model_dump and self.field_constraints.
- Around line 1320-1325: The current strict check requiring len(number_kwargs)
== 1 prevents emitting NonNegative*/NonPositive* types when one of the numeric
bounds equals 0 but additional numeric constraints exist; locate the logic in
jsonschema.py around number_kwargs, kwargs_to_pass and
data_type_manager.use_non_positive_negative_number_constrained_types and replace
the single-constraint gate with a check that detects a zero bound (e.g. check if
0 is present in number_kwargs.values() or specifically if
minimum/exclusiveMinimum/maximum/exclusiveMaximum == 0) and, when
use_non_positive_negative_number_constrained_types is true, set kwargs_to_pass =
number_kwargs so the specialized NonNegative*/NonPositive* types are emitted
while leftover numeric constraints remain on Field().

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9955314 and 11465e8.

📒 Files selected for processing (2)
  • src/datamodel_code_generator/parser/jsonschema.py
  • tests/test_main_kr.py

Comment thread src/datamodel_code_generator/parser/jsonschema.py Outdated
Comment thread src/datamodel_code_generator/parser/jsonschema.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (bff6a30) to head (25ed824).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #3015   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           94        94           
  Lines        18152     18176   +24     
  Branches      2101      2108    +7     
=========================================
+ Hits         18152     18176   +24     
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 Sentry.
📢 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/datamodel_code_generator/parser/jsonschema.py (1)

1317-1321: ⚠️ Potential issue | 🟠 Major

Relax the single-constraint gate so zero-bound specialized types still emit with extra numeric constraints.

On Line 1317, len(number_kwargs) == 1 is too strict. Cases like minimum=0 plus another numeric constraint won’t emit NonNegative*/NonPositive*, even though the extra constraints can remain on Field(...).

Suggested fix
-                # Only works if there is exactly one number constraint
-                if len(number_kwargs) == 1:
-                    v = next(iter(number_kwargs.values()))
-                    if v == 0 and self.data_type_manager.use_non_positive_negative_number_constrained_types:
-                        kwargs_to_pass = number_kwargs
+                if self.data_type_manager.use_non_positive_negative_number_constrained_types:
+                    zero_bound_keys = [k for k, v in number_kwargs.items() if v == 0]
+                    if len(zero_bound_keys) == 1:
+                        k = zero_bound_keys[0]
+                        kwargs_to_pass = {k: number_kwargs[k]}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/datamodel_code_generator/parser/jsonschema.py` around lines 1317 - 1321,
The code currently only enables zero-bound specialized types when number_kwargs
has exactly one entry; relax this to detect any numeric constraint equal to 0 so
NonNegative*/NonPositive* types still emit even with extra constraints. In the
block referencing number_kwargs, kwargs_to_pass, and
self.data_type_manager.use_non_positive_negative_number_constrained_types,
replace the len(number_kwargs) == 1 check with a condition that checks whether
any value in number_kwargs equals 0 (e.g., any(v == 0 for v in
number_kwargs.values())) and keep setting kwargs_to_pass = number_kwargs when
true.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/datamodel_code_generator/parser/jsonschema.py`:
- Around line 1317-1321: The code currently only enables zero-bound specialized
types when number_kwargs has exactly one entry; relax this to detect any numeric
constraint equal to 0 so NonNegative*/NonPositive* types still emit even with
extra constraints. In the block referencing number_kwargs, kwargs_to_pass, and
self.data_type_manager.use_non_positive_negative_number_constrained_types,
replace the len(number_kwargs) == 1 check with a condition that checks whether
any value in number_kwargs equals 0 (e.g., any(v == 0 for v in
number_kwargs.values())) and keep setting kwargs_to_pass = number_kwargs when
true.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11465e8 and 0670733.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/parser/jsonschema.py

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/datamodel_code_generator/parser/jsonschema.py (1)

1320-1325: ⚠️ Potential issue | 🟠 Major

Line 1321 gating is still too strict for mixed numeric constraints.

Requiring len(number_kwargs) == 1 prevents NonNegative*/NonPositive* from being used when a zero-bound exists alongside another numeric bound (for example, minimum=0 with maximum=10), even though the extra constraint can remain in Field(...).

Suggested fix
-                # Only works if there is exactly one number constraint
-                if len(number_kwargs) == 1:
-                    v = next(iter(number_kwargs.values()))
-                    if v == 0 and self.data_type_manager.use_non_positive_negative_number_constrained_types:
-                        kwargs_to_pass = number_kwargs
+                if self.data_type_manager.use_non_positive_negative_number_constrained_types:
+                    zero_bound_keys = [k for k, v in number_kwargs.items() if v == 0]
+                    if len(zero_bound_keys) == 1:
+                        key = zero_bound_keys[0]
+                        # Pass only the zero-bound key so the type manager can pick
+                        # NonNegative*/NonPositive* while remaining constraints stay on Field(...)
+                        kwargs_to_pass = {key: number_kwargs[key]}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/datamodel_code_generator/parser/jsonschema.py` around lines 1320 - 1325,
The guard requiring len(number_kwargs) == 1 is too strict; change the logic in
the JSON Schema parser so that if
self.data_type_manager.use_non_positive_negative_number_constrained_types is
true and any numeric bound in number_kwargs has the zero value (i.e., any(v == 0
for v in number_kwargs.values())), set kwargs_to_pass to a dict containing only
that zero-bound (extract the key/value where v == 0) so the
NonNegative*/NonPositive* typed choice can be used while leaving the remaining
numeric constraints in number_kwargs to be emitted later (keep references to
number_kwargs, kwargs_to_pass, and
self.data_type_manager.use_non_positive_negative_number_constrained_types).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/datamodel_code_generator/parser/jsonschema.py`:
- Around line 1320-1325: The guard requiring len(number_kwargs) == 1 is too
strict; change the logic in the JSON Schema parser so that if
self.data_type_manager.use_non_positive_negative_number_constrained_types is
true and any numeric bound in number_kwargs has the zero value (i.e., any(v == 0
for v in number_kwargs.values())), set kwargs_to_pass to a dict containing only
that zero-bound (extract the key/value where v == 0) so the
NonNegative*/NonPositive* typed choice can be used while leaving the remaining
numeric constraints in number_kwargs to be emitted later (keep references to
number_kwargs, kwargs_to_pass, and
self.data_type_manager.use_non_positive_negative_number_constrained_types).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c761dec and 9148e4e.

📒 Files selected for processing (1)
  • src/datamodel_code_generator/parser/jsonschema.py

@koxudaxi koxudaxi force-pushed the support-use-annotated-with-non-positive branch from dc72fac to 0133e16 Compare March 3, 2026 01:58
@koxudaxi koxudaxi force-pushed the support-use-annotated-with-non-positive branch from 0133e16 to 3475626 Compare March 3, 2026 02:13
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@tests/test_main_kr.py`:
- Around line 1388-1415: Add the same Pydantic-version guard as the neighboring
tests: decorate
test_use_non_positive_negative_number_constrained_types_with_other_constraints_with
`@pytest.mark.skipif`(pydantic.VERSION < "2.0.0", reason="requires pydantic>=2.0")
so the test is skipped for Pydantic < 2.0.0; ensure pytest and pydantic are
imported at the top if not already, and match the exact decorator style used on
the adjacent tests.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0133e16 and 445886b.

⛔ Files ignored due to path filters (1)
  • tests/data/jsonschema/use_non_positive_negative_with_other_constraints.json is excluded by !tests/data/**/*.json and included by none
📒 Files selected for processing (3)
  • src/datamodel_code_generator/parser/jsonschema.py
  • tests/data/expected/main_kr/use_non_positive_negative_with_other_constraints/output.py
  • tests/test_main_kr.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/data/expected/main_kr/use_non_positive_negative_with_other_constraints/output.py

Comment thread tests/test_main_kr.py Outdated
@koxudaxi koxudaxi merged commit 875b3cf into koxudaxi:main Mar 3, 2026
40 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 3, 2026

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR is a bug fix that enables the existing --use-non-positive-negative-number-constrained-types flag to work correctly when combined with --use-annotated. Previously, this combination produced incorrect or duplicated constraints. The fix ensures: (1) specialized types like NonNegativeInt work properly with Annotated[], (2) constraints already encoded in the type are not duplicated in Field(), and (3) additional constraints are properly preserved. No CLI flags were changed, no defaults were altered, no Python version changes, and no custom template updates are required. Users who were not using both flags together will see no change. Users who were using both flags together will now get correct output instead of buggy output.


This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 4, 2026

🎉 Released in 0.54.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.

Doesn't work to combine --use-annotated and --use-non-positive-negative-number-constrained-types

3 participants