Browse Source

🐛 Fix `TypeError` when encoding a decimal with a `NaN` or `Infinity` value (#12935)

Signed-off-by: Kent Huang <[email protected]>
pull/14432/head
Kent Huang 6 months ago
committed by GitHub
parent
commit
20f40b29c0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      fastapi/encoders.py
  2. 15
      tests/test_jsonable_encoder.py

12
fastapi/encoders.py

@ -34,14 +34,14 @@ def isoformat(o: Union[datetime.date, datetime.time]) -> str:
return o.isoformat()
# Taken from Pydantic v1 as is
# Adapted from Pydantic v1
# TODO: pv2 should this return strings instead?
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)
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.
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"))
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)
else:
return float(dec_value)

15
tests/test_jsonable_encoder.py

@ -3,6 +3,7 @@ from dataclasses import dataclass
from datetime import datetime, timezone
from decimal import Decimal
from enum import Enum
from math import isinf, isnan
from pathlib import PurePath, PurePosixPath, PureWindowsPath
from typing import Optional
@ -306,6 +307,20 @@ def test_decimal_encoder_int():
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():
class Model(BaseModel):
test: str

Loading…
Cancel
Save