This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 33e410eb13 [#8796] feat(client-python): add partition name list
response (#9024)
33e410eb13 is described below
commit 33e410eb138f6fba5ed1890340cab7d43440baaa
Author: George T. C. Lai <[email protected]>
AuthorDate: Wed Nov 12 03:47:31 2025 +0800
[#8796] feat(client-python): add partition name list response (#9024)
### What changes were proposed in this pull request?
We need to add class `PartitionNameListResponse` and the corresponding
error handler.
- `PartitionNameListResponse`
- `ErrorHandlers.PartitionNameListResponse`
### Why are the changes needed?
We need to enable table operations in Python client that requires
implementation of class `PartitionNameListResponse` used in
`RelationalTable`.
Fix: #8796
### Does this PR introduce _any_ user-facing change?
No
### How was this patch tested?
Unit tests
---------
Signed-off-by: George T. C. Lai <[email protected]>
---
.../dto/responses/partition_name_list_response.py | 49 +++++++++
clients/client-python/gravitino/exceptions/base.py | 12 +++
.../exceptions/handlers/partition_error_handler.py | 71 +++++++++++++
.../tests/unittests/test_error_handler.py | 113 ++++++++++++++++++---
.../tests/unittests/test_responses.py | 18 ++++
5 files changed, 249 insertions(+), 14 deletions(-)
diff --git
a/clients/client-python/gravitino/dto/responses/partition_name_list_response.py
b/clients/client-python/gravitino/dto/responses/partition_name_list_response.py
new file mode 100644
index 0000000000..c8bd7a2bed
--- /dev/null
+++
b/clients/client-python/gravitino/dto/responses/partition_name_list_response.py
@@ -0,0 +1,49 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from dataclasses import dataclass, field
+
+from dataclasses_json import config
+
+from gravitino.dto.responses.base_response import BaseResponse
+from gravitino.exceptions.base import IllegalArgumentException
+
+
+@dataclass
+class PartitionNameListResponse(BaseResponse):
+ """Represents a response containing a list of partition names."""
+
+ _partition_names: list[str] = field(metadata=config(field_name="names"))
+
+ def partition_names(self) -> list[str]:
+ """Get the list of partition names.
+
+ Returns:
+ list[str]: The list of partition names.
+ """
+ return self._partition_names
+
+ def validate(self) -> None:
+ """Validates the response data.
+
+ Raises:
+ IllegalArgumentException: if partition names are not set.
+ """
+
+ super().validate()
+ if self._partition_names is None:
+ raise IllegalArgumentException("partition names must not be null")
diff --git a/clients/client-python/gravitino/exceptions/base.py
b/clients/client-python/gravitino/exceptions/base.py
index 0193ad78fe..62422082e2 100644
--- a/clients/client-python/gravitino/exceptions/base.py
+++ b/clients/client-python/gravitino/exceptions/base.py
@@ -191,3 +191,15 @@ class NoSuchJobException(NotFoundException):
class ForbiddenException(GravitinoRuntimeException):
"""An exception thrown when a user is forbidden to perform an action."""
+
+
+class NoSuchTableException(NotFoundException):
+ """An exception thrown when a table with specified name is not existed."""
+
+
+class NoSuchPartitionException(NotFoundException):
+ """An exception thrown when a partition with specified name is not
existed."""
+
+
+class PartitionAlreadyExistsException(AlreadyExistsException):
+ """An exception thrown when a partition with specified name already
exists."""
diff --git
a/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
new file mode 100644
index 0000000000..52fb36c3e7
--- /dev/null
+++
b/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
@@ -0,0 +1,71 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from gravitino.constants.error import ErrorConstants
+from gravitino.dto.responses.error_response import ErrorResponse
+from gravitino.exceptions.base import (
+ CatalogNotInUseException,
+ IllegalArgumentException,
+ MetalakeNotInUseException,
+ NoSuchPartitionException,
+ NoSuchSchemaException,
+ NoSuchTableException,
+ NotFoundException,
+ NotInUseException,
+ PartitionAlreadyExistsException,
+ UnsupportedOperationException,
+)
+from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+
+
+class PartitionErrorHandler(RestErrorHandler):
+ def handle(self, error_response: ErrorResponse):
+ error_message = error_response.format_error_message()
+ code = ErrorConstants(error_response.code())
+ exception_type = error_response.type()
+
+ if code is ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
+ raise IllegalArgumentException(error_message)
+
+ if code is ErrorConstants.NOT_FOUND_CODE:
+ if exception_type == NoSuchSchemaException.__name__:
+ raise NoSuchSchemaException(error_message)
+ if exception_type == NoSuchTableException.__name__:
+ raise NoSuchTableException(error_message)
+ if exception_type == NoSuchPartitionException.__name__:
+ raise NoSuchPartitionException(error_message)
+ raise NotFoundException(error_message)
+
+ if code is ErrorConstants.ALREADY_EXISTS_CODE:
+ raise PartitionAlreadyExistsException(error_message)
+
+ if code is ErrorConstants.INTERNAL_ERROR_CODE:
+ raise RuntimeError(error_message)
+
+ if code is ErrorConstants.UNSUPPORTED_OPERATION_CODE:
+ raise UnsupportedOperationException(error_message)
+
+ if code is ErrorConstants.NOT_IN_USE_CODE:
+ if exception_type == CatalogNotInUseException.__name__:
+ raise CatalogNotInUseException(error_message)
+ if exception_type == MetalakeNotInUseException.__name__:
+ raise MetalakeNotInUseException(error_message)
+ raise NotInUseException(error_message)
+ super().handle(error_response)
+
+
+PARTITION_ERROR_HANDLER = PartitionErrorHandler()
diff --git a/clients/client-python/tests/unittests/test_error_handler.py
b/clients/client-python/tests/unittests/test_error_handler.py
index 688e6c3480..291daa6d49 100644
--- a/clients/client-python/tests/unittests/test_error_handler.py
+++ b/clients/client-python/tests/unittests/test_error_handler.py
@@ -19,32 +19,40 @@ import unittest
from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
+ AlreadyExistsException,
+ CatalogAlreadyExistsException,
+ CatalogNotInUseException,
+ ConnectionFailedException,
+ IllegalArgumentException,
+ InternalError,
+ MetalakeAlreadyExistsException,
+ MetalakeNotInUseException,
+ NonEmptySchemaException,
NoSuchCatalogException,
- NoSuchSchemaException,
+ NoSuchCredentialException,
NoSuchFilesetException,
NoSuchMetalakeException,
- MetalakeAlreadyExistsException,
- InternalError,
- RESTException,
- NotFoundException,
- IllegalArgumentException,
- AlreadyExistsException,
+ NoSuchPartitionException,
+ NoSuchSchemaException,
+ NoSuchTableException,
NotEmptyException,
- NonEmptySchemaException,
+ NotFoundException,
+ NotInUseException,
+ PartitionAlreadyExistsException,
+ RESTException,
SchemaAlreadyExistsException,
UnsupportedOperationException,
- ConnectionFailedException,
- CatalogAlreadyExistsException,
- NoSuchCredentialException,
)
+from gravitino.exceptions.handlers.catalog_error_handler import
CATALOG_ERROR_HANDLER
from gravitino.exceptions.handlers.credential_error_handler import (
CREDENTIAL_ERROR_HANDLER,
)
-
-from gravitino.exceptions.handlers.rest_error_handler import REST_ERROR_HANDLER
from gravitino.exceptions.handlers.fileset_error_handler import
FILESET_ERROR_HANDLER
from gravitino.exceptions.handlers.metalake_error_handler import
METALAKE_ERROR_HANDLER
-from gravitino.exceptions.handlers.catalog_error_handler import
CATALOG_ERROR_HANDLER
+from gravitino.exceptions.handlers.partition_error_handler import (
+ PARTITION_ERROR_HANDLER,
+)
+from gravitino.exceptions.handlers.rest_error_handler import REST_ERROR_HANDLER
from gravitino.exceptions.handlers.schema_error_handler import
SCHEMA_ERROR_HANDLER
@@ -255,3 +263,80 @@ class TestErrorHandler(unittest.TestCase):
SCHEMA_ERROR_HANDLER.handle(
ErrorResponse.generate_error_response(Exception, "mock error")
)
+
+ def test_partition_error_handler(self):
+ with self.assertRaises(IllegalArgumentException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ IllegalArgumentException, "mock error"
+ )
+ )
+
+ with self.assertRaises(NoSuchSchemaException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ NoSuchSchemaException, "mock error"
+ )
+ )
+
+ with self.assertRaises(NoSuchTableException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ NoSuchTableException, "mock error"
+ )
+ )
+
+ with self.assertRaises(NoSuchPartitionException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ NoSuchPartitionException, "mock error"
+ )
+ )
+
+ with self.assertRaises(NotFoundException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(NotFoundException, "mock
error")
+ )
+
+ with self.assertRaises(PartitionAlreadyExistsException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ PartitionAlreadyExistsException, "mock error"
+ )
+ )
+
+ with self.assertRaises(RuntimeError):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(RuntimeError, "mock
error")
+ )
+
+ with self.assertRaises(UnsupportedOperationException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ UnsupportedOperationException, "mock error"
+ )
+ )
+
+ with self.assertRaises(CatalogNotInUseException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ CatalogNotInUseException, "mock error"
+ )
+ )
+
+ with self.assertRaises(MetalakeNotInUseException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(
+ MetalakeNotInUseException, "mock error"
+ )
+ )
+
+ with self.assertRaises(NotInUseException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(NotInUseException, "mock
error")
+ )
+
+ with self.assertRaises(RESTException):
+ PARTITION_ERROR_HANDLER.handle(
+ ErrorResponse.generate_error_response(Exception, "mock error")
+ )
diff --git a/clients/client-python/tests/unittests/test_responses.py
b/clients/client-python/tests/unittests/test_responses.py
index 3cc451ffc4..423a87acfa 100644
--- a/clients/client-python/tests/unittests/test_responses.py
+++ b/clients/client-python/tests/unittests/test_responses.py
@@ -23,6 +23,9 @@ from gravitino.dto.responses.model_response import
ModelResponse
from gravitino.dto.responses.model_version_list_response import
ModelVersionListResponse
from gravitino.dto.responses.model_version_response import ModelVersionResponse
from gravitino.dto.responses.model_version_uri_response import
ModelVersionUriResponse
+from gravitino.dto.responses.partition_name_list_response import (
+ PartitionNameListResponse,
+)
from gravitino.exceptions.base import IllegalArgumentException
@@ -275,3 +278,18 @@ class TestResponses(unittest.TestCase):
json_str_missing_1, infer_missing=True
)
self.assertRaises(IllegalArgumentException, resp_missing_1.validate)
+
+ def test_partition_name_list_response(self):
+ partition_names = [f"partition_{i}" for i in range(3)]
+ json_data = {"code": 0, "names": partition_names}
+ json_str = json.dumps(json_data)
+ resp: PartitionNameListResponse =
PartitionNameListResponse.from_json(json_str)
+ self.assertListEqual(resp.partition_names(), partition_names)
+ resp.validate()
+
+ def test_partition_name_list_response_exception(self):
+ json_data = {"code": 0, "names": None}
+ json_str = json.dumps(json_data)
+ resp: PartitionNameListResponse =
PartitionNameListResponse.from_json(json_str)
+ with self.assertRaises(IllegalArgumentException):
+ resp.validate()