mirror of https://github.com/tiangolo/fastapi.git
🐛 Fix `TypeError` when encoding a decimal with a `NaN` or `Infinity` value (#12935)
Signed-off-by: Kent Huang <kent@infuseai.io>
This commit is contained in:
parent
ee490906d8
commit
20f40b29c0
|
|
@ -34,14 +34,14 @@ def isoformat(o: Union[datetime.date, datetime.time]) -> str:
|
||||||
return o.isoformat()
|
return o.isoformat()
|
||||||
|
|
||||||
|
|
||||||
# Taken from Pydantic v1 as is
|
# Adapted from Pydantic v1
|
||||||
# TODO: pv2 should this return strings instead?
|
# TODO: pv2 should this return strings instead?
|
||||||
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
||||||
"""
|
"""
|
||||||
Encodes a Decimal as int of there's no exponent, otherwise float
|
Encodes a Decimal as int if there's no exponent, otherwise float
|
||||||
|
|
||||||
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
|
This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
|
||||||
where a integer (but not int typed) is used. Encoding this as a float
|
where an integer (but not int typed) is used. Encoding this as a float
|
||||||
results in failed round-tripping between encode and parse.
|
results in failed round-tripping between encode and parse.
|
||||||
Our Id type is a prime example of this.
|
Our Id type is a prime example of this.
|
||||||
|
|
||||||
|
|
@ -50,8 +50,12 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
||||||
|
|
||||||
>>> decimal_encoder(Decimal("1"))
|
>>> decimal_encoder(Decimal("1"))
|
||||||
1
|
1
|
||||||
|
|
||||||
|
>>> decimal_encoder(Decimal("NaN"))
|
||||||
|
nan
|
||||||
"""
|
"""
|
||||||
if dec_value.as_tuple().exponent >= 0: # type: ignore[operator]
|
exponent = dec_value.as_tuple().exponent
|
||||||
|
if isinstance(exponent, int) and exponent >= 0:
|
||||||
return int(dec_value)
|
return int(dec_value)
|
||||||
else:
|
else:
|
||||||
return float(dec_value)
|
return float(dec_value)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from dataclasses import dataclass
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from math import isinf, isnan
|
||||||
from pathlib import PurePath, PurePosixPath, PureWindowsPath
|
from pathlib import PurePath, PurePosixPath, PureWindowsPath
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
@ -306,6 +307,20 @@ def test_decimal_encoder_int():
|
||||||
assert jsonable_encoder(data) == {"value": 2}
|
assert jsonable_encoder(data) == {"value": 2}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_pydanticv2
|
||||||
|
def test_decimal_encoder_nan():
|
||||||
|
data = {"value": Decimal("NaN")}
|
||||||
|
assert isnan(jsonable_encoder(data)["value"])
|
||||||
|
|
||||||
|
|
||||||
|
@needs_pydanticv2
|
||||||
|
def test_decimal_encoder_infinity():
|
||||||
|
data = {"value": Decimal("Infinity")}
|
||||||
|
assert isinf(jsonable_encoder(data)["value"])
|
||||||
|
data = {"value": Decimal("-Infinity")}
|
||||||
|
assert isinf(jsonable_encoder(data)["value"])
|
||||||
|
|
||||||
|
|
||||||
def test_encode_deque_encodes_child_models():
|
def test_encode_deque_encodes_child_models():
|
||||||
class Model(BaseModel):
|
class Model(BaseModel):
|
||||||
test: str
|
test: str
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue