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