From 47bd23ee0b41bf3112199afa892128ff06f7ba94 Mon Sep 17 00:00:00 2001 From: Serge Smertin Date: Fri, 10 Nov 2023 17:17:45 +0100 Subject: [PATCH] .. --- .codegen/error_mapping.py.tmpl | 41 ++++++------------------------- databricks/sdk/errors/__init__.py | 1 + databricks/sdk/errors/mapper.py | 19 ++++++++++++++ databricks/sdk/errors/mapping.py | 41 +++++++++---------------------- tests/test_errors.py | 18 ++++++-------- 5 files changed, 47 insertions(+), 73 deletions(-) create mode 100644 databricks/sdk/errors/mapper.py diff --git a/.codegen/error_mapping.py.tmpl b/.codegen/error_mapping.py.tmpl index 81dfb32e8..f5fa900eb 100644 --- a/.codegen/error_mapping.py.tmpl +++ b/.codegen/error_mapping.py.tmpl @@ -2,48 +2,21 @@ from .base import DatabricksError -__all__ = ['error_mapper'{{range .ExceptionTypes}}, '{{.PascalName}}'{{end}}] +__all__ = [{{range $i, $v := .ExceptionTypes}}{{if $i}}, {{end}}'{{.PascalName}}'{{end}}] -{{$builtinErrors := dict - "DEADLINE_EXCEEDED" "TimeoutError" - "NOT_FOUND" "LookupError" - "UNAUTHENTICATED" "PermissionError" - "PERMISSION_DENIED" "PermissionError" - "TOO_MANY_REQUESTS" "ResourceWarning" - "NOT_IMPLEMENTED" "NotImplementedError" -}} - -{{range .ExceptionTypes}}{{- $builtin := getOrDefault $builtinErrors .Name "" -}} - class {{.PascalName}}({{if .Inherit -}} +{{range .ExceptionTypes}} +class {{.PascalName}}({{if .Inherit -}} {{.Inherit.PascalName}} {{- else -}} DatabricksError - {{- end -}}{{if ne "" $builtin -}} - , {{ $builtin }} - {{- end}}): - """{{.Comment " " 100 | trimSuffix "\"" }}""" + {{- end -}}): + """{{.Comment " " 100 | trimSuffix "\"" }}""" {{end}} -_STATUS_CODE_MAPPING = { {{range .ErrorStatusCodeMapping}} +STATUS_CODE_MAPPING = { {{range .ErrorStatusCodeMapping}} {{.StatusCode}}: {{.PascalName}},{{- end}} } -_ERROR_CODE_MAPPING = { {{range .ErrorCodeMapping}} +ERROR_CODE_MAPPING = { {{range .ErrorCodeMapping}} '{{.ErrorCode}}': {{.PascalName}},{{- end}} } - -def error_mapper(status_code: int, raw: dict) -> DatabricksError: - error_code = raw.get('error_code', None) - if error_code in _ERROR_CODE_MAPPING: - # more specific error codes override more generic HTTP status codes - return _ERROR_CODE_MAPPING[error_code](**raw) - - if status_code in _STATUS_CODE_MAPPING: - # more generic HTTP status codes matched after more specific error codes, - # where there's a default exception class per HTTP status code, and we do - # rely on Databricks platform exception mapper to do the right thing. - return _STATUS_CODE_MAPPING[status_code](**raw) - - # backwards-compatible error creation for cases like using older versions of - # the SDK on way never releases of the platform. - return DatabricksError(**raw) diff --git a/databricks/sdk/errors/__init__.py b/databricks/sdk/errors/__init__.py index f703a71d0..a12c3cfe9 100644 --- a/databricks/sdk/errors/__init__.py +++ b/databricks/sdk/errors/__init__.py @@ -1,3 +1,4 @@ from .base import DatabricksError, ErrorDetail +from .mapper import error_mapper from .mapping import * from .sdk import * diff --git a/databricks/sdk/errors/mapper.py b/databricks/sdk/errors/mapper.py new file mode 100644 index 000000000..dd47b34d0 --- /dev/null +++ b/databricks/sdk/errors/mapper.py @@ -0,0 +1,19 @@ +from databricks.sdk.errors import mapping +from databricks.sdk.errors.base import DatabricksError + + +def error_mapper(status_code: int, raw: dict) -> DatabricksError: + error_code = raw.get('error_code', None) + if error_code in mapping.ERROR_CODE_MAPPING: + # more specific error codes override more generic HTTP status codes + return mapping.ERROR_CODE_MAPPING[error_code](**raw) + + if status_code in mapping.STATUS_CODE_MAPPING: + # more generic HTTP status codes matched after more specific error codes, + # where there's a default exception class per HTTP status code, and we do + # rely on Databricks platform exception mapper to do the right thing. + return mapping.STATUS_CODE_MAPPING[status_code](**raw) + + # backwards-compatible error creation for cases like using older versions of + # the SDK on way never releases of the platform. + return DatabricksError(**raw) diff --git a/databricks/sdk/errors/mapping.py b/databricks/sdk/errors/mapping.py index 27cc5001b..68416a289 100755 --- a/databricks/sdk/errors/mapping.py +++ b/databricks/sdk/errors/mapping.py @@ -3,10 +3,10 @@ from .base import DatabricksError __all__ = [ - 'error_mapper', 'BadRequest', 'Unauthenticated', 'PermissionDenied', 'NotFound', 'ResourceConflict', - 'TooManyRequests', 'Cancelled', 'InternalError', 'NotImplemented', 'TemporarilyUnavailable', - 'DeadlineExceeded', 'InvalidParameterValue', 'Aborted', 'AlreadyExists', 'ResourceAlreadyExists', - 'ResourceExhausted', 'RequestLimitExceeded', 'Unknown', 'DataLoss' + 'BadRequest', 'Unauthenticated', 'PermissionDenied', 'NotFound', 'ResourceConflict', 'TooManyRequests', + 'Cancelled', 'InternalError', 'NotImplemented', 'TemporarilyUnavailable', 'DeadlineExceeded', + 'InvalidParameterValue', 'Aborted', 'AlreadyExists', 'ResourceAlreadyExists', 'ResourceExhausted', + 'RequestLimitExceeded', 'Unknown', 'DataLoss' ] @@ -14,15 +14,15 @@ class BadRequest(DatabricksError): """the request is invalid""" -class Unauthenticated(DatabricksError, PermissionError): +class Unauthenticated(DatabricksError): """the request does not have valid authentication (AuthN) credentials for the operation""" -class PermissionDenied(DatabricksError, PermissionError): +class PermissionDenied(DatabricksError): """the caller does not have permission to execute the specified operation""" -class NotFound(DatabricksError, LookupError): +class NotFound(DatabricksError): """the operation was performed on a resource that does not exist""" @@ -30,7 +30,7 @@ class ResourceConflict(DatabricksError): """maps to all HTTP 409 (Conflict) responses""" -class TooManyRequests(DatabricksError, ResourceWarning): +class TooManyRequests(DatabricksError): """maps to HTTP code: 429 Too Many Requests""" @@ -42,7 +42,7 @@ class InternalError(DatabricksError): """some invariants expected by the underlying system have been broken""" -class NotImplemented(DatabricksError, NotImplementedError): +class NotImplemented(DatabricksError): """the operation is not implemented or is not supported/enabled in this service""" @@ -50,7 +50,7 @@ class TemporarilyUnavailable(DatabricksError): """the service is currently unavailable""" -class DeadlineExceeded(DatabricksError, TimeoutError): +class DeadlineExceeded(DatabricksError): """the deadline expired before the operation could complete""" @@ -87,7 +87,7 @@ class DataLoss(InternalError): """unrecoverable data loss or corruption""" -_STATUS_CODE_MAPPING = { +STATUS_CODE_MAPPING = { 400: BadRequest, 401: Unauthenticated, 403: PermissionDenied, @@ -101,7 +101,7 @@ class DataLoss(InternalError): 504: DeadlineExceeded, } -_ERROR_CODE_MAPPING = { +ERROR_CODE_MAPPING = { 'INVALID_PARAMETER_VALUE': InvalidParameterValue, 'ABORTED': Aborted, 'ALREADY_EXISTS': AlreadyExists, @@ -111,20 +111,3 @@ class DataLoss(InternalError): 'UNKNOWN': Unknown, 'DATA_LOSS': DataLoss, } - - -def error_mapper(status_code: int, raw: dict) -> DatabricksError: - error_code = raw.get('error_code', None) - if error_code in _ERROR_CODE_MAPPING: - # more specific error codes override more generic HTTP status codes - return _ERROR_CODE_MAPPING[error_code](**raw) - - if status_code in _STATUS_CODE_MAPPING: - # more generic HTTP status codes matched after more specific error codes, - # where there's a default exception class per HTTP status code, and we do - # rely on Databricks platform exception mapper to do the right thing. - return _STATUS_CODE_MAPPING[status_code](**raw) - - # backwards-compatible error creation for cases like using older versions of - # the SDK on way never releases of the platform. - return DatabricksError(**raw) diff --git a/tests/test_errors.py b/tests/test_errors.py index a0c9899cd..2b7cafa6e 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -27,26 +27,24 @@ def test_missing_error_code(): [(400, ..., errors.BadRequest), (400, 'INVALID_PARAMETER_VALUE', errors.BadRequest), (400, 'INVALID_PARAMETER_VALUE', errors.InvalidParameterValue), (400, 'REQUEST_LIMIT_EXCEEDED', errors.TooManyRequests), (400, ..., IOError), - (401, ..., errors.Unauthenticated), (401, ..., PermissionError), - (401, ..., IOError), (403, ..., errors.PermissionDenied), - (403, ..., PermissionError), (403, ..., IOError), (404, ..., errors.NotFound), - (404, ..., LookupError), (404, ..., IOError), (409, ..., errors.ResourceConflict), - (409, 'ABORTED', errors.Aborted), (409, 'ABORTED', errors.ResourceConflict), + (401, ..., errors.Unauthenticated), (401, ..., IOError), + (403, ..., errors.PermissionDenied), + (403, ..., IOError), (404, ..., errors.NotFound), (404, ..., IOError), + (409, ..., errors.ResourceConflict), (409, 'ABORTED', errors.Aborted), + (409, 'ABORTED', errors.ResourceConflict), (409, 'ALREADY_EXISTS', errors.AlreadyExists), (409, 'ALREADY_EXISTS', errors.ResourceConflict), (409, ..., IOError), (429, ..., errors.TooManyRequests), (429, 'REQUEST_LIMIT_EXCEEDED', errors.TooManyRequests), (429, 'REQUEST_LIMIT_EXCEEDED', errors.RequestLimitExceeded), (429, 'RESOURCE_EXHAUSTED', errors.TooManyRequests), - (429, 'RESOURCE_EXHAUSTED', errors.ResourceExhausted), (429, ..., ResourceWarning), - (417, 'REQUEST_LIMIT_EXCEEDED', ResourceWarning), (429, ..., IOError), + (429, 'RESOURCE_EXHAUSTED', errors.ResourceExhausted), (429, ..., IOError), (499, ..., errors.Cancelled), (499, ..., IOError), (500, ..., errors.InternalError), (500, 'UNKNOWN', errors.InternalError), (500, 'UNKNOWN', errors.Unknown), (500, 'DATA_LOSS', errors.InternalError), (500, 'DATA_LOSS', errors.DataLoss), - (500, ..., IOError), (501, ..., errors.NotImplemented), - (501, ..., NotImplementedError), (501, ..., IOError), + (500, ..., IOError), (501, ..., errors.NotImplemented), (501, ..., IOError), (503, ..., errors.TemporarilyUnavailable), (503, ..., IOError), - (504, ..., errors.DeadlineExceeded), (504, ..., TimeoutError), (504, ..., IOError), + (504, ..., errors.DeadlineExceeded), (504, ..., IOError), (444, ..., errors.DatabricksError), (444, ..., IOError), ]) def test_subclasses(status_code, error_code, klass): try: