-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add regex patterns to JSON schema for Decimal type
#11987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fde9d94
07db820
b6d3c4b
aa17cce
b9dc9cf
e0e7e56
5874292
73afd01
7b25a74
23210d7
88adb2d
f9ec7a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -674,7 +674,49 @@ def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: | |||||||||||||||||
| Returns: | ||||||||||||||||||
| The generated JSON schema. | ||||||||||||||||||
| """ | ||||||||||||||||||
| json_schema = self.str_schema(core_schema.str_schema()) | ||||||||||||||||||
|
|
||||||||||||||||||
| def get_decimal_pattern(schema: core_schema.DecimalSchema) -> str: | ||||||||||||||||||
| max_digits = schema.get('max_digits') | ||||||||||||||||||
| decimal_places = schema.get('decimal_places') | ||||||||||||||||||
|
|
||||||||||||||||||
| pattern = ( | ||||||||||||||||||
| r'^(?!^[-+.]*$)[+-]?0*' # check it is not empty string and not one or sequence of ".+-" characters. | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Case 1: Both max_digits and decimal_places are set | ||||||||||||||||||
| if max_digits is not None and decimal_places is not None: | ||||||||||||||||||
| integer_places = max(0, max_digits - decimal_places) | ||||||||||||||||||
| pattern += ( | ||||||||||||||||||
| rf'(?:' | ||||||||||||||||||
| rf'\d{{0,{integer_places}}}' | ||||||||||||||||||
| rf'|' | ||||||||||||||||||
| rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' | ||||||||||||||||||
| rf'\d{{0,{integer_places}}}\.\d{{0,{decimal_places}}}0*$' | ||||||||||||||||||
| rf')' | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Case 2: Only max_digits is set | ||||||||||||||||||
| elif max_digits is not None and decimal_places is None: | ||||||||||||||||||
| pattern += ( | ||||||||||||||||||
| rf'(?:' | ||||||||||||||||||
| rf'\d{{0,{max_digits}}}' | ||||||||||||||||||
| rf'|' | ||||||||||||||||||
| rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' | ||||||||||||||||||
| rf'\d*\.\d*0*$' | ||||||||||||||||||
| rf')' | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Case 3: Only decimal_places is set | ||||||||||||||||||
| elif max_digits is None and decimal_places is not None: | ||||||||||||||||||
| pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$' | ||||||||||||||||||
|
|
||||||||||||||||||
| # Case 4: Both are None (no restrictions) | ||||||||||||||||||
| else: | ||||||||||||||||||
| pattern += r'\d*\.?\d*$' # look for arbitrary integer or decimal | ||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for pointing this out. The complete pattern includes an additional regex component that prevents pydantic/pydantic/json_schema.py Lines 682 to 684 in 88adb2d
Additionally, the edge case for pydantic/tests/test_json_schema.py Lines 7005 to 7009 in 88adb2d
|
||||||||||||||||||
|
|
||||||||||||||||||
| return pattern | ||||||||||||||||||
|
|
||||||||||||||||||
| json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema))) | ||||||||||||||||||
| if self.mode == 'validation': | ||||||||||||||||||
| multiple_of = schema.get('multiple_of') | ||||||||||||||||||
| le = schema.get('le') | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With
max_digitsset to e.g. 5, it wrongfully matches'1000.1111111', etc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your feedback.
From my testing, the pattern appears to work correctly when
max_digits=5and the invalid value'1000.1111111'is rejected as expected.Here is a test demonstrating this behavior:
Let me know if there's a specific case I may have missed!