databricks-sdk 0.28.0__py3-none-any.whl → 0.30.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 +74 -22
- databricks/sdk/config.py +89 -48
- databricks/sdk/core.py +38 -9
- databricks/sdk/credentials_provider.py +134 -57
- databricks/sdk/data_plane.py +65 -0
- databricks/sdk/dbutils.py +81 -3
- databricks/sdk/mixins/files.py +12 -4
- databricks/sdk/oauth.py +8 -6
- databricks/sdk/service/apps.py +977 -0
- databricks/sdk/service/billing.py +602 -218
- databricks/sdk/service/catalog.py +263 -62
- databricks/sdk/service/compute.py +515 -94
- databricks/sdk/service/dashboards.py +1310 -2
- databricks/sdk/service/iam.py +99 -88
- databricks/sdk/service/jobs.py +159 -166
- databricks/sdk/service/marketplace.py +74 -58
- databricks/sdk/service/oauth2.py +149 -70
- databricks/sdk/service/pipelines.py +73 -53
- databricks/sdk/service/serving.py +332 -694
- databricks/sdk/service/settings.py +424 -4
- databricks/sdk/service/sharing.py +235 -26
- databricks/sdk/service/sql.py +2484 -553
- databricks/sdk/service/vectorsearch.py +75 -0
- databricks/sdk/useragent.py +144 -0
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/METADATA +37 -16
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/RECORD +31 -28
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/WHEEL +1 -1
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/LICENSE +0 -0
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/NOTICE +0 -0
- {databricks_sdk-0.28.0.dist-info → databricks_sdk-0.30.0.dist-info}/top_level.txt +0 -0
databricks/sdk/__init__.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import databricks.sdk.core as client
|
|
2
2
|
import databricks.sdk.dbutils as dbutils
|
|
3
3
|
from databricks.sdk import azure
|
|
4
|
-
from databricks.sdk.credentials_provider import
|
|
4
|
+
from databricks.sdk.credentials_provider import CredentialsStrategy
|
|
5
5
|
from databricks.sdk.mixins.compute import ClustersExt
|
|
6
6
|
from databricks.sdk.mixins.files import DbfsExt
|
|
7
7
|
from databricks.sdk.mixins.workspace import WorkspaceExt
|
|
8
|
+
from databricks.sdk.service.apps import AppsAPI
|
|
8
9
|
from databricks.sdk.service.billing import (BillableUsageAPI, BudgetsAPI,
|
|
9
|
-
LogDeliveryAPI)
|
|
10
|
+
LogDeliveryAPI, UsageDashboardsAPI)
|
|
10
11
|
from databricks.sdk.service.catalog import (AccountMetastoreAssignmentsAPI,
|
|
11
12
|
AccountMetastoresAPI,
|
|
12
13
|
AccountStorageCredentialsAPI,
|
|
@@ -27,7 +28,7 @@ from databricks.sdk.service.compute import (ClusterPoliciesAPI, ClustersAPI,
|
|
|
27
28
|
InstancePoolsAPI,
|
|
28
29
|
InstanceProfilesAPI, LibrariesAPI,
|
|
29
30
|
PolicyFamiliesAPI)
|
|
30
|
-
from databricks.sdk.service.dashboards import LakeviewAPI
|
|
31
|
+
from databricks.sdk.service.dashboards import GenieAPI, LakeviewAPI
|
|
31
32
|
from databricks.sdk.service.files import DbfsAPI, FilesAPI
|
|
32
33
|
from databricks.sdk.service.iam import (AccountAccessControlAPI,
|
|
33
34
|
AccountAccessControlProxyAPI,
|
|
@@ -55,7 +56,8 @@ from databricks.sdk.service.provisioning import (CredentialsAPI,
|
|
|
55
56
|
NetworksAPI, PrivateAccessAPI,
|
|
56
57
|
StorageAPI, VpcEndpointsAPI,
|
|
57
58
|
Workspace, WorkspacesAPI)
|
|
58
|
-
from databricks.sdk.service.serving import
|
|
59
|
+
from databricks.sdk.service.serving import (ServingEndpointsAPI,
|
|
60
|
+
ServingEndpointsDataPlaneAPI)
|
|
59
61
|
from databricks.sdk.service.settings import (AccountIpAccessListsAPI,
|
|
60
62
|
AccountSettingsAPI,
|
|
61
63
|
AutomaticClusterUpdateAPI,
|
|
@@ -67,6 +69,7 @@ from databricks.sdk.service.settings import (AccountIpAccessListsAPI,
|
|
|
67
69
|
EsmEnablementAccountAPI,
|
|
68
70
|
IpAccessListsAPI,
|
|
69
71
|
NetworkConnectivityAPI,
|
|
72
|
+
NotificationDestinationsAPI,
|
|
70
73
|
PersonalComputeAPI,
|
|
71
74
|
RestrictWorkspaceAdminsAPI,
|
|
72
75
|
SettingsAPI, TokenManagementAPI,
|
|
@@ -74,11 +77,13 @@ from databricks.sdk.service.settings import (AccountIpAccessListsAPI,
|
|
|
74
77
|
from databricks.sdk.service.sharing import (CleanRoomsAPI, ProvidersAPI,
|
|
75
78
|
RecipientActivationAPI,
|
|
76
79
|
RecipientsAPI, SharesAPI)
|
|
77
|
-
from databricks.sdk.service.sql import (AlertsAPI,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
from databricks.sdk.service.sql import (AlertsAPI, AlertsLegacyAPI,
|
|
81
|
+
DashboardsAPI, DashboardWidgetsAPI,
|
|
82
|
+
DataSourcesAPI, DbsqlPermissionsAPI,
|
|
83
|
+
QueriesAPI, QueriesLegacyAPI,
|
|
80
84
|
QueryHistoryAPI,
|
|
81
85
|
QueryVisualizationsAPI,
|
|
86
|
+
QueryVisualizationsLegacyAPI,
|
|
82
87
|
StatementExecutionAPI, WarehousesAPI)
|
|
83
88
|
from databricks.sdk.service.vectorsearch import (VectorSearchEndpointsAPI,
|
|
84
89
|
VectorSearchIndexesAPI)
|
|
@@ -131,7 +136,8 @@ class WorkspaceClient:
|
|
|
131
136
|
debug_headers: bool = None,
|
|
132
137
|
product="unknown",
|
|
133
138
|
product_version="0.0.0",
|
|
134
|
-
|
|
139
|
+
credentials_strategy: CredentialsStrategy = None,
|
|
140
|
+
credentials_provider: CredentialsStrategy = None,
|
|
135
141
|
config: client.Config = None):
|
|
136
142
|
if not config:
|
|
137
143
|
config = client.Config(host=host,
|
|
@@ -152,6 +158,7 @@ class WorkspaceClient:
|
|
|
152
158
|
cluster_id=cluster_id,
|
|
153
159
|
google_credentials=google_credentials,
|
|
154
160
|
google_service_account=google_service_account,
|
|
161
|
+
credentials_strategy=credentials_strategy,
|
|
155
162
|
credentials_provider=credentials_provider,
|
|
156
163
|
debug_truncate_bytes=debug_truncate_bytes,
|
|
157
164
|
debug_headers=debug_headers,
|
|
@@ -160,8 +167,10 @@ class WorkspaceClient:
|
|
|
160
167
|
self._config = config.copy()
|
|
161
168
|
self._dbutils = _make_dbutils(self._config)
|
|
162
169
|
self._api_client = client.ApiClient(self._config)
|
|
170
|
+
serving_endpoints = ServingEndpointsAPI(self._api_client)
|
|
163
171
|
self._account_access_control_proxy = AccountAccessControlProxyAPI(self._api_client)
|
|
164
172
|
self._alerts = AlertsAPI(self._api_client)
|
|
173
|
+
self._alerts_legacy = AlertsLegacyAPI(self._api_client)
|
|
165
174
|
self._apps = AppsAPI(self._api_client)
|
|
166
175
|
self._artifact_allowlists = ArtifactAllowlistsAPI(self._api_client)
|
|
167
176
|
self._catalogs = CatalogsAPI(self._api_client)
|
|
@@ -186,6 +195,7 @@ class WorkspaceClient:
|
|
|
186
195
|
self._external_locations = ExternalLocationsAPI(self._api_client)
|
|
187
196
|
self._files = FilesAPI(self._api_client)
|
|
188
197
|
self._functions = FunctionsAPI(self._api_client)
|
|
198
|
+
self._genie = GenieAPI(self._api_client)
|
|
189
199
|
self._git_credentials = GitCredentialsAPI(self._api_client)
|
|
190
200
|
self._global_init_scripts = GlobalInitScriptsAPI(self._api_client)
|
|
191
201
|
self._grants = GrantsAPI(self._api_client)
|
|
@@ -199,6 +209,7 @@ class WorkspaceClient:
|
|
|
199
209
|
self._metastores = MetastoresAPI(self._api_client)
|
|
200
210
|
self._model_registry = ModelRegistryAPI(self._api_client)
|
|
201
211
|
self._model_versions = ModelVersionsAPI(self._api_client)
|
|
212
|
+
self._notification_destinations = NotificationDestinationsAPI(self._api_client)
|
|
202
213
|
self._online_tables = OnlineTablesAPI(self._api_client)
|
|
203
214
|
self._permission_migration = PermissionMigrationAPI(self._api_client)
|
|
204
215
|
self._permissions = PermissionsAPI(self._api_client)
|
|
@@ -215,8 +226,10 @@ class WorkspaceClient:
|
|
|
215
226
|
self._providers = ProvidersAPI(self._api_client)
|
|
216
227
|
self._quality_monitors = QualityMonitorsAPI(self._api_client)
|
|
217
228
|
self._queries = QueriesAPI(self._api_client)
|
|
229
|
+
self._queries_legacy = QueriesLegacyAPI(self._api_client)
|
|
218
230
|
self._query_history = QueryHistoryAPI(self._api_client)
|
|
219
231
|
self._query_visualizations = QueryVisualizationsAPI(self._api_client)
|
|
232
|
+
self._query_visualizations_legacy = QueryVisualizationsLegacyAPI(self._api_client)
|
|
220
233
|
self._recipient_activation = RecipientActivationAPI(self._api_client)
|
|
221
234
|
self._recipients = RecipientsAPI(self._api_client)
|
|
222
235
|
self._registered_models = RegisteredModelsAPI(self._api_client)
|
|
@@ -224,7 +237,8 @@ class WorkspaceClient:
|
|
|
224
237
|
self._schemas = SchemasAPI(self._api_client)
|
|
225
238
|
self._secrets = SecretsAPI(self._api_client)
|
|
226
239
|
self._service_principals = ServicePrincipalsAPI(self._api_client)
|
|
227
|
-
self._serving_endpoints =
|
|
240
|
+
self._serving_endpoints = serving_endpoints
|
|
241
|
+
self._serving_endpoints_data_plane = ServingEndpointsDataPlaneAPI(self._api_client, serving_endpoints)
|
|
228
242
|
self._settings = SettingsAPI(self._api_client)
|
|
229
243
|
self._shares = SharesAPI(self._api_client)
|
|
230
244
|
self._statement_execution = StatementExecutionAPI(self._api_client)
|
|
@@ -265,6 +279,11 @@ class WorkspaceClient:
|
|
|
265
279
|
"""The alerts API can be used to perform CRUD operations on alerts."""
|
|
266
280
|
return self._alerts
|
|
267
281
|
|
|
282
|
+
@property
|
|
283
|
+
def alerts_legacy(self) -> AlertsLegacyAPI:
|
|
284
|
+
"""The alerts API can be used to perform CRUD operations on alerts."""
|
|
285
|
+
return self._alerts_legacy
|
|
286
|
+
|
|
268
287
|
@property
|
|
269
288
|
def apps(self) -> AppsAPI:
|
|
270
289
|
"""Apps run directly on a customer’s Databricks instance, integrate with their data, use and extend Databricks services, and enable users to interact through single sign-on."""
|
|
@@ -385,6 +404,11 @@ class WorkspaceClient:
|
|
|
385
404
|
"""Functions implement User-Defined Functions (UDFs) in Unity Catalog."""
|
|
386
405
|
return self._functions
|
|
387
406
|
|
|
407
|
+
@property
|
|
408
|
+
def genie(self) -> GenieAPI:
|
|
409
|
+
"""Genie provides a no-code experience for business users, powered by AI/BI."""
|
|
410
|
+
return self._genie
|
|
411
|
+
|
|
388
412
|
@property
|
|
389
413
|
def git_credentials(self) -> GitCredentialsAPI:
|
|
390
414
|
"""Registers personal access token for Databricks to do operations on behalf of the user."""
|
|
@@ -450,6 +474,11 @@ class WorkspaceClient:
|
|
|
450
474
|
"""Databricks provides a hosted version of MLflow Model Registry in Unity Catalog."""
|
|
451
475
|
return self._model_versions
|
|
452
476
|
|
|
477
|
+
@property
|
|
478
|
+
def notification_destinations(self) -> NotificationDestinationsAPI:
|
|
479
|
+
"""The notification destinations API lets you programmatically manage a workspace's notification destinations."""
|
|
480
|
+
return self._notification_destinations
|
|
481
|
+
|
|
453
482
|
@property
|
|
454
483
|
def online_tables(self) -> OnlineTablesAPI:
|
|
455
484
|
"""Online tables provide lower latency and higher QPS access to data from Delta tables."""
|
|
@@ -457,7 +486,7 @@ class WorkspaceClient:
|
|
|
457
486
|
|
|
458
487
|
@property
|
|
459
488
|
def permission_migration(self) -> PermissionMigrationAPI:
|
|
460
|
-
"""
|
|
489
|
+
"""APIs for migrating acl permissions, used only by the ucx tool: https://github.com/databrickslabs/ucx."""
|
|
461
490
|
return self._permission_migration
|
|
462
491
|
|
|
463
492
|
@property
|
|
@@ -522,19 +551,29 @@ class WorkspaceClient:
|
|
|
522
551
|
|
|
523
552
|
@property
|
|
524
553
|
def queries(self) -> QueriesAPI:
|
|
525
|
-
"""
|
|
554
|
+
"""The queries API can be used to perform CRUD operations on queries."""
|
|
526
555
|
return self._queries
|
|
527
556
|
|
|
557
|
+
@property
|
|
558
|
+
def queries_legacy(self) -> QueriesLegacyAPI:
|
|
559
|
+
"""These endpoints are used for CRUD operations on query definitions."""
|
|
560
|
+
return self._queries_legacy
|
|
561
|
+
|
|
528
562
|
@property
|
|
529
563
|
def query_history(self) -> QueryHistoryAPI:
|
|
530
|
-
"""
|
|
564
|
+
"""A service responsible for storing and retrieving the list of queries run against SQL endpoints, serverless compute, and DLT."""
|
|
531
565
|
return self._query_history
|
|
532
566
|
|
|
533
567
|
@property
|
|
534
568
|
def query_visualizations(self) -> QueryVisualizationsAPI:
|
|
535
|
-
"""This is an evolving API that facilitates the addition and removal of
|
|
569
|
+
"""This is an evolving API that facilitates the addition and removal of visualizations from existing queries in the Databricks Workspace."""
|
|
536
570
|
return self._query_visualizations
|
|
537
571
|
|
|
572
|
+
@property
|
|
573
|
+
def query_visualizations_legacy(self) -> QueryVisualizationsLegacyAPI:
|
|
574
|
+
"""This is an evolving API that facilitates the addition and removal of vizualisations from existing queries within the Databricks Workspace."""
|
|
575
|
+
return self._query_visualizations_legacy
|
|
576
|
+
|
|
538
577
|
@property
|
|
539
578
|
def recipient_activation(self) -> RecipientActivationAPI:
|
|
540
579
|
"""The Recipient Activation API is only applicable in the open sharing model where the recipient object has the authentication type of `TOKEN`."""
|
|
@@ -575,6 +614,11 @@ class WorkspaceClient:
|
|
|
575
614
|
"""The Serving Endpoints API allows you to create, update, and delete model serving endpoints."""
|
|
576
615
|
return self._serving_endpoints
|
|
577
616
|
|
|
617
|
+
@property
|
|
618
|
+
def serving_endpoints_data_plane(self) -> ServingEndpointsDataPlaneAPI:
|
|
619
|
+
"""Serving endpoints DataPlane provides a set of operations to interact with data plane endpoints for Serving endpoints service."""
|
|
620
|
+
return self._serving_endpoints_data_plane
|
|
621
|
+
|
|
578
622
|
@property
|
|
579
623
|
def settings(self) -> SettingsAPI:
|
|
580
624
|
"""Workspace Settings API allows users to manage settings at the workspace level."""
|
|
@@ -700,7 +744,8 @@ class AccountClient:
|
|
|
700
744
|
debug_headers: bool = None,
|
|
701
745
|
product="unknown",
|
|
702
746
|
product_version="0.0.0",
|
|
703
|
-
|
|
747
|
+
credentials_strategy: CredentialsStrategy = None,
|
|
748
|
+
credentials_provider: CredentialsStrategy = None,
|
|
704
749
|
config: client.Config = None):
|
|
705
750
|
if not config:
|
|
706
751
|
config = client.Config(host=host,
|
|
@@ -721,6 +766,7 @@ class AccountClient:
|
|
|
721
766
|
cluster_id=cluster_id,
|
|
722
767
|
google_credentials=google_credentials,
|
|
723
768
|
google_service_account=google_service_account,
|
|
769
|
+
credentials_strategy=credentials_strategy,
|
|
724
770
|
credentials_provider=credentials_provider,
|
|
725
771
|
debug_truncate_bytes=debug_truncate_bytes,
|
|
726
772
|
debug_headers=debug_headers,
|
|
@@ -730,7 +776,6 @@ class AccountClient:
|
|
|
730
776
|
self._api_client = client.ApiClient(self._config)
|
|
731
777
|
self._access_control = AccountAccessControlAPI(self._api_client)
|
|
732
778
|
self._billable_usage = BillableUsageAPI(self._api_client)
|
|
733
|
-
self._budgets = BudgetsAPI(self._api_client)
|
|
734
779
|
self._credentials = CredentialsAPI(self._api_client)
|
|
735
780
|
self._custom_app_integration = CustomAppIntegrationAPI(self._api_client)
|
|
736
781
|
self._encryption_keys = EncryptionKeysAPI(self._api_client)
|
|
@@ -749,10 +794,12 @@ class AccountClient:
|
|
|
749
794
|
self._settings = AccountSettingsAPI(self._api_client)
|
|
750
795
|
self._storage = StorageAPI(self._api_client)
|
|
751
796
|
self._storage_credentials = AccountStorageCredentialsAPI(self._api_client)
|
|
797
|
+
self._usage_dashboards = UsageDashboardsAPI(self._api_client)
|
|
752
798
|
self._users = AccountUsersAPI(self._api_client)
|
|
753
799
|
self._vpc_endpoints = VpcEndpointsAPI(self._api_client)
|
|
754
800
|
self._workspace_assignment = WorkspaceAssignmentAPI(self._api_client)
|
|
755
801
|
self._workspaces = WorkspacesAPI(self._api_client)
|
|
802
|
+
self._budgets = BudgetsAPI(self._api_client)
|
|
756
803
|
|
|
757
804
|
@property
|
|
758
805
|
def config(self) -> client.Config:
|
|
@@ -772,11 +819,6 @@ class AccountClient:
|
|
|
772
819
|
"""This API allows you to download billable usage logs for the specified account and date range."""
|
|
773
820
|
return self._billable_usage
|
|
774
821
|
|
|
775
|
-
@property
|
|
776
|
-
def budgets(self) -> BudgetsAPI:
|
|
777
|
-
"""These APIs manage budget configuration including notifications for exceeding a budget for a period."""
|
|
778
|
-
return self._budgets
|
|
779
|
-
|
|
780
822
|
@property
|
|
781
823
|
def credentials(self) -> CredentialsAPI:
|
|
782
824
|
"""These APIs manage credential configurations for this workspace."""
|
|
@@ -784,7 +826,7 @@ class AccountClient:
|
|
|
784
826
|
|
|
785
827
|
@property
|
|
786
828
|
def custom_app_integration(self) -> CustomAppIntegrationAPI:
|
|
787
|
-
"""These APIs enable administrators to manage custom
|
|
829
|
+
"""These APIs enable administrators to manage custom OAuth app integrations, which is required for adding/using Custom OAuth App Integration like Tableau Cloud for Databricks in AWS cloud."""
|
|
788
830
|
return self._custom_app_integration
|
|
789
831
|
|
|
790
832
|
@property
|
|
@@ -839,7 +881,7 @@ class AccountClient:
|
|
|
839
881
|
|
|
840
882
|
@property
|
|
841
883
|
def published_app_integration(self) -> PublishedAppIntegrationAPI:
|
|
842
|
-
"""These APIs enable administrators to manage published
|
|
884
|
+
"""These APIs enable administrators to manage published OAuth app integrations, which is required for adding/using Published OAuth App Integration like Tableau Desktop for Databricks in AWS cloud."""
|
|
843
885
|
return self._published_app_integration
|
|
844
886
|
|
|
845
887
|
@property
|
|
@@ -867,6 +909,11 @@ class AccountClient:
|
|
|
867
909
|
"""These APIs manage storage credentials for a particular metastore."""
|
|
868
910
|
return self._storage_credentials
|
|
869
911
|
|
|
912
|
+
@property
|
|
913
|
+
def usage_dashboards(self) -> UsageDashboardsAPI:
|
|
914
|
+
"""These APIs manage usage dashboards for this account."""
|
|
915
|
+
return self._usage_dashboards
|
|
916
|
+
|
|
870
917
|
@property
|
|
871
918
|
def users(self) -> AccountUsersAPI:
|
|
872
919
|
"""User identities recognized by Databricks and represented by email addresses."""
|
|
@@ -887,6 +934,11 @@ class AccountClient:
|
|
|
887
934
|
"""These APIs manage workspaces for this account."""
|
|
888
935
|
return self._workspaces
|
|
889
936
|
|
|
937
|
+
@property
|
|
938
|
+
def budgets(self) -> BudgetsAPI:
|
|
939
|
+
"""These APIs manage budget configurations for this account."""
|
|
940
|
+
return self._budgets
|
|
941
|
+
|
|
890
942
|
def get_workspace_client(self, workspace: Workspace) -> WorkspaceClient:
|
|
891
943
|
"""Constructs a ``WorkspaceClient`` for the given workspace.
|
|
892
944
|
|
databricks/sdk/config.py
CHANGED
|
@@ -3,19 +3,18 @@ import copy
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
import pathlib
|
|
6
|
-
import platform
|
|
7
6
|
import sys
|
|
8
7
|
import urllib.parse
|
|
9
8
|
from typing import Dict, Iterable, Optional
|
|
10
9
|
|
|
11
10
|
import requests
|
|
12
11
|
|
|
12
|
+
from . import useragent
|
|
13
13
|
from .clock import Clock, RealClock
|
|
14
|
-
from .credentials_provider import
|
|
14
|
+
from .credentials_provider import CredentialsStrategy, DefaultCredentials
|
|
15
15
|
from .environments import (ALL_ENVS, AzureEnvironment, Cloud,
|
|
16
16
|
DatabricksEnvironment, get_environment_for_hostname)
|
|
17
|
-
from .oauth import OidcEndpoints
|
|
18
|
-
from .version import __version__
|
|
17
|
+
from .oauth import OidcEndpoints, Token
|
|
19
18
|
|
|
20
19
|
logger = logging.getLogger('databricks.sdk')
|
|
21
20
|
|
|
@@ -44,6 +43,16 @@ class ConfigAttribute:
|
|
|
44
43
|
return f"<ConfigAttribute '{self.name}' {self.transform.__name__}>"
|
|
45
44
|
|
|
46
45
|
|
|
46
|
+
def with_product(product: str, product_version: str):
|
|
47
|
+
"""[INTERNAL API] Change the product name and version used in the User-Agent header."""
|
|
48
|
+
useragent.with_product(product, product_version)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def with_user_agent_extra(key: str, value: str):
|
|
52
|
+
"""[INTERNAL API] Add extra metadata to the User-Agent header when developing a library."""
|
|
53
|
+
useragent.with_extra(key, value)
|
|
54
|
+
|
|
55
|
+
|
|
47
56
|
class Config:
|
|
48
57
|
host: str = ConfigAttribute(env='DATABRICKS_HOST')
|
|
49
58
|
account_id: str = ConfigAttribute(env='DATABRICKS_ACCOUNT_ID')
|
|
@@ -66,6 +75,7 @@ class Config:
|
|
|
66
75
|
auth_type: str = ConfigAttribute(env='DATABRICKS_AUTH_TYPE')
|
|
67
76
|
cluster_id: str = ConfigAttribute(env='DATABRICKS_CLUSTER_ID')
|
|
68
77
|
warehouse_id: str = ConfigAttribute(env='DATABRICKS_WAREHOUSE_ID')
|
|
78
|
+
serverless_compute_id: str = ConfigAttribute(env='DATABRICKS_SERVERLESS_COMPUTE_ID')
|
|
69
79
|
skip_verify: bool = ConfigAttribute()
|
|
70
80
|
http_timeout_seconds: float = ConfigAttribute()
|
|
71
81
|
debug_truncate_bytes: int = ConfigAttribute(env='DATABRICKS_DEBUG_TRUNCATE_BYTES')
|
|
@@ -81,15 +91,25 @@ class Config:
|
|
|
81
91
|
|
|
82
92
|
def __init__(self,
|
|
83
93
|
*,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
94
|
+
# Deprecated. Use credentials_strategy instead.
|
|
95
|
+
credentials_provider: CredentialsStrategy = None,
|
|
96
|
+
credentials_strategy: CredentialsStrategy = None,
|
|
97
|
+
product=None,
|
|
98
|
+
product_version=None,
|
|
87
99
|
clock: Clock = None,
|
|
88
100
|
**kwargs):
|
|
89
101
|
self._header_factory = None
|
|
90
102
|
self._inner = {}
|
|
91
103
|
self._user_agent_other_info = []
|
|
92
|
-
|
|
104
|
+
if credentials_strategy and credentials_provider:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
"When providing `credentials_strategy` field, `credential_provider` cannot be specified.")
|
|
107
|
+
if credentials_provider:
|
|
108
|
+
logger.warning(
|
|
109
|
+
"parameter 'credentials_provider' is deprecated. Use 'credentials_strategy' instead.")
|
|
110
|
+
self._credentials_strategy = next(
|
|
111
|
+
s for s in [credentials_strategy, credentials_provider,
|
|
112
|
+
DefaultCredentials()] if s is not None)
|
|
93
113
|
if 'databricks_environment' in kwargs:
|
|
94
114
|
self.databricks_environment = kwargs['databricks_environment']
|
|
95
115
|
del kwargs['databricks_environment']
|
|
@@ -101,12 +121,14 @@ class Config:
|
|
|
101
121
|
self._fix_host_if_needed()
|
|
102
122
|
self._validate()
|
|
103
123
|
self.init_auth()
|
|
104
|
-
self.
|
|
105
|
-
self._product_version = product_version
|
|
124
|
+
self._init_product(product, product_version)
|
|
106
125
|
except ValueError as e:
|
|
107
126
|
message = self.wrap_debug_info(str(e))
|
|
108
127
|
raise ValueError(message) from e
|
|
109
128
|
|
|
129
|
+
def oauth_token(self) -> Token:
|
|
130
|
+
return self._credentials_strategy.oauth_token(self)
|
|
131
|
+
|
|
110
132
|
def wrap_debug_info(self, message: str) -> str:
|
|
111
133
|
debug_string = self.debug_string()
|
|
112
134
|
if debug_string:
|
|
@@ -211,41 +233,19 @@ class Config:
|
|
|
211
233
|
@property
|
|
212
234
|
def user_agent(self):
|
|
213
235
|
""" Returns User-Agent header used by this SDK """
|
|
214
|
-
py_version = platform.python_version()
|
|
215
|
-
os_name = platform.uname().system.lower()
|
|
216
|
-
|
|
217
|
-
ua = [
|
|
218
|
-
f"{self._product}/{self._product_version}", f"databricks-sdk-py/{__version__}",
|
|
219
|
-
f"python/{py_version}", f"os/{os_name}", f"auth/{self.auth_type}",
|
|
220
|
-
]
|
|
221
|
-
if len(self._user_agent_other_info) > 0:
|
|
222
|
-
ua.append(' '.join(self._user_agent_other_info))
|
|
223
|
-
if len(self._upstream_user_agent) > 0:
|
|
224
|
-
ua.append(self._upstream_user_agent)
|
|
225
|
-
if 'DATABRICKS_RUNTIME_VERSION' in os.environ:
|
|
226
|
-
runtime_version = os.environ['DATABRICKS_RUNTIME_VERSION']
|
|
227
|
-
if runtime_version != '':
|
|
228
|
-
runtime_version = self._sanitize_header_value(runtime_version)
|
|
229
|
-
ua.append(f'runtime/{runtime_version}')
|
|
230
|
-
|
|
231
|
-
return ' '.join(ua)
|
|
232
236
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
# global user agent includes SDK version, product name & version, platform info,
|
|
238
|
+
# and global extra info. Config can have specific extra info associated with it,
|
|
239
|
+
# such as an override product, auth type, and other user-defined information.
|
|
240
|
+
return useragent.to_string(self._product_info,
|
|
241
|
+
[("auth", self.auth_type)] + self._user_agent_other_info)
|
|
238
242
|
|
|
239
243
|
@property
|
|
240
244
|
def _upstream_user_agent(self) -> str:
|
|
241
|
-
|
|
242
|
-
product_version = os.environ.get('DATABRICKS_SDK_UPSTREAM_VERSION', None)
|
|
243
|
-
if product is not None and product_version is not None:
|
|
244
|
-
return f"upstream/{product} upstream-version/{product_version}"
|
|
245
|
-
return ""
|
|
245
|
+
return " ".join(f"{k}/{v}" for k, v in useragent._get_upstream_user_agent_info())
|
|
246
246
|
|
|
247
247
|
def with_user_agent_extra(self, key: str, value: str) -> 'Config':
|
|
248
|
-
self._user_agent_other_info.append(
|
|
248
|
+
self._user_agent_other_info.append((key, value))
|
|
249
249
|
return self
|
|
250
250
|
|
|
251
251
|
@property
|
|
@@ -348,13 +348,47 @@ class Config:
|
|
|
348
348
|
def _fix_host_if_needed(self):
|
|
349
349
|
if not self.host:
|
|
350
350
|
return
|
|
351
|
-
|
|
351
|
+
|
|
352
|
+
# Add a default scheme if it's missing
|
|
353
|
+
if '://' not in self.host:
|
|
354
|
+
self.host = 'https://' + self.host
|
|
355
|
+
|
|
352
356
|
o = urllib.parse.urlparse(self.host)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
# remove trailing slash
|
|
358
|
+
path = o.path.rstrip('/')
|
|
359
|
+
# remove port if 443
|
|
360
|
+
netloc = o.netloc
|
|
361
|
+
if o.port == 443:
|
|
362
|
+
netloc = netloc.split(':')[0]
|
|
363
|
+
|
|
364
|
+
self.host = urllib.parse.urlunparse((o.scheme, netloc, path, o.params, o.query, o.fragment))
|
|
365
|
+
|
|
366
|
+
def load_azure_tenant_id(self):
|
|
367
|
+
"""[Internal] Load the Azure tenant ID from the Azure Databricks login page.
|
|
368
|
+
|
|
369
|
+
If the tenant ID is already set, this method does nothing."""
|
|
370
|
+
if not self.is_azure or self.azure_tenant_id is not None or self.host is None:
|
|
371
|
+
return
|
|
372
|
+
login_url = f'{self.host}/aad/auth'
|
|
373
|
+
logger.debug(f'Loading tenant ID from {login_url}')
|
|
374
|
+
resp = requests.get(login_url, allow_redirects=False)
|
|
375
|
+
if resp.status_code // 100 != 3:
|
|
376
|
+
logger.debug(
|
|
377
|
+
f'Failed to get tenant ID from {login_url}: expected status code 3xx, got {resp.status_code}')
|
|
378
|
+
return
|
|
379
|
+
entra_id_endpoint = resp.headers.get('Location')
|
|
380
|
+
if entra_id_endpoint is None:
|
|
381
|
+
logger.debug(f'No Location header in response from {login_url}')
|
|
382
|
+
return
|
|
383
|
+
# The Location header has the following form: https://login.microsoftonline.com/<tenant-id>/oauth2/authorize?...
|
|
384
|
+
# The domain may change depending on the Azure cloud (e.g. login.microsoftonline.us for US Government cloud).
|
|
385
|
+
url = urllib.parse.urlparse(entra_id_endpoint)
|
|
386
|
+
path_segments = url.path.split('/')
|
|
387
|
+
if len(path_segments) < 2:
|
|
388
|
+
logger.debug(f'Invalid path in Location header: {url.path}')
|
|
389
|
+
return
|
|
390
|
+
self.azure_tenant_id = path_segments[1]
|
|
391
|
+
logger.debug(f'Loaded tenant ID: {self.azure_tenant_id}')
|
|
358
392
|
|
|
359
393
|
def _set_inner_config(self, keyword_args: Dict[str, any]):
|
|
360
394
|
for attr in self.attributes():
|
|
@@ -436,12 +470,19 @@ class Config:
|
|
|
436
470
|
|
|
437
471
|
def init_auth(self):
|
|
438
472
|
try:
|
|
439
|
-
self._header_factory = self.
|
|
440
|
-
self.auth_type = self.
|
|
473
|
+
self._header_factory = self._credentials_strategy(self)
|
|
474
|
+
self.auth_type = self._credentials_strategy.auth_type()
|
|
441
475
|
if not self._header_factory:
|
|
442
476
|
raise ValueError('not configured')
|
|
443
477
|
except ValueError as e:
|
|
444
|
-
raise ValueError(f'{self.
|
|
478
|
+
raise ValueError(f'{self._credentials_strategy.auth_type()} auth: {e}') from e
|
|
479
|
+
|
|
480
|
+
def _init_product(self, product, product_version):
|
|
481
|
+
if product is not None or product_version is not None:
|
|
482
|
+
default_product, default_version = useragent.product()
|
|
483
|
+
self._product_info = (product or default_product, product_version or default_version)
|
|
484
|
+
else:
|
|
485
|
+
self._product_info = None
|
|
445
486
|
|
|
446
487
|
def __repr__(self):
|
|
447
488
|
return f'<{self.debug_string()}>'
|
databricks/sdk/core.py
CHANGED
|
@@ -4,6 +4,7 @@ from datetime import timedelta
|
|
|
4
4
|
from json import JSONDecodeError
|
|
5
5
|
from types import TracebackType
|
|
6
6
|
from typing import Any, BinaryIO, Iterator, Type
|
|
7
|
+
from urllib.parse import urlencode
|
|
7
8
|
|
|
8
9
|
from requests.adapters import HTTPAdapter
|
|
9
10
|
|
|
@@ -13,12 +14,17 @@ from .config import *
|
|
|
13
14
|
from .credentials_provider import *
|
|
14
15
|
from .errors import DatabricksError, error_mapper
|
|
15
16
|
from .errors.private_link import _is_private_link_redirect
|
|
17
|
+
from .oauth import retrieve_token
|
|
16
18
|
from .retries import retried
|
|
17
19
|
|
|
18
20
|
__all__ = ['Config', 'DatabricksError']
|
|
19
21
|
|
|
20
22
|
logger = logging.getLogger('databricks.sdk')
|
|
21
23
|
|
|
24
|
+
URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"
|
|
25
|
+
JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
26
|
+
OIDC_TOKEN_PATH = "/oidc/v1/token"
|
|
27
|
+
|
|
22
28
|
|
|
23
29
|
class ApiClient:
|
|
24
30
|
_cfg: Config
|
|
@@ -109,33 +115,54 @@ class ApiClient:
|
|
|
109
115
|
flattened = dict(flatten_dict(with_fixed_bools))
|
|
110
116
|
return flattened
|
|
111
117
|
|
|
118
|
+
def get_oauth_token(self, auth_details: str) -> Token:
|
|
119
|
+
if not self._cfg.auth_type:
|
|
120
|
+
self._cfg.authenticate()
|
|
121
|
+
original_token = self._cfg.oauth_token()
|
|
122
|
+
headers = {"Content-Type": URL_ENCODED_CONTENT_TYPE}
|
|
123
|
+
params = urlencode({
|
|
124
|
+
"grant_type": JWT_BEARER_GRANT_TYPE,
|
|
125
|
+
"authorization_details": auth_details,
|
|
126
|
+
"assertion": original_token.access_token
|
|
127
|
+
})
|
|
128
|
+
return retrieve_token(client_id=self._cfg.client_id,
|
|
129
|
+
client_secret=self._cfg.client_secret,
|
|
130
|
+
token_url=self._cfg.host + OIDC_TOKEN_PATH,
|
|
131
|
+
params=params,
|
|
132
|
+
headers=headers)
|
|
133
|
+
|
|
112
134
|
def do(self,
|
|
113
135
|
method: str,
|
|
114
|
-
path: str,
|
|
136
|
+
path: str = None,
|
|
137
|
+
url: str = None,
|
|
115
138
|
query: dict = None,
|
|
116
139
|
headers: dict = None,
|
|
117
140
|
body: dict = None,
|
|
118
141
|
raw: bool = False,
|
|
119
142
|
files=None,
|
|
120
143
|
data=None,
|
|
144
|
+
auth: Callable[[requests.PreparedRequest], requests.PreparedRequest] = None,
|
|
121
145
|
response_headers: List[str] = None) -> Union[dict, BinaryIO]:
|
|
122
|
-
# Remove extra `/` from path for Files API
|
|
123
|
-
# Once we've fixed the OpenAPI spec, we can remove this
|
|
124
|
-
path = re.sub('^/api/2.0/fs/files//', '/api/2.0/fs/files/', path)
|
|
125
146
|
if headers is None:
|
|
126
147
|
headers = {}
|
|
148
|
+
if url is None:
|
|
149
|
+
# Remove extra `/` from path for Files API
|
|
150
|
+
# Once we've fixed the OpenAPI spec, we can remove this
|
|
151
|
+
path = re.sub('^/api/2.0/fs/files//', '/api/2.0/fs/files/', path)
|
|
152
|
+
url = f"{self._cfg.host}{path}"
|
|
127
153
|
headers['User-Agent'] = self._user_agent_base
|
|
128
154
|
retryable = retried(timeout=timedelta(seconds=self._retry_timeout_seconds),
|
|
129
155
|
is_retryable=self._is_retryable,
|
|
130
156
|
clock=self._cfg.clock)
|
|
131
157
|
response = retryable(self._perform)(method,
|
|
132
|
-
|
|
158
|
+
url,
|
|
133
159
|
query=query,
|
|
134
160
|
headers=headers,
|
|
135
161
|
body=body,
|
|
136
162
|
raw=raw,
|
|
137
163
|
files=files,
|
|
138
|
-
data=data
|
|
164
|
+
data=data,
|
|
165
|
+
auth=auth)
|
|
139
166
|
|
|
140
167
|
resp = dict()
|
|
141
168
|
for header in response_headers if response_headers else []:
|
|
@@ -217,20 +244,22 @@ class ApiClient:
|
|
|
217
244
|
|
|
218
245
|
def _perform(self,
|
|
219
246
|
method: str,
|
|
220
|
-
|
|
247
|
+
url: str,
|
|
221
248
|
query: dict = None,
|
|
222
249
|
headers: dict = None,
|
|
223
250
|
body: dict = None,
|
|
224
251
|
raw: bool = False,
|
|
225
252
|
files=None,
|
|
226
|
-
data=None
|
|
253
|
+
data=None,
|
|
254
|
+
auth: Callable[[requests.PreparedRequest], requests.PreparedRequest] = None):
|
|
227
255
|
response = self._session.request(method,
|
|
228
|
-
|
|
256
|
+
url,
|
|
229
257
|
params=self._fix_query_string(query),
|
|
230
258
|
json=body,
|
|
231
259
|
headers=headers,
|
|
232
260
|
files=files,
|
|
233
261
|
data=data,
|
|
262
|
+
auth=auth,
|
|
234
263
|
stream=raw,
|
|
235
264
|
timeout=self._http_timeout_seconds)
|
|
236
265
|
try:
|