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

Files changed (33) hide show
  1. databricks/sdk/__init__.py +30 -1
  2. databricks/sdk/azure.py +14 -0
  3. databricks/sdk/clock.py +49 -0
  4. databricks/sdk/config.py +7 -0
  5. databricks/sdk/core.py +2 -1
  6. databricks/sdk/credentials_provider.py +14 -3
  7. databricks/sdk/environments.py +1 -1
  8. databricks/sdk/errors/__init__.py +1 -1
  9. databricks/sdk/errors/mapper.py +5 -5
  10. databricks/sdk/mixins/workspace.py +3 -3
  11. databricks/sdk/retries.py +9 -5
  12. databricks/sdk/service/catalog.py +173 -78
  13. databricks/sdk/service/compute.py +86 -25
  14. databricks/sdk/service/files.py +136 -22
  15. databricks/sdk/service/iam.py +42 -36
  16. databricks/sdk/service/jobs.py +192 -14
  17. databricks/sdk/service/ml.py +27 -36
  18. databricks/sdk/service/oauth2.py +3 -4
  19. databricks/sdk/service/pipelines.py +50 -29
  20. databricks/sdk/service/settings.py +338 -57
  21. databricks/sdk/service/sharing.py +3 -4
  22. databricks/sdk/service/sql.py +24 -17
  23. databricks/sdk/service/vectorsearch.py +13 -17
  24. databricks/sdk/service/workspace.py +18 -7
  25. databricks/sdk/version.py +1 -1
  26. {databricks_sdk-0.18.0.dist-info → databricks_sdk-0.19.0.dist-info}/METADATA +1 -1
  27. databricks_sdk-0.19.0.dist-info/RECORD +53 -0
  28. databricks_sdk-0.18.0.dist-info/RECORD +0 -52
  29. /databricks/sdk/errors/{mapping.py → platform.py} +0 -0
  30. {databricks_sdk-0.18.0.dist-info → databricks_sdk-0.19.0.dist-info}/LICENSE +0 -0
  31. {databricks_sdk-0.18.0.dist-info → databricks_sdk-0.19.0.dist-info}/NOTICE +0 -0
  32. {databricks_sdk-0.18.0.dist-info → databricks_sdk-0.19.0.dist-info}/WHEEL +0 -0
  33. {databricks_sdk-0.18.0.dist-info → databricks_sdk-0.19.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import databricks.sdk.core as client
2
2
  import databricks.sdk.dbutils as dbutils
3
+ from databricks.sdk import azure
3
4
  from databricks.sdk.credentials_provider import CredentialsProvider
4
5
  from databricks.sdk.mixins.compute import ClustersExt
5
6
  from databricks.sdk.mixins.files import DbfsExt
@@ -46,7 +47,7 @@ from databricks.sdk.service.provisioning import (CredentialsAPI,
46
47
  EncryptionKeysAPI,
47
48
  NetworksAPI, PrivateAccessAPI,
48
49
  StorageAPI, VpcEndpointsAPI,
49
- WorkspacesAPI)
50
+ Workspace, WorkspacesAPI)
50
51
  from databricks.sdk.service.serving import AppsAPI, ServingEndpointsAPI
