Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions python/pyiceberg/expressions/literals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion python/tests/expressions/test_literals.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
[
Expand All @@ -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])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add one more tests where we use literal(float("NaN"))? The literal is meant as the public API, and people shouldn't necessarily initialize the specific literal instances themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

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


Expand Down