From a9ea785b15fc45a79ad9dbe0beeba6a966737b02 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Tue, 2 Sep 2025 16:32:32 -0700 Subject: [PATCH] fix: remote python sdk namespace typing (#2620) This changes the default values for some namespace parameters in the remote python SDK from None to [], to match the underlying code it calls. Prior to this commit, failing to supply "namespace" with the remote SDK would cause an error because the underlying code it dispatches to does not consider None to be valid input. --- python/python/lancedb/db.py | 6 ++++-- python/python/lancedb/namespace.py | 17 +++++++++-------- python/python/lancedb/remote/db.py | 9 +++++---- python/python/tests/test_db.py | 12 ++++++++++++ python/python/tests/test_namespace.py | 9 +++++++++ 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/python/python/lancedb/db.py b/python/python/lancedb/db.py index 07d95b18..2de54694 100644 --- a/python/python/lancedb/db.py +++ b/python/python/lancedb/db.py @@ -95,9 +95,10 @@ class DBConnection(EnforceOverrides): @abstractmethod def table_names( self, - namespace: List[str] = [], page_token: Optional[str] = None, limit: int = 10, + *, + namespace: List[str] = [], ) -> Iterable[str]: """List all tables in this database, in sorted order @@ -543,9 +544,10 @@ class LanceDBConnection(DBConnection): @override def table_names( self, - namespace: List[str] = [], page_token: Optional[str] = None, limit: int = 10, + *, + namespace: List[str] = [], ) -> Iterable[str]: """Get the names of all tables in the database. The names are sorted. diff --git a/python/python/lancedb/namespace.py b/python/python/lancedb/namespace.py index b18fdb23..b0c07d06 100644 --- a/python/python/lancedb/namespace.py +++ b/python/python/lancedb/namespace.py @@ -138,9 +138,10 @@ class LanceNamespaceDBConnection(DBConnection): @override def table_names( self, - namespace: List[str] = [], page_token: Optional[str] = None, limit: int = 10, + *, + namespace: List[str] = [], ) -> Iterable[str]: request = ListTablesRequest(id=namespace, page_token=page_token, limit=limit) response = self._ns.list_tables(request) @@ -190,7 +191,7 @@ class LanceNamespaceDBConnection(DBConnection): json_schema = _convert_pyarrow_schema_to_json(schema) # Create table request with namespace - table_id = (namespace or []) + [name] + table_id = namespace + [name] request = CreateTableRequest(id=table_id, var_schema=json_schema) # Create empty Arrow IPC stream bytes @@ -219,7 +220,7 @@ class LanceNamespaceDBConnection(DBConnection): storage_options: Optional[Dict[str, str]] = None, index_cache_size: Optional[int] = None, ) -> Table: - table_id = (namespace or []) + [name] + table_id = namespace + [name] request = DescribeTableRequest(id=table_id) response = self._ns.describe_table(request) @@ -236,9 +237,9 @@ class LanceNamespaceDBConnection(DBConnection): ) @override - def drop_table(self, name: str, namespace: Optional[List[str]] = None): + def drop_table(self, name: str, namespace: List[str] = []): # Use namespace drop_table directly - table_id = (namespace or []) + [name] + table_id = namespace + [name] request = DropTableRequest(id=table_id) self._ns.drop_table(request) @@ -247,8 +248,8 @@ class LanceNamespaceDBConnection(DBConnection): self, cur_name: str, new_name: str, - cur_namespace: Optional[List[str]] = None, - new_namespace: Optional[List[str]] = None, + cur_namespace: List[str] = [], + new_namespace: List[str] = [], ): raise NotImplementedError( "rename_table is not supported for namespace connections" @@ -261,7 +262,7 @@ class LanceNamespaceDBConnection(DBConnection): ) @override - def drop_all_tables(self, namespace: Optional[List[str]] = None): + def drop_all_tables(self, namespace: List[str] = []): for table_name in self.table_names(namespace=namespace): self.drop_table(table_name, namespace=namespace) diff --git a/python/python/lancedb/remote/db.py b/python/python/lancedb/remote/db.py index 6ca110b2..a337cf51 100644 --- a/python/python/lancedb/remote/db.py +++ b/python/python/lancedb/remote/db.py @@ -151,9 +151,10 @@ class RemoteDBConnection(DBConnection): @override def table_names( self, - namespace: List[str] = [], page_token: Optional[str] = None, limit: int = 10, + *, + namespace: List[str] = [], ) -> Iterable[str]: """List the names of all tables in the database. @@ -343,7 +344,7 @@ class RemoteDBConnection(DBConnection): return RemoteTable(table, self.db_name) @override - def drop_table(self, name: str, namespace: Optional[List[str]] = None): + def drop_table(self, name: str, namespace: List[str] = []): """Drop a table from the database. Parameters @@ -361,8 +362,8 @@ class RemoteDBConnection(DBConnection): self, cur_name: str, new_name: str, - cur_namespace: Optional[List[str]] = None, - new_namespace: Optional[List[str]] = None, + cur_namespace: List[str] = [], + new_namespace: List[str] = [], ): """Rename a table in the database. diff --git a/python/python/tests/test_db.py b/python/python/tests/test_db.py index 7c2a8c36..f4381e94 100644 --- a/python/python/tests/test_db.py +++ b/python/python/tests/test_db.py @@ -175,6 +175,18 @@ def test_table_names(tmp_db: lancedb.DBConnection): tmp_db.create_table("test3", data=data) assert tmp_db.table_names() == ["test1", "test2", "test3"] + # Test that positional arguments for page_token and limit + result = list(tmp_db.table_names("test1", 1)) # page_token="test1", limit=1 + assert result == ["test2"], f"Expected ['test2'], got {result}" + + # Test mixed positional and keyword arguments + result = list(tmp_db.table_names("test2", limit=2)) + assert result == ["test3"], f"Expected ['test3'], got {result}" + + # Test that namespace parameter can be passed as keyword + result = list(tmp_db.table_names(namespace=[])) + assert len(result) == 3 + @pytest.mark.asyncio async def test_table_names_async(tmp_path): diff --git a/python/python/tests/test_namespace.py b/python/python/tests/test_namespace.py index e7f6b017..c5ebecfc 100644 --- a/python/python/tests/test_namespace.py +++ b/python/python/tests/test_namespace.py @@ -420,6 +420,10 @@ class TestNamespaceConnection: assert "table2" in table_names assert len(table_names) == 1 + # Test that drop_table works without explicit namespace parameter + db.drop_table("table2") + assert len(list(db.table_names())) == 0 + # Should not be able to open dropped table with pytest.raises(RuntimeError): db.open_table("table1") @@ -487,6 +491,11 @@ class TestNamespaceConnection: # Verify all tables are gone assert len(list(db.table_names())) == 0 + # Test that table_names works with keyword-only namespace parameter + db.create_table("test_table", schema=schema) + result = list(db.table_names(namespace=[])) + assert "test_table" in result + def test_table_operations(self): """Test various table operations through namespace.""" db = lancedb.connect_namespace("temp", {"root": self.temp_dir})