51
52
  from databricks.sdk.service.settings import (AccountIpAccessListsAPI,
52
53
  AccountSettingsAPI,
@@ -779,5 +780,33 @@ class AccountClient:
779
780
  """These APIs manage workspaces for this account."""
780
781
  return self._workspaces
781
782
 
783
+ def get_workspace_client(self, workspace: Workspace) -> WorkspaceClient:
784
+ """Constructs a ``WorkspaceClient`` for the given workspace.
785
+
786
+ Returns a ``WorkspaceClient`` that is configured to use the same
787
+ credentials as this ``AccountClient``. The underlying config is
788
+ copied from this ``AccountClient``, but the ``host`` and
789
+ ``azure_workspace_resource_id`` are overridden to match the
790
+ given workspace, and the ``account_id`` field is cleared.
791
+
792
+ Usage:
793
+
794
+ .. code-block::
795
+
796
+ wss = list(a.workspaces.list())
797
+ if len(wss) == 0:
798
+ pytest.skip("no workspaces")
799
+ w = a.get_workspace_client(wss[0])
800
+ assert w.current_user.me().active
801
+
802
+ :param workspace: The workspace to construct a client for.
803
+ :return: A ``WorkspaceClient`` for the given workspace.
804
+ """
805
+ config = self._config.copy()
806
+ config.host = config.environment.deployment_url(workspace.deployment_name)
807
+ config.azure_workspace_resource_id = azure.get_azure_resource_id(workspace)
808
+ config.account_id = None
809
+ return WorkspaceClient(config=config)
810
+
782
811
  def __repr__(self):
783
812
  return f"AccountClient(account_id='{self._config.account_id}', auth_type='{self._config.auth_type}', ...)"
databricks/sdk/azure.py CHANGED
@@ -2,6 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import Dict
3
3
 
4
4
  from .oauth import TokenSource
5
+ from .service.provisioning import Workspace
5
6
 
6
7
 
7
8
  @dataclass
@@ -38,3 +39,16 @@ def add_workspace_id_header(cfg: 'Config', headers: Dict[str, str]):
38
39
  def add_sp_management_token(token_source: 'TokenSource', headers: Dict[str, str]):
39
40
  mgmt_token = token_source.token()
40
41
  headers['X-Databricks-Azure-SP-Management-Token'] = mgmt_token.access_token
42
+
43
+
44
+ def get_azure_resource_id(workspace: Workspace):
45
+ """
46
+ Returns the Azure Resource ID for the given workspace, if it is an Azure workspace.
47
+ :param workspace:
48
+ :return:
49
+ """
50
+ if workspace.azure_workspace_info is None:
51
+ return None
52
+ return (f'/subscriptions/{workspace.azure_workspace_info.subscription_id}'
53
+ f'/resourceGroups/{workspace.azure_workspace_info.resource_group}'
54
+ f'/providers/Microsoft.Databricks/workspaces/{workspace.workspace_name}')
@@ -0,0 +1,49 @@
1
+ import abc
2
+ import time
3
+
4
+
5
+ class Clock(metaclass=abc.ABCMeta):
6
+
7
+ @abc.abstractmethod
8
+ def time(self) -> float:
9
+ """
10
+ Return the current time in seconds since the Epoch.
11
+ Fractions of a second may be present if the system clock provides them.
12
+
13
+ :return: The current time in seconds since the Epoch.
14
+ """
15
+
16
+ @abc.abstractmethod
17
+ def sleep(self, seconds: float) -> None:
18
+ """
19
+ Delay execution for a given number of seconds. The argument may be
20
+ a floating point number for subsecond precision.
21
+
22
+ :param seconds: The duration to sleep in seconds.
23
+ :return:
24
+ """
25
+
26
+
27
+ class RealClock(Clock):
28
+ """
29
+ A real clock that uses the ``time`` module to get the current time and sleep.
30
+ """
31
+
32
+ def time(self) -> float:
33
+ """
34
+ Return the current time in seconds since the Epoch.
35
+ Fractions of a second may be present if the system clock provides them.
36
+
37
+ :return: The current time in seconds since the Epoch.
38
+ """
39
+ return time.time()
40
+
41
+ def sleep(self, seconds: float) -> None:
42
+ """
43
+ Delay execution for a given number of seconds. The argument may be
44
+ a floating point number for subsecond precision.
45
+
46
+ :param seconds: The duration to sleep in seconds.
47
+ :return:
48
+ """
49
+ time.sleep(seconds)
databricks/sdk/config.py CHANGED
@@ -11,6 +11,7 @@ from typing import Dict, Iterable, Optional
11
11
  import requests
12
12
 
13
13
  from .azure import AzureEnvironment
14
+ from .clock import Clock, RealClock
14
15
  from .credentials_provider import CredentialsProvider, DefaultCredentials
15
16
  from .environments import (ALL_ENVS, DEFAULT_ENVIRONMENT, Cloud,
16
17
  DatabricksEnvironment)
@@ -84,6 +85,7 @@ class Config:
84
85
  credentials_provider: CredentialsProvider = None,
85
86
  product="unknown",
86
87
  product_version="0.0.0",
88
+ clock: Clock = None,
87
89
  **kwargs):
88
90
  self._inner = {}
89
91
  self._user_agent_other_info = []
@@ -91,6 +93,7 @@ class Config:
91
93
  if 'databricks_environment' in kwargs:
