This commit is contained in:
Christian Mantha
2026-03-02 19:10:52 -05:00
commit 2ca0b9ef7c
28907 changed files with 5233713 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
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