Skip to content

Support numeric constraints for decimal.Decimal#1006

Open
Siyet wants to merge 2 commits intomainfrom
decimal-constraints-683
Open

Support numeric constraints for decimal.Decimal#1006
Siyet wants to merge 2 commits intomainfrom
decimal-constraints-683

Conversation

@Siyet
Copy link
Copy Markdown
Collaborator

@Siyet Siyet commented Apr 9, 2026

Reopening #998 (auto-closed when fork was deleted).

Fixes #683.

Previously, the numeric constraints gt, ge, lt, le, and multiple_of in msgspec.Meta were only valid for int and float types. This PR extends support to decimal.Decimal.

PR #692 by @cocorigon has been open since May 2024. The author has since explicitly declined to continue it and invited others to take it over. This is a fresh implementation with documentation and full test coverage.

Changes

  • _core.c: Added five new TypeNode constraint flags backed by PyObject* slots storing Decimal instances. A new ms_check_decimal_constraints() function is called after every Decimal decode path.
  • __init__.pyi: Updated Meta.__init__ parameter types to include Decimal.
  • docs/constraints.rst: Updated Numeric Constraints section to mention decimal.Decimal.
  • tests/unit/test_constraints.py: Added TestDecimalConstraints covering all constraint types.

Notable design decisions

  • multiple_of for Decimal uses Python's % operator, which gives exact arithmetic.
  • Constraint bounds passed as float are converted to Decimal via string representation to preserve precision.

@ofek
Copy link
Copy Markdown
Collaborator

ofek commented Apr 9, 2026

It looks like the Git history needs to be fixed on this one.

@Siyet Siyet requested review from jcrist and ofek April 10, 2026 07:32
@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 10, 2026

The failing build job here is unrelated to this PR: it's the link checker tripping on the Pydantic docs redirect (docs.pydantic.dev/latest/pydantic.dev/docs/validation/...), which is fixed in #1008. Once #1008 lands and this branch is rebased, CI should go green.

@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 10, 2026

@ofek done — force-pushed a clean rewrite of the branch:

  • Squashed into a single commit with a proper message and Closes #683 reference.
  • Rebased onto current main, dropping all the leftover commits from the deleted fork (CODEOWNERS, package rename, doc-link rewrites, plus changes that belong to other PRs).
  • Scope is now strictly the four Decimal-relevant files: _core.c, __init__.pyi, docs/constraints.rst, tests/unit/test_constraints.py. Diff stat is +353/-52.
  • MS_CONSTR_DECIMAL_* flags claim bits 36-40, the lowest unused slots in TypeNode->types. If Support Literal[True] and Literal[False] types #1004 lands first, I'll rebase to whatever bits are next free.

Build job is still red on the link checker but that's the unrelated #1008 fix; nothing in this branch touches it.

Extends gt, ge, lt, le, and multiple_of Meta constraints to work with
decimal.Decimal types, enabling exact arithmetic comparisons and
sub-integer precision constraints without floating point issues.

Constraint bounds passed as int or float are coerced to Decimal via
their string representation to preserve precision. multiple_of uses
Python's % operator for exact arithmetic.

Closes #683
@Siyet Siyet force-pushed the decimal-constraints-683 branch from d2ce46f to 023b9e1 Compare April 20, 2026 19:22
@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 20, 2026

Rebased onto main now that #1004 has landed. #1004 took bits 36-37 for MS_TYPE_BOOLLITERAL_TRUE/FALSE, so I shifted the Decimal constraint flags to the next free slots:

  • MS_CONSTR_DECIMAL_GT/GE/LT/LE -> bits 38-41 (the remaining gap between types and existing constraints)
  • MS_CONSTR_DECIMAL_MULTIPLE_OF -> bit 61 (the only free slot that's left below the reserved MS_EXTRA_FLAG at 63)

No other constraint bits were moved. SLOT numbering and the TypeNode->details[] ordering are unchanged (SLOTs use the named constants, not raw bit positions).

Copy link
Copy Markdown
Contributor

@provinzkraut provinzkraut left a comment

Choose a reason for hiding this comment

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

I think this should have a check that disallows Decimal for gt, ge, etc. constraints where the type is not also a Decimal

Coercing a Decimal bound to int or float silently drops precision, which
defeats the point of specifying the bound as a Decimal in the first
place. Only allow Decimal bounds when the annotated type is also
Decimal, and raise a TypeError otherwise at TypeNode construction time.
@Siyet
Copy link
Copy Markdown
Collaborator Author

Siyet commented Apr 21, 2026

@provinzkraut added the check: a Decimal in gt/ge/lt/le/multiple_of now raises TypeError at encoder construction time when the annotated type isn't Decimal. Coercing to int/float loses precision, so a Decimal bound there is pointless.

Covered with a parametrized test, docs updated.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support numeric constraints for Decimal values

3 participants