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
@@ -676,13 +676,22 @@ class DbtTask:
676
676
  specified. If no warehouse_id is specified and this folder is unset, the root directory is used."""
677
677
 
678
678
  project_directory: Optional[str] = None
679
- """Optional (relative) path to the project directory, if no value is provided, the root of the git
680
- repository is used."""
679
+ """Path to the project directory. Optional for Git sourced tasks, in which case if no value is
680
+ provided, the root of the Git repository is used."""
681
681
 
682
682
  schema: Optional[str] = None
683
683
  """Optional schema to write to. This parameter is only used when a warehouse_id is also provided.
684
684
  If not provided, the `default` schema is used."""
685
685
 
686
+ source: Optional[Source] = None
687
+ """Optional location type of the project directory. When set to `WORKSPACE`, the project will be
688
+ retrieved from the local <Databricks> workspace. When set to `GIT`, the project will be
689
+ retrieved from a Git repository defined in `git_source`. If the value is empty, the task will
690
+ use `GIT` if `git_source` is defined and `WORKSPACE` otherwise.
691
+
692
+ * `WORKSPACE`: Project is located in <Databricks> workspace. * `GIT`: Project is located in
693
+ cloud Git provider."""
694
+
686
695
  warehouse_id: Optional[str] = None
687
696
  """ID of the SQL warehouse to connect to. If provided, we automatically generate and provide the
688
697
  profile and connection details to dbt. It can be overridden on a per-command basis by using the
@@ -696,6 +705,7 @@ class DbtTask:
696
705
  if self.profiles_directory is not None: body['profiles_directory'] = self.profiles_directory
697
706
  if self.project_directory is not None: body['project_directory'] = self.project_directory
698
707
  if self.schema is not None: body['schema'] = self.schema
708
+ if self.source is not None: body['source'] = self.source.value
699
709
  if self.warehouse_id is not None: body['warehouse_id'] = self.warehouse_id
700
710
  return body
701
711
 
@@ -707,6 +717,7 @@ class DbtTask:
707
717
  profiles_directory=d.get('profiles_directory', None),
708
718
  project_directory=d.get('project_directory', None),
709
719
  schema=d.get('schema', None),
720
+ source=_enum(d, 'source', Source),
710
721
  warehouse_id=d.get('warehouse_id', None))
711
722
 
712
723
 
@@ -771,8 +782,8 @@ class FileArrivalTriggerConfiguration:
771
782
  time the trigger fired. The minimum allowed value is 60 seconds"""
772
783
 
773
784
  url: Optional[str] = None
774
- """URL to be monitored for file arrivals. The path must point to the root or a subpath of the
775
- external location."""
785
+ """The storage location to monitor for file arrivals. The value must point to the root or a subpath
786
+ of an external location URL or the root or subpath of a Unity Catalog volume."""
776
787
 
777
788
  wait_after_last_change_seconds: Optional[int] = None
778
789
  """If set, the trigger starts a run only after no file activity has occurred for the specified
@@ -797,6 +808,117 @@ class FileArrivalTriggerConfiguration:
797
808
  wait_after_last_change_seconds=d.get('wait_after_last_change_seconds', None))
798
809
 
799
810
 
