diff --git a/pyiceberg/catalog/__init__.py b/pyiceberg/catalog/__init__.py index a4f1d47bea..46a3731f7d 100644 --- a/pyiceberg/catalog/__init__.py +++ b/pyiceberg/catalog/__init__.py @@ -22,7 +22,7 @@ import re import uuid from abc import ABC, abstractmethod -from collections.abc import Callable +from collections.abc import Callable, Iterator from dataclasses import dataclass from enum import Enum from typing import ( @@ -581,42 +581,42 @@ def drop_namespace(self, namespace: str | Identifier) -> None: """ @abstractmethod - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: """List tables under the given namespace in the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist. """ @abstractmethod - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: """List namespaces from the given namespace. If not given, list top-level namespaces from the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: a List of namespace identifiers. + Iterator[Identifier]: iterator of namespace identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist. """ @abstractmethod - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: """List views under the given namespace in the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist. diff --git a/pyiceberg/catalog/bigquery_metastore.py b/pyiceberg/catalog/bigquery_metastore.py index b762c1047c..ec8048d6d3 100644 --- a/pyiceberg/catalog/bigquery_metastore.py +++ b/pyiceberg/catalog/bigquery_metastore.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import json +from collections.abc import Iterator from typing import TYPE_CHECKING, Any, Union from google.api_core.exceptions import NotFound @@ -244,7 +245,7 @@ def drop_namespace(self, namespace: str | Identifier) -> None: except NotFound as e: raise NoSuchNamespaceError(f"Namespace {namespace} does not exist.") from e - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: database_name = self.identifier_to_database(namespace) iceberg_tables: list[Identifier] = [] try: @@ -256,9 +257,9 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: iceberg_tables.append((database_name, bq_table_list_item.table_id)) except NotFound: raise NoSuchNamespaceError(f"Namespace (dataset) '{database_name}' not found.") from None - return iceberg_tables + yield from iceberg_tables - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: # Since this catalog only supports one-level namespaces, it always returns an empty list unless # passed an empty namespace to list all namespaces within the catalog. if namespace: @@ -266,7 +267,7 @@ def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: # List top-level datasets datasets_iterator = self.client.list_datasets() - return [(dataset.dataset_id,) for dataset in datasets_iterator] + yield from ((dataset.dataset_id,) for dataset in datasets_iterator) def register_table(self, identifier: str | Identifier, metadata_location: str) -> Table: """Register a new table using existing metadata. @@ -299,7 +300,7 @@ def register_table(self, identifier: str | Identifier, metadata_location: str) - return self.load_table(identifier=identifier) - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def drop_view(self, identifier: str | Identifier) -> None: diff --git a/pyiceberg/catalog/dynamodb.py b/pyiceberg/catalog/dynamodb.py index 2d35b2c5e2..fde71549aa 100644 --- a/pyiceberg/catalog/dynamodb.py +++ b/pyiceberg/catalog/dynamodb.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import uuid +from collections.abc import Iterator from time import time from typing import ( TYPE_CHECKING, @@ -384,7 +385,7 @@ def drop_namespace(self, namespace: str | Identifier) -> None: database_name = self.identifier_to_database(namespace, NoSuchNamespaceError) table_identifiers = self.list_tables(namespace=database_name) - if len(table_identifiers) > 0: + if len(list(table_identifiers)) > 0: raise NamespaceNotEmptyError(f"Database {database_name} is not empty") try: @@ -396,14 +397,14 @@ def drop_namespace(self, namespace: str | Identifier) -> None: except ConditionalCheckFailedException as e: raise NoSuchNamespaceError(f"Database does not exist: {database_name}") from e - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: """List Iceberg tables under the given namespace in the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. """ database_name = self.identifier_to_database(namespace, NoSuchNamespaceError) @@ -428,7 +429,6 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: ) as e: raise GenericDynamoDbError(e.message) from e - table_identifiers = [] for page in page_iterator: for item in page["Items"]: _dict = _convert_dynamo_item_to_regular_dict(item) @@ -436,21 +436,19 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: if identifier_col == DYNAMODB_NAMESPACE: continue - table_identifiers.append(self.identifier_to_tuple(identifier_col)) + yield self.identifier_to_tuple(identifier_col) - return table_identifiers - - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: """List top-level namespaces from the catalog. We do not support hierarchical namespace. Returns: - List[Identifier]: a List of namespace identifiers. + Iterator[Identifier]: iterator of namespace identifiers. """ # Hierarchical namespace is not supported. Return an empty list if namespace: - return [] + return paginator = self.dynamodb.get_paginator("query") @@ -473,14 +471,11 @@ def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: ) as e: raise GenericDynamoDbError(e.message) from e - database_identifiers = [] for page in page_iterator: for item in page["Items"]: _dict = _convert_dynamo_item_to_regular_dict(item) namespace_col = _dict[DYNAMODB_COL_NAMESPACE] - database_identifiers.append(self.identifier_to_tuple(namespace_col)) - - return database_identifiers + yield self.identifier_to_tuple(namespace_col) def load_namespace_properties(self, namespace: str | Identifier) -> Properties: """ @@ -537,7 +532,7 @@ def update_namespace_properties( return properties_update_summary - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def drop_view(self, identifier: str | Identifier) -> None: diff --git a/pyiceberg/catalog/glue.py b/pyiceberg/catalog/glue.py index 7260b29447..ec9325bab0 100644 --- a/pyiceberg/catalog/glue.py +++ b/pyiceberg/catalog/glue.py @@ -16,6 +16,7 @@ # under the License. +from collections.abc import Iterator from typing import ( TYPE_CHECKING, Any, @@ -92,7 +93,6 @@ from mypy_boto3_glue.type_defs import ( ColumnTypeDef, DatabaseInputTypeDef, - DatabaseTypeDef, StorageDescriptorTypeDef, TableInputTypeDef, TableTypeDef, @@ -701,20 +701,19 @@ def drop_namespace(self, namespace: str | Identifier) -> None: ) self.glue.delete_database(Name=database_name) - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: """List Iceberg tables under the given namespace in the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist, or the identifier is invalid. """ database_name = self.identifier_to_database(namespace, NoSuchNamespaceError) - table_list: list[TableTypeDef] = [] next_token: str | None = None try: while True: @@ -723,37 +722,36 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: if not next_token else self.glue.get_tables(DatabaseName=database_name, NextToken=next_token) ) - table_list.extend(table_list_response["TableList"]) + for table in table_list_response["TableList"]: + if self.__is_iceberg_table(table): + yield (database_name, table["Name"]) next_token = table_list_response.get("NextToken") if not next_token: break except self.glue.exceptions.EntityNotFoundException as e: raise NoSuchNamespaceError(f"Database does not exist: {database_name}") from e - return [(database_name, table["Name"]) for table in table_list if self.__is_iceberg_table(table)] - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: """List namespaces from the given namespace. If not given, list top-level namespaces from the catalog. Returns: - List[Identifier]: a List of namespace identifiers. + Iterator[Identifier]: iterator of namespace identifiers. """ # Hierarchical namespace is not supported. Return an empty list if namespace: - return [] + return - database_list: list[DatabaseTypeDef] = [] next_token: str | None = None while True: databases_response = self.glue.get_databases() if not next_token else self.glue.get_databases(NextToken=next_token) - database_list.extend(databases_response["DatabaseList"]) + for database in databases_response["DatabaseList"]: + yield self.identifier_to_tuple(database["Name"]) next_token = databases_response.get("NextToken") if not next_token: break - return [self.identifier_to_tuple(database["Name"]) for database in database_list] - def load_namespace_properties(self, namespace: str | Identifier) -> Properties: """Get properties for a namespace. @@ -808,7 +806,7 @@ def update_namespace_properties( return properties_update_summary - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def drop_view(self, identifier: str | Identifier) -> None: diff --git a/pyiceberg/catalog/hive.py b/pyiceberg/catalog/hive.py index e096470451..1371eb6a79 100644 --- a/pyiceberg/catalog/hive.py +++ b/pyiceberg/catalog/hive.py @@ -18,6 +18,7 @@ import logging import socket import time +from collections.abc import Iterator from types import TracebackType from typing import ( TYPE_CHECKING, @@ -464,7 +465,7 @@ def register_table(self, identifier: str | Identifier, metadata_location: str) - return self._convert_hive_into_iceberg(hive_table) - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def view_exists(self, identifier: str | Identifier) -> bool: @@ -710,7 +711,7 @@ def drop_namespace(self, namespace: str | Identifier) -> None: except MetaException as e: raise NoSuchNamespaceError(f"Database does not exists: {database_name}") from e - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: """List Iceberg tables under the given namespace in the catalog. When the database doesn't exist, it will just return an empty list. @@ -719,14 +720,14 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: namespace: Database to list. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist, or the identifier is invalid. """ database_name = self.identifier_to_database(namespace, NoSuchNamespaceError) with self._client as open_client: - return [ + yield from [ (database_name, table.tableName) for table in open_client.get_table_objects_by_name( dbname=database_name, tbl_names=open_client.get_all_tables(db_name=database_name) @@ -734,18 +735,18 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: if table.parameters.get(TABLE_TYPE, "").lower() == ICEBERG ] - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: """List namespaces from the given namespace. If not given, list top-level namespaces from the catalog. Returns: - List[Identifier]: a List of namespace identifiers. + Iterator[Identifier]: an iterator of namespace identifiers. """ # Hierarchical namespace is not supported. Return an empty list if namespace: - return [] + return iter([]) with self._client as open_client: - return list(map(self.identifier_to_tuple, open_client.get_all_databases())) + return map(self.identifier_to_tuple, open_client.get_all_databases()) def load_namespace_properties(self, namespace: str | Identifier) -> Properties: """Get properties for a namespace. diff --git a/pyiceberg/catalog/noop.py b/pyiceberg/catalog/noop.py index ac2423c198..ae582361a7 100644 --- a/pyiceberg/catalog/noop.py +++ b/pyiceberg/catalog/noop.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from collections.abc import Iterator from typing import ( TYPE_CHECKING, Union, @@ -102,10 +103,10 @@ def create_namespace(self, namespace: str | Identifier, properties: Properties = def drop_namespace(self, namespace: str | Identifier) -> None: raise NotImplementedError - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: raise NotImplementedError def load_namespace_properties(self, namespace: str | Identifier) -> Properties: @@ -116,7 +117,7 @@ def update_namespace_properties( ) -> PropertiesUpdateSummary: raise NotImplementedError - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def view_exists(self, identifier: str | Identifier) -> bool: diff --git a/pyiceberg/catalog/rest/__init__.py b/pyiceberg/catalog/rest/__init__.py index 3b77fd47f0..5129df580a 100644 --- a/pyiceberg/catalog/rest/__init__.py +++ b/pyiceberg/catalog/rest/__init__.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from collections.abc import Iterator from enum import Enum from typing import ( TYPE_CHECKING, @@ -598,7 +599,7 @@ def register_table(self, identifier: str | Identifier, metadata_location: str) - return self._response_to_table(self.identifier_to_tuple(identifier), table_response) @retry(**_RETRY_ARGS) - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def _fetch_tables(self, namespace: str | Identifier) -> list[Identifier]: namespace_tuple = self._check_valid_namespace_identifier(namespace) namespace_concat = NAMESPACE_SEPARATOR.join(namespace_tuple) response = self._session.get(self.url(Endpoints.list_tables, namespace=namespace_concat)) @@ -608,6 +609,11 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: _handle_non_200_response(exc, {404: NoSuchNamespaceError}) return [(*table.namespace, table.name) for table in ListTablesResponse.model_validate_json(response.text).identifiers] + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: + """List tables, returning an iterator to handle retry logic properly.""" + tables = self._fetch_tables(namespace) + yield from tables + @retry(**_RETRY_ARGS) def load_table(self, identifier: str | Identifier) -> Table: params = {} @@ -679,7 +685,7 @@ def _remove_catalog_name_from_table_request_identifier(self, table_request: Comm return table_request @retry(**_RETRY_ARGS) - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def _fetch_views(self, namespace: str | Identifier) -> list[Identifier]: namespace_tuple = self._check_valid_namespace_identifier(namespace) namespace_concat = NAMESPACE_SEPARATOR.join(namespace_tuple) response = self._session.get(self.url(Endpoints.list_views, namespace=namespace_concat)) @@ -689,6 +695,10 @@ def list_views(self, namespace: str | Identifier) -> list[Identifier]: _handle_non_200_response(exc, {404: NoSuchNamespaceError}) return [(*view.namespace, view.name) for view in ListViewsResponse.model_validate_json(response.text).identifiers] + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: + """List views, returning an iterator to handle retry logic properly.""" + yield from self._fetch_views(namespace) + @retry(**_RETRY_ARGS) def commit_table( self, table: Table, requirements: tuple[TableRequirement, ...], updates: tuple[TableUpdate, ...] @@ -756,7 +766,7 @@ def drop_namespace(self, namespace: str | Identifier) -> None: _handle_non_200_response(exc, {404: NoSuchNamespaceError, 409: NamespaceNotEmptyError}) @retry(**_RETRY_ARGS) - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def _fetch_namespaces(self, namespace: str | Identifier) -> list[Identifier]: namespace_tuple = self.identifier_to_tuple(namespace) response = self._session.get( self.url( @@ -772,6 +782,10 @@ def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: return ListNamespaceResponse.model_validate_json(response.text).namespaces + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: + """List namespaces, returning an iterator to handle retry logic properly.""" + yield from self._fetch_namespaces(namespace) + @retry(**_RETRY_ARGS) def load_namespace_properties(self, namespace: str | Identifier) -> Properties: namespace_tuple = self._check_valid_namespace_identifier(namespace) diff --git a/pyiceberg/catalog/sql.py b/pyiceberg/catalog/sql.py index 2b6fa74517..f8f8202808 100644 --- a/pyiceberg/catalog/sql.py +++ b/pyiceberg/catalog/sql.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +from collections.abc import Iterator from typing import ( TYPE_CHECKING, Union, @@ -568,8 +569,12 @@ def drop_namespace(self, namespace: str | Identifier) -> None: raise NoSuchNamespaceError(f"Namespace does not exist: {namespace}") namespace_str = Catalog.namespace_to_string(namespace) - if tables := self.list_tables(namespace): - raise NamespaceNotEmptyError(f"Namespace {namespace_str} is not empty. {len(tables)} tables exist.") + tables = self.list_tables(namespace) + try: + next(tables) + raise NamespaceNotEmptyError(f"Namespace {namespace_str} is not empty. {len(list(tables))} tables exist.") + except StopIteration: + pass with Session(self.engine) as session: session.execute( @@ -580,14 +585,14 @@ def drop_namespace(self, namespace: str | Identifier) -> None: ) session.commit() - def list_tables(self, namespace: str | Identifier) -> list[Identifier]: + def list_tables(self, namespace: str | Identifier) -> Iterator[Identifier]: """List tables under the given namespace in the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: list of table identifiers. + Iterator[Identifier]: iterator of table identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist. @@ -599,16 +604,17 @@ def list_tables(self, namespace: str | Identifier) -> list[Identifier]: stmt = select(IcebergTables).where(IcebergTables.catalog_name == self.name, IcebergTables.table_namespace == namespace) with Session(self.engine) as session: result = session.scalars(stmt) - return [(Catalog.identifier_to_tuple(table.table_namespace) + (table.table_name,)) for table in result] + identifiers = [(Catalog.identifier_to_tuple(table.table_namespace) + (table.table_name,)) for table in result] + yield from identifiers - def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: + def list_namespaces(self, namespace: str | Identifier = ()) -> Iterator[Identifier]: """List namespaces from the given namespace. If not given, list top-level namespaces from the catalog. Args: namespace (str | Identifier): Namespace identifier to search. Returns: - List[Identifier]: a List of namespace identifiers. + Iterator[Identifier]: iterator of namespace identifiers. Raises: NoSuchNamespaceError: If a namespace with the given name does not exist. @@ -629,7 +635,6 @@ def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: with Session(self.engine) as session: namespace_tuple = Catalog.identifier_to_tuple(namespace) sub_namespaces_level_length = len(namespace_tuple) + 1 - namespaces = list( { # only get distinct namespaces ns[:sub_namespaces_level_length] # truncate to the required level @@ -640,7 +645,7 @@ def list_namespaces(self, namespace: str | Identifier = ()) -> list[Identifier]: } ) - return namespaces + yield from namespaces def load_namespace_properties(self, namespace: str | Identifier) -> Properties: """Get properties for a namespace. @@ -721,7 +726,7 @@ def update_namespace_properties( session.commit() return properties_update_summary - def list_views(self, namespace: str | Identifier) -> list[Identifier]: + def list_views(self, namespace: str | Identifier) -> Iterator[Identifier]: raise NotImplementedError def view_exists(self, identifier: str | Identifier) -> bool: diff --git a/pyiceberg/cli/console.py b/pyiceberg/cli/console.py index 9baa813eff..0f5ac7250b 100644 --- a/pyiceberg/cli/console.py +++ b/pyiceberg/cli/console.py @@ -108,13 +108,20 @@ def list(ctx: Context, parent: str | None) -> None: # pylint: disable=redefined """List tables or namespaces.""" catalog, output = _catalog_and_output(ctx) - identifiers = [] + identifiers = None if parent: - # Do we have tables under parent namespace? identifiers = catalog.list_tables(parent) - if not identifiers: - # List hierarchical namespaces if parent, root namespaces otherwise. + try: + first_item = next(identifiers) + from itertools import chain + + identifiers = chain([first_item], identifiers) + except StopIteration: + identifiers = None + + if identifiers is None: identifiers = catalog.list_namespaces(parent or ()) + output.identifiers(identifiers) diff --git a/pyiceberg/cli/output.py b/pyiceberg/cli/output.py index 332221008c..a6581d087f 100644 --- a/pyiceberg/cli/output.py +++ b/pyiceberg/cli/output.py @@ -16,6 +16,7 @@ # under the License. import json from abc import ABC, abstractmethod +from collections.abc import Iterator from typing import ( Any, ) @@ -40,7 +41,7 @@ class Output(ABC): def exception(self, ex: Exception) -> None: ... @abstractmethod - def identifiers(self, identifiers: list[Identifier]) -> None: ... + def identifiers(self, identifiers: Iterator[Identifier]) -> None: ... @abstractmethod def describe_table(self, table: Table) -> None: ... @@ -88,7 +89,7 @@ def exception(self, ex: Exception) -> None: else: Console(stderr=True).print(ex) - def identifiers(self, identifiers: list[Identifier]) -> None: + def identifiers(self, identifiers: Iterator[Identifier]) -> None: table = self._table for identifier in identifiers: table.add_row(".".join(identifier)) @@ -199,7 +200,7 @@ def _out(self, d: Any) -> None: def exception(self, ex: Exception) -> None: self._out({"type": ex.__class__.__name__, "message": str(ex)}) - def identifiers(self, identifiers: list[Identifier]) -> None: + def identifiers(self, identifiers: Iterator[Identifier]) -> None: self._out([".".join(identifier) for identifier in identifiers]) def describe_table(self, table: Table) -> None: diff --git a/tests/catalog/integration_test_dynamodb.py b/tests/catalog/integration_test_dynamodb.py index 6ae14bca06..87988f9237 100644 --- a/tests/catalog/integration_test_dynamodb.py +++ b/tests/catalog/integration_test_dynamodb.py @@ -119,7 +119,7 @@ def test_list_tables(test_catalog: Catalog, table_schema_nested: Schema, databas test_catalog.create_namespace(database_name) for table_name in table_list: test_catalog.create_table((database_name, table_name), table_schema_nested) - identifier_list = test_catalog.list_tables(database_name) + identifier_list = list(test_catalog.list_tables(database_name)) assert len(identifier_list) == LIST_TEST_NUMBER for table_name in table_list: assert (database_name, table_name) in identifier_list @@ -207,10 +207,10 @@ def test_create_namespace_with_comment_and_location(test_catalog: Catalog, datab def test_list_namespaces(test_catalog: Catalog, database_list: list[str]) -> None: for database_name in database_list: test_catalog.create_namespace(database_name) - db_list = test_catalog.list_namespaces() + db_list = list(test_catalog.list_namespaces()) for database_name in database_list: assert (database_name,) in db_list - assert len(test_catalog.list_namespaces(list(database_list)[0])) == 0 + assert len(list(test_catalog.list_namespaces(list(database_list)[0]))) == 0 def test_drop_namespace(test_catalog: Catalog, table_schema_nested: Schema, table_name: str, database_name: str) -> None: diff --git a/tests/catalog/integration_test_glue.py b/tests/catalog/integration_test_glue.py index c429770268..c73035b48e 100644 --- a/tests/catalog/integration_test_glue.py +++ b/tests/catalog/integration_test_glue.py @@ -227,7 +227,7 @@ def test_list_tables(test_catalog: Catalog, table_schema_nested: Schema, databas test_catalog.create_namespace(database_name) for table_name in table_list: test_catalog.create_table((database_name, table_name), table_schema_nested) - identifier_list = test_catalog.list_tables(database_name) + identifier_list = list(test_catalog.list_tables(database_name)) assert len(identifier_list) == LIST_TEST_NUMBER for table_name in table_list: assert (database_name, table_name) in identifier_list @@ -316,10 +316,10 @@ def test_create_namespace_with_comment_and_location(test_catalog: Catalog, datab def test_list_namespaces(test_catalog: Catalog, database_list: list[str]) -> None: for database_name in database_list: test_catalog.create_namespace(database_name) - db_list = test_catalog.list_namespaces() + db_list = list(test_catalog.list_namespaces()) for database_name in database_list: assert (database_name,) in db_list - assert len(test_catalog.list_namespaces(list(database_list)[0])) == 0 + assert len(list(test_catalog.list_namespaces(list(database_list)[0]))) == 0 def test_drop_namespace(test_catalog: Catalog, table_schema_nested: Schema, database_name: str, table_name: str) -> None: diff --git a/tests/catalog/test_base.py b/tests/catalog/test_base.py index 42702c8c2b..e68935d0a6 100644 --- a/tests/catalog/test_base.py +++ b/tests/catalog/test_base.py @@ -376,12 +376,12 @@ def test_list_namespaces(catalog: InMemoryCatalog) -> None: # Given catalog.create_namespace(TEST_TABLE_NAMESPACE, TEST_TABLE_PROPERTIES) # When - namespaces = catalog.list_namespaces() + namespaces = list(catalog.list_namespaces()) # Then assert TEST_TABLE_NAMESPACE[:1] in namespaces # When - namespaces = catalog.list_namespaces(TEST_TABLE_NAMESPACE) + namespaces = list(catalog.list_namespaces(TEST_TABLE_NAMESPACE)) # Then assert not namespaces @@ -425,7 +425,7 @@ def test_list_tables_under_a_namespace(catalog: InMemoryCatalog) -> None: catalog.create_namespace(new_namespace) # When all_tables = catalog.list_tables(namespace=TEST_TABLE_NAMESPACE) - new_namespace_tables = catalog.list_tables(new_namespace) + new_namespace_tables = list(catalog.list_tables(new_namespace)) # Then assert all_tables assert TEST_TABLE_IDENTIFIER in all_tables diff --git a/tests/catalog/test_bigquery_metastore.py b/tests/catalog/test_bigquery_metastore.py index c8c7584262..bfca9ec081 100644 --- a/tests/catalog/test_bigquery_metastore.py +++ b/tests/catalog/test_bigquery_metastore.py @@ -151,7 +151,7 @@ def test_list_tables(mocker: MockFixture, gcp_dataset_name: str) -> None: catalog_name = "test_catalog" test_catalog = BigQueryMetastoreCatalog(catalog_name, **{"gcp.bigquery.project-id": "my-project"}) - tables = test_catalog.list_tables(gcp_dataset_name) + tables = list(test_catalog.list_tables(gcp_dataset_name)) # Assert that all tables returned by client.list_tables are listed assert len(tables) == 2 @@ -173,7 +173,7 @@ def test_list_namespaces(mocker: MockFixture) -> None: catalog_name = "test_catalog" test_catalog = BigQueryMetastoreCatalog(catalog_name, **{"gcp.bigquery.project-id": "my-project"}) - namespaces = test_catalog.list_namespaces() + namespaces = list(test_catalog.list_namespaces()) assert len(namespaces) == 2 assert ("dataset1",) in namespaces assert ("dataset2",) in namespaces diff --git a/tests/catalog/test_dynamodb.py b/tests/catalog/test_dynamodb.py index 5933e7d472..adf44bb0ec 100644 --- a/tests/catalog/test_dynamodb.py +++ b/tests/catalog/test_dynamodb.py @@ -417,7 +417,7 @@ def test_list_namespaces(_bucket_initialize: None, database_list: list[str]) -> def test_create_namespace_no_properties(_bucket_initialize: None, database_name: str) -> None: test_catalog = DynamoDbCatalog("test_ddb_catalog") test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list properties = test_catalog.load_namespace_properties(database_name) @@ -433,7 +433,7 @@ def test_create_namespace_with_comment_and_location(_bucket_initialize: None, da } test_catalog = DynamoDbCatalog("test_ddb_catalog") test_catalog.create_namespace(namespace=database_name, properties=test_properties) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list properties = test_catalog.load_namespace_properties(database_name) @@ -445,7 +445,7 @@ def test_create_namespace_with_comment_and_location(_bucket_initialize: None, da def test_create_duplicated_namespace(_bucket_initialize: None, database_name: str) -> None: test_catalog = DynamoDbCatalog("test_ddb_catalog") test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list with pytest.raises(NamespaceAlreadyExistsError): @@ -456,11 +456,11 @@ def test_create_duplicated_namespace(_bucket_initialize: None, database_name: st def test_drop_namespace(_bucket_initialize: None, database_name: str) -> None: test_catalog = DynamoDbCatalog("test_ddb_catalog") test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list test_catalog.drop_namespace(database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 0 @@ -472,7 +472,7 @@ def test_drop_non_empty_namespace( test_catalog = DynamoDbCatalog("test_ddb_catalog", **{"warehouse": f"s3://{BUCKET_NAME}", "s3.endpoint": moto_endpoint_url}) test_catalog.create_namespace(namespace=database_name) test_catalog.create_table(identifier, table_schema_nested) - assert len(test_catalog.list_tables(database_name)) == 1 + assert len(list(test_catalog.list_tables(database_name))) == 1 with pytest.raises(NamespaceNotEmptyError): test_catalog.drop_namespace(database_name) diff --git a/tests/catalog/test_glue.py b/tests/catalog/test_glue.py index 5273db22f8..aa64337fc0 100644 --- a/tests/catalog/test_glue.py +++ b/tests/catalog/test_glue.py @@ -465,7 +465,7 @@ def test_list_tables( for table_name in table_list: test_catalog.create_table((database_name, table_name), table_schema_nested) - loaded_table_list = test_catalog.list_tables(database_name) + loaded_table_list = list(test_catalog.list_tables(database_name)) assert (database_name, non_iceberg_table_name) not in loaded_table_list assert (database_name, non_table_type_table_name) not in loaded_table_list @@ -487,7 +487,7 @@ def test_list_namespaces(_bucket_initialize: None, moto_endpoint_url: str, datab def test_create_namespace_no_properties(_bucket_initialize: None, moto_endpoint_url: str, database_name: str) -> None: test_catalog = GlueCatalog("glue", **{"s3.endpoint": moto_endpoint_url}) test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list properties = test_catalog.load_namespace_properties(database_name) @@ -503,7 +503,7 @@ def test_create_namespace_with_comment_and_location(_bucket_initialize: None, mo } test_catalog = GlueCatalog("glue", **{"s3.endpoint": moto_endpoint_url}) test_catalog.create_namespace(namespace=database_name, properties=test_properties) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list properties = test_catalog.load_namespace_properties(database_name) @@ -515,7 +515,7 @@ def test_create_namespace_with_comment_and_location(_bucket_initialize: None, mo def test_create_duplicated_namespace(_bucket_initialize: None, moto_endpoint_url: str, database_name: str) -> None: test_catalog = GlueCatalog("glue", **{"s3.endpoint": moto_endpoint_url}) test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list with pytest.raises(NamespaceAlreadyExistsError): @@ -526,11 +526,11 @@ def test_create_duplicated_namespace(_bucket_initialize: None, moto_endpoint_url def test_drop_namespace(_bucket_initialize: None, moto_endpoint_url: str, database_name: str) -> None: test_catalog = GlueCatalog("glue", **{"s3.endpoint": moto_endpoint_url}) test_catalog.create_namespace(namespace=database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 1 assert (database_name,) in loaded_database_list test_catalog.drop_namespace(database_name) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert len(loaded_database_list) == 0 @@ -542,7 +542,7 @@ def test_drop_non_empty_namespace( test_catalog = GlueCatalog("glue", **{"s3.endpoint": moto_endpoint_url, "warehouse": f"s3://{BUCKET_NAME}/"}) test_catalog.create_namespace(namespace=database_name) test_catalog.create_table(identifier, table_schema_nested) - assert len(test_catalog.list_tables(database_name)) == 1 + assert len(list(test_catalog.list_tables(database_name))) == 1 with pytest.raises(NamespaceNotEmptyError): test_catalog.drop_namespace(database_name) diff --git a/tests/catalog/test_hive.py b/tests/catalog/test_hive.py index 1a3978a045..4c6b43d2ab 100644 --- a/tests/catalog/test_hive.py +++ b/tests/catalog/test_hive.py @@ -1037,7 +1037,7 @@ def test_list_tables(hive_table: HiveTable) -> None: catalog._client.__enter__().get_all_tables.return_value = ["table1", "table2", "table3", "table4"] catalog._client.__enter__().get_table_objects_by_name.return_value = [tbl1, tbl2, tbl3, tbl4] - got_tables = catalog.list_tables("database") + got_tables = list(catalog.list_tables("database")) assert got_tables == [("database", "table1"), ("database", "table2")] catalog._client.__enter__().get_all_tables.assert_called_with(db_name="database") catalog._client.__enter__().get_table_objects_by_name.assert_called_with( @@ -1051,7 +1051,7 @@ def test_list_namespaces() -> None: catalog._client = MagicMock() catalog._client.__enter__().get_all_databases.return_value = ["namespace1", "namespace2"] - assert catalog.list_namespaces() == [("namespace1",), ("namespace2",)] + assert list(catalog.list_namespaces()) == [("namespace1",), ("namespace2",)] catalog._client.__enter__().get_all_databases.assert_called() diff --git a/tests/catalog/test_rest.py b/tests/catalog/test_rest.py index b8bee00225..ebafa9327f 100644 --- a/tests/catalog/test_rest.py +++ b/tests/catalog/test_rest.py @@ -411,7 +411,7 @@ def test_list_tables_200(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_tables(namespace) == [("examples", "fooshare")] + assert list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_tables(namespace)) == [("examples", "fooshare")] def test_list_tables_200_sigv4(rest_mock: Mocker) -> None: @@ -423,9 +423,9 @@ def test_list_tables_200_sigv4(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "rest.sigv4-enabled": "true"}).list_tables(namespace) == [ - ("examples", "fooshare") - ] + assert list( + RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "rest.sigv4-enabled": "true"}).list_tables(namespace) + ) == [("examples", "fooshare")] assert rest_mock.called @@ -444,7 +444,7 @@ def test_list_tables_404(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) with pytest.raises(NoSuchNamespaceError) as e: - RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_tables(namespace) + list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_tables(namespace)) assert "Namespace does not exist" in str(e.value) @@ -457,7 +457,7 @@ def test_list_views_200(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace) == [("examples", "fooshare")] + assert list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace)) == [("examples", "fooshare")] def test_list_views_200_sigv4(rest_mock: Mocker) -> None: @@ -469,9 +469,9 @@ def test_list_views_200_sigv4(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "rest.sigv4-enabled": "true"}).list_views(namespace) == [ - ("examples", "fooshare") - ] + assert list( + RestCatalog("rest", **{"uri": TEST_URI, "token": TEST_TOKEN, "rest.sigv4-enabled": "true"}).list_views(namespace) + ) == [("examples", "fooshare")] assert rest_mock.called @@ -490,7 +490,7 @@ def test_list_views_404(rest_mock: Mocker) -> None: request_headers=TEST_HEADERS, ) with pytest.raises(NoSuchNamespaceError) as e: - RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace) + list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_views(namespace)) assert "Namespace does not exist" in str(e.value) @@ -537,7 +537,7 @@ def test_list_namespaces_200(rest_mock: Mocker) -> None: status_code=200, request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces() == [ + assert list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces()) == [ ("default",), ("examples",), ("fokko",), @@ -552,7 +552,7 @@ def test_list_namespace_with_parent_200(rest_mock: Mocker) -> None: status_code=200, request_headers=TEST_HEADERS, ) - assert RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces(("accounting",)) == [ + assert list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces(("accounting",))) == [ ("accounting", "tax"), ] @@ -572,7 +572,7 @@ def test_list_namespace_with_parent_404(rest_mock: Mocker) -> None: ) with pytest.raises(NoSuchNamespaceError): - RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces(("some_namespace",)) + list(RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN).list_namespaces(("some_namespace",))) @pytest.mark.filterwarnings( @@ -626,7 +626,7 @@ def test_list_namespaces_token_expired_success_on_retries(rest_mock: Mocker, sta # which results in the token being refreshed twice when the RestCatalog is initialized. assert tokens.call_count == 2 - assert catalog.list_namespaces() == [ + assert list(catalog.list_namespaces()) == [ ("default",), ("examples",), ("fokko",), @@ -635,7 +635,7 @@ def test_list_namespaces_token_expired_success_on_retries(rest_mock: Mocker, sta assert namespaces.call_count == 2 assert tokens.call_count == 3 - assert catalog.list_namespaces() == [ + assert list(catalog.list_namespaces()) == [ ("default",), ("examples",), ("fokko",), diff --git a/tests/catalog/test_sql.py b/tests/catalog/test_sql.py index 22b9883c6f..1ede8bf0ea 100644 --- a/tests/catalog/test_sql.py +++ b/tests/catalog/test_sql.py @@ -982,11 +982,11 @@ def test_list_tables( catalog.create_namespace(namespace_2) catalog.create_table(table_identifier_1, table_schema_nested) catalog.create_table(table_identifier_2, table_schema_nested) - identifier_list = catalog.list_tables(namespace_1) + identifier_list = list(catalog.list_tables(namespace_1)) assert len(identifier_list) == 1 assert table_identifier_1 in identifier_list - identifier_list = catalog.list_tables(namespace_2) + identifier_list = list(catalog.list_tables(namespace_2)) assert len(identifier_list) == 1 assert table_identifier_2 in identifier_list @@ -1001,7 +1001,7 @@ def test_list_tables( @pytest.mark.parametrize("namespace", [lazy_fixture("database_name"), lazy_fixture("hierarchical_namespace_name")]) def test_list_tables_when_missing_namespace(catalog: SqlCatalog, namespace: str) -> None: with pytest.raises(NoSuchNamespaceError): - catalog.list_tables(namespace) + list(catalog.list_tables(namespace)) @pytest.mark.parametrize( @@ -1142,17 +1142,17 @@ def test_list_namespaces(catalog: SqlCatalog) -> None: if not catalog._namespace_exists(namespace): catalog.create_namespace(namespace) - ns_list = catalog.list_namespaces() + ns_list = list(catalog.list_namespaces()) for ns in [("db",), ("db%",), ("db2",)]: assert ns in ns_list - ns_list = catalog.list_namespaces("db") + ns_list = list(catalog.list_namespaces("db")) assert sorted(ns_list) == [("db", "ns1"), ("db", "ns2")] - ns_list = catalog.list_namespaces("db.ns1") + ns_list = list(catalog.list_namespaces("db.ns1")) assert sorted(ns_list) == [("db", "ns1", "ns2")] - ns_list = catalog.list_namespaces("db.ns1.ns2") + ns_list = list(catalog.list_namespaces("db.ns1.ns2")) assert len(ns_list) == 0 @@ -1169,9 +1169,9 @@ def test_list_namespaces_fuzzy_match(catalog: SqlCatalog) -> None: if not catalog._namespace_exists(namespace): catalog.create_namespace(namespace) - assert catalog.list_namespaces("db.ns1") == [("db", "ns1", "ns2")] + assert list(catalog.list_namespaces("db.ns1")) == [("db", "ns1", "ns2")] - assert catalog.list_namespaces("db_.ns1") == [("db_", "ns1", "ns2")] + assert list(catalog.list_namespaces("db_.ns1")) == [("db_", "ns1", "ns2")] @pytest.mark.parametrize( @@ -1183,7 +1183,7 @@ def test_list_namespaces_fuzzy_match(catalog: SqlCatalog) -> None: ) def test_list_non_existing_namespaces(catalog: SqlCatalog) -> None: with pytest.raises(NoSuchNamespaceError): - catalog.list_namespaces("does_not_exist") + list(catalog.list_namespaces("does_not_exist")) @pytest.mark.parametrize( diff --git a/tests/integration/test_catalog.py b/tests/integration/test_catalog.py index 0c77666568..5e7c887b33 100644 --- a/tests/integration/test_catalog.py +++ b/tests/integration/test_catalog.py @@ -175,7 +175,7 @@ def test_list_tables(test_catalog: Catalog, table_schema_nested: Schema, databas test_catalog.create_namespace(database_name) for table_name in table_list: test_catalog.create_table((database_name, table_name), table_schema_nested) - identifier_list = test_catalog.list_tables(database_name) + identifier_list = list(test_catalog.list_tables(database_name)) assert len(identifier_list) == len(table_list) for table_name in table_list: assert (database_name, table_name) in identifier_list @@ -409,7 +409,7 @@ def test_concurrent_create_transaction(test_catalog: Catalog, test_schema: Schem @pytest.mark.parametrize("test_catalog", CATALOGS) def test_create_namespace(test_catalog: Catalog, database_name: str) -> None: test_catalog.create_namespace(database_name) - assert (database_name,) in test_catalog.list_namespaces() + assert (database_name,) in list(test_catalog.list_namespaces()) @pytest.mark.integration @@ -425,7 +425,7 @@ def test_create_duplicate_namespace(test_catalog: Catalog, database_name: str) - def test_create_namepsace_if_not_exists(test_catalog: Catalog, database_name: str) -> None: test_catalog.create_namespace(database_name) test_catalog.create_namespace_if_not_exists(database_name) - assert (database_name,) in test_catalog.list_namespaces() + assert (database_name,) in list(test_catalog.list_namespaces()) @pytest.mark.integration @@ -435,7 +435,7 @@ def test_create_namespace_with_comment(test_catalog: Catalog, database_name: str "comment": "this is a test description", } test_catalog.create_namespace(namespace=database_name, properties=test_properties) - loaded_database_list = test_catalog.list_namespaces() + loaded_database_list = list(test_catalog.list_namespaces()) assert (database_name,) in loaded_database_list properties = test_catalog.load_namespace_properties(database_name) assert properties["comment"] == "this is a test description" @@ -446,23 +446,23 @@ def test_create_namespace_with_comment(test_catalog: Catalog, database_name: str def test_list_namespaces(test_catalog: Catalog, database_list: list[str]) -> None: for database_name in database_list: test_catalog.create_namespace(database_name) - db_list = test_catalog.list_namespaces() + db_list = list(test_catalog.list_namespaces()) for database_name in database_list: assert (database_name,) in db_list - assert len(test_catalog.list_namespaces(list(database_list)[0])) == 0 + assert len(list(test_catalog.list_namespaces(list(database_list)[0]))) == 0 @pytest.mark.integration @pytest.mark.parametrize("test_catalog", CATALOGS) def test_drop_namespace(test_catalog: Catalog, table_schema_nested: Schema, table_name: str, database_name: str) -> None: test_catalog.create_namespace(database_name) - assert (database_name,) in test_catalog.list_namespaces() + assert (database_name,) in list(test_catalog.list_namespaces()) test_catalog.create_table((database_name, table_name), table_schema_nested) with pytest.raises(NamespaceNotEmptyError): test_catalog.drop_namespace(database_name) test_catalog.drop_table((database_name, table_name)) test_catalog.drop_namespace(database_name) - assert (database_name,) not in test_catalog.list_namespaces() + assert (database_name,) not in list(test_catalog.list_namespaces()) @pytest.mark.integration