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 92a3e2fc Add missing catalog tests (#2955)
92a3e2fc is described below
commit 92a3e2fc0f0c7a25fba4133e9754f1ec67522831
Author: Alex Stephen <[email protected]>
AuthorDate: Wed Jan 28 20:14:11 2026 -0800
Add missing catalog tests (#2955)
<!--
Thanks for opening a pull request!
-->
<!-- In the case this PR will resolve an issue, please replace
${GITHUB_ISSUE_ID} below with the actual Github issue id. -->
<!-- Closes #${GITHUB_ISSUE_ID} -->
# Rationale for this change
There's a couple missing catalog tests around supporting namespaces +
tables with slashes/dots. Along the way, I found an issue in how we
create the CreateTableRequest + RegisterTableRequest
## Are these changes tested?
Tests are included.
## Are there any user-facing changes?
<!-- In the case of user-facing changes, please add the changelog label.
-->
---
pyiceberg/catalog/rest/__init__.py | 4 +-
tests/integration/test_catalog.py | 131 +++++++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 2 deletions(-)
diff --git a/pyiceberg/catalog/rest/__init__.py
b/pyiceberg/catalog/rest/__init__.py
index e3cc09ed..802be285 100644
--- a/pyiceberg/catalog/rest/__init__.py
+++ b/pyiceberg/catalog/rest/__init__.py
@@ -790,7 +790,7 @@ class RestCatalog(Catalog):
if location:
location = location.rstrip("/")
request = CreateTableRequest(
- name=namespace_and_table["table"],
+ name=self._identifier_to_validated_tuple(identifier)[-1],
location=location,
table_schema=fresh_schema,
partition_spec=fresh_partition_spec,
@@ -869,7 +869,7 @@ class RestCatalog(Catalog):
self._check_endpoint(Capability.V1_REGISTER_TABLE)
namespace_and_table = self._split_identifier_for_path(identifier)
request = RegisterTableRequest(
- name=namespace_and_table["table"],
+ name=self._identifier_to_validated_tuple(identifier)[-1],
metadata_location=metadata_location,
)
serialized_json = request.model_dump_json().encode(UTF8)
diff --git a/tests/integration/test_catalog.py
b/tests/integration/test_catalog.py
index f9d174dd..0e39beb2 100644
--- a/tests/integration/test_catalog.py
+++ b/tests/integration/test_catalog.py
@@ -672,6 +672,29 @@ def test_incompatible_partitioned_schema_evolution(
assert table.schema() == Schema(NestedField(2, "tpep_pickup_datetime",
TimestampType(), False))
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_namespace_with_slash(test_catalog: Catalog) -> None:
+ if isinstance(test_catalog, HiveCatalog):
+ pytest.skip(f"{type(test_catalog).__name__} does not support slash in
namespace")
+
+ namespace = ("new/db",)
+
+ if test_catalog.namespace_exists(namespace):
+ test_catalog.drop_namespace(namespace)
+
+ assert not test_catalog.namespace_exists(namespace)
+
+ test_catalog.create_namespace(namespace)
+ assert test_catalog.namespace_exists(namespace)
+
+ properties = test_catalog.load_namespace_properties(namespace)
+ assert properties is not None
+
+ test_catalog.drop_namespace(namespace)
+ assert not test_catalog.namespace_exists(namespace)
+
+
@pytest.mark.integration
@pytest.mark.parametrize("test_catalog", CATALOGS)
def test_incompatible_sorted_schema_evolution(
@@ -692,3 +715,111 @@ def test_incompatible_sorted_schema_evolution(
assert table.schema() == Schema(
NestedField(1, "VendorID", IntegerType(), False), NestedField(2,
"tpep_pickup_datetime", TimestampType(), False)
)
+
+
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_namespace_with_dot(test_catalog: Catalog) -> None:
+ if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
+ pytest.skip(f"{type(test_catalog).__name__} does not support dot in
namespace")
+
+ namespace = ("new.db",)
+
+ if test_catalog.namespace_exists(namespace):
+ test_catalog.drop_namespace(namespace)
+
+ assert not test_catalog.namespace_exists(namespace)
+
+ test_catalog.create_namespace(namespace)
+ assert test_catalog.namespace_exists(namespace)
+
+ # REST Catalog fixture treats this as a hierarchical namespace.
+ # Calling list namespaces will get `new`, not `new.db`.
+ if isinstance(test_catalog, RestCatalog):
+ namespaces = test_catalog.list_namespaces()
+ assert ("new",) in namespaces or ("new.db",) in namespaces
+ else:
+ assert namespace in test_catalog.list_namespaces()
+
+ properties = test_catalog.load_namespace_properties(namespace)
+ assert properties is not None
+
+ test_catalog.drop_namespace(namespace)
+ assert not test_catalog.namespace_exists(namespace)
+
+
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_table_name_with_slash(test_catalog: Catalog, table_schema_simple:
Schema) -> None:
+ if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
+ pytest.skip(f"{type(test_catalog).__name__} does not support slash in
table name")
+
+ namespace = ("ns_slash",)
+ table_ident = ("ns_slash", "tab/le")
+
+ if not test_catalog.namespace_exists(namespace):
+ test_catalog.create_namespace(namespace)
+
+ if test_catalog.table_exists(table_ident):
+ test_catalog.drop_table(table_ident)
+
+ assert not test_catalog.table_exists(table_ident)
+
+ test_catalog.create_table(table_ident, table_schema_simple)
+ assert test_catalog.table_exists(table_ident)
+
+ table = test_catalog.load_table(table_ident)
+ assert table.schema().as_struct() == table_schema_simple.as_struct()
+
+ test_catalog.drop_table(table_ident)
+ assert not test_catalog.table_exists(table_ident)
+
+
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_table_name_with_dot(test_catalog: Catalog, table_schema_simple:
Schema) -> None:
+ if isinstance(test_catalog, (HiveCatalog, SqlCatalog)):
+ pytest.skip(f"{type(test_catalog).__name__} does not support dot in
table name")
+
+ namespace = ("ns_dot",)
+ table_ident = ("ns_dot", "ta.ble")
+
+ if not test_catalog.namespace_exists(namespace):
+ test_catalog.create_namespace(namespace)
+
+ if test_catalog.table_exists(table_ident):
+ test_catalog.drop_table(table_ident)
+
+ assert not test_catalog.table_exists(table_ident)
+
+ test_catalog.create_table(table_ident, table_schema_simple)
+ assert test_catalog.table_exists(table_ident)
+
+ assert table_ident in test_catalog.list_tables(namespace)
+
+ table = test_catalog.load_table(table_ident)
+ assert table.schema().as_struct() == table_schema_simple.as_struct()
+
+ test_catalog.drop_table(table_ident)
+ assert not test_catalog.table_exists(table_ident)
+
+
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_drop_missing_table(test_catalog: Catalog, database_name: str) -> None:
+ test_catalog.create_namespace_if_not_exists(database_name)
+ table_ident = (database_name, "missing_table")
+ assert not test_catalog.table_exists(table_ident)
+ with pytest.raises(NoSuchTableError):
+ test_catalog.drop_table(table_ident)
+
+
[email protected]
[email protected]("test_catalog", CATALOGS)
+def test_drop_nonexistent_namespace(test_catalog: Catalog) -> None:
+ if isinstance(test_catalog, HiveCatalog):
+ pytest.skip("HiveCatalog raises NoSuchObjectException instead of
NoSuchNamespaceError")
+
+ namespace = ("non_existent_namespace",)
+ with pytest.raises(NoSuchNamespaceError):
+ test_catalog.drop_namespace(namespace)