This is an automated email from the ASF dual-hosted git repository.
yuqi1129 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 040e298517 [#3767] refactor(client-python): Share table-driven error
handler base to reduce duplicate code (#13672)
040e298517 is described below
commit 040e2985179c52e39e782f5e3a5be29401b40408
Author: dennismdejong <[email protected]>
AuthorDate: Thu Oct 8 02:54:25 2026 +0200
[#3767] refactor(client-python): Share table-driven error handler base to
reduce duplicate code (#13672)
### What changes were proposed in this pull request?
Introduce a `CodeMappingErrorHandler` base class in
`rest_error_handler.py` that maps error codes to exception classes (or
exception-type dictionaries) and delegates unmapped codes to the generic
REST error mapping.
Convert the 19 specialized error handlers (catalog, credential, fileset,
function, group, job, metalake, model, owner, partition, permission,
role, schema, secret, statistics, table, tag, user, view) to declare a
`_code_exception_map` instead of duplicating the same `if/elif raise`
branches.
### Why are the changes needed?
This removes ~300 lines of duplicated code and reduces the
`duplicate-code` Pylint violations from 277 to 259. Behavior is
preserved: all error-handler unit tests pass.
Fix: #3767
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
```bash
./gradlew clients:client-python:pylint
./gradlew clients:client-python:test -PskipITs
```
---------
Co-authored-by: Qi Yu <[email protected]>
---
.../exceptions/handlers/catalog_error_handler.py | 59 ++--
.../handlers/credential_error_handler.py | 26 +-
.../exceptions/handlers/fileset_error_handler.py | 40 +--
.../exceptions/handlers/function_error_handler.py | 51 +---
.../exceptions/handlers/group_error_handler.py | 49 +---
.../exceptions/handlers/job_error_handler.py | 64 ++--
.../exceptions/handlers/metalake_error_handler.py | 32 +-
.../exceptions/handlers/model_error_handler.py | 79 ++---
.../exceptions/handlers/owner_error_handler.py | 40 +--
.../exceptions/handlers/partition_error_handler.py | 61 ++--
.../handlers/permission_error_handler.py | 65 ++---
.../exceptions/handlers/rest_error_handler.py | 29 ++
.../exceptions/handlers/role_error_handler.py | 67 ++---
.../exceptions/handlers/schema_error_handler.py | 45 +--
.../exceptions/handlers/secret_error_handler.py | 23 +-
.../handlers/statistics_error_handler.py | 67 ++---
.../exceptions/handlers/table_error_handler.py | 62 ++--
.../exceptions/handlers/tag_error_handler.py | 54 +---
.../exceptions/handlers/user_error_handler.py | 50 +---
.../exceptions/handlers/view_error_handler.py | 62 ++--
.../unittests/test_code_mapping_error_handler.py | 324 +++++++++++++++++++++
21 files changed, 682 insertions(+), 667 deletions(-)
diff --git
a/clients/client-python/gravitino/exceptions/handlers/catalog_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/catalog_error_handler.py
index 2a921fd268..52159cdb85 100644
---
a/clients/client-python/gravitino/exceptions/handlers/catalog_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/catalog_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,45 +16,32 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
- ConnectionFailedException,
- CatalogInUseException,
- NoSuchMetalakeException,
- NoSuchCatalogException,
CatalogAlreadyExistsException,
+ CatalogInUseException,
CatalogNotInUseException,
+ ConnectionFailedException,
+ NoSuchCatalogException,
+ NoSuchMetalakeException,
)
-
-
-class CatalogErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.CONNECTION_FAILED_CODE:
- raise ConnectionFailedException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
- if exception_type == NoSuchCatalogException.__name__:
- raise NoSuchCatalogException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise CatalogAlreadyExistsException(error_message)
-
- if code == ErrorConstants.IN_USE_CODE:
- if exception_type == CatalogInUseException.__name__:
- raise CatalogInUseException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- if exception_type == CatalogNotInUseException.__name__:
- raise CatalogNotInUseException(error_message)
-
- super().handle(error_response)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class CatalogErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.CONNECTION_FAILED_CODE: ConnectionFailedException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchCatalogException.__name__: NoSuchCatalogException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: CatalogAlreadyExistsException,
+ ErrorConstants.IN_USE_CODE: {
+ CatalogInUseException.__name__: CatalogInUseException
+ },
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException
+ },
+ }
CATALOG_ERROR_HANDLER = CatalogErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/credential_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/credential_error_handler.py
index 3706163fb3..04793d48a4 100644
---
a/clients/client-python/gravitino/exceptions/handlers/credential_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/credential_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,28 +16,20 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
CatalogNotInUseException,
NoSuchCredentialException,
)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class CredentialErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchCredentialException.__name__:
- raise NoSuchCredentialException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise CatalogNotInUseException(error_message)
-
- super().handle(error_response)
+class CredentialErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchCredentialException.__name__: NoSuchCredentialException
+ },
+ ErrorConstants.NOT_IN_USE_CODE: CatalogNotInUseException,
+ }
CREDENTIAL_ERROR_HANDLER = CredentialErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/fileset_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/fileset_error_handler.py
index f11bda7ba2..92710650ef 100644
---
a/clients/client-python/gravitino/exceptions/handlers/fileset_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/fileset_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,34 +16,24 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
- NoSuchFilesetException,
- NoSuchSchemaException,
CatalogNotInUseException,
+ NoSuchFilesetException,
NoSuchLocationNameException,
+ NoSuchSchemaException,
)
-
-
-class FilesetErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
- if exception_type == NoSuchFilesetException.__name__:
- raise NoSuchFilesetException(error_message)
- if exception_type == NoSuchLocationNameException.__name__:
- raise NoSuchLocationNameException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise CatalogNotInUseException(error_message)
-
- super().handle(error_response)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class FilesetErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchFilesetException.__name__: NoSuchFilesetException,
+ NoSuchLocationNameException.__name__: NoSuchLocationNameException,
+ },
+ ErrorConstants.NOT_IN_USE_CODE: CatalogNotInUseException,
+ }
FILESET_ERROR_HANDLER = FilesetErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/function_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/function_error_handler.py
index 8e69f8b862..17d14afc79 100644
---
a/clients/client-python/gravitino/exceptions/handlers/function_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/function_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,51 +16,32 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
- AlreadyExistsException,
CatalogNotInUseException,
FunctionAlreadyExistsException,
MetalakeNotInUseException,
NoSuchFunctionException,
NoSuchSchemaException,
- NotFoundException,
- NotInUseException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class FunctionErrorHandler(RestErrorHandler):
+class FunctionErrorHandler(CodeMappingErrorHandler):
"""Error handler for function-related operations."""
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
- if exception_type == NoSuchFunctionException.__name__:
- raise NoSuchFunctionException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- if exception_type == FunctionAlreadyExistsException.__name__:
- raise FunctionAlreadyExistsException(error_message)
-
- raise AlreadyExistsException(error_message)
-
- if code == 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)
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchFunctionException.__name__: NoSuchFunctionException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: {
+ FunctionAlreadyExistsException.__name__:
FunctionAlreadyExistsException
+ },
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
FUNCTION_ERROR_HANDLER = FunctionErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/group_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/group_error_handler.py
index 5eddb7f397..81c9642bba 100644
--- a/clients/client-python/gravitino/exceptions/handlers/group_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/group_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,7 +16,6 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
GroupAlreadyExistsException,
IllegalArgumentException,
@@ -24,44 +23,24 @@ from gravitino.exceptions.base import (
NoSuchGroupException,
NoSuchMetalakeException,
NoSuchRoleException,
- NotFoundException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class GroupErrorHandler(RestErrorHandler):
+class GroupErrorHandler(CodeMappingErrorHandler):
"""Error handler specific to Group operations."""
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
-
- if exception_type == NoSuchGroupException.__name__:
- raise NoSuchGroupException(error_message)
-
- if exception_type == NoSuchRoleException.__name__:
- raise NoSuchRoleException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise GroupAlreadyExistsException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchGroupException.__name__: NoSuchGroupException,
+ NoSuchRoleException.__name__: NoSuchRoleException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: GroupAlreadyExistsException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
GROUP_ERROR_HANDLER = GroupErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/job_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/job_error_handler.py
index a769cd1605..0d7cb6b33e 100644
--- a/clients/client-python/gravitino/exceptions/handlers/job_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/job_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,48 +16,36 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
- NoSuchMetalakeException,
- JobTemplateAlreadyExistsException,
IllegalJobTemplateOperationException,
- NoSuchJobTemplateException,
- NoSuchJobException,
InUseException,
+ JobTemplateAlreadyExistsException,
MetalakeNotInUseException,
+ NoSuchJobException,
+ NoSuchJobTemplateException,
+ NoSuchMetalakeException,
)
-
-
-class JobErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- if exception_type == IllegalJobTemplateOperationException.__name__:
- raise IllegalJobTemplateOperationException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
- if exception_type == NoSuchJobTemplateException.__name__:
- raise NoSuchJobTemplateException(error_message)
- if exception_type == NoSuchJobException.__name__:
- raise NoSuchJobException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise JobTemplateAlreadyExistsException(error_message)
-
- if code == ErrorConstants.IN_USE_CODE:
- raise InUseException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- if exception_type == MetalakeNotInUseException.__name__:
- raise MetalakeNotInUseException(error_message)
-
- super().handle(error_response)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class JobErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: {
+ IllegalJobTemplateOperationException.__name__: (
+ IllegalJobTemplateOperationException
+ )
+ },
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchJobTemplateException.__name__: NoSuchJobTemplateException,
+ NoSuchJobException.__name__: NoSuchJobException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: JobTemplateAlreadyExistsException,
+ ErrorConstants.IN_USE_CODE: InUseException,
+ ErrorConstants.NOT_IN_USE_CODE: {
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException
+ },
+ }
JOB_ERROR_HANDLER = JobErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
index 86f7404a08..2e5f9ae30d 100644
---
a/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/metalake_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,34 +16,22 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
- NoSuchMetalakeException,
MetalakeAlreadyExistsException,
MetalakeInUseException,
MetalakeNotInUseException,
+ NoSuchMetalakeException,
)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class MetalakeErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- raise NoSuchMetalakeException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise MetalakeAlreadyExistsException(error_message)
-
- if code == ErrorConstants.IN_USE_CODE:
- raise MetalakeInUseException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- super().handle(error_response)
+class MetalakeErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: NoSuchMetalakeException,
+ ErrorConstants.ALREADY_EXISTS_CODE: MetalakeAlreadyExistsException,
+ ErrorConstants.IN_USE_CODE: MetalakeInUseException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ }
METALAKE_ERROR_HANDLER = MetalakeErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/model_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/model_error_handler.py
index a611a76725..4085def8bc 100644
--- a/clients/client-python/gravitino/exceptions/handlers/model_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/model_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -14,59 +14,42 @@
# 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 (
- NoSuchSchemaException,
+ CatalogNotInUseException,
+ MetalakeNotInUseException,
+ ModelAlreadyExistsException,
+ ModelVersionAliasesAlreadyExistException,
NoSuchModelException,
NoSuchModelVersionException,
NoSuchModelVersionURINameException,
- NotFoundException,
- ModelAlreadyExistsException,
- ModelVersionAliasesAlreadyExistException,
- AlreadyExistsException,
- CatalogNotInUseException,
- MetalakeNotInUseException,
- NotInUseException,
+ NoSuchSchemaException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
-
-
-class ModelErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
- if exception_type == NoSuchModelException.__name__:
- raise NoSuchModelException(error_message)
- if exception_type == NoSuchModelVersionException.__name__:
- raise NoSuchModelVersionException(error_message)
- if exception_type == NoSuchModelVersionURINameException.__name__:
- raise NoSuchModelVersionURINameException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- if exception_type == ModelAlreadyExistsException.__name__:
- raise ModelAlreadyExistsException(error_message)
- if exception_type ==
ModelVersionAliasesAlreadyExistException.__name__:
- raise ModelVersionAliasesAlreadyExistException(error_message)
-
- raise AlreadyExistsException(error_message)
-
- if code == 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)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class ModelErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchModelException.__name__: NoSuchModelException,
+ NoSuchModelVersionException.__name__: NoSuchModelVersionException,
+ NoSuchModelVersionURINameException.__name__: (
+ NoSuchModelVersionURINameException
+ ),
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: {
+ ModelAlreadyExistsException.__name__: ModelAlreadyExistsException,
+ ModelVersionAliasesAlreadyExistException.__name__: (
+ ModelVersionAliasesAlreadyExistException
+ ),
+ },
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
MODEL_ERROR_HANDLER = ModelErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/owner_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/owner_error_handler.py
index bda819cd82..a02ab334e5 100644
--- a/clients/client-python/gravitino/exceptions/handlers/owner_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/owner_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -20,39 +20,23 @@ from gravitino.exceptions.base import (
IllegalArgumentException,
MetalakeNotInUseException,
NoSuchMetadataObjectException,
- NotFoundException,
UnsupportedOperationException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class OwnerErrorHandler(RestErrorHandler):
+class OwnerErrorHandler(CodeMappingErrorHandler):
"""Error handler specific to Owner operations."""
- def handle(self, error_response) -> None:
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetadataObjectException.__name__:
- raise NoSuchMetadataObjectException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetadataObjectException.__name__:
NoSuchMetadataObjectException
+ },
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
OWNER_ERROR_HANDLER = OwnerErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
index 0d5b921ea2..bdf9b20133 100644
---
a/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/partition_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,7 +16,6 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
CatalogNotInUseException,
IllegalArgumentException,
@@ -24,48 +23,28 @@ from gravitino.exceptions.base import (
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 = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == 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 == ErrorConstants.ALREADY_EXISTS_CODE:
- raise PartitionAlreadyExistsException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == 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)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class PartitionErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchTableException.__name__: NoSuchTableException,
+ NoSuchPartitionException.__name__: NoSuchPartitionException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: PartitionAlreadyExistsException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
PARTITION_ERROR_HANDLER = PartitionErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/permission_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/permission_error_handler.py
index d5a6fc904c..20f381bc82 100644
---
a/clients/client-python/gravitino/exceptions/handlers/permission_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/permission_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -15,11 +15,8 @@
# 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 (
- IllegalArgumentException,
IllegalMetadataObjectException,
IllegalPrivilegeException,
IllegalRoleException,
@@ -29,53 +26,31 @@ from gravitino.exceptions.base import (
NoSuchMetalakeException,
NoSuchRoleException,
NoSuchUserException,
- NotFoundException,
UnsupportedOperationException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class PermissionErrorHandler(RestErrorHandler):
+class PermissionErrorHandler(CodeMappingErrorHandler):
"""Error handler for permission operations (grant/revoke)."""
- # pylint: disable=too-many-branches
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- if exception_type == IllegalPrivilegeException.__name__:
- raise IllegalPrivilegeException(error_message)
- if exception_type == IllegalMetadataObjectException.__name__:
- raise IllegalMetadataObjectException(error_message)
- if exception_type == IllegalRoleException.__name__:
- raise IllegalRoleException(error_message)
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
- if exception_type == NoSuchUserException.__name__:
- raise NoSuchUserException(error_message)
- if exception_type == NoSuchGroupException.__name__:
- raise NoSuchGroupException(error_message)
- if exception_type == NoSuchRoleException.__name__:
- raise NoSuchRoleException(error_message)
- if exception_type == NoSuchMetadataObjectException.__name__:
- raise NoSuchMetadataObjectException(error_message)
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: {
+ IllegalPrivilegeException.__name__: IllegalPrivilegeException,
+ IllegalMetadataObjectException.__name__:
IllegalMetadataObjectException,
+ IllegalRoleException.__name__: IllegalRoleException,
+ },
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchUserException.__name__: NoSuchUserException,
+ NoSuchGroupException.__name__: NoSuchGroupException,
+ NoSuchRoleException.__name__: NoSuchRoleException,
+ NoSuchMetadataObjectException.__name__:
NoSuchMetadataObjectException,
+ },
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
PERMISSION_ERROR_HANDLER = PermissionErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/rest_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/rest_error_handler.py
index 578410cea2..4acbf5a937 100644
--- a/clients/client-python/gravitino/exceptions/handlers/rest_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/rest_error_handler.py
@@ -34,4 +34,33 @@ class RestErrorHandler(ErrorHandler):
)
+class CodeMappingErrorHandler(RestErrorHandler):
+ """Base error handler that maps error codes to exceptions.
+
+ Subclasses declare a class-level ``_code_exception_map`` that maps an error
+ code to either an exception class or a dict of exception-type names to
+ exception classes for codes that need to disambiguate by the response
+ ``type`` field. Codes not present in the map are delegated to
+ ``RestErrorHandler.handle``.
+ """
+
+ _code_exception_map = {}
+
+ def handle(self, error_response: ErrorResponse):
+ error_message = error_response.format_error_message()
+ code = error_response.code()
+ exception_type = error_response.type()
+
+ mapping = self._code_exception_map.get(code)
+ if mapping is not None:
+ if isinstance(mapping, dict):
+ exception_class = mapping.get(exception_type)
+ if exception_class is not None:
+ raise exception_class(error_message)
+ else:
+ raise mapping(error_message)
+
+ super().handle(error_response)
+
+
REST_ERROR_HANDLER = RestErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/role_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/role_error_handler.py
index e10837d6d2..56282a9096 100644
--- a/clients/client-python/gravitino/exceptions/handlers/role_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/role_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -15,69 +15,40 @@
# 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 (
ForbiddenException,
- IllegalArgumentException,
IllegalMetadataObjectException,
IllegalPrivilegeException,
MetalakeNotInUseException,
NoSuchMetadataObjectException,
NoSuchMetalakeException,
NoSuchRoleException,
- NotFoundException,
RoleAlreadyExistsException,
UnsupportedOperationException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class RoleErrorHandler(RestErrorHandler):
+class RoleErrorHandler(CodeMappingErrorHandler):
"""Error handler specific to Role operations."""
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- if exception_type == IllegalPrivilegeException.__name__:
- raise IllegalPrivilegeException(error_message)
- if exception_type == IllegalMetadataObjectException.__name__:
- raise IllegalMetadataObjectException(error_message)
-
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
-
- if exception_type == NoSuchRoleException.__name__:
- raise NoSuchRoleException(error_message)
-
- if exception_type == NoSuchMetadataObjectException.__name__:
- raise NoSuchMetadataObjectException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise RoleAlreadyExistsException(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == ErrorConstants.FORBIDDEN_CODE:
- raise ForbiddenException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: {
+ IllegalPrivilegeException.__name__: IllegalPrivilegeException,
+ IllegalMetadataObjectException.__name__:
IllegalMetadataObjectException,
+ },
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchRoleException.__name__: NoSuchRoleException,
+ NoSuchMetadataObjectException.__name__:
NoSuchMetadataObjectException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: RoleAlreadyExistsException,
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.FORBIDDEN_CODE: ForbiddenException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
ROLE_ERROR_HANDLER = RoleErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/schema_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/schema_error_handler.py
index 42a0a8894b..3412a26d75 100644
---
a/clients/client-python/gravitino/exceptions/handlers/schema_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/schema_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,39 +16,26 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
from gravitino.exceptions.base import (
+ CatalogNotInUseException,
NoSuchCatalogException,
NoSuchSchemaException,
- CatalogNotInUseException,
- SchemaAlreadyExistsException,
NonEmptySchemaException,
+ SchemaAlreadyExistsException,
)
-
-
-class SchemaErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchCatalogException.__name__:
- raise NoSuchCatalogException(error_message)
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise SchemaAlreadyExistsException(error_message)
-
- if code == ErrorConstants.NON_EMPTY_CODE:
- raise NonEmptySchemaException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise CatalogNotInUseException(error_message)
-
- super().handle(error_response)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class SchemaErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchCatalogException.__name__: NoSuchCatalogException,
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: SchemaAlreadyExistsException,
+ ErrorConstants.NON_EMPTY_CODE: NonEmptySchemaException,
+ ErrorConstants.NOT_IN_USE_CODE: CatalogNotInUseException,
+ }
SCHEMA_ERROR_HANDLER = SchemaErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/secret_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/secret_error_handler.py
index 4eff8c9544..59051c6302 100644
---
a/clients/client-python/gravitino/exceptions/handlers/secret_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/secret_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,23 +16,14 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
-from gravitino.exceptions.base import CatalogNotInUseException,
NotFoundException
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.base import CatalogNotInUseException
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class SecretErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise CatalogNotInUseException(error_message)
-
- super().handle(error_response)
+class SecretErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.NOT_IN_USE_CODE: CatalogNotInUseException,
+ }
SECRET_ERROR_HANDLER = SecretErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/statistics_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/statistics_error_handler.py
index c453b66205..554578d80d 100644
---
a/clients/client-python/gravitino/exceptions/handlers/statistics_error_handler.py
+++
b/clients/client-python/gravitino/exceptions/handlers/statistics_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,62 +16,39 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
CatalogNotInUseException,
ForbiddenException,
- IllegalArgumentException,
IllegalStatisticNameException,
MetalakeNotInUseException,
NoSuchMetadataObjectException,
NoSuchSchemaException,
NoSuchTableException,
- NotFoundException,
- NotInUseException,
UnmodifiableStatisticException,
- UnsupportedOperationException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class StatisticsErrorHandler(RestErrorHandler):
- def handle(
- self, error_response: ErrorResponse
- ): # pylint: disable=too-many-branches
- error_message = error_response.format_error_message()
- # Keep unknown server codes intact so the common handler can report
them.
- code = error_response.code()
- exception_type = error_response.type()
-
- match code:
- case ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- if exception_type == IllegalStatisticNameException.__name__:
- raise IllegalStatisticNameException(error_message)
- raise IllegalArgumentException(error_message)
- case 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 == NoSuchMetadataObjectException.__name__:
- raise NoSuchMetadataObjectException(error_message)
- raise NotFoundException(error_message)
- case ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
- case ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- if exception_type == UnmodifiableStatisticException.__name__:
- raise UnmodifiableStatisticException(error_message)
- raise UnsupportedOperationException(error_message)
- case ErrorConstants.FORBIDDEN_CODE:
- raise ForbiddenException(error_message)
- case 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)
- case _:
- super().handle(error_response)
+class StatisticsErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: {
+ IllegalStatisticNameException.__name__:
IllegalStatisticNameException
+ },
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchTableException.__name__: NoSuchTableException,
+ NoSuchMetadataObjectException.__name__:
NoSuchMetadataObjectException,
+ },
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE: {
+ UnmodifiableStatisticException.__name__:
UnmodifiableStatisticException
+ },
+ ErrorConstants.FORBIDDEN_CODE: ForbiddenException,
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
STATISTICS_ERROR_HANDLER = StatisticsErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/table_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/table_error_handler.py
index f3b16cbed9..9327a63f74 100644
--- a/clients/client-python/gravitino/exceptions/handlers/table_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/table_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,7 +16,6 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
CatalogNotInUseException,
ForbiddenException,
@@ -24,49 +23,28 @@ from gravitino.exceptions.base import (
MetalakeNotInUseException,
NoSuchSchemaException,
NoSuchTableException,
- NotFoundException,
- NotInUseException,
TableAlreadyExistsException,
UnsupportedOperationException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
-
-
-class TableErrorHandler(RestErrorHandler):
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
- if exception_type == NoSuchTableException.__name__:
- raise NoSuchTableException(error_message)
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise TableAlreadyExistsException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == ErrorConstants.FORBIDDEN_CODE:
- raise ForbiddenException(error_message)
-
- if code == 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)
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
+
+
+class TableErrorHandler(CodeMappingErrorHandler):
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchTableException.__name__: NoSuchTableException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: TableAlreadyExistsException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.FORBIDDEN_CODE: ForbiddenException,
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
TABLE_ERROR_HANDLER = TableErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/tag_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/tag_error_handler.py
index 61bd0a5f0e..ae82e17c83 100644
--- a/clients/client-python/gravitino/exceptions/handlers/tag_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/tag_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -17,54 +17,32 @@
from gravitino.constants.error import ErrorConstants
from gravitino.exceptions.base import (
- AlreadyExistsException,
IllegalArgumentException,
MetalakeNotInUseException,
NoSuchMetalakeException,
NoSuchTagException,
- NotFoundException,
TagAlreadyAssociatedException,
TagAlreadyExistsException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class TagErrorHandler(RestErrorHandler):
+class TagErrorHandler(CodeMappingErrorHandler):
"""Error handler specific to Tag operations."""
- def handle(self, error_response) -> None:
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
-
- if exception_type == NoSuchTagException.__name__:
- raise NoSuchTagException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- if exception_type == TagAlreadyExistsException.__name__:
- raise TagAlreadyExistsException(error_message)
-
- if exception_type == TagAlreadyAssociatedException.__name__:
- raise TagAlreadyAssociatedException(error_message)
-
- raise AlreadyExistsException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchTagException.__name__: NoSuchTagException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: {
+ TagAlreadyExistsException.__name__: TagAlreadyExistsException,
+ TagAlreadyAssociatedException.__name__:
TagAlreadyAssociatedException,
+ },
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
TAG_ERROR_HANDLER = TagErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/user_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/user_error_handler.py
index 61640b7285..d97ca232dd 100644
--- a/clients/client-python/gravitino/exceptions/handlers/user_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/user_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -15,54 +15,32 @@
# 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 (
IllegalArgumentException,
MetalakeNotInUseException,
NoSuchMetalakeException,
NoSuchRoleException,
NoSuchUserException,
- NotFoundException,
UserAlreadyExistsException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class UserErrorHandler(RestErrorHandler):
+class UserErrorHandler(CodeMappingErrorHandler):
"""Error handler specific to User operations."""
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchMetalakeException.__name__:
- raise NoSuchMetalakeException(error_message)
-
- if exception_type == NoSuchUserException.__name__:
- raise NoSuchUserException(error_message)
-
- if exception_type == NoSuchRoleException.__name__:
- raise NoSuchRoleException(error_message)
-
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise UserAlreadyExistsException(error_message)
-
- if code == ErrorConstants.NOT_IN_USE_CODE:
- raise MetalakeNotInUseException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- super().handle(error_response)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchMetalakeException.__name__: NoSuchMetalakeException,
+ NoSuchUserException.__name__: NoSuchUserException,
+ NoSuchRoleException.__name__: NoSuchRoleException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: UserAlreadyExistsException,
+ ErrorConstants.NOT_IN_USE_CODE: MetalakeNotInUseException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ }
USER_ERROR_HANDLER = UserErrorHandler()
diff --git
a/clients/client-python/gravitino/exceptions/handlers/view_error_handler.py
b/clients/client-python/gravitino/exceptions/handlers/view_error_handler.py
index a40632bbcf..da2d9ad0cf 100644
--- a/clients/client-python/gravitino/exceptions/handlers/view_error_handler.py
+++ b/clients/client-python/gravitino/exceptions/handlers/view_error_handler.py
@@ -6,7 +6,7 @@
# "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
+# 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
@@ -16,7 +16,6 @@
# under the License.
from gravitino.constants.error import ErrorConstants
-from gravitino.dto.responses.error_response import ErrorResponse
from gravitino.exceptions.base import (
CatalogNotInUseException,
ForbiddenException,
@@ -25,54 +24,31 @@ from gravitino.exceptions.base import (
NoSuchCatalogException,
NoSuchSchemaException,
NoSuchViewException,
- NotFoundException,
- NotInUseException,
UnsupportedOperationException,
ViewAlreadyExistsException,
)
-from gravitino.exceptions.handlers.rest_error_handler import RestErrorHandler
+from gravitino.exceptions.handlers.rest_error_handler import
CodeMappingErrorHandler
-class ViewErrorHandler(RestErrorHandler):
+class ViewErrorHandler(CodeMappingErrorHandler):
"""Error handler for view operations."""
- def handle(self, error_response: ErrorResponse):
- error_message = error_response.format_error_message()
- code = error_response.code()
- exception_type = error_response.type()
-
- if code == ErrorConstants.ILLEGAL_ARGUMENTS_CODE:
- raise IllegalArgumentException(error_message)
-
- if code == ErrorConstants.NOT_FOUND_CODE:
- if exception_type == NoSuchCatalogException.__name__:
- raise NoSuchCatalogException(error_message)
- if exception_type == NoSuchSchemaException.__name__:
- raise NoSuchSchemaException(error_message)
- if exception_type == NoSuchViewException.__name__:
- raise NoSuchViewException(error_message)
- raise NotFoundException(error_message)
-
- if code == ErrorConstants.ALREADY_EXISTS_CODE:
- raise ViewAlreadyExistsException(error_message)
-
- if code == ErrorConstants.INTERNAL_ERROR_CODE:
- raise RuntimeError(error_message)
-
- if code == ErrorConstants.UNSUPPORTED_OPERATION_CODE:
- raise UnsupportedOperationException(error_message)
-
- if code == ErrorConstants.FORBIDDEN_CODE:
- raise ForbiddenException(error_message)
-
- if code == 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)
+ _code_exception_map = {
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE: IllegalArgumentException,
+ ErrorConstants.NOT_FOUND_CODE: {
+ NoSuchCatalogException.__name__: NoSuchCatalogException,
+ NoSuchSchemaException.__name__: NoSuchSchemaException,
+ NoSuchViewException.__name__: NoSuchViewException,
+ },
+ ErrorConstants.ALREADY_EXISTS_CODE: ViewAlreadyExistsException,
+ ErrorConstants.INTERNAL_ERROR_CODE: RuntimeError,
+ ErrorConstants.UNSUPPORTED_OPERATION_CODE:
UnsupportedOperationException,
+ ErrorConstants.FORBIDDEN_CODE: ForbiddenException,
+ ErrorConstants.NOT_IN_USE_CODE: {
+ CatalogNotInUseException.__name__: CatalogNotInUseException,
+ MetalakeNotInUseException.__name__: MetalakeNotInUseException,
+ },
+ }
VIEW_ERROR_HANDLER = ViewErrorHandler()
diff --git
a/clients/client-python/tests/unittests/test_code_mapping_error_handler.py
b/clients/client-python/tests/unittests/test_code_mapping_error_handler.py
new file mode 100644
index 0000000000..80909aa3bb
--- /dev/null
+++ b/clients/client-python/tests/unittests/test_code_mapping_error_handler.py
@@ -0,0 +1,324 @@
+# 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.
+
+import unittest
+from io import BytesIO
+from unittest.mock import patch
+from urllib.error import HTTPError
+
+from gravitino.constants.error import ErrorConstants
+from gravitino.dto.responses.error_response import ErrorResponse
+from gravitino.exceptions.base import (
+ CatalogNotInUseException,
+ FunctionAlreadyExistsException,
+ IllegalArgumentException,
+ IllegalJobTemplateOperationException,
+ InUseException,
+ JobTemplateAlreadyExistsException,
+ MetalakeNotInUseException,
+ ModelAlreadyExistsException,
+ ModelVersionAliasesAlreadyExistException,
+ NoSuchFunctionException,
+ NoSuchJobException,
+ NoSuchJobTemplateException,
+ NoSuchMetalakeException,
+ NoSuchModelException,
+ NoSuchModelVersionException,
+ NoSuchModelVersionURINameException,
+ NoSuchSchemaException,
+ NoSuchTagException,
+ NotFoundException,
+ RESTException,
+ TagAlreadyAssociatedException,
+ TagAlreadyExistsException,
+)
+from gravitino.exceptions.handlers.function_error_handler import
FUNCTION_ERROR_HANDLER
+from gravitino.exceptions.handlers.job_error_handler import JOB_ERROR_HANDLER
+from gravitino.exceptions.handlers.model_error_handler import
MODEL_ERROR_HANDLER
+from gravitino.exceptions.handlers.secret_error_handler import
SECRET_ERROR_HANDLER
+from gravitino.exceptions.handlers.tag_error_handler import TAG_ERROR_HANDLER
+from gravitino.utils.http_client import HTTPClient
+
+
+class TestCodeMappingErrorHandler(unittest.TestCase):
+ """Regression tests for the table-driven specialized error handlers.
+
+ These cover the code-to-exception mappings of the function, model, job, tag
+ and secret handlers, which the rest of the suite does not exercise. The
+ ``ErrorResponse`` is built from explicit wire values and the expected
+ exception is defined locally, so the assertions do not depend on the
+ mapping table under test.
+ """
+
+ def _assert_code_exception_map(self, handler, cases):
+ """Asserts the handler raises the expected exception for each case.
+
+ ``cases`` is a sequence of ``(code, type, expected_exception)`` tuples.
+ """
+ for code, error_type, expected in cases:
+ with self.subTest(
+ handler=type(handler).__name__, code=code,
error_type=error_type
+ ):
+ response = ErrorResponse(code, error_type, "mock error", None)
+ with self.assertRaises(expected):
+ handler.handle(response)
+
+ def test_function_error_handler_specialized_mappings(self):
+ self._assert_code_exception_map(
+ FUNCTION_ERROR_HANDLER,
+ [
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchSchemaException",
+ NoSuchSchemaException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchFunctionException",
+ NoSuchFunctionException,
+ ),
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "FunctionAlreadyExistsException",
+ FunctionAlreadyExistsException,
+ ),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "CatalogNotInUseException",
+ CatalogNotInUseException,
+ ),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "MetalakeNotInUseException",
+ MetalakeNotInUseException,
+ ),
+ ],
+ )
+
+ def test_model_error_handler_specialized_mappings(self):
+ self._assert_code_exception_map(
+ MODEL_ERROR_HANDLER,
+ [
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchSchemaException",
+ NoSuchSchemaException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchModelException",
+ NoSuchModelException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchModelVersionException",
+ NoSuchModelVersionException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchModelVersionURINameException",
+ NoSuchModelVersionURINameException,
+ ),
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "ModelAlreadyExistsException",
+ ModelAlreadyExistsException,
+ ),
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "ModelVersionAliasesAlreadyExistException",
+ ModelVersionAliasesAlreadyExistException,
+ ),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "CatalogNotInUseException",
+ CatalogNotInUseException,
+ ),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "MetalakeNotInUseException",
+ MetalakeNotInUseException,
+ ),
+ ],
+ )
+
+ def test_job_error_handler_specialized_mappings(self):
+ self._assert_code_exception_map(
+ JOB_ERROR_HANDLER,
+ [
+ (
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
+ "IllegalJobTemplateOperationException",
+ IllegalJobTemplateOperationException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchMetalakeException",
+ NoSuchMetalakeException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchJobTemplateException",
+ NoSuchJobTemplateException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchJobException",
+ NoSuchJobException,
+ ),
+ # ALREADY_EXISTS_CODE and IN_USE_CODE map to a single class
+ # regardless of the response type.
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "AnyType",
+ JobTemplateAlreadyExistsException,
+ ),
+ (ErrorConstants.IN_USE_CODE, "AnyType", InUseException),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "MetalakeNotInUseException",
+ MetalakeNotInUseException,
+ ),
+ ],
+ )
+
+ def test_tag_error_handler_specialized_mappings(self):
+ self._assert_code_exception_map(
+ TAG_ERROR_HANDLER,
+ [
+ (
+ ErrorConstants.ILLEGAL_ARGUMENTS_CODE,
+ "AnyType",
+ IllegalArgumentException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchMetalakeException",
+ NoSuchMetalakeException,
+ ),
+ (
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchTagException",
+ NoSuchTagException,
+ ),
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "TagAlreadyExistsException",
+ TagAlreadyExistsException,
+ ),
+ (
+ ErrorConstants.ALREADY_EXISTS_CODE,
+ "TagAlreadyAssociatedException",
+ TagAlreadyAssociatedException,
+ ),
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "AnyType",
+ MetalakeNotInUseException,
+ ),
+ (ErrorConstants.INTERNAL_ERROR_CODE, "AnyType", RuntimeError),
+ ],
+ )
+
+ def test_secret_error_handler_specialized_mappings(self):
+ self._assert_code_exception_map(
+ SECRET_ERROR_HANDLER,
+ [
+ (
+ ErrorConstants.NOT_IN_USE_CODE,
+ "AnyType",
+ CatalogNotInUseException,
+ ),
+ ],
+ )
+
+ def test_specialized_handlers_known_code_unknown_type_fall_back(self):
+ # The code is present in the handler's map, but the response type does
+ # not match any entry, so the handler must fall back to the generic
+ # mapping for that code.
+ for handler in (FUNCTION_ERROR_HANDLER, MODEL_ERROR_HANDLER):
+ with self.subTest(handler=type(handler).__name__):
+ response = ErrorResponse(
+ ErrorConstants.NOT_FOUND_CODE,
+ "UnexpectedNotFoundType",
+ "mock error",
+ None,
+ )
+ with self.assertRaises(NotFoundException):
+ handler.handle(response)
+
+ def test_specialized_handlers_unknown_code_fall_back(self):
+ for handler in (
+ FUNCTION_ERROR_HANDLER,
+ MODEL_ERROR_HANDLER,
+ JOB_ERROR_HANDLER,
+ TAG_ERROR_HANDLER,
+ SECRET_ERROR_HANDLER,
+ ):
+ with self.subTest(handler=type(handler).__name__):
+ response = ErrorResponse(
+ 1999, "FutureServerException", "Future error", None
+ )
+ with self.assertRaisesRegex(
+ RESTException, "Unable to process: Future error"
+ ):
+ handler.handle(response)
+
+ def test_specialized_handler_message_includes_server_stack(self):
+ response = ErrorResponse(
+ ErrorConstants.NOT_FOUND_CODE,
+ "NoSuchFunctionException",
+ "Function missing",
+ [
+ "at com.example.Foo.bar(Foo.java:1)",
+ "at com.example.Baz.qux(Baz.java:2)",
+ ],
+ )
+ with self.assertRaises(NoSuchFunctionException) as context:
+ FUNCTION_ERROR_HANDLER.handle(response)
+ self.assertEqual(
+ "Function missing\n"
+ "at com.example.Foo.bar(Foo.java:1)\n"
+ "at com.example.Baz.qux(Baz.java:2)",
+ str(context.exception),
+ )
+
+ generic_response = ErrorResponse(
+ 1999, "FutureServerException", "Future error", ["stack line"]
+ )
+ with self.assertRaisesRegex(
+ RESTException, "Unable to process: Future error\nstack line"
+ ):
+ SECRET_ERROR_HANDLER.handle(generic_response)
+
+ def test_function_error_handler_through_http_error_response(self):
+ body = (
+ b'{"code":1003,"type":"NoSuchFunctionException",'
+ b'"message":"Function not found","stack":null}'
+ )
+ with patch("gravitino.utils.http_client.build_opener") as build_opener:
+ build_opener.return_value.open.side_effect = HTTPError(
+ "http://localhost:8090/api/test",
+ 404,
+ "Not Found",
+ None,
+ BytesIO(body),
+ )
+ with self.assertRaisesRegex(NoSuchFunctionException, "Function not
found"):
+ HTTPClient("http://localhost:8090").get(
+ "/api/test", error_handler=FUNCTION_ERROR_HANDLER
+ )