Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pyiceberg/catalog/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,14 @@ class ListNamespaceResponse(IcebergBaseModel):

class NamespaceResponse(IcebergBaseModel):
namespace: Identifier = Field()
properties: Properties = Field()
properties: Properties = Field(default_factory=dict)

@field_validator("properties", mode="before")
@classmethod
def replace_none_with_dict(cls, v: Any) -> Properties:
if v is None:
return {}
return v
Comment on lines +340 to +347
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
properties: Properties = Field(default_factory=dict)
@field_validator("properties", mode="before")
@classmethod
def replace_none_with_dict(cls, v: Any) -> Properties:
if v is None:
return {}
return v
properties: Properties | None = Field(default_factory=dict)

how about just

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would allow none through to the properties field since the default_factory only applies when the field is ommitted from the response. It would be easier to normalize at the deserialization layer to not miss out on the null checks within the code base but we can.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we actually want to pass null through; for this scenario

        If the server does not support namespace properties, it should return null for this field.

and make load_namespace_properties return both Properties and None

  • None for not supported
  • Properties for none are set

def load_namespace_properties(self, namespace: str | Identifier) -> Properties:

Wdyt?



class UpdateNamespacePropertiesResponse(IcebergBaseModel):
Expand Down
33 changes: 33 additions & 0 deletions tests/catalog/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,39 @@ def test_load_namespace_properties_200(rest_mock: Mocker) -> None:
assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).load_namespace_properties(namespace) == {"prop": "yes"}


def test_load_namespace_properties_200_without_properties(rest_mock: Mocker) -> None:
namespace = "leden"
rest_mock.get(
f"{TEST_URI}v1/namespaces/{namespace}",
json={"namespace": ["leden"]},
status_code=200,
request_headers=TEST_HEADERS,
)
assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).load_namespace_properties(namespace) == {}


def test_load_namespace_properties_200_with_null_properties(rest_mock: Mocker) -> None:
namespace = "leden"
rest_mock.get(
f"{TEST_URI}v1/namespaces/{namespace}",
json={"namespace": ["leden"], "properties": None},
status_code=200,
request_headers=TEST_HEADERS,
)
assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).load_namespace_properties(namespace) == {}


def test_load_namespace_properties_200_with_empty_properties(rest_mock: Mocker) -> None:
namespace = "leden"
rest_mock.get(
f"{TEST_URI}v1/namespaces/{namespace}",
json={"namespace": ["leden"], "properties": {}},
status_code=200,
request_headers=TEST_HEADERS,
)
assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).load_namespace_properties(namespace) == {}


def test_load_namespace_properties_404(rest_mock: Mocker) -> None:
namespace = "leden"
rest_mock.get(
Expand Down
Loading