databricks-sdk 0.19.0__py3-none-any.whl → 0.20.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.

@@ -15,6 +15,7 @@ from databricks.sdk.service.catalog import (AccountMetastoreAssignmentsAPI,
15
15
  ExternalLocationsAPI, FunctionsAPI,
16
16
  GrantsAPI, LakehouseMonitorsAPI,
17
17
  MetastoresAPI, ModelVersionsAPI,
18
+ OnlineTablesAPI,
18
19
  RegisteredModelsAPI, SchemasAPI,
19
20
  StorageCredentialsAPI,
20
21
  SystemSchemasAPI,
@@ -180,6 +181,7 @@ class WorkspaceClient:
180
181
  self._metastores = MetastoresAPI(self._api_client)
181
182
  self._model_registry = ModelRegistryAPI(self._api_client)
182
183
  self._model_versions = ModelVersionsAPI(self._api_client)
184
+ self._online_tables = OnlineTablesAPI(self._api_client)
183
185
  self._permissions = PermissionsAPI(self._api_client)
184
186
  self._pipelines = PipelinesAPI(self._api_client)
185
187
  self._policy_families = PolicyFamiliesAPI(self._api_client)
@@ -322,7 +324,7 @@ class WorkspaceClient:
322
324
 
323
325
  @property
324
326
  def files(self) -> FilesAPI:
325
- """The Files API allows you to read, write, and delete files and directories in Unity Catalog volumes."""
327
+ """The Files API allows you to read, write, list, and delete files and directories."""
326
328
  return self._files
327
329
 
328
330
  @property
@@ -400,6 +402,11 @@ class WorkspaceClient:
400
402
  """Databricks provides a hosted version of MLflow Model Registry in Unity Catalog."""
401
403
  return self._model_versions
402
404
 
405
+ @property
406
+ def online_tables(self) -> OnlineTablesAPI:
407
+ """Online tables provide lower latency and higher QPS access to data from Delta tables."""
408
+ return self._online_tables
409
+
403
410
  @property
404
411
  def permissions(self) -> PermissionsAPI:
405
412
  """Permissions API are used to create read, write, edit, update and manage access for various users on different objects and endpoints."""
@@ -560,6 +567,13 @@ class WorkspaceClient:
560
567
  """This API allows updating known workspace settings for advanced users."""
561
568
  return self._workspace_conf
562
569
 
570
+ def get_workspace_id(self) -> int:
571
+ """Get the workspace ID of the workspace that this client is connected to."""
572
+ response = self._api_client.do("GET",
573
+ "/api/2.0/preview/scim/v2/Me",
574
+ response_headers=['X-Databricks-Org-Id'])
575
+ return int(response["X-Databricks-Org-Id"])
576
+
563
577
  def __repr__(self):
564
578
  return f"WorkspaceClient(host='{self._config.host}', auth_type='{self._config.auth_type}', ...)"
565
579
 
@@ -802,7 +816,7 @@ class AccountClient:
802
816
  :param workspace: The workspace to construct a client for.
803
817
  :return: A ``WorkspaceClient`` for the given workspace.
804
818
  """
805
- config = self._config.copy()
819
+ config = self._config.deep_copy()
806
820
  config.host = config.environment.deployment_url(workspace.deployment_name)
807
821
  config.azure_workspace_resource_id = azure.get_azure_resource_id(workspace)
808
822
  config.account_id = None
@@ -0,0 +1,38 @@
1
+ class _Name(object):
2
+ """Parses a name in camelCase, PascalCase, snake_case, or kebab-case into its segments."""
3
+
4
+ def __init__(self, raw_name: str):
5
+ #
6
+ self._segments = []
7
+ segment = []
8
+ for ch in raw_name:
9
+ if ch.isupper():
10
+ if segment:
11
+ self._segments.append(''.join(segment))
12
+ segment = [ch.lower()]
13
+ elif ch.islower():
14
+ segment.append(ch)
15
+ else:
16
+ if segment:
17
+ self._segments.append(''.join(segment))
18
+ segment = []
19
+ if segment:
20
+ self._segments.append(''.join(segment))
21
+
22
+ def to_snake_case(self) -> str:
23
+ return '_'.join(self._segments)
24
+
25
+ def to_header_case(self) -> str:
26
+ return '-'.join([s.capitalize() for s in self._segments])
27
+
28
+
29
+ class Casing(object):
30
+
31
+ @staticmethod
32
+ def to_header_case(name: str) -> str:
33
+ """
34
+ Convert a name from camelCase, PascalCase, snake_case, or kebab-case to header-case.
35
+ :param name:
36
+ :return:
37
+ """
38
+ return _Name(name).to_header_case()
databricks/sdk/config.py CHANGED
@@ -457,3 +457,8 @@ class Config:
457
457
  cpy: Config = copy.copy(self)
458
458
  cpy._user_agent_other_info = copy.deepcopy(self._user_agent_other_info)
459
459
  return cpy
460
+
461
+ def deep_copy(self):
462
+ """Creates a deep copy of the config object.
463
+ """
464
+ return copy.deepcopy(self)
databricks/sdk/core.py CHANGED
@@ -7,6 +7,7 @@ from typing import Any, BinaryIO, Iterator, Type
7
7
 
8
8
  from requests.adapters import HTTPAdapter
9
9
 
10
+ from .casing import Casing
10
11
  from .config import *
11
12
  # To preserve backwards compatibility (as these definitions were previously in this module)
12
13
  from .credentials_provider import *
@@ -115,7 +116,8 @@ class ApiClient:
115
116
  body: dict = None,
116
117
  raw: bool = False,
117
118
  files=None,
118
- data=None) -> Union[dict, BinaryIO]:
119
+ data=None,
120
+ response_headers: List[str] = None) -> Union[dict, BinaryIO]:
119
121
  # Remove extra `/` from path for Files API
120
122
  # Once we've fixed the OpenAPI spec, we can remove this
121
123
  path = re.sub('^/api/2.0/fs/files//', '/api/2.0/fs/files/', path)
@@ -125,14 +127,29 @@ class ApiClient:
125
127
  retryable = retried(timeout=timedelta(seconds=self._retry_timeout_seconds),
126
128
  is_retryable=self._is_retryable,
127
129
  clock=self._cfg.clock)
128
- return retryable(self._perform)(method,
129
- path,
130
- query=query,
131
- headers=headers,
132
- body=body,
133
- raw=raw,
134
- files=files,
135
- data=data)
130
+ response = retryable(self._perform)(method,
131
+ path,
132
+ query=query,
133
+ headers=headers,
134
+ body=body,
135
+ raw=raw,
136
+ files=files,
137
+ data=data)
138
+
139
+ resp = dict()
140
+ for header in response_headers if response_headers else []:
141
+ resp[header] = response.headers.get(Casing.to_header_case(header))
142
+ if raw:
143
+ resp["contents"] = StreamingResponse(response)
144
+ return resp
145
+ if not len(response.content):
146
+ return resp
147
+
148
+ json = response.json()
149
+ if isinstance(json, list):
150
+ return json
151
+
152
+ return {**resp, **json}
136
153
 
137
154
  @staticmethod
138
155
  def _is_retryable(err: BaseException) -> Optional[str]:
@@ -219,11 +236,7 @@ class ApiClient:
219
236
  # See https://stackoverflow.com/a/58821552/277035
220
237
  payload = response.json()
221
238
  raise self._make_nicer_error(response=response, **payload) from None
222
- if raw:
223
- return StreamingResponse(response)
224
- if not len(response.content):
225
- return {}
226
- return response.json()
239
+ return response
227
240
  except requests.exceptions.JSONDecodeError:
228
241
  message = self._make_sense_from_html(response.text)
229
242
  if not message:
@@ -102,4 +102,5 @@ class WorkspaceExt(WorkspaceAPI):
102
102
  """
103
103
  query = {'path': path, 'direct_download': 'true'}
104
104
  if format: query['format'] = format.value
105
- return self._api.do('GET', '/api/2.0/workspace/export', query=query, raw=True)
105
+ response = self._api.do('GET', '/api/2.0/workspace/export', query=query, raw=True)
106
+ return response["contents"]
@@ -335,6 +335,17 @@ class DeliveryStatus(Enum):
335
335
  class DownloadResponse:
336
336
  contents: Optional[BinaryIO] = None
337
337
 
338
+ def as_dict(self) -> dict:
339
+ """Serializes the DownloadResponse into a dictionary suitable for use as a JSON request body."""
340
+ body = {}
341
+ if self.contents: body['contents'] = self.contents
342
+ return body
343
+
344
+ @classmethod
345
+ def from_dict(cls, d: Dict[str, any]) -> DownloadResponse:
346
+ """Deserializes the DownloadResponse from a dictionary."""
347
+ return cls(contents=d.get('contents', None))
348
+
338
349
 
339
350
  class LogDeliveryConfigStatus(Enum):
340
351
  """Status of log delivery configuration. Set to `ENABLED` (enabled) or `DISABLED` (disabled).
@@ -704,12 +715,13 @@ class BillableUsageAPI:
704
715
  if personal_data is not None: query['personal_data'] = personal_data
705
716
  if start_month is not None: query['start_month'] = start_month
706
717
  headers = {'Accept': 'text/plain', }
718
+
707
719
  res = self._api.do('GET',
708
720
  f'/api/2.0/accounts/{self._api.account_id}/usage/download',
709
721
  query=query,
710
722
  headers=headers,
711
723
  raw=True)
712
- return DownloadResponse(contents=res)
724
+ return DownloadResponse.from_dict(res)
713
725
 
714
726
 
715
727
  class BudgetsAPI:
@@ -732,6 +744,7 @@ class BudgetsAPI:
732
744
  body = {}
733
745
  if budget is not None: body['budget'] = budget.as_dict()
734
746
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
747
+
735
748
  res = self._api.do('POST',
736
749
  f'/api/2.0/accounts/{self._api.account_id}/budget',
737
750
  body=body,
@@ -750,6 +763,7 @@ class BudgetsAPI:
750
763
  """
751
764
 
752
765
  headers = {'Accept': 'application/json', }
766
+
753
767
  self._api.do('DELETE',
754
768
  f'/api/2.0/accounts/{self._api.account_id}/budget/{budget_id}',
755
769
  headers=headers)
@@ -767,6 +781,7 @@ class BudgetsAPI:
767
781
  """
768
782
 
769
783
  headers = {'Accept': 'application/json', }
784
+
770
785
  res = self._api.do('GET',
771
786
  f'/api/2.0/accounts/{self._api.account_id}/budget/{budget_id}',
772
787
  headers=headers)
@@ -782,6 +797,7 @@ class BudgetsAPI:
782
797
  """
783
798
 
784
799
  headers = {'Accept': 'application/json', }
800
+
785
801
  json = self._api.do('GET', f'/api/2.0/accounts/{self._api.account_id}/budget', headers=headers)
786
802
  parsed = BudgetList.from_dict(json).budgets
787
803
  return parsed if parsed is not None else []
@@ -801,6 +817,7 @@ class BudgetsAPI:
801
817
  body = {}
802
818
  if budget is not None: body['budget'] = budget.as_dict()
803
819
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
820
+
804
821
  self._api.do('PATCH',
805
822
  f'/api/2.0/accounts/{self._api.account_id}/budget/{budget_id}',
806
823
  body=body,
@@ -894,6 +911,7 @@ class LogDeliveryAPI:
894
911
  if log_delivery_configuration is not None:
895
912
  body['log_delivery_configuration'] = log_delivery_configuration.as_dict()
896
913
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
914
+
897
915
  res = self._api.do('POST',
898
916
  f'/api/2.0/accounts/{self._api.account_id}/log-delivery',
899
917
  body=body,
@@ -912,6 +930,7 @@ class LogDeliveryAPI:
912
930
  """
913
931
 
914
932
  headers = {'Accept': 'application/json', }
933
+
915
934
  res = self._api.do(
916
935
  'GET',
917
936
  f'/api/2.0/accounts/{self._api.account_id}/log-delivery/{log_delivery_configuration_id}',
@@ -942,6 +961,7 @@ class LogDeliveryAPI:
942
961
  if status is not None: query['status'] = status.value
943
962
  if storage_configuration_id is not None: query['storage_configuration_id'] = storage_configuration_id
944
963
  headers = {'Accept': 'application/json', }
964
+
945
965
  json = self._api.do('GET',
946
966
  f'/api/2.0/accounts/{self._api.account_id}/log-delivery',
947
967
  query=query,
@@ -970,6 +990,7 @@ class LogDeliveryAPI:
970
990
  body = {}
971
991
  if status is not None: body['status'] = status.value
972
992
  headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
993
+
973
994
  self._api.do('PATCH',
974
995
  f'/api/2.0/accounts/{self._api.account_id}/log-delivery/{log_delivery_configuration_id}',
975
996
  body=body,