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 fa9094ba Allow snapshot-id in assert-ref-snapshot-id requirement to 
serialize to null in json (#2343)
fa9094ba is described below

commit fa9094ba84f5c8479d750ae5fc2606a2c9cd221c
Author: Artem Titoulenko <[email protected]>
AuthorDate: Tue Aug 19 09:19:47 2025 -0400

    Allow snapshot-id in assert-ref-snapshot-id requirement to serialize to 
null in json (#2343)
    
    Closes #2342
    
    # Rationale for this change
    
    The OpenAPI spec specifies that `snapshot-id` is required [1], even if
    it's `null`. The current code omits the field b/c `exclude_none=True` is
    set for all models that extend `IcebergBaseModel`. Setting
    `exclude=False` on the field doesn't override the behavior and thus the
    model needs to control it's own serialization. This is the only model I
    know of so far that has this "required null" problem so I held back from
    making another class for this model to extend.
    
    # Are these changes tested?
    
    Yes
    
    # Are there any user-facing changes?
    
    Set `snapshot-id: null` when committing tables that don't have a current
    snapshot-id.
    
    [1]:
    
https://github.com/apache/iceberg/blob/main/open-api/rest-catalog-open-api.yaml#L3138-L3155
    
    ---------
    
    Co-authored-by: Fokko Driesprong <[email protected]>
    Co-authored-by: Kevin Liu <[email protected]>
---
 pyiceberg/table/update/__init__.py | 9 ++++++++-
 tests/table/test_init.py           | 3 +++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/pyiceberg/table/update/__init__.py 
b/pyiceberg/table/update/__init__.py
index 6619c486..2f0e6c13 100644
--- a/pyiceberg/table/update/__init__.py
+++ b/pyiceberg/table/update/__init__.py
@@ -21,7 +21,7 @@ import uuid
 from abc import ABC, abstractmethod
 from datetime import datetime
 from functools import singledispatch
-from typing import TYPE_CHECKING, Annotated, Any, Dict, Generic, List, 
Literal, Optional, Tuple, TypeVar, Union, cast
+from typing import TYPE_CHECKING, Annotated, Any, Dict, Generic, List, 
Literal, Optional, Set, Tuple, TypeVar, Union, cast
 
 from pydantic import Field, field_validator, model_validator
 
@@ -745,6 +745,13 @@ class AssertRefSnapshotId(ValidatableTableRequirement):
         elif self.snapshot_id is not None:
             raise CommitFailedException(f"Requirement failed: branch or tag 
{self.ref} is missing, expected {self.snapshot_id}")
 
+    # override the override method, allowing None to serialize to `null` 
instead of being omitted.
+    def model_dump_json(
+        self, exclude_none: bool = False, exclude: Optional[Set[str]] = None, 
by_alias: bool = True, **kwargs: Any
+    ) -> str:
+        # `snapshot-id` is required in json response, even if null
+        return super().model_dump_json(exclude_none=False)
+
 
 class AssertLastAssignedFieldId(ValidatableTableRequirement):
     """The table's last assigned column id must match the requirement's 
`last-assigned-field-id`."""
diff --git a/tests/table/test_init.py b/tests/table/test_init.py
index 748a77ee..9d284e77 100644
--- a/tests/table/test_init.py
+++ b/tests/table/test_init.py
@@ -1039,6 +1039,9 @@ def test_assert_ref_snapshot_id(table_v2: Table) -> None:
     ):
         AssertRefSnapshotId(ref="test", 
snapshot_id=3055729675574597004).validate(base_metadata)
 
+    expected_json = 
'{"type":"assert-ref-snapshot-id","ref":"main","snapshot-id":null}'
+    assert AssertRefSnapshotId(ref="main").model_dump_json() == expected_json
+
 
 def test_assert_last_assigned_field_id(table_v2: Table) -> None:
     base_metadata = table_v2.metadata

Reply via email to