databricks-sdk 0.44.1__py3-none-any.whl → 0.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of databricks-sdk might be problematic. Click here for more details.
- databricks/sdk/__init__.py +123 -115
- databricks/sdk/_base_client.py +112 -88
- databricks/sdk/_property.py +12 -7
- databricks/sdk/_widgets/__init__.py +13 -2
- databricks/sdk/_widgets/default_widgets_utils.py +21 -15
- databricks/sdk/_widgets/ipywidgets_utils.py +47 -24
- databricks/sdk/azure.py +8 -6
- databricks/sdk/casing.py +5 -5
- databricks/sdk/config.py +152 -99
- databricks/sdk/core.py +57 -47
- databricks/sdk/credentials_provider.py +300 -205
- databricks/sdk/data_plane.py +86 -3
- databricks/sdk/dbutils.py +123 -87
- databricks/sdk/environments.py +52 -35
- databricks/sdk/errors/base.py +61 -35
- databricks/sdk/errors/customizer.py +3 -3
- databricks/sdk/errors/deserializer.py +38 -25
- databricks/sdk/errors/details.py +417 -0
- databricks/sdk/errors/mapper.py +1 -1
- databricks/sdk/errors/overrides.py +27 -24
- databricks/sdk/errors/parser.py +26 -14
- databricks/sdk/errors/platform.py +10 -10
- databricks/sdk/errors/private_link.py +24 -24
- databricks/sdk/logger/round_trip_logger.py +28 -20
- databricks/sdk/mixins/compute.py +90 -60
- databricks/sdk/mixins/files.py +815 -145
- databricks/sdk/mixins/jobs.py +191 -16
- databricks/sdk/mixins/open_ai_client.py +26 -20
- databricks/sdk/mixins/workspace.py +45 -34
- databricks/sdk/oauth.py +372 -196
- databricks/sdk/retries.py +14 -12
- databricks/sdk/runtime/__init__.py +34 -17
- databricks/sdk/runtime/dbutils_stub.py +52 -39
- databricks/sdk/service/_internal.py +12 -7
- databricks/sdk/service/apps.py +618 -418
- databricks/sdk/service/billing.py +827 -604
- databricks/sdk/service/catalog.py +6552 -4474
- databricks/sdk/service/cleanrooms.py +550 -388
- databricks/sdk/service/compute.py +5241 -3531
- databricks/sdk/service/dashboards.py +1313 -923
- databricks/sdk/service/files.py +442 -309
- databricks/sdk/service/iam.py +2115 -1483
- databricks/sdk/service/jobs.py +4151 -2588
- databricks/sdk/service/marketplace.py +2210 -1517
- databricks/sdk/service/ml.py +3364 -2255
- databricks/sdk/service/oauth2.py +922 -584
- databricks/sdk/service/pipelines.py +1865 -1203
- databricks/sdk/service/provisioning.py +1435 -1029
- databricks/sdk/service/serving.py +2040 -1278
- databricks/sdk/service/settings.py +2846 -1929
- databricks/sdk/service/sharing.py +2201 -877
- databricks/sdk/service/sql.py +4650 -3103
- databricks/sdk/service/vectorsearch.py +816 -550
- databricks/sdk/service/workspace.py +1330 -906
- databricks/sdk/useragent.py +36 -22
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.44.1.dist-info → databricks_sdk-0.45.0.dist-info}/METADATA +31 -31
- databricks_sdk-0.45.0.dist-info/RECORD +70 -0
- {databricks_sdk-0.44.1.dist-info → databricks_sdk-0.45.0.dist-info}/WHEEL +1 -1
- databricks_sdk-0.44.1.dist-info/RECORD +0 -69
- {databricks_sdk-0.44.1.dist-info → databricks_sdk-0.45.0.dist-info}/LICENSE +0 -0
- {databricks_sdk-0.44.1.dist-info → databricks_sdk-0.45.0.dist-info}/NOTICE +0 -0
- {databricks_sdk-0.44.1.dist-info → databricks_sdk-0.45.0.dist-info}/top_level.txt +0 -0
databricks/sdk/errors/base.py
CHANGED
|
@@ -1,47 +1,55 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import warnings
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Dict, List, Optional
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
import requests
|
|
7
7
|
|
|
8
|
+
from . import details as errdetails
|
|
8
9
|
|
|
9
|
-
class ErrorDetail:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
# Deprecated.
|
|
12
|
+
class ErrorDetail:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
type: Optional[str] = None,
|
|
16
|
+
reason: Optional[str] = None,
|
|
17
|
+
domain: Optional[str] = None,
|
|
18
|
+
metadata: Optional[dict] = None,
|
|
19
|
+
**kwargs,
|
|
20
|
+
):
|
|
17
21
|
self.type = type
|
|
18
22
|
self.reason = reason
|
|
19
23
|
self.domain = domain
|
|
20
24
|
self.metadata = metadata
|
|
21
25
|
|
|
22
26
|
@classmethod
|
|
23
|
-
def from_dict(cls, d: Dict[str,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
def from_dict(cls, d: Dict[str, Any]) -> "ErrorDetail":
|
|
28
|
+
# Key "@type" is not a valid keyword argument name in Python. Rename
|
|
29
|
+
# it to "type" to avoid conflicts.
|
|
30
|
+
safe_args = {}
|
|
31
|
+
for k, v in d.items():
|
|
32
|
+
safe_args[k if k != "@type" else "type"] = v
|
|
33
|
+
|
|
34
|
+
return cls(**safe_args)
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
class DatabricksError(IOError):
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
"""Generic error from Databricks REST API"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
message: Optional[str] = None,
|
|
43
|
+
*,
|
|
44
|
+
error_code: Optional[str] = None,
|
|
45
|
+
detail: Optional[str] = None,
|
|
46
|
+
status: Optional[str] = None,
|
|
47
|
+
scimType: Optional[str] = None,
|
|
48
|
+
error: Optional[str] = None,
|
|
49
|
+
retry_after_secs: Optional[int] = None,
|
|
50
|
+
details: Optional[List[Dict[str, Any]]] = None,
|
|
51
|
+
**kwargs,
|
|
52
|
+
):
|
|
45
53
|
"""
|
|
46
54
|
|
|
47
55
|
:param message:
|
|
@@ -50,11 +58,11 @@ class DatabricksError(IOError):
|
|
|
50
58
|
:param status: [Deprecated]
|
|
51
59
|
:param scimType: [Deprecated]
|
|
52
60
|
:param error: [Deprecated]
|
|
53
|
-
:param retry_after_secs:
|
|
61
|
+
:param retry_after_secs: [Deprecated]
|
|
54
62
|
:param details:
|
|
55
63
|
:param kwargs:
|
|
56
64
|
"""
|
|
57
|
-
# SCIM-specific parameters are deprecated
|
|
65
|
+
# SCIM-specific parameters are deprecated.
|
|
58
66
|
if detail:
|
|
59
67
|
warnings.warn(
|
|
60
68
|
"The 'detail' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
@@ -68,12 +76,18 @@ class DatabricksError(IOError):
|
|
|
68
76
|
"The 'status' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
69
77
|
)
|
|
70
78
|
|
|
71
|
-
# API 1.2-specific parameters are deprecated
|
|
79
|
+
# API 1.2-specific parameters are deprecated.
|
|
72
80
|
if error:
|
|
73
81
|
warnings.warn(
|
|
74
82
|
"The 'error' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
75
83
|
)
|
|
76
84
|
|
|
85
|
+
# Retry-after is deprecated.
|
|
86
|
+
if retry_after_secs:
|
|
87
|
+
warnings.warn(
|
|
88
|
+
"The 'retry_after_secs' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
89
|
+
)
|
|
90
|
+
|
|
77
91
|
if detail:
|
|
78
92
|
# Handle SCIM error message details
|
|
79
93
|
# @see https://tools.ietf.org/html/rfc7644#section-3.7.3
|
|
@@ -84,20 +98,32 @@ class DatabricksError(IOError):
|
|
|
84
98
|
# add more context from SCIM responses
|
|
85
99
|
message = f"{scimType} {message}".strip(" ")
|
|
86
100
|
error_code = f"SCIM_{status}"
|
|
101
|
+
|
|
87
102
|
super().__init__(message if message else error)
|
|
88
103
|
self.error_code = error_code
|
|
89
104
|
self.retry_after_secs = retry_after_secs
|
|
90
|
-
self.
|
|
105
|
+
self._error_details = errdetails.parse_error_details(details or [])
|
|
91
106
|
self.kwargs = kwargs
|
|
92
107
|
|
|
108
|
+
# Deprecated.
|
|
109
|
+
self.details = []
|
|
110
|
+
if details:
|
|
111
|
+
for d in details:
|
|
112
|
+
if not isinstance(d, dict):
|
|
113
|
+
continue
|
|
114
|
+
self.details.append(ErrorDetail.from_dict(d))
|
|
115
|
+
|
|
93
116
|
def get_error_info(self) -> List[ErrorDetail]:
|
|
94
|
-
return self._get_details_by_type(
|
|
117
|
+
return self._get_details_by_type(errdetails._ERROR_INFO_TYPE)
|
|
95
118
|
|
|
96
119
|
def _get_details_by_type(self, error_type) -> List[ErrorDetail]:
|
|
97
|
-
if self.details
|
|
120
|
+
if self.details is None:
|
|
98
121
|
return []
|
|
99
122
|
return [detail for detail in self.details if detail.type == error_type]
|
|
100
123
|
|
|
124
|
+
def get_error_details(self) -> errdetails.ErrorDetails:
|
|
125
|
+
return self._error_details
|
|
126
|
+
|
|
101
127
|
|
|
102
128
|
@dataclass
|
|
103
129
|
class _ErrorOverride:
|
|
@@ -132,8 +158,8 @@ class _ErrorOverride:
|
|
|
132
158
|
return False
|
|
133
159
|
if self.status_code_matcher and not self.status_code_matcher.match(str(response.status_code)):
|
|
134
160
|
return False
|
|
135
|
-
if self.error_code_matcher and not self.error_code_matcher.match(raw_error.get(
|
|
161
|
+
if self.error_code_matcher and not self.error_code_matcher.match(raw_error.get("error_code", "")):
|
|
136
162
|
return False
|
|
137
|
-
if self.message_matcher and not self.message_matcher.match(raw_error.get(
|
|
163
|
+
if self.message_matcher and not self.message_matcher.match(raw_error.get("message", "")):
|
|
138
164
|
return False
|
|
139
165
|
return True
|
|
@@ -24,7 +24,7 @@ class _RetryAfterCustomizer(_ErrorCustomizer):
|
|
|
24
24
|
retry_after = response.headers.get("Retry-After")
|
|
25
25
|
if retry_after is None:
|
|
26
26
|
logging.debug(
|
|
27
|
-
f
|
|
27
|
+
f"No Retry-After header received in response with status code 429 or 503. Defaulting to {cls._DEFAULT_RETRY_AFTER_SECONDS}"
|
|
28
28
|
)
|
|
29
29
|
# 429 requests should include a `Retry-After` header, but if it's missing,
|
|
30
30
|
# we default to 1 second.
|
|
@@ -40,11 +40,11 @@ class _RetryAfterCustomizer(_ErrorCustomizer):
|
|
|
40
40
|
return int(retry_after)
|
|
41
41
|
except ValueError:
|
|
42
42
|
logging.debug(
|
|
43
|
-
f
|
|
43
|
+
f"Invalid Retry-After header received: {retry_after}. Defaulting to {cls._DEFAULT_RETRY_AFTER_SECONDS}"
|
|
44
44
|
)
|
|
45
45
|
# defaulting to 1 sleep second to make self._is_retryable() simpler
|
|
46
46
|
return cls._DEFAULT_RETRY_AFTER_SECONDS
|
|
47
47
|
|
|
48
48
|
def customize_error(self, response: requests.Response, kwargs: dict):
|
|
49
49
|
if response.status_code in (429, 503):
|
|
50
|
-
kwargs[
|
|
50
|
+
kwargs["retry_after_secs"] = self._parse_retry_after(response)
|
|
@@ -20,7 +20,7 @@ class _EmptyDeserializer(_ErrorDeserializer):
|
|
|
20
20
|
|
|
21
21
|
def deserialize_error(self, response: requests.Response, response_body: bytes) -> Optional[dict]:
|
|
22
22
|
if len(response_body) == 0:
|
|
23
|
-
return {
|
|
23
|
+
return {"message": response.reason}
|
|
24
24
|
return None
|
|
25
25
|
|
|
26
26
|
|
|
@@ -31,39 +31,45 @@ class _StandardErrorDeserializer(_ErrorDeserializer):
|
|
|
31
31
|
|
|
32
32
|
def deserialize_error(self, response: requests.Response, response_body: bytes) -> Optional[dict]:
|
|
33
33
|
try:
|
|
34
|
-
payload_str = response_body.decode(
|
|
34
|
+
payload_str = response_body.decode("utf-8")
|
|
35
35
|
resp = json.loads(payload_str)
|
|
36
36
|
except UnicodeDecodeError as e:
|
|
37
|
-
logging.debug(
|
|
37
|
+
logging.debug(
|
|
38
|
+
"_StandardErrorParser: unable to decode response using utf-8",
|
|
39
|
+
exc_info=e,
|
|
40
|
+
)
|
|
38
41
|
return None
|
|
39
42
|
except json.JSONDecodeError as e:
|
|
40
|
-
logging.debug(
|
|
43
|
+
logging.debug(
|
|
44
|
+
"_StandardErrorParser: unable to deserialize response as json",
|
|
45
|
+
exc_info=e,
|
|
46
|
+
)
|
|
41
47
|
return None
|
|
42
48
|
if not isinstance(resp, dict):
|
|
43
|
-
logging.debug(
|
|
49
|
+
logging.debug("_StandardErrorParser: response is valid JSON but not a dictionary")
|
|
44
50
|
return None
|
|
45
51
|
|
|
46
52
|
error_args = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
"message": resp.get("message", "request failed"),
|
|
54
|
+
"error_code": resp.get("error_code"),
|
|
55
|
+
"details": resp.get("details"),
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
# Handle API 1.2-style errors
|
|
53
|
-
if
|
|
54
|
-
error_args[
|
|
59
|
+
if "error" in resp:
|
|
60
|
+
error_args["message"] = resp["error"]
|
|
55
61
|
|
|
56
62
|
# Handle SCIM Errors
|
|
57
|
-
detail = resp.get(
|
|
58
|
-
status = resp.get(
|
|
59
|
-
scim_type = resp.get(
|
|
63
|
+
detail = resp.get("detail")
|
|
64
|
+
status = resp.get("status")
|
|
65
|
+
scim_type = resp.get("scimType")
|
|
60
66
|
if detail:
|
|
61
67
|
# Handle SCIM error message details
|
|
62
68
|
# @see https://tools.ietf.org/html/rfc7644#section-3.7.3
|
|
63
69
|
if detail == "null":
|
|
64
70
|
detail = "SCIM API Internal Error"
|
|
65
|
-
error_args[
|
|
66
|
-
error_args[
|
|
71
|
+
error_args["message"] = f"{scim_type} {detail}".strip(" ")
|
|
72
|
+
error_args["error_code"] = f"SCIM_{status}"
|
|
67
73
|
return error_args
|
|
68
74
|
|
|
69
75
|
|
|
@@ -72,16 +78,20 @@ class _StringErrorDeserializer(_ErrorDeserializer):
|
|
|
72
78
|
Parses errors from the Databricks REST API in the format "ERROR_CODE: MESSAGE".
|
|
73
79
|
"""
|
|
74
80
|
|
|
75
|
-
__STRING_ERROR_REGEX = re.compile(r
|
|
81
|
+
__STRING_ERROR_REGEX = re.compile(r"([A-Z_]+): (.*)")
|
|
76
82
|
|
|
77
83
|
def deserialize_error(self, response: requests.Response, response_body: bytes) -> Optional[dict]:
|
|
78
|
-
payload_str = response_body.decode(
|
|
84
|
+
payload_str = response_body.decode("utf-8")
|
|
79
85
|
match = self.__STRING_ERROR_REGEX.match(payload_str)
|
|
80
86
|
if not match:
|
|
81
|
-
logging.debug(
|
|
87
|
+
logging.debug("_StringErrorParser: unable to parse response as string")
|
|
82
88
|
return None
|
|
83
89
|
error_code, message = match.groups()
|
|
84
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
"error_code": error_code,
|
|
92
|
+
"message": message,
|
|
93
|
+
"status": response.status_code,
|
|
94
|
+
}
|
|
85
95
|
|
|
86
96
|
|
|
87
97
|
class _HtmlErrorDeserializer(_ErrorDeserializer):
|
|
@@ -89,18 +99,21 @@ class _HtmlErrorDeserializer(_ErrorDeserializer):
|
|
|
89
99
|
Parses errors from the Databricks REST API in HTML format.
|
|
90
100
|
"""
|
|
91
101
|
|
|
92
|
-
__HTML_ERROR_REGEXES = [
|
|
102
|
+
__HTML_ERROR_REGEXES = [
|
|
103
|
+
re.compile(r"<pre>(.*)</pre>"),
|
|
104
|
+
re.compile(r"<title>(.*)</title>"),
|
|
105
|
+
]
|
|
93
106
|
|
|
94
107
|
def deserialize_error(self, response: requests.Response, response_body: bytes) -> Optional[dict]:
|
|
95
|
-
payload_str = response_body.decode(
|
|
108
|
+
payload_str = response_body.decode("utf-8")
|
|
96
109
|
for regex in self.__HTML_ERROR_REGEXES:
|
|
97
110
|
match = regex.search(payload_str)
|
|
98
111
|
if match:
|
|
99
112
|
message = match.group(1) if match.group(1) else response.reason
|
|
100
113
|
return {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
"status": response.status_code,
|
|
115
|
+
"message": message,
|
|
116
|
+
"error_code": response.reason.upper().replace(" ", "_"),
|
|
104
117
|
}
|
|
105
|
-
logging.debug(
|
|
118
|
+
logging.debug("_HtmlErrorParser: no <pre> tag found in error response")
|
|
106
119
|
return None
|