Skip to content

Commit d8c6c94

Browse files
authored
Implementing namespace_exists function on the REST Catalog (#1434)
* - Added the namespace_exists function in the RESTCatalog - Added the relevant unit tests * - Removed docstring to match other namespace functions * - Added integration test for REST Catalog namespace_exists functionality * - Added ASF license to test_rest_catalog.py to recover from failing test
1 parent e15f355 commit d8c6c94

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

pyiceberg/catalog/rest.py

+19
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class Endpoints:
9494
load_namespace_metadata: str = "namespaces/{namespace}"
9595
drop_namespace: str = "namespaces/{namespace}"
9696
update_namespace_properties: str = "namespaces/{namespace}/properties"
97+
namespace_exists: str = "namespaces/{namespace}"
9798
list_tables: str = "namespaces/{namespace}/tables"
9899
create_table: str = "namespaces/{namespace}/tables"
99100
register_table = "namespaces/{namespace}/register"
@@ -870,6 +871,24 @@ def update_namespace_properties(
870871
missing=parsed_response.missing,
871872
)
872873

874+
@retry(**_RETRY_ARGS)
875+
def namespace_exists(self, namespace: Union[str, Identifier]) -> bool:
876+
namespace_tuple = self._check_valid_namespace_identifier(namespace)
877+
namespace = NAMESPACE_SEPARATOR.join(namespace_tuple)
878+
response = self._session.head(self.url(Endpoints.namespace_exists, namespace=namespace))
879+
880+
if response.status_code == 404:
881+
return False
882+
elif response.status_code in (200, 204):
883+
return True
884+
885+
try:
886+
response.raise_for_status()
887+
except HTTPError as exc:
888+
self._handle_non_200_response(exc, {})
889+
890+
return False
891+
873892
@retry(**_RETRY_ARGS)
874893
def table_exists(self, identifier: Union[str, Identifier]) -> bool:
875894
"""Check if a table exists.

tests/catalog/test_rest.py

+45
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,51 @@ def test_update_namespace_properties_200(rest_mock: Mocker) -> None:
681681
assert response == PropertiesUpdateSummary(removed=[], updated=["prop"], missing=["abc"])
682682

683683

684+
def test_namespace_exists_200(rest_mock: Mocker) -> None:
685+
rest_mock.head(
686+
f"{TEST_URI}v1/namespaces/fokko",
687+
status_code=200,
688+
request_headers=TEST_HEADERS,
689+
)
690+
catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
691+
692+
assert catalog.namespace_exists("fokko")
693+
694+
695+
def test_namespace_exists_204(rest_mock: Mocker) -> None:
696+
rest_mock.head(
697+
f"{TEST_URI}v1/namespaces/fokko",
698+
status_code=204,
699+
request_headers=TEST_HEADERS,
700+
)
701+
catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
702+
703+
assert catalog.namespace_exists("fokko")
704+
705+
706+
def test_namespace_exists_404(rest_mock: Mocker) -> None:
707+
rest_mock.head(
708+
f"{TEST_URI}v1/namespaces/fokko",
709+
status_code=404,
710+
request_headers=TEST_HEADERS,
711+
)
712+
catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
713+
714+
assert not catalog.namespace_exists("fokko")
715+
716+
717+
def test_namespace_exists_500(rest_mock: Mocker) -> None:
718+
rest_mock.head(
719+
f"{TEST_URI}v1/namespaces/fokko",
720+
status_code=500,
721+
request_headers=TEST_HEADERS,
722+
)
723+
catalog = RestCatalog("rest", uri=TEST_URI, token=TEST_TOKEN)
724+
725+
with pytest.raises(ServerError):
726+
catalog.namespace_exists("fokko")
727+
728+
684729
def test_update_namespace_properties_404(rest_mock: Mocker) -> None:
685730
rest_mock.post(
686731
f"{TEST_URI}v1/namespaces/fokko/properties",
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
# pylint:disable=redefined-outer-name
18+
19+
import pytest
20+
21+
from pyiceberg.catalog.rest import RestCatalog
22+
23+
TEST_NAMESPACE_IDENTIFIER = "TEST NS"
24+
25+
26+
@pytest.mark.integration
27+
@pytest.mark.parametrize("catalog", [pytest.lazy_fixture("session_catalog")])
28+
def test_namespace_exists(catalog: RestCatalog) -> None:
29+
if not catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER):
30+
catalog.create_namespace(TEST_NAMESPACE_IDENTIFIER)
31+
32+
assert catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER)
33+
34+
35+
@pytest.mark.integration
36+
@pytest.mark.parametrize("catalog", [pytest.lazy_fixture("session_catalog")])
37+
def test_namespace_not_exists(catalog: RestCatalog) -> None:
38+
if catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER):
39+
catalog.drop_namespace(TEST_NAMESPACE_IDENTIFIER)
40+
41+
assert not catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER)
42+
43+
44+
@pytest.mark.integration
45+
@pytest.mark.parametrize("catalog", [pytest.lazy_fixture("session_catalog")])
46+
def test_create_namespace_if_not_exists(catalog: RestCatalog) -> None:
47+
if catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER):
48+
catalog.drop_namespace(TEST_NAMESPACE_IDENTIFIER)
49+
50+
catalog.create_namespace_if_not_exists(TEST_NAMESPACE_IDENTIFIER)
51+
52+
assert catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER)
53+
54+
55+
@pytest.mark.integration
56+
@pytest.mark.parametrize("catalog", [pytest.lazy_fixture("session_catalog")])
57+
def test_create_namespace_if_already_existing(catalog: RestCatalog) -> None:
58+
if not catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER):
59+
catalog.create_namespace(TEST_NAMESPACE_IDENTIFIER)
60+
61+
catalog.create_namespace_if_not_exists(TEST_NAMESPACE_IDENTIFIER)
62+
63+
assert catalog.namespace_exists(TEST_NAMESPACE_IDENTIFIER)

0 commit comments

Comments
 (0)