Browse Source

🐛 Fix decimal_encoder crash on Decimal("sNaN") (signaling NaN)

Decimal('sNaN') is a valid Decimal value but float(Decimal('sNaN'))
raises ValueError. The else branch in decimal_encoder always called
float(), so any signaling NaN value reaching jsonable_encoder caused
an unhandled crash.

Fix: wrap the float() call in a try/except ValueError and fall back to
float('nan') for signaling NaN, consistent with the existing behaviour
for Decimal('NaN') (quiet NaN).

Added test_decimal_encoder_signaling_nan to cover the regression.
pull/15783/head
JSap0914 1 month ago
parent
commit
5729e674d3
  1. 10
      fastapi/encoders.py
  2. 8
      tests/test_jsonable_encoder.py

10
fastapi/encoders.py

@ -73,12 +73,20 @@ def decimal_encoder(dec_value: Decimal) -> int | float:
>>> decimal_encoder(Decimal("NaN"))
nan
>>> decimal_encoder(Decimal("sNaN"))
nan
"""
exponent = dec_value.as_tuple().exponent
if isinstance(exponent, int) and exponent >= 0:
return int(dec_value)
else:
return float(dec_value)
try:
return float(dec_value)
except ValueError:
# Signaling NaN (sNaN) cannot be converted to float directly.
# Treat it as a quiet NaN, consistent with Decimal("NaN") behaviour.
return float("nan")
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {

8
tests/test_jsonable_encoder.py

@ -299,6 +299,14 @@ def test_decimal_encoder_infinity():
assert isinf(jsonable_encoder(data)["value"])
def test_decimal_encoder_signaling_nan():
# Decimal("sNaN") (signaling NaN) previously raised ValueError when
# decimal_encoder called float() on it. It should now return float("nan")
# consistently with Decimal("NaN") behaviour.
data = {"value": Decimal("sNaN")}
assert isnan(jsonable_encoder(data)["value"])
def test_encode_deque_encodes_child_models():
class Model(BaseModel):
test: str

Loading…
Cancel
Save