This is an automated email from the ASF dual-hosted git repository.

kevinjqliu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git


The following commit(s) were added to refs/heads/main by this push:
     new d9bdf8e7 fix: Serialize AlwaysTrue/AlwaysFalse as boolean literals 
(#2780)
d9bdf8e7 is described below

commit d9bdf8e76049e98bed426b29a79681fcdf114c01
Author: Drew Gallardo <[email protected]>
AuthorDate: Mon Nov 24 17:23:07 2025 -0800

    fix: Serialize AlwaysTrue/AlwaysFalse as boolean literals (#2780)
    
    related to #2775
    
    # Rationale for this change
    
    This PR aligns the `AlwaysTrue` and `AlwaysFalse` expression
    serialization to use the boolean primitives `true`/`false` instead of
    string representation, matching the Iceberg
    
[ExpressionParser](https://github.com/apache/iceberg/blob/main/core/src/main/java/org/apache/iceberg/expressions/ExpressionParser.java#L101-L108).
    
    I know the spec defines the true/false expressions as string but today
    we can't hit the scan planning client endpoint following these models.
    
    I know the spec defines the true/false expressions as a string
    representation, but currently we can't successfully use the scan
    planning client endpoint with those models. Java's actual implementation
    serializes these as boolean literals.
    
    For instance, with what we have today we would throw an illegal argument
    exception:
    
    ```
    {
        "snapshot-id": 2540284336700708540,
        "filter": "true",
        "case-sensitive": true
    }
    ```
    Throws: `java.lang.IllegalArgumentException: argument
    &quot;content&quot; is null`
    
    But, this works just fine (notice no quotes):
    
    ```
    {
        "snapshot-id": 2540284336700708540,
        "filter": true,
        "case-sensitive": true
    }
    
    ```
    
    ## Are these changes tested?
    
    Yes
    
    ## Are there any user-facing changes?
    
    No, this only affects the JSON serialization format to match Java Cores
    behavior.
---
 pyiceberg/expressions/__init__.py     | 8 ++++----
 tests/expressions/test_expressions.py | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/pyiceberg/expressions/__init__.py 
b/pyiceberg/expressions/__init__.py
index f0dc4094..4871da3e 100644
--- a/pyiceberg/expressions/__init__.py
+++ b/pyiceberg/expressions/__init__.py
@@ -370,10 +370,10 @@ class Not(IcebergBaseModel, BooleanExpression):
         return (self.child,)
 
 
-class AlwaysTrue(BooleanExpression, Singleton, IcebergRootModel[str]):
+class AlwaysTrue(BooleanExpression, Singleton, IcebergRootModel[bool]):
     """TRUE expression."""
 
-    root: str = "true"
+    root: bool = True
 
     def __invert__(self) -> AlwaysFalse:
         """Transform the Expression into its negated version."""
@@ -388,10 +388,10 @@ class AlwaysTrue(BooleanExpression, Singleton, 
IcebergRootModel[str]):
         return "AlwaysTrue()"
 
 
-class AlwaysFalse(BooleanExpression, Singleton, IcebergRootModel[str]):
+class AlwaysFalse(BooleanExpression, Singleton, IcebergRootModel[bool]):
     """FALSE expression."""
 
-    root: str = "false"
+    root: bool = False
 
     def __invert__(self) -> AlwaysTrue:
         """Transform the Expression into its negated version."""
diff --git a/tests/expressions/test_expressions.py 
b/tests/expressions/test_expressions.py
index 1fbe8d7a..47c26c8e 100644
--- a/tests/expressions/test_expressions.py
+++ b/tests/expressions/test_expressions.py
@@ -770,7 +770,7 @@ def test_not_json_serialization_and_deserialization() -> 
None:
 
 def test_always_true() -> None:
     always_true = AlwaysTrue()
-    assert always_true.model_dump_json() == '"true"'
+    assert always_true.model_dump_json() == "true"
     assert str(always_true) == "AlwaysTrue()"
     assert repr(always_true) == "AlwaysTrue()"
     assert always_true == eval(repr(always_true))
@@ -779,7 +779,7 @@ def test_always_true() -> None:
 
 def test_always_false() -> None:
     always_false = AlwaysFalse()
-    assert always_false.model_dump_json() == '"false"'
+    assert always_false.model_dump_json() == "false"
     assert str(always_false) == "AlwaysFalse()"
     assert repr(always_false) == "AlwaysFalse()"
     assert always_false == eval(repr(always_false))

Reply via email to