101 lines
4.7 KiB
Python
101 lines
4.7 KiB
Python
import logging
|
|
from typing import List, Optional
|
|
|
|
import requests
|
|
|
|
from ..logger import RoundTrip
|
|
from .base import DatabricksError
|
|
from .customizer import _ErrorCustomizer, _RetryAfterCustomizer
|
|
from .deserializer import (_EmptyDeserializer, _ErrorDeserializer,
|
|
_HtmlErrorDeserializer, _StandardErrorDeserializer,
|
|
_StringErrorDeserializer)
|
|
from .mapper import _error_mapper
|
|
from .private_link import (_get_private_link_validation_error,
|
|
_is_private_link_redirect)
|
|
|
|
# A list of _ErrorDeserializers that are tried in order to parse an API error from a response body. Most errors should
|
|
# be parsable by the _StandardErrorDeserializer, but additional parsers can be added here for specific error formats.
|
|
# The order of the parsers is not important, as the set of errors that can be parsed by each parser should be disjoint.
|
|
_error_deserializers = [
|
|
_EmptyDeserializer(),
|
|
_StandardErrorDeserializer(),
|
|
_StringErrorDeserializer(),
|
|
_HtmlErrorDeserializer(),
|
|
]
|
|
|
|
# A list of _ErrorCustomizers that are applied to the error arguments after they are parsed. Customizers can modify the
|
|
# error arguments in any way, including adding or removing fields. Customizers are applied in order, so later
|
|
# customizers can override the changes made by earlier customizers.
|
|
_error_customizers = [
|
|
_RetryAfterCustomizer(),
|
|
]
|
|
|
|
|
|
def _unknown_error(response: requests.Response, debug_headers: bool = False) -> str:
|
|
"""A standard error message that can be shown when an API response cannot be parsed.
|
|
|
|
This error message includes a link to the issue tracker for the SDK for users to report the issue to us.
|
|
|
|
:param response: The response object from the API request.
|
|
:param debug_headers: Whether to include headers in the request log. Defaults to False to defensively handle cases where request headers might contain sensitive data (e.g. tokens).
|
|
"""
|
|
request_log = RoundTrip(response, debug_headers=debug_headers, debug_truncate_bytes=10 * 1024).generate()
|
|
return (
|
|
"This is likely a bug in the Databricks SDK for Python or the underlying "
|
|
"API. Please report this issue with the following debugging information to the SDK issue tracker at "
|
|
f"https://github.com/databricks/databricks-sdk-go/issues. Request log:```{request_log}```"
|
|
)
|
|
|
|
|
|
class _Parser:
|
|
"""
|
|
A parser for errors from the Databricks REST API. It attempts to deserialize an error using a sequence of
|
|
deserializers, and then customizes the deserialized error using a sequence of customizers. If the error cannot be
|
|
deserialized, it returns a generic error with debugging information and instructions to report the issue to the SDK
|
|
issue tracker.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
extra_error_parsers: List[_ErrorDeserializer] = [],
|
|
extra_error_customizers: List[_ErrorCustomizer] = [],
|
|
debug_headers: bool = False,
|
|
):
|
|
self._error_parsers = _error_deserializers + (extra_error_parsers if extra_error_parsers is not None else [])
|
|
self._error_customizers = _error_customizers + (
|
|
extra_error_customizers if extra_error_customizers is not None else []
|
|
)
|
|
self._debug_headers = debug_headers
|
|
|
|
def get_api_error(self, response: requests.Response) -> Optional[DatabricksError]:
|
|
"""
|
|
Handles responses from the REST API and returns a DatabricksError if the response indicates an error.
|
|
:param response: The response from the REST API.
|
|
:return: A DatabricksError if the response indicates an error, otherwise None.
|
|
"""
|
|
if not response.ok:
|
|
content = response.content
|
|
for parser in self._error_parsers:
|
|
try:
|
|
error_args = parser.deserialize_error(response, content)
|
|
if error_args:
|
|
for customizer in self._error_customizers:
|
|
customizer.customize_error(response, error_args)
|
|
return _error_mapper(response, error_args)
|
|
except Exception as e:
|
|
logging.debug(
|
|
f"Error parsing response with {parser}, continuing",
|
|
exc_info=e,
|
|
)
|
|
return _error_mapper(
|
|
response,
|
|
{"message": "unable to parse response. " + _unknown_error(response, self._debug_headers)},
|
|
)
|
|
|
|
# Private link failures happen via a redirect to the login page. From a requests-perspective, the request
|
|
# is successful, but the response is not what we expect. We need to handle this case separately.
|
|
if _is_private_link_redirect(response):
|
|
return _get_private_link_validation_error(response.url)
|
|
|
|
return None
|