811
+ @dataclass
812
+ class ForEachStats:
813
+ error_message_stats: Optional[ForEachTaskErrorMessageStats] = None
814
+ """Sample of 3 most common error messages occurred during the iteration."""
815
+
816
+ task_run_stats: Optional[ForEachTaskTaskRunStats] = None
817
+ """Describes stats of the iteration. Only latest retries are considered."""
818
+
819
+ def as_dict(self) -> dict:
820
+ """Serializes the ForEachStats into a dictionary suitable for use as a JSON request body."""
821
+ body = {}
822
+ if self.error_message_stats: body['error_message_stats'] = self.error_message_stats.as_dict()
823
+ if self.task_run_stats: body['task_run_stats'] = self.task_run_stats.as_dict()
824
+ return body
825
+
826
+ @classmethod
827
+ def from_dict(cls, d: Dict[str, any]) -> ForEachStats:
828
+ """Deserializes the ForEachStats from a dictionary."""
829
+ return cls(error_message_stats=_from_dict(d, 'error_message_stats', ForEachTaskErrorMessageStats),
830
+ task_run_stats=_from_dict(d, 'task_run_stats', ForEachTaskTaskRunStats))
831
+
832
+
833
+ @dataclass
834
+ class ForEachTask:
835
+ inputs: str
836
+ """Array for task to iterate on. This can be a JSON string or a reference to an array parameter."""
837
+
838
+ task: Task
839
+
840
+ concurrency: Optional[int] = None
841
+ """Controls the number of active iterations task runs. Default is 100 (maximal value)."""
842
+
843
+ def as_dict(self) -> dict:
844
+ """Serializes the ForEachTask into a dictionary suitable for use as a JSON request body."""
845
+ body = {}
846
+ if self.concurrency is not None: body['concurrency'] = self.concurrency
847
+ if self.inputs is not None: body['inputs'] = self.inputs
848
+ if self.task: body['task'] = self.task.as_dict()
849
+ return body
850
+
851
+ @classmethod
852
+ def from_dict(cls, d: Dict[str, any]) -> ForEachTask:
853
+ """Deserializes the ForEachTask from a dictionary."""
854
+ return cls(concurrency=d.get('concurrency', None),
855
+ inputs=d.get('inputs', None),
856
+ task=_from_dict(d, 'task', Task))
857
+
858
+
859
+ @dataclass
860
+ class ForEachTaskErrorMessageStats:
861
+ count: Optional[str] = None
862
+ """Describes the count of such error message encountered during the iterations."""
863
+
864
+ error_message: Optional[str] = None
865
+ """Describes the error message occured during the iterations."""
866
+
867
+ def as_dict(self) -> dict:
868
+ """Serializes the ForEachTaskErrorMessageStats into a dictionary suitable for use as a JSON request body."""
869
+ body = {}
870
+ if self.count is not None: body['count'] = self.count
871
+ if self.error_message is not None: body['error_message'] = self.error_message
872
+ return body
873
+
874
+ @classmethod
875
+ def from_dict(cls, d: Dict[str, any]) -> ForEachTaskErrorMessageStats:
876
+ """Deserializes the ForEachTaskErrorMessageStats from a dictionary."""
877
+ return cls(count=d.get('count', None), error_message=d.get('error_message', None))
878
+
879
+
880
+ @dataclass
881
+ class ForEachTaskTaskRunStats:
882
+ active_iterations: Optional[int] = None
883
+ """Describes the iteration runs having an active lifecycle state or an active run sub state."""
884
+
885
+ completed_iterations: Optional[int] = None
886
+ """Describes the number of failed and succeeded iteration runs."""
887
+
888
+ failed_iterations: Optional[int] = None
889
+ """Describes the number of failed iteration runs."""
890
+
891
+ scheduled_iterations: Optional[int] = None
892
+ """Describes the number of iteration runs that have been scheduled."""
893
+
894
+ succeeded_iterations: Optional[int] = None
895
+ """Describes the number of succeeded iteration runs."""
896
+
897
+ total_iterations: Optional[int] = None
898
+ """Describes the length of the list of items to iterate over."""
899
+
900
+ def as_dict(self) -> dict:
901
+ """Serializes the ForEachTaskTaskRunStats into a dictionary suitable for use as a JSON request body."""
902
+ body = {}
903
+ if self.active_iterations is not None: body['active_iterations'] = self.active_iterations
904
+ if self.completed_iterations is not None: body['completed_iterations'] = self.completed_iterations
905
+ if self.failed_iterations is not None: body['failed_iterations'] = self.failed_iterations
906
+ if self.scheduled_iterations is not None: body['scheduled_iterations'] = self.scheduled_iterations
907
+ if self.succeeded_iterations is not None: body['succeeded_iterations'] = self.succeeded_iterations
908
+ if self.total_iterations is not None: body['total_iterations'] = self.total_iterations
909
+ return body
910
+
911
+ @classmethod
912
+ def from_dict(cls, d: Dict[str, any]) -> ForEachTaskTaskRunStats:
913
+ """Deserializes the ForEachTaskTaskRunStats from a dictionary."""
914
+ return cls(active_iterations=d.get('active_iterations', None),
915
+ completed_iterations=d.get('completed_iterations', None),
916
+ failed_iterations=d.get('failed_iterations', None),
917
+ scheduled_iterations=d.get('scheduled_iterations', None),
918
+ succeeded_iterations=d.get('succeeded_iterations', None),
919
+ total_iterations=d.get('total_iterations', None))
920
+
921
+
800
922
  class Format(Enum):
801
923
 
802
924
  MULTI_TASK = 'MULTI_TASK'
@@ -2531,6 +2653,36 @@ class RunConditionTaskOp(Enum):
2531
2653
  NOT_EQUAL = 'NOT_EQUAL'
2532
2654
 
2533
2655
 
2656
+ @dataclass
2657
+ class RunForEachTask:
2658
+ concurrency: Optional[int] = None
2659
+ """Controls the number of active iterations task runs. Default is 100 (maximal value)."""
2660
+
2661
+ inputs: Optional[str] = None
2662
+ """Array for task to iterate on. This can be a JSON string or a reference to an array parameter."""
2663
+
2664
+ stats: Optional[ForEachStats] = None
2665
+
2666
+ task: Optional[Task] = None
2667
+
2668
+ def as_dict(self) -> dict:
2669
+ """Serializes the RunForEachTask into a dictionary suitable for use as a JSON request body."""
2670
+ body = {}
2671
+ if self.concurrency is not None: body['concurrency'] = self.concurrency
2672
+ if self.inputs is not None: body['inputs'] = self.inputs
2673
+ if self.stats: body['stats'] = self.stats.as_dict()
2674
+ if self.task: body['task'] = self.task.as_dict()
2675
+ return body
2676
+
2677
+ @classmethod
2678
+ def from_dict(cls, d: Dict[str, any]) -> RunForEachTask:
2679
+ """Deserializes the RunForEachTask from a dictionary."""
2680
+ return cls(concurrency=d.get('concurrency', None),
2681
+ inputs=d.get('inputs', None),
2682
+ stats=_from_dict(d, 'stats', ForEachStats),
2683
+ task=_from_dict(d, 'task', Task))
2684
+
2685
+
2534
2686
  class RunIf(Enum):
2535
2687
  """An optional value indicating the condition that determines whether the task should be run once
2536
2688
  its dependencies have been completed. When omitted, defaults to `ALL_SUCCESS`.
@@ -3056,6 +3208,9 @@ class RunTask:
3056
3208
  When running jobs on an existing cluster, you may need to manually restart the cluster if it
3057
3209
  stops responding. We suggest running jobs on new clusters for greater reliability."""
3058
3210
 
3211
+ for_each_task: Optional[RunForEachTask] = None
3212
+ """If for_each_task, indicates that this task must execute the nested task within it."""
3213
+
3059
3214
  git_source: Optional[GitSource] = None
3060
3215
  """An optional specification for a remote Git repository containing the source code used by tasks.
3061
3216
  Version-controlled source code is supported by notebook, dbt, Python script, and SQL File tasks.
@@ -3159,6 +3314,7 @@ class RunTask:
3159
3314
  if self.end_time is not None: body['end_time'] = self.end_time
3160
3315
  if self.execution_duration is not None: body['execution_duration'] = self.execution_duration
3161
3316
  if self.existing_cluster_id is not None: body['existing_cluster_id'] = self.existing_cluster_id
3317
+ if self.for_each_task: body['for_each_task'] = self.for_each_task.as_dict()
3162
3318
  if self.git_source: body['git_source'] = self.git_source.as_dict()
3163
3319
  if self.libraries: body['libraries'] = [v.as_dict() for v in self.libraries]
3164
3320
  if self.new_cluster: body['new_cluster'] = self.new_cluster.as_dict()
@@ -3193,6 +3349,7 @@ class RunTask:
3193
3349
  end_time=d.get('end_time', None),
3194
3350
  execution_duration=d.get('execution_duration', None),
3195
3351
  existing_cluster_id=d.get('existing_cluster_id', None),
3352
+ for_each_task=_from_dict(d, 'for_each_task', RunForEachTask),
3196
3353
  git_source=_from_dict(d, 'git_source', GitSource),
3197
3354
  libraries=_repeated_dict(d, 'libraries', compute.Library),
3198
3355
  new_cluster=_from_dict(d, 'new_cluster', compute.ClusterSpec),
@@ -3662,18 +3819,29 @@ class SqlTaskDashboard:
3662
3819
  @dataclass
3663
3820
  class SqlTaskFile:
3664
3821
  path: str
3665
- """Relative path of the SQL file in the remote Git repository."""
3822
+ """Path of the SQL file. Must be relative if the source is a remote Git repository and absolute for
3823
+ workspace paths."""
3824
+
3825
+ source: Optional[Source] = None
3826
+ """Optional location type of the SQL file. When set to `WORKSPACE`, the SQL file will be retrieved
3827
+ from the local <Databricks> workspace. When set to `GIT`, the SQL file will be retrieved from a
3828
+ Git repository defined in `git_source`. If the value is empty, the task will use `GIT` if
3829
+ `git_source` is defined and `WORKSPACE` otherwise.
3830
+
3831
+ * `WORKSPACE`: SQL file is located in <Databricks> workspace. * `GIT`: SQL file is located in
3832
+ cloud Git provider."""
3666
3833
 
3667
3834
  def as_dict(self) -> dict:
3668
3835
  """Serializes the SqlTaskFile into a dictionary suitable for use as a JSON request body."""
3669
3836
  body = {}
3670
3837
  if self.path is not None: body['path'] = self.path
3838
+ if self.source is not None: body['source'] = self.source.value
3671
3839
  return body
3672
3840
 
3673
3841
  @classmethod
3674
3842
  def from_dict(cls, d: Dict[str, any]) -> SqlTaskFile:
3675
3843
  """Deserializes the SqlTaskFile from a dictionary."""
3676
- return cls(path=d.get('path', None))
3844
+ return cls(path=d.get('path', None), source=_enum(d, 'source', Source))
3677
3845
 
3678
3846
 
3679
3847
  @dataclass
@@ -3847,6 +4015,10 @@ class SubmitTask:
3847
4015
  to manually restart the cluster if it stops responding. We suggest running jobs on new clusters
3848
4016
  for greater reliability."""
3849
4017
 
4018
+ for_each_task: Optional[ForEachTask] = None
4019
+ """If for_each_task, indicates that this must execute the nested task within it for the inputs
4020
+ provided."""
4021
+
3850
4022
  health: Optional[JobsHealthRules] = None
3851
4023
  """An optional set of health rules that can be defined for this job."""
3852
4024
 
@@ -3920,6 +4092,7 @@ class SubmitTask:
3920
4092
  if self.depends_on: body['depends_on'] = [v.as_dict() for v in self.depends_on]
3921
4093
  if self.email_notifications: body['email_notifications'] = self.email_notifications.as_dict()
3922
4094
  if self.existing_cluster_id is not None: body['existing_cluster_id'] = self.existing_cluster_id
4095
+ if self.for_each_task: body['for_each_task'] = self.for_each_task.as_dict()
3923
4096
  if self.health: body['health'] = self.health.as_dict()
3924
4097
  if self.libraries: body['libraries'] = [v.as_dict() for v in self.libraries]
3925
4098
  if self.new_cluster: body['new_cluster'] = self.new_cluster.as_dict()
@@ -3945,6 +4118,7 @@ class SubmitTask:
3945
4118
  depends_on=_repeated_dict(d, 'depends_on', TaskDependency),
3946
4119
  email_notifications=_from_dict(d, 'email_notifications', JobEmailNotifications),
3947
4120
  existing_cluster_id=d.get('existing_cluster_id', None),
4121
+ for_each_task=_from_dict(d, 'for_each_task', ForEachTask),
3948
4122
  health=_from_dict(d, 'health', JobsHealthRules),
3949
4123
  libraries=_repeated_dict(d, 'libraries', compute.Library),
3950
4124
  new_cluster=_from_dict(d, 'new_cluster', compute.ClusterSpec),
@@ -4002,6 +4176,10 @@ class Task:
4002
4176
  to manually restart the cluster if it stops responding. We suggest running jobs on new clusters
4003
4177
  for greater reliability."""
4004
4178
 
4179
+ for_each_task: Optional[ForEachTask] = None
4180
+ """If for_each_task, indicates that this must execute the nested task within it for the inputs
4181
+ provided."""
4182
+
4005
4183
  health: Optional[JobsHealthRules] = None
4006
4184
  """An optional set of health rules that can be defined for this job."""
4007
4185
 
@@ -4098,6 +4276,7 @@ class Task:
4098
4276
  if self.description is not None: body['description'] = self.description
4099
4277
  if self.email_notifications: body['email_notifications'] = self.email_notifications.as_dict()
4100
4278
  if self.existing_cluster_id is not None: body['existing_cluster_id'] = self.existing_cluster_id
4279
+ if self.for_each_task: body['for_each_task'] = self.for_each_task.as_dict()
4101
4280
  if self.health: body['health'] = self.health.as_dict()
4102
4281
  if self.job_cluster_key is not None: body['job_cluster_key'] = self.job_cluster_key
4103
4282
  if self.libraries: body['libraries'] = [v.as_dict() for v in self.libraries]
@@ -4131,6 +4310,7 @@ class Task:
4131
4310
  description=d.get('description', None),
4132
4311
  email_notifications=_from_dict(d, 'email_notifications', TaskEmailNotifications),
4133
4312
  existing_cluster_id=d.get('existing_cluster_id', None),
4313
+ for_each_task=_from_dict(d, 'for_each_task', ForEachTask),
4134
4314
  health=_from_dict(d, 'health', JobsHealthRules),
4135
4315
  job_cluster_key=d.get('job_cluster_key', None),
4136
4316
  libraries=_repeated_dict(d, 'libraries', compute.Library),
@@ -4945,10 +5125,9 @@ class JobsAPI:
4945
5125
 
4946
5126
  while True:
4947
5127
  json = self._api.do('GET', '/api/2.1/jobs/list', query=query, headers=headers)
4948
- if 'jobs' not in json or not json['jobs']:
4949
- return
4950
- for v in json['jobs']:
4951
- yield BaseJob.from_dict(v)
5128
+ if 'jobs' in json:
5129
+ for v in json['jobs']:
5130
+ yield BaseJob.from_dict(v)
4952
5131
  if 'next_page_token' not in json or not json['next_page_token']:
4953
5132
  return
4954
5133
  query['page_token'] = json['next_page_token']
@@ -5017,10 +5196,9 @@ class JobsAPI:
5017
5196
 
5018
5197
  while True:
5019
5198
  json = self._api.do('GET', '/api/2.1/jobs/runs/list', query=query, headers=headers)
5020
- if 'runs' not in json or not json['runs']:
5021
- return
5022
- for v in json['runs']:
5023
- yield BaseRun.from_dict(v)
5199
+ if 'runs' in json:
5200
+ for v in json['runs']:
5201
+ yield BaseRun.from_dict(v)
5024
5202
  if 'next_page_token' not in json or not json['next_page_token']:
5025
5203
  return
5026
5204
  query['page_token'] = json['next_page_token']
@@ -3710,10 +3710,9 @@ class ExperimentsAPI:
3710
3710
 
3711
3711
  while True:
3712
3712
  json = self._api.do('GET', '/api/2.0/mlflow/metrics/get-history', query=query, headers=headers)
3713
- if 'metrics' not in json or not json['metrics']:
3714
- return
3715
- for v in json['metrics']:
3716
- yield Metric.from_dict(v)
3713
+ if 'metrics' in json:
3714
+ for v in json['metrics']:
3715
+ yield Metric.from_dict(v)
3717
3716
  if 'next_page_token' not in json or not json['next_page_token']:
3718
3717
  return
3719
3718
  query['page_token'] = json['next_page_token']
@@ -3807,10 +3806,9 @@ class ExperimentsAPI:
3807
3806
 
3808
3807
  while True:
3809
3808
  json = self._api.do('GET', '/api/2.0/mlflow/artifacts/list', query=query, headers=headers)
3810
- if 'files' not in json or not json['files']:
3811
- return
3812
- for v in json['files']:
3813
- yield FileInfo.from_dict(v)
3809
+ if 'files' in json:
3810
+ for v in json['files']:
3811
+ yield FileInfo.from_dict(v)
3814
3812
  if 'next_page_token' not in json or not json['next_page_token']:
3815
3813
  return
3816
3814
  query['page_token'] = json['next_page_token']
@@ -3844,10 +3842,9 @@ class ExperimentsAPI:
3844
3842
 
3845
3843
  while True:
3846
3844
  json = self._api.do('GET', '/api/2.0/mlflow/experiments/list', query=query, headers=headers)
3847
- if 'experiments' not in json or not json['experiments']:
3848
- return
3849
- for v in json['experiments']:
3850
- yield Experiment.from_dict(v)
3845
+ if 'experiments' in json:
3846
+ for v in json['experiments']:
3847
+ yield Experiment.from_dict(v)
3851
3848
  if 'next_page_token' not in json or not json['next_page_token']:
3852
3849
  return
3853
3850
  query['page_token'] = json['next_page_token']
@@ -4125,10 +4122,9 @@ class ExperimentsAPI:
4125
4122
 
4126
4123
  while True:
4127
4124
  json = self._api.do('POST', '/api/2.0/mlflow/experiments/search', body=body, headers=headers)
4128
- if 'experiments' not in json or not json['experiments']:
4129
- return
4130
- for v in json['experiments']:
4131
- yield Experiment.from_dict(v)
4125
+ if 'experiments' in json:
4126
+ for v in json['experiments']:
4127
+ yield Experiment.from_dict(v)
4132
4128
  if 'next_page_token' not in json or not json['next_page_token']:
4133
4129
  return
4134
4130
  body['page_token'] = json['next_page_token']
@@ -4186,10 +4182,9 @@ class ExperimentsAPI:
4186
4182
 
4187
4183
  while True:
4188
4184
  json = self._api.do('POST', '/api/2.0/mlflow/runs/search', body=body, headers=headers)
4189
- if 'runs' not in json or not json['runs']:
4190
- return
4191
- for v in json['runs']:
4192
- yield Run.from_dict(v)
4185
+ if 'runs' in json:
4186
+ for v in json['runs']:
4187
+ yield Run.from_dict(v)
4193
4188
  if 'next_page_token' not in json or not json['next_page_token']:
4194
4189
  return
4195
4190
  body['page_token'] = json['next_page_token']
@@ -4903,10 +4898,9 @@ class ModelRegistryAPI:
4903
4898
 
4904
4899
  while True:
4905
4900
  json = self._api.do('GET', '/api/2.0/mlflow/registered-models/list', query=query, headers=headers)
4906
- if 'registered_models' not in json or not json['registered_models']:
4907
- return
4908
- for v in json['registered_models']:
4909
- yield Model.from_dict(v)
4901
+ if 'registered_models' in json:
4902
+ for v in json['registered_models']:
4903
+ yield Model.from_dict(v)
4910
4904
  if 'next_page_token' not in json or not json['next_page_token']:
4911
4905
  return
4912
4906
  query['page_token'] = json['next_page_token']
@@ -4963,10 +4957,9 @@ class ModelRegistryAPI:
4963
4957
 
4964
4958
  while True:
4965
4959
  json = self._api.do('GET', '/api/2.0/mlflow/registry-webhooks/list', query=query, headers=headers)
4966
- if 'webhooks' not in json or not json['webhooks']:
4967
- return
4968
- for v in json['webhooks']:
4969
- yield RegistryWebhook.from_dict(v)
4960
+ if 'webhooks' in json:
4961
+ for v in json['webhooks']:
4962
+ yield RegistryWebhook.from_dict(v)
4970
4963
  if 'next_page_token' not in json or not json['next_page_token']:
4971
4964
  return
4972
4965
  query['page_token'] = json['next_page_token']
@@ -5062,10 +5055,9 @@ class ModelRegistryAPI:
5062
5055
 
5063
5056
  while True:
5064
5057
  json = self._api.do('GET', '/api/2.0/mlflow/model-versions/search', query=query, headers=headers)
5065
- if 'model_versions' not in json or not json['model_versions']:
5066
- return
5067
- for v in json['model_versions']:
5068
- yield ModelVersion.from_dict(v)
5058
+ if 'model_versions' in json:
5059
+ for v in json['model_versions']:
5060
+ yield ModelVersion.from_dict(v)
5069
5061
  if 'next_page_token' not in json or not json['next_page_token']:
5070
5062
  return
5071
5063
  query['page_token'] = json['next_page_token']
@@ -5108,10 +5100,9 @@ class ModelRegistryAPI:
5108
5100
  '/api/2.0/mlflow/registered-models/search',
5109
5101
  query=query,
5110
5102
  headers=headers)
5111
- if 'registered_models' not in json or not json['registered_models']:
5112
- return
5113
- for v in json['registered_models']:
5114
- yield Model.from_dict(v)
5103
+ if 'registered_models' in json:
5104
+ for v in json['registered_models']:
5105
+ yield Model.from_dict(v)
5115
5106
  if 'next_page_token' not in json or not json['next_page_token']:
5116
5107
  return
5117
5108
  query['page_token'] = json['next_page_token']
@@ -629,10 +629,9 @@ class OAuthPublishedAppsAPI:
629
629
  f'/api/2.0/accounts/{self._api.account_id}/oauth2/published-apps/',
630
630
  query=query,
631
631
  headers=headers)
632
- if 'apps' not in json or not json['apps']:
633
- return
634
- for v in json['apps']:
635
- yield PublishedAppOutput.from_dict(v)
632
+ if 'apps' in json:
633
+ for v in json['apps']:
634
+ yield PublishedAppOutput.from_dict(v)
636
635
  if 'next_page_token' not in json or not json['next_page_token']:
637
636
  return
638
637
  query['page_token'] = json['next_page_token']
@@ -791,7 +791,7 @@ class PipelineCluster:
791
791
  apply_policy_default_values: Optional[bool] = None
792
792
  """Note: This field won't be persisted. Only API users will check this field."""
793
793
 
794
- autoscale: Optional[compute.AutoScale] = None
794
+ autoscale: Optional[PipelineClusterAutoscale] = None
795
795
  """Parameters needed in order to automatically scale clusters up and down based on load. Note:
796
796
  autoscaling works best with DB runtime versions 3.0 or later."""
797
797
 
@@ -914,7 +914,7 @@ class PipelineCluster:
914
914
  def from_dict(cls, d: Dict[str, any]) -> PipelineCluster:
915
915
  """Deserializes the PipelineCluster from a dictionary."""
916
916
  return cls(apply_policy_default_values=d.get('apply_policy_default_values', None),
917
- autoscale=_from_dict(d, 'autoscale', compute.AutoScale),
917
+ autoscale=_from_dict(d, 'autoscale', PipelineClusterAutoscale),
918
918
  aws_attributes=_from_dict(d, 'aws_attributes', compute.AwsAttributes),
919
919
  azure_attributes=_from_dict(d, 'azure_attributes', compute.AzureAttributes),
920
920
  cluster_log_conf=_from_dict(d, 'cluster_log_conf', compute.ClusterLogConf),
@@ -933,6 +933,48 @@ class PipelineCluster:
933
933
  ssh_public_keys=d.get('ssh_public_keys', None))
934
934
 
