This is an automated email from the ASF dual-hosted git repository.

fokko 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 781096eb Add `table_exists` method to Catalog (#512)
781096eb is described below

commit 781096eb0c71fa540357e0e6e3b51104ad6469ee
Author: Anupam Saini <[email protected]>
AuthorDate: Thu Mar 14 07:06:45 2024 -0400

    Add `table_exists` method to Catalog (#512)
    
    * Add `table_exist` method to Catalog
    
    * `table_exist` -> `table_exists`
    
    * Add `table_exists` for RestCatalog
    
    * Add doc
---
 mkdocs/docs/api.md            | 10 ++++++++++
 pyiceberg/catalog/__init__.py |  7 +++++++
 pyiceberg/catalog/rest.py     |  8 ++++++++
 tests/catalog/test_base.py    | 11 +++++++++++
 tests/catalog/test_rest.py    | 20 ++++++++++++++++++++
 5 files changed, 56 insertions(+)

diff --git a/mkdocs/docs/api.md b/mkdocs/docs/api.md
index 724a45c5..35b271ae 100644
--- a/mkdocs/docs/api.md
+++ b/mkdocs/docs/api.md
@@ -194,6 +194,16 @@ static_table = StaticTable.from_metadata(
 
 The static-table is considered read-only.
 
+## Check if a table exists
+
+To check whether the `bids` table exists:
+
+```python
+catalog.table_exists("docs_example.bids")
+```
+
+Returns `True` if the table already exists.
+
 ## Write support
 
 With PyIceberg 0.6.0 write support is added through Arrow. Let's consider an 
Arrow Table:
diff --git a/pyiceberg/catalog/__init__.py b/pyiceberg/catalog/__init__.py
index 4f7b5a32..f2b46fcd 100644
--- a/pyiceberg/catalog/__init__.py
+++ b/pyiceberg/catalog/__init__.py
@@ -646,6 +646,13 @@ class Catalog(ABC):
         delete_files(io, prev_metadata_files, PREVIOUS_METADATA)
         delete_files(io, {table.metadata_location}, METADATA)
 
+    def table_exists(self, identifier: Union[str, Identifier]) -> bool:
+        try:
+            self.load_table(identifier)
+            return True
+        except NoSuchTableError:
+            return False
+
     @staticmethod
     def _write_metadata(metadata: TableMetadata, io: FileIO, metadata_path: 
str) -> None:
         ToOutputFile.table_metadata(metadata, io.new_output(metadata_path))
diff --git a/pyiceberg/catalog/rest.py b/pyiceberg/catalog/rest.py
index f67fe2c1..9f0d0544 100644
--- a/pyiceberg/catalog/rest.py
+++ b/pyiceberg/catalog/rest.py
@@ -717,3 +717,11 @@ class RestCatalog(Catalog):
             updated=parsed_response.updated,
             missing=parsed_response.missing,
         )
+
+    @retry(**_RETRY_ARGS)
+    def table_exists(self, identifier: Union[str, Identifier]) -> bool:
+        identifier_tuple = self.identifier_to_tuple_without_catalog(identifier)
+        response = self._session.head(
+            self.url(Endpoints.load_table, prefixed=True, 
**self._split_identifier_for_path(identifier_tuple))
+        )
+        return response.status_code == 200
diff --git a/tests/catalog/test_base.py b/tests/catalog/test_base.py
index 44c36a7d..5f78eb3b 100644
--- a/tests/catalog/test_base.py
+++ b/tests/catalog/test_base.py
@@ -413,6 +413,17 @@ def test_table_raises_error_on_table_not_found(catalog: 
InMemoryCatalog) -> None
         catalog.load_table(TEST_TABLE_IDENTIFIER)
 
 
+def test_table_exists(catalog: InMemoryCatalog) -> None:
+    # Given
+    given_catalog_has_a_table(catalog)
+    # Then
+    assert catalog.table_exists(TEST_TABLE_IDENTIFIER)
+
+
+def test_table_exists_on_table_not_found(catalog: InMemoryCatalog) -> None:
+    assert not catalog.table_exists(TEST_TABLE_IDENTIFIER)
+
+
 def test_drop_table(catalog: InMemoryCatalog) -> None:
     # Given
     given_catalog_has_a_table(catalog)
diff --git a/tests/catalog/test_rest.py b/tests/catalog/test_rest.py
index 850e5f01..4956fffe 100644
--- a/tests/catalog/test_rest.py
+++ b/tests/catalog/test_rest.py
@@ -644,6 +644,26 @@ def test_load_table_404(rest_mock: Mocker) -> None:
     assert "Table does not exist" in str(e.value)
 
 
+def test_table_exist_200(rest_mock: Mocker) -> None:
+    rest_mock.head(
+        f"{TEST_URI}v1/namespaces/fokko/tables/table",
+        status_code=200,
+        request_headers=TEST_HEADERS,
+    )
+    catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
+    assert catalog.table_exists(("fokko", "table"))
+
+
+def test_table_exist_500(rest_mock: Mocker) -> None:
+    rest_mock.head(
+        f"{TEST_URI}v1/namespaces/fokko/tables/table",
+        status_code=500,
+        request_headers=TEST_HEADERS,
+    )
+    catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
+    assert not catalog.table_exists(("fokko", "table"))
+
+
 def test_drop_table_404(rest_mock: Mocker) -> None:
     rest_mock.delete(
         f"{TEST_URI}v1/namespaces/fokko/tables/does_not_exists",

Reply via email to