Skip to content

Commit 7d1c461

Browse files
committed
Add serializer for AssertRefSnapshotId allowing null json value
1 parent fa9094b commit 7d1c461

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed

pyiceberg/table/update/__init__.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
from abc import ABC, abstractmethod
2222
from datetime import datetime
2323
from functools import singledispatch
24-
from typing import TYPE_CHECKING, Annotated, Any, Dict, Generic, List, Literal, Optional, Set, Tuple, TypeVar, Union, cast
24+
from typing import TYPE_CHECKING, Annotated, Any, Dict, Generic, List, Literal, Optional, Tuple, TypeVar, Union, cast
2525

26-
from pydantic import Field, field_validator, model_validator
26+
from pydantic import Field, field_serializer, field_validator, model_validator
2727

2828
from pyiceberg.exceptions import CommitFailedException
2929
from pyiceberg.partitioning import PARTITION_FIELD_ID_START, PartitionSpec
@@ -725,7 +725,17 @@ class AssertRefSnapshotId(ValidatableTableRequirement):
725725

726726
type: Literal["assert-ref-snapshot-id"] = Field(default="assert-ref-snapshot-id")
727727
ref: str = Field(...)
728-
snapshot_id: Optional[int] = Field(default=None, alias="snapshot-id")
728+
snapshot_id: int = Field(default=-1, alias="snapshot-id")
729+
730+
# serialize -1 to null when serializing to json
731+
# TODO: make more generic Field so this can be used on other models that need
732+
# an explicit null.
733+
@field_serializer("snapshot_id", when_used="json")
734+
def snapshot_id_can_be_null(self, snapshot_id: int) -> Optional[int]:
735+
if snapshot_id == -1:
736+
return None
737+
else:
738+
return snapshot_id
729739

730740
def validate(self, base_metadata: Optional[TableMetadata]) -> None:
731741
if base_metadata is None:
@@ -745,13 +755,6 @@ def validate(self, base_metadata: Optional[TableMetadata]) -> None:
745755
elif self.snapshot_id is not None:
746756
raise CommitFailedException(f"Requirement failed: branch or tag {self.ref} is missing, expected {self.snapshot_id}")
747757

748-
# override the override method, allowing None to serialize to `null` instead of being omitted.
749-
def model_dump_json(
750-
self, exclude_none: bool = False, exclude: Optional[Set[str]] = None, by_alias: bool = True, **kwargs: Any
751-
) -> str:
752-
# `snapshot-id` is required in json response, even if null
753-
return super().model_dump_json(exclude_none=False)
754-
755758

756759
class AssertLastAssignedFieldId(ValidatableTableRequirement):
757760
"""The table's last assigned column id must match the requirement's `last-assigned-field-id`."""

tests/test_serializers.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,16 @@
1818
import json
1919
import os
2020
import uuid
21-
from typing import Any, Dict
21+
from typing import Any, Dict, Tuple
2222

2323
import pytest
2424
from pytest_mock import MockFixture
2525

2626
from pyiceberg.serializers import ToOutputFile
2727
from pyiceberg.table import StaticTable
2828
from pyiceberg.table.metadata import TableMetadataV1
29+
from pyiceberg.table.update import AssertRefSnapshotId, TableRequirement
30+
from pyiceberg.typedef import IcebergBaseModel
2931

3032

3133
def test_legacy_current_snapshot_id(
@@ -48,3 +50,13 @@ def test_legacy_current_snapshot_id(
4850
backwards_compatible_static_table = StaticTable.from_metadata(metadata_location)
4951
assert backwards_compatible_static_table.metadata.current_snapshot_id is None
5052
assert backwards_compatible_static_table.metadata == static_table.metadata
53+
54+
55+
def test_null_serializer_field() -> None:
56+
class ExampleRequest(IcebergBaseModel):
57+
requirements: Tuple[TableRequirement, ...]
58+
59+
request = ExampleRequest(requirements=(AssertRefSnapshotId(ref="main"),))
60+
dumped_json = request.model_dump_json()
61+
expected_json = """{"type":"assert-ref-snapshot-id","ref":"main","snapshot-id":null}"""
62+
assert expected_json in dumped_json

0 commit comments

Comments
 (0)