Skip to content

Commit

Permalink
Add ConfigDict.ser_json_inf_nan (#8159)
Browse files Browse the repository at this point in the history
Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
Co-authored-by: Egil-nov <95861118+Egil-nov@users.noreply.github.com>
  • Loading branch information
3 people committed Nov 17, 2023
1 parent 7f4cfe5 commit 24272b4
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/concepts/json_schema.md
Expand Up @@ -187,6 +187,7 @@ Specifically, the following config options are relevant:
* [`json_schema_extra`][pydantic.config.ConfigDict.json_schema_extra]
* [`ser_json_timedelta`][pydantic.config.ConfigDict.ser_json_timedelta]
* [`ser_json_bytes`][pydantic.config.ConfigDict.ser_json_bytes]
* [`ser_json_inf_nan`][pydantic.config.ConfigDict.ser_json_inf_nan]

### Unenforced `Field` constraints

Expand Down
3 changes: 3 additions & 0 deletions pydantic/_internal/_config.py
Expand Up @@ -67,6 +67,7 @@ class ConfigWrapper:
revalidate_instances: Literal['always', 'never', 'subclass-instances']
ser_json_timedelta: Literal['iso8601', 'float']
ser_json_bytes: Literal['utf8', 'base64']
ser_json_inf_nan: Literal['null', 'constants']
# whether to validate default values during validation, default False
validate_default: bool
validate_return: bool
Expand Down Expand Up @@ -167,6 +168,7 @@ def dict_not_none(**kwargs: Any) -> Any:
strict=self.config_dict.get('strict'),
ser_json_timedelta=self.config_dict.get('ser_json_timedelta'),
ser_json_bytes=self.config_dict.get('ser_json_bytes'),
ser_json_inf_nan=self.config_dict.get('ser_json_inf_nan'),
from_attributes=self.config_dict.get('from_attributes'),
loc_by_alias=self.config_dict.get('loc_by_alias'),
revalidate_instances=self.config_dict.get('revalidate_instances'),
Expand Down Expand Up @@ -236,6 +238,7 @@ def push(self, config_wrapper: ConfigWrapper | ConfigDict | None):
revalidate_instances='never',
ser_json_timedelta='iso8601',
ser_json_bytes='utf8',
ser_json_inf_nan='null',
validate_default=False,
validate_return=False,
protected_namespaces=('model_',),
Expand Down
9 changes: 9 additions & 0 deletions pydantic/config.py
Expand Up @@ -540,6 +540,15 @@ class Transaction(BaseModel):
- `'base64'` will serialize bytes to URL safe base64 strings.
"""

ser_json_inf_nan: Literal['null', 'constants']
"""
The encoding of JSON serialized infinity and NaN float values. Accepts the string values of `'null'` and `'constants'`.
Defaults to `'null'`.
- `'null'` will serialize infinity and NaN values as `null`.
- `'constants'` will serialize infinity and NaN values as `Infinity` and `NaN`.
"""

# whether to validate default values during validation, default False
validate_default: bool
"""Whether to validate default values during validation. Defaults to `False`."""
Expand Down
25 changes: 23 additions & 2 deletions tests/test_types.py
Expand Up @@ -38,7 +38,7 @@
import annotated_types
import dirty_equals
import pytest
from dirty_equals import HasRepr, IsOneOf, IsStr
from dirty_equals import HasRepr, IsFloatNan, IsOneOf, IsStr
from pydantic_core import CoreSchema, PydanticCustomError, SchemaError, core_schema
from typing_extensions import Annotated, Literal, TypedDict, get_args

Expand Down Expand Up @@ -2529,7 +2529,7 @@ class Model(BaseModel):
]


def test_finite_float_validation():
def test_infinite_float_validation():
class Model(BaseModel):
a: float = None

Expand All @@ -2538,6 +2538,27 @@ class Model(BaseModel):
assert math.isnan(Model(a=float('nan')).a)


@pytest.mark.parametrize(
('ser_json_inf_nan', 'input', 'output', 'python_roundtrip'),
(
('null', float('inf'), 'null', None),
('null', float('-inf'), 'null', None),
('null', float('nan'), 'null', None),
('constants', float('inf'), 'Infinity', float('inf')),
('constants', float('-inf'), '-Infinity', float('-inf')),
('constants', float('nan'), 'NaN', IsFloatNan),
),
)
def test_infinite_float_json_serialization(ser_json_inf_nan, input, output, python_roundtrip):
class Model(BaseModel):
model_config = ConfigDict(ser_json_inf_nan=ser_json_inf_nan)
a: float

json_string = Model(a=input).model_dump_json()
assert json_string == f'{{"a":{output}}}'
assert json.loads(json_string) == {'a': python_roundtrip}


@pytest.mark.parametrize('value', [float('inf'), float('-inf'), float('nan')])
def test_finite_float_validation_error(value):
class Model(BaseModel):
Expand Down

0 comments on commit 24272b4

Please sign in to comment.