diff --git a/python/pyiceberg/expressions/literals.py b/python/pyiceberg/expressions/literals.py index 4832da591fa8..cd53ab6d41dc 100644 --- a/python/pyiceberg/expressions/literals.py +++ b/python/pyiceberg/expressions/literals.py @@ -25,6 +25,7 @@ from abc import ABC, abstractmethod from decimal import ROUND_HALF_UP, Decimal from functools import singledispatchmethod +from math import isnan from typing import Any, Generic, Type from uuid import UUID @@ -65,6 +66,8 @@ class Literal(Generic[L], ABC): def __init__(self, value: L, value_type: Type[L]): if value is None or not isinstance(value, value_type): raise TypeError(f"Invalid literal value: {value!r} (not a {value_type})") + if isinstance(value, float) and isnan(value): + raise ValueError("Cannot create expression literal from NaN.") self._value = value @property @@ -108,10 +111,10 @@ def __ge__(self, other: Any) -> bool: def literal(value: L) -> Literal[L]: """ - A generic Literal factory to construct an iceberg Literal based on python primitive data type + A generic Literal factory to construct an Iceberg Literal based on Python primitive data type Args: - value(python primitive type): the value to be associated with literal + value(Python primitive type): the value to be associated with literal Example: from pyiceberg.expressions.literals import literal diff --git a/python/tests/expressions/test_literals.py b/python/tests/expressions/test_literals.py index 453f04f4ee72..7373ff040b38 100644 --- a/python/tests/expressions/test_literals.py +++ b/python/tests/expressions/test_literals.py @@ -73,6 +73,12 @@ def test_literal_from_none_error() -> None: assert "Invalid literal value: None" in str(e.value) +def test_literal_from_nan_error() -> None: + with pytest.raises(ValueError) as e: + literal(float("nan")) + assert "Cannot create expression literal from NaN." in str(e.value) + + @pytest.mark.parametrize( "literal_class", [ @@ -89,12 +95,19 @@ def test_literal_from_none_error() -> None: BinaryLiteral, ], ) -def test_string_literal_with_none_value_error(literal_class: Type[PrimitiveType]) -> None: +def test_literal_classes_with_none_type_error(literal_class: Type[PrimitiveType]) -> None: with pytest.raises(TypeError) as e: literal_class(None) assert "Invalid literal value: None" in str(e.value) +@pytest.mark.parametrize("literal_class", [FloatLiteral, DoubleLiteral]) +def test_literal_classes_with_nan_value_error(literal_class: Type[PrimitiveType]) -> None: + with pytest.raises(ValueError) as e: + literal_class(float("nan")) + assert "Cannot create expression literal from NaN." in str(e.value) + + # Numeric