935
935
 
936
+ @dataclass
937
+ class PipelineClusterAutoscale:
938
+ min_workers: int
939
+ """The minimum number of workers the cluster can scale down to when underutilized. It is also the
940
+ initial number of workers the cluster will have after creation."""
941
+
942
+ max_workers: int
943
+ """The maximum number of workers to which the cluster can scale up when overloaded. `max_workers`
944
+ must be strictly greater than `min_workers`."""
945
+
946
+ mode: Optional[PipelineClusterAutoscaleMode] = None
947
+ """Databricks Enhanced Autoscaling optimizes cluster utilization by automatically allocating
948
+ cluster resources based on workload volume, with minimal impact to the data processing latency
949
+ of your pipelines. Enhanced Autoscaling is available for `updates` clusters only. The legacy
950
+ autoscaling feature is used for `maintenance` clusters."""
951
+
952
+ def as_dict(self) -> dict:
953
+ """Serializes the PipelineClusterAutoscale into a dictionary suitable for use as a JSON request body."""
954
+ body = {}
955
+ if self.max_workers is not None: body['max_workers'] = self.max_workers
956
+ if self.min_workers is not None: body['min_workers'] = self.min_workers
957
+ if self.mode is not None: body['mode'] = self.mode.value
958
+ return body
959
+
960
+ @classmethod
961
+ def from_dict(cls, d: Dict[str, any]) -> PipelineClusterAutoscale:
962
+ """Deserializes the PipelineClusterAutoscale from a dictionary."""
963
+ return cls(max_workers=d.get('max_workers', None),
964
+ min_workers=d.get('min_workers', None),
965
+ mode=_enum(d, 'mode', PipelineClusterAutoscaleMode))
966
+
967
+
968
+ class PipelineClusterAutoscaleMode(Enum):
969
+ """Databricks Enhanced Autoscaling optimizes cluster utilization by automatically allocating
970
+ cluster resources based on workload volume, with minimal impact to the data processing latency
971
+ of your pipelines. Enhanced Autoscaling is available for `updates` clusters only. The legacy
972
+ autoscaling feature is used for `maintenance` clusters."""
973
+
974
+ ENHANCED = 'ENHANCED'
975
+ LEGACY = 'LEGACY'
976
+
977
+
936
978
  @dataclass
