Skip to content

Commit 90c64f9

Browse files
committed
Merge remote-tracking branch 'upstream/main'
merge with upstream
2 parents f26fcac + 4f02298 commit 90c64f9

3 files changed

Lines changed: 25 additions & 23 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,14 @@ repos:
5454
additional_dependencies:
5555
- tomli==2.0.1
5656
- repo: https://github.com/ikamensh/flynt
57-
rev: 1.0.1
57+
rev: 1.0.6
5858
hooks:
5959
- id: flynt
6060
args:
6161
# --line-length is set to a high value to deal with very long lines
6262
- --line-length
6363
- '99999'
6464
- repo: https://github.com/codespell-project/codespell
65-
rev: v2.3.0
65+
rev: v2.4.1
6666
hooks:
6767
- id: codespell
68-
ci:
69-
autofix_commit_msg: |
70-
[pre-commit.ci] auto fixes from pre-commit.com hooks
71-
72-
for more information, see https://pre-commit.ci
73-
autofix_prs: true
74-
autoupdate_branch: ''
75-
autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'
76-
autoupdate_schedule: weekly
77-
skip: []
78-
submodules: false

pyiceberg/table/update/__init__.py

Lines changed: 10 additions & 9 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_validator, model_serializer, model_validator
2727

2828
from pyiceberg.exceptions import CommitFailedException
2929
from pyiceberg.partitioning import PARTITION_FIELD_ID_START, PartitionSpec
@@ -52,6 +52,8 @@
5252
from pyiceberg.utils.properties import property_as_int
5353

5454
if TYPE_CHECKING:
55+
from pydantic.functional_serializers import ModelWrapSerializerWithoutInfo
56+
5557
from pyiceberg.table import Transaction
5658

5759
U = TypeVar("U")
@@ -727,6 +729,12 @@ class AssertRefSnapshotId(ValidatableTableRequirement):
727729
ref: str = Field(...)
728730
snapshot_id: Optional[int] = Field(default=None, alias="snapshot-id")
729731

732+
@model_serializer(mode="wrap")
733+
def serialize_model(self, handler: ModelWrapSerializerWithoutInfo) -> dict[str, Any]:
734+
partial_result = handler(self)
735+
# Ensure "snapshot-id" is always present, even if value is None
736+
return {**partial_result, "snapshot-id": self.snapshot_id}
737+
730738
def validate(self, base_metadata: Optional[TableMetadata]) -> None:
731739
if base_metadata is None:
732740
raise CommitFailedException("Requirement failed: current table metadata is missing")
@@ -745,13 +753,6 @@ def validate(self, base_metadata: Optional[TableMetadata]) -> None:
745753
elif self.snapshot_id is not None:
746754
raise CommitFailedException(f"Requirement failed: branch or tag {self.ref} is missing, expected {self.snapshot_id}")
747755

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-
755756

756757
class AssertLastAssignedFieldId(ValidatableTableRequirement):
757758
"""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", snapshot_id=None),))
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)