92
94
  self.databricks_environment = kwargs['databricks_environment']
93
95
  del kwargs['databricks_environment']
96
+ self._clock = clock if clock is not None else RealClock()
94
97
  try:
95
98
  self._set_inner_config(kwargs)
96
99
  self._load_from_env()
@@ -317,6 +320,10 @@ class Config:
317
320
  if self.warehouse_id:
318
321
  return f'/sql/1.0/warehouses/{self.warehouse_id}'
319
322
 
323
+ @property
324
+ def clock(self) -> Clock:
325
+ return self._clock
326
+
320
327
  @classmethod
321
328
  def attributes(cls) -> Iterable[ConfigAttribute]:
322
329
  """ Returns a list of Databricks SDK configuration metadata """
databricks/sdk/core.py CHANGED
@@ -123,7 +123,8 @@ class ApiClient:
123
123
  headers = {}
124
124
  headers['User-Agent'] = self._user_agent_base
125
125
  retryable = retried(timeout=timedelta(seconds=self._retry_timeout_seconds),
126
- is_retryable=self._is_retryable)
126
+ is_retryable=self._is_retryable,
127
+ clock=self._cfg.clock)
127
128
  return retryable(self._perform)(method,
128
129
  path,
129
130
  query=query,
@@ -6,6 +6,7 @@ import json
6
6
  import logging
7
7
  import os
8
8
  import pathlib
9
+ import platform
9
10
  import subprocess
10
11
  import sys
11
12
  from datetime import datetime
@@ -473,11 +474,21 @@ class DatabricksCliTokenSource(CliTokenSource):
473
474
  args += ['--account-id', cfg.account_id]
474
475
 
475
476
  cli_path = cfg.databricks_cli_path
477
+
478
+ # If the path is not specified look for "databricks" / "databricks.exe" in PATH.
476
479
  if not cli_path:
477
- cli_path = 'databricks'
480
+ try:
481
+ # Try to find "databricks" in PATH
482
+ cli_path = self.__class__._find_executable("databricks")
483
+ except FileNotFoundError as e:
484
+ # If "databricks" is not found, try to find "databricks.exe" in PATH (Windows)
485
+ if platform.system() == "Windows":
486
+ cli_path = self.__class__._find_executable("databricks.exe")
487
+ else:
488
+ raise e
478
489
 
479
490
  # If the path is unqualified, look it up in PATH.
480
- if cli_path.count("/") == 0:
491
+ elif cli_path.count("/") == 0:
481
492
  cli_path = self.__class__._find_executable(cli_path)
482
493
 
483
494
  super().__init__(cmd=[cli_path, *args],
@@ -505,7 +516,7 @@ class DatabricksCliTokenSource(CliTokenSource):
505
516
  raise err
506
517
 
507
518
 
508
- @credentials_provider('databricks-cli', ['host', 'is_aws'])
519
+ @credentials_provider('databricks-cli', ['host'])
509
520
  def databricks_cli(cfg: 'Config') -> Optional[HeaderFactory]:
510
521
  try:
511
522
  token_source = DatabricksCliTokenSource(cfg)
@@ -19,7 +19,7 @@ class DatabricksEnvironment:
19
19
  azure_environment: Optional[AzureEnvironment] = None
20
20
 
21
21
  def deployment_url(self, name: str) -> str:
22
- return f"https://{name}.{self.dns_zone}"
22
+ return f"https://{name}{self.dns_zone}"
23
23
 
24
24
  @property
25
25
  def azure_service_management_endpoint(self) -> Optional[str]:
@@ -1,4 +1,4 @@
1
1
  from .base import DatabricksError, ErrorDetail
2
2
  from .mapper import error_mapper
3
- from .mapping import *
3
+ from .platform import *
4
4
  from .sdk import *
@@ -1,18 +1,18 @@
1
- from databricks.sdk.errors import mapping
1
+ from databricks.sdk.errors import platform
2
2
  from databricks.sdk.errors.base import DatabricksError
3
3
 
4
4
 
5
5
  def error_mapper(status_code: int, raw: dict) -> DatabricksError:
6
6
  error_code = raw.get('error_code', None)
7
- if error_code in mapping.ERROR_CODE_MAPPING:
7
+ if error_code in platform.ERROR_CODE_MAPPING:
8
8
  # more specific error codes override more generic HTTP status codes
9
- return mapping.ERROR_CODE_MAPPING[error_code](**raw)
9
+ return platform.ERROR_CODE_MAPPING[error_code](**raw)
10
10
 
11
- if status_code in mapping.STATUS_CODE_MAPPING:
11
+ if status_code in platform.STATUS_CODE_MAPPING:
12
12
  # more generic HTTP status codes matched after more specific error codes,
13
13
  # where there's a default exception class per HTTP status code, and we do
14
14
  # rely on Databricks platform exception mapper to do the right thing.
15
- return mapping.STATUS_CODE_MAPPING[status_code](**raw)
15
+ return platform.STATUS_CODE_MAPPING[status_code](**raw)
16
16
 
17
17
  # backwards-compatible error creation for cases like using older versions of
18
18
  # the SDK on way never releases of the platform.
@@ -1,4 +1,4 @@
1
- from typing import BinaryIO, Iterator, Optional
1
+ from typing import BinaryIO, Iterator, Optional, Union
2
2
 
3
3
  from ..core import DatabricksError
4
4
  from ..service.workspace import (ExportFormat, ImportFormat, Language,
@@ -37,7 +37,7 @@ class WorkspaceExt(WorkspaceAPI):
37
37
 
38
38
  def upload(self,
39
39
  path: str,
40
- content: BinaryIO,
40
+ content: Union[bytes, BinaryIO],
41
41
  *,
42
42
  format: Optional[ImportFormat] = None,
43
43
  language: Optional[Language] = None,
@@ -51,7 +51,7 @@ class WorkspaceExt(WorkspaceAPI):
51
51
  * `INVALID_PARAMETER_VALUE`: if `format` and `content` values are not compatible.
52
52
 
53
53
  :param path: target location of the file on workspace.
54
- :param content: file-like `io.BinaryIO` of the `path` contents.
54
+ :param content: the contents as either raw binary data `bytes` or a file-like the file-like `io.BinaryIO` of the `path` contents.
55
55
  :param format: By default, `ImportFormat.SOURCE`. If using `ImportFormat.AUTO` the `path`
56
56
  is imported or exported as either a workspace file or a notebook, depending
57
57
  on an analysis of the `item`’s extension and the header content provided in
databricks/sdk/retries.py CHANGED
@@ -1,30 +1,34 @@
1
1
  import functools
2
2
  import logging
3
- import time
4
3
  from datetime import timedelta
5
4
  from random import random
6
5
  from typing import Callable, Optional, Sequence, Type
7
6
 
7
+ from .clock import Clock, RealClock
8
+
8
9
  logger = logging.getLogger(__name__)
9
10
 
10
11
 
11
12
  def retried(*,
12
13
  on: Sequence[Type[BaseException]] = None,
13
14
  is_retryable: Callable[[BaseException], Optional[str]] = None,
14
- timeout=timedelta(minutes=20)):
15
+ timeout=timedelta(minutes=20),
16
+ clock: Clock = None):
15
17
  has_allowlist = on is not None
16
18
  has_callback = is_retryable is not None
17
19
  if not (has_allowlist or has_callback) or (has_allowlist and has_callback):
18
20
  raise SyntaxError('either on=[Exception] or callback=lambda x: .. is required')
21
+ if clock is None:
22
+ clock = RealClock()
19
23
 
20
24
  def decorator(func):
21
25
 
22
26
  @functools.wraps(func)
23
27
  def wrapper(*args, **kwargs):
24
- deadline = time.time() + timeout.total_seconds()
28
+ deadline = clock.time() + timeout.total_seconds()
25
29
  attempt = 1
26
30
  last_err = None
27
- while time.time() < deadline:
31
+ while clock.time() < deadline:
28
32
  try:
29
33
  return func(*args, **kwargs)
30
34
  except Exception as err:
@@ -50,7 +54,7 @@ def retried(*,
50
54
  raise err
51
55
 
52
56
  logger.debug(f'Retrying: {retry_reason} (sleeping ~{sleep}s)')
53
- time.sleep(sleep + random())
57
+ clock.sleep(sleep + random())
54
58
  attempt += 1
55
59
  raise TimeoutError(f'Timed out after {timeout}') from last_err
56
60