937
979
  class PipelineEvent:
938
980
  error: Optional[ErrorDetail] = None
@@ -1891,10 +1933,9 @@ class PipelinesAPI:
1891
1933
  f'/api/2.0/pipelines/{pipeline_id}/events',
1892
1934
  query=query,
1893
1935
  headers=headers)
1894
- if 'events' not in json or not json['events']:
1895
- return
1896
- for v in json['events']:
1897
- yield PipelineEvent.from_dict(v)
1936
+ if 'events' in json:
1937
+ for v in json['events']:
1938
+ yield PipelineEvent.from_dict(v)
1898
1939
  if 'next_page_token' not in json or not json['next_page_token']:
1899
1940
  return
1900
1941
  query['page_token'] = json['next_page_token']
@@ -1940,10 +1981,9 @@ class PipelinesAPI:
1940
1981
 
1941
1982
  while True:
1942
1983
  json = self._api.do('GET', '/api/2.0/pipelines', query=query, headers=headers)
1943
- if 'statuses' not in json or not json['statuses']:
1944
- return
1945
- for v in json['statuses']:
1946
- yield PipelineStateInfo.from_dict(v)
1984
+ if 'statuses' in json:
1985
+ for v in json['statuses']:
1986
+ yield PipelineStateInfo.from_dict(v)
1947
1987
  if 'next_page_token' not in json or not json['next_page_token']:
1948
1988
  return
1949
1989
  query['page_token'] = json['next_page_token']
@@ -1978,25 +2018,6 @@ class PipelinesAPI:
1978
2018
  res = self._api.do('GET', f'/api/2.0/pipelines/{pipeline_id}/updates', query=query, headers=headers)
1979
2019
  return ListUpdatesResponse.from_dict(res)
1980
2020
 
1981
- def reset(self, pipeline_id: str) -> Wait[GetPipelineResponse]:
1982
- """Reset a pipeline.
1983
-
1984
- Resets a pipeline.
1985
-
1986
- :param pipeline_id: str
1987
-
1988
- :returns:
1989
- Long-running operation waiter for :class:`GetPipelineResponse`.
1990
- See :method:wait_get_pipeline_running for more details.
1991
- """
1992
-
1993
- headers = {'Accept': 'application/json', }
1994
- self._api.do('POST', f'/api/2.0/pipelines/{pipeline_id}/reset', headers=headers)
1995
- return Wait(self.wait_get_pipeline_running, pipeline_id=pipeline_id)
1996
-
1997
- def reset_and_wait(self, pipeline_id: str, timeout=timedelta(minutes=20)) -> GetPipelineResponse:
1998
- return self.reset(pipeline_id=pipeline_id).result(timeout=timeout)
1999
-
2000
2021
  def set_permissions(
2001
2022
  self,
2002
2023
  pipeline_id: str,