ygg 0.1.56__py3-none-any.whl → 0.1.60__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.
Files changed (42) hide show
  1. {ygg-0.1.56.dist-info → ygg-0.1.60.dist-info}/METADATA +1 -1
  2. ygg-0.1.60.dist-info/RECORD +74 -0
  3. {ygg-0.1.56.dist-info → ygg-0.1.60.dist-info}/WHEEL +1 -1
  4. yggdrasil/ai/__init__.py +2 -0
  5. yggdrasil/ai/session.py +89 -0
  6. yggdrasil/ai/sql_session.py +310 -0
  7. yggdrasil/databricks/__init__.py +0 -3
  8. yggdrasil/databricks/compute/cluster.py +68 -113
  9. yggdrasil/databricks/compute/command_execution.py +674 -0
  10. yggdrasil/databricks/compute/exceptions.py +7 -2
  11. yggdrasil/databricks/compute/execution_context.py +465 -277
  12. yggdrasil/databricks/compute/remote.py +4 -14
  13. yggdrasil/databricks/exceptions.py +10 -0
  14. yggdrasil/databricks/sql/__init__.py +0 -4
  15. yggdrasil/databricks/sql/engine.py +161 -173
  16. yggdrasil/databricks/sql/exceptions.py +9 -1
  17. yggdrasil/databricks/sql/statement_result.py +108 -120
  18. yggdrasil/databricks/sql/warehouse.py +331 -92
  19. yggdrasil/databricks/workspaces/io.py +92 -9
  20. yggdrasil/databricks/workspaces/path.py +120 -74
  21. yggdrasil/databricks/workspaces/workspace.py +212 -68
  22. yggdrasil/libs/databrickslib.py +23 -18
  23. yggdrasil/libs/extensions/spark_extensions.py +1 -1
  24. yggdrasil/libs/pandaslib.py +15 -6
  25. yggdrasil/libs/polarslib.py +49 -13
  26. yggdrasil/pyutils/__init__.py +1 -0
  27. yggdrasil/pyutils/callable_serde.py +12 -19
  28. yggdrasil/pyutils/exceptions.py +16 -0
  29. yggdrasil/pyutils/mimetypes.py +0 -0
  30. yggdrasil/pyutils/python_env.py +13 -12
  31. yggdrasil/pyutils/waiting_config.py +171 -0
  32. yggdrasil/types/cast/arrow_cast.py +3 -0
  33. yggdrasil/types/cast/pandas_cast.py +157 -169
  34. yggdrasil/types/cast/polars_cast.py +11 -43
  35. yggdrasil/types/dummy_class.py +81 -0
  36. yggdrasil/version.py +1 -1
  37. ygg-0.1.56.dist-info/RECORD +0 -68
  38. yggdrasil/databricks/ai/__init__.py +0 -1
  39. yggdrasil/databricks/ai/loki.py +0 -374
  40. {ygg-0.1.56.dist-info → ygg-0.1.60.dist-info}/entry_points.txt +0 -0
  41. {ygg-0.1.56.dist-info → ygg-0.1.60.dist-info}/licenses/LICENSE +0 -0
  42. {ygg-0.1.56.dist-info → ygg-0.1.60.dist-info}/top_level.txt +0 -0
@@ -24,10 +24,11 @@ from .execution_context import ExecutionContext
24
24
  from ..workspaces.workspace import WorkspaceService, Workspace
25
25
  from ...libs.databrickslib import databricks_sdk
26
26
  from ...pyutils.callable_serde import CallableSerde
27
- from ...pyutils.equality import dicts_equal, dict_diff
27
+ from ...pyutils.equality import dicts_equal
28
28
  from ...pyutils.expiring_dict import ExpiringDict
29
29
  from ...pyutils.modules import PipIndexSettings
30
30
  from ...pyutils.python_env import PythonEnv
31
+ from ...pyutils.waiting_config import WaitingConfig, WaitingConfigArg
31
32
 
32
33
  if databricks_sdk is None: # pragma: no cover - import guard
33
34
  ResourceDoesNotExist = Exception # type: ignore
@@ -115,6 +116,21 @@ class Cluster(WorkspaceService):
115
116
  # host → Cluster instance
116
117
  _env_clusters: ClassVar[Dict[str, "Cluster"]] = {}
117
118
 
119
+ def __repr__(self):
120
+ return "%s(url=%s)" % (
121
+ self.__class__.__name__,
122
+ self.url()
123
+ )
124
+
125
+ def __str__(self):
126
+ return self.url()
127
+
128
+ def url(self) -> str:
129
+ return "%s/compute/clusters/%s" % (
130
+ self.workspace.safe_host,
131
+ self.cluster_id or "unknown"
132
+ )
133
+
118
134
  @property
119
135
  def id(self):
120
136
  """Return the current cluster id."""
@@ -144,7 +160,7 @@ class Cluster(WorkspaceService):
144
160
  single_user_name: Optional[str] = None,
145
161
  runtime_engine: Optional["RuntimeEngine"] = None,
146
162
  libraries: Optional[list[str]] = None,
147
- update_timeout: Optional[Union[float, dt.timedelta]] = dt.timedelta(minutes=20),
163
+ wait_update: Optional[WaitingConfigArg] = True,
148
164
  **kwargs
149
165
  ) -> "Cluster":
150
166
  """Create or reuse a cluster that mirrors the current Python environment.
@@ -156,7 +172,7 @@ class Cluster(WorkspaceService):
156
172
  single_user_name: Optional username for single-user clusters.
157
173
  runtime_engine: Optional Databricks runtime engine.
158
174
  libraries: Optional list of libraries to install.
159
- update_timeout: wait timeout, if None it will not wait completion
175
+ wait_update: wait timeout, if None it will not wait completion
160
176
  **kwargs: Additional cluster specification overrides.
161
177
 
162
178
  Returns:
@@ -178,7 +194,7 @@ class Cluster(WorkspaceService):
178
194
  single_user_name=single_user_name,
179
195
  runtime_engine=runtime_engine,
180
196
  libraries=libraries,
181
- update_timeout=update_timeout,
197
+ wait_update=wait_update,
182
198
  **kwargs
183
199
  )
184
200
  )
@@ -193,7 +209,7 @@ class Cluster(WorkspaceService):
193
209
  single_user_name: Optional[str] = "current",
194
210
  runtime_engine: Optional["RuntimeEngine"] = None,
195
211
  libraries: Optional[list[str]] = None,
196
- update_timeout: Optional[Union[float, dt.timedelta]] = dt.timedelta(minutes=20),
212
+ wait_update: Optional[WaitingConfigArg] = True,
197
213
  **kwargs
198
214
  ) -> "Cluster":
199
215
  """Create/update a cluster to match the local Python environment.
@@ -205,7 +221,7 @@ class Cluster(WorkspaceService):
205
221
  single_user_name: Optional single username for the cluster.
206
222
  runtime_engine: Optional runtime engine selection.
207
223
  libraries: Optional list of libraries to install.
208
- update_timeout: wait timeout, if None it will not wait completion
224
+ wait_update: wait timeout, if None it will not wait completion
209
225
  **kwargs: Additional cluster specification overrides.
210
226
 
211
227
  Returns:
@@ -215,12 +231,6 @@ class Cluster(WorkspaceService):
215
231
  source = PythonEnv.get_current()
216
232
 
217
233
  libraries = list(libraries) if libraries is not None else []
218
- libraries.extend([
219
- _ for _ in [
220
- "ygg",
221
- "uv",
222
- ] if _ not in libraries
223
- ])
224
234
 
225
235
  python_version = source.version_info
226
236
 
@@ -247,51 +257,12 @@ class Cluster(WorkspaceService):
247
257
  single_user_name=single_user_name,
248
258
  runtime_engine=runtime_engine or RuntimeEngine.PHOTON,
249
259
  libraries=libraries,
250
- update_timeout=update_timeout,
260
+ wait_update=wait_update,
251
261
  **kwargs
252
262
  )
253
263
 
254
264
  return inst
255
265
 
256
- def pull_python_environment(
257
- self,
258
- name: Optional[str] = None,
259
- target: PythonEnv | str | None = None,
260
- ):
261
- """Update or create a local PythonEnv based on remote metadata.
262
-
263
- Args:
264
- name: Optional name for the local PythonEnv.
265
- target: Existing PythonEnv or name to update.
266
-
267
- Returns:
268
- The updated PythonEnv instance.
269
- """
270
- m = self.system_context.remote_metadata
271
- version_info = m.version_info
272
-
273
- python_version = ".".join(str(_) for _ in version_info)
274
-
275
- if target is None:
276
- target = PythonEnv.create(
277
- name=name or self.name,
278
- python=python_version
279
- )
280
- elif isinstance(target, str):
281
- if target.casefold() == "current":
282
- target = PythonEnv.get_current()
283
- else:
284
- target = PythonEnv.create(
285
- name=target,
286
- python=python_version
287
- )
288
-
289
- target.update(
290
- python=python_version,
291
- )
292
-
293
- return target
294
-
295
266
  @property
296
267
  def details(self):
297
268
  """Return cached cluster details, refreshing when needed."""
@@ -365,44 +336,32 @@ class Cluster(WorkspaceService):
365
336
 
366
337
  def wait_for_status(
367
338
  self,
368
- tick: float = 0.5,
369
- timeout: Union[float, dt.timedelta] = 600,
370
- backoff: int = 2,
371
- max_sleep_time: float = 15,
372
- wait_libraries: bool = True
339
+ wait: Optional[WaitingConfigArg] = True,
340
+ raise_error: bool = True
373
341
  ):
374
342
  """Wait for the cluster to exit pending states.
375
343
 
376
344
  Args:
377
- tick: Initial sleep interval in seconds.
378
- timeout: Max seconds to wait before timing out.
379
- backoff: Backoff multiplier for the sleep interval.
380
- max_sleep_time: Maximum sleep interval in seconds.
381
- wait_libraries: Wait libraries to install fully
345
+ wait: Waiting config
346
+ raise_error: Raise error if failed
382
347
 
383
348
  Returns:
384
349
  The current Cluster instance.
385
350
  """
386
- start = time.time()
387
- sleep_time = tick
388
-
389
- if not timeout:
390
- timeout = 20 * 60.0
391
- elif isinstance(timeout, dt.timedelta):
392
- timeout = timeout.total_seconds()
393
-
394
- while self.is_pending:
395
- time.sleep(sleep_time)
351
+ wait = WaitingConfig.check_arg(wait)
352
+ iteration, start = 0, time.time()
396
353
 
397
- if time.time() - start > timeout:
398
- raise TimeoutError("Waiting state for %s timed out")
354
+ if wait.timeout:
355
+ while self.is_pending:
356
+ wait.sleep(iteration=iteration, start=start)
357
+ iteration += 1
399
358
 
400
- sleep_time = min(max_sleep_time, sleep_time * backoff)
401
-
402
- if wait_libraries:
403
- self.wait_installed_libraries()
359
+ self.wait_installed_libraries(
360
+ raise_error=raise_error
361
+ )
404
362
 
405
- self.raise_for_status()
363
+ if raise_error:
364
+ self.raise_for_status()
406
365
 
407
366
  return self
408
367
 
@@ -451,6 +410,16 @@ class Cluster(WorkspaceService):
451
410
  """Return the Databricks clusters API client."""
452
411
  return self.workspace.sdk().clusters
453
412
 
413
+ def shared_cache_path(
414
+ self,
415
+ suffix: str
416
+ ):
417
+ assert suffix, "Missing suffix arg"
418
+
419
+ return self.workspace.shared_cache_path(
420
+ suffix="/cluster/%s" % suffix.lstrip("/")
421
+ )
422
+
454
423
  def spark_versions(
455
424
  self,
456
425
  photon: Optional[bool] = None,
@@ -598,7 +567,7 @@ class Cluster(WorkspaceService):
598
567
  cluster_id: Optional[str] = None,
599
568
  cluster_name: Optional[str] = None,
600
569
  libraries: Optional[List[Union[str, "Library"]]] = None,
601
- update_timeout: Optional[Union[float, dt.timedelta]] = dt.timedelta(minutes=20),
570
+ wait_update: Optional[WaitingConfigArg] = True,
602
571
  **cluster_spec: Any
603
572
  ):
604
573
  """Create a new cluster or update an existing one.
@@ -607,7 +576,7 @@ class Cluster(WorkspaceService):
607
576
  cluster_id: Optional cluster id to update.
608
577
  cluster_name: Optional cluster name to update or create.
609
578
  libraries: Optional libraries to install.
610
- update_timeout: wait timeout, if None it will not wait completion
579
+ wait_update: wait timeout, if None it will not wait completion
611
580
  **cluster_spec: Cluster specification overrides.
612
581
 
613
582
  Returns:
@@ -623,28 +592,28 @@ class Cluster(WorkspaceService):
623
592
  return found.update(
624
593
  cluster_name=cluster_name,
625
594
  libraries=libraries,
626
- wait_timeout=update_timeout,
595
+ wait=wait_update,
627
596
  **cluster_spec
628
597
  )
629
598
 
630
599
  return self.create(
631
600
  cluster_name=cluster_name,
632
601
  libraries=libraries,
633
- wait_timeout=update_timeout,
602
+ wait=wait_update,
634
603
  **cluster_spec
635
604
  )
636
605
 
637
606
  def create(
638
607
  self,
639
608
  libraries: Optional[List[Union[str, "Library"]]] = None,
640
- wait_timeout: Union[float, dt.timedelta] = dt.timedelta(minutes=20),
609
+ wait: Union[WaitingConfigArg] = True,
641
610
  **cluster_spec: Any
642
611
  ) -> str:
643
612
  """Create a new cluster and optionally install libraries.
644
613
 
645
614
  Args:
646
615
  libraries: Optional list of libraries to install after creation.
647
- wait_timeout: wait timeout, if None it will not wait completion
616
+ wait: wait timeout, if None it will not wait completion
648
617
  **cluster_spec: Cluster specification overrides.
649
618
 
650
619
  Returns:
@@ -673,8 +642,7 @@ class Cluster(WorkspaceService):
673
642
 
674
643
  self.install_libraries(libraries=libraries, raise_error=False, wait_timeout=None)
675
644
 
676
- if wait_timeout:
677
- self.wait_for_status(timeout=wait_timeout)
645
+ self.wait_for_status(wait=wait)
678
646
 
679
647
  return self
680
648
 
@@ -682,7 +650,7 @@ class Cluster(WorkspaceService):
682
650
  self,
683
651
  libraries: Optional[List[Union[str, "Library"]]] = None,
684
652
  access_control_list: Optional[List["ClusterAccessControlRequest"]] = None,
685
- wait_timeout: Optional[Union[float, dt.timedelta]] = dt.timedelta(minutes=20),
653
+ wait: Optional[WaitingConfigArg] = True,
686
654
  **cluster_spec: Any
687
655
  ) -> "Cluster":
688
656
  """Update cluster configuration and optionally install libraries.
@@ -690,7 +658,7 @@ class Cluster(WorkspaceService):
690
658
  Args:
691
659
  libraries: Optional libraries to install.
692
660
  access_control_list: List of permissions
693
- wait_timeout: waiting timeout until done, if None it does not wait
661
+ wait: waiting timeout until done, if None it does not wait
694
662
  **cluster_spec: Cluster specification overrides.
695
663
 
696
664
  Returns:
@@ -714,22 +682,15 @@ class Cluster(WorkspaceService):
714
682
  existing_details,
715
683
  update_details,
716
684
  keys=_EDIT_ARG_NAMES,
717
- treat_missing_as_none=True,
718
- float_tol=0.0, # set e.g. 1e-6 if you have float-y stuff
719
685
  )
720
686
 
721
687
  if not same:
722
- diff = {
723
- k: v[1]
724
- for k, v in dict_diff(existing_details, update_details, keys=_EDIT_ARG_NAMES).items()
725
- }
726
-
727
688
  LOGGER.debug(
728
689
  "Updating %s with %s",
729
- self, diff
690
+ self, update_details
730
691
  )
731
692
 
732
- self.wait_for_status(timeout=wait_timeout)
693
+ self.wait_for_status(wait=wait)
733
694
  self.clusters_client().edit(**update_details)
734
695
  self.update_permissions(access_control_list=access_control_list)
735
696
 
@@ -738,8 +699,7 @@ class Cluster(WorkspaceService):
738
699
  self
739
700
  )
740
701
 
741
- if wait_timeout:
742
- self.wait_for_status(timeout=wait_timeout)
702
+ self.wait_for_status(wait=wait)
743
703
 
744
704
  return self
745
705
 
@@ -866,18 +826,18 @@ class Cluster(WorkspaceService):
866
826
 
867
827
  def ensure_running(
868
828
  self,
869
- wait_timeout: Optional[dt.timedelta] = dt.timedelta(minutes=20)
829
+ wait: Optional[WaitingConfigArg] = True
870
830
  ) -> "Cluster":
871
831
  """Ensure the cluster is running.
872
832
 
873
833
  Returns:
874
834
  The current Cluster instance.
875
835
  """
876
- return self.start(wait_timeout=wait_timeout)
836
+ return self.start(wait=wait)
877
837
 
878
838
  def start(
879
839
  self,
880
- wait_timeout: Optional[dt.timedelta] = dt.timedelta(minutes=20)
840
+ wait: Optional[WaitingConfigArg] = True
881
841
  ) -> "Cluster":
882
842
  """Start the cluster if it is not already running.
883
843
 
@@ -887,7 +847,9 @@ class Cluster(WorkspaceService):
887
847
  if self.is_running:
888
848
  return self
889
849
 
890
- self.wait_for_status()
850
+ wait = WaitingConfig.check_arg(wait)
851
+
852
+ self.wait_for_status(wait=wait)
891
853
 
892
854
  if self.is_running:
893
855
  return self
@@ -898,8 +860,7 @@ class Cluster(WorkspaceService):
898
860
 
899
861
  LOGGER.info("Started %s", self)
900
862
 
901
- if wait_timeout:
902
- self.wait_for_status(timeout=wait_timeout.total_seconds())
863
+ self.wait_for_status(wait=wait)
903
864
 
904
865
  return self
905
866
 
@@ -961,7 +922,6 @@ class Cluster(WorkspaceService):
961
922
  kwargs: Dict[str, Any] = None,
962
923
  env_keys: Optional[List[str]] = None,
963
924
  timeout: Optional[dt.timedelta] = None,
964
- result_tag: Optional[str] = None,
965
925
  context: Optional[ExecutionContext] = None,
966
926
  ):
967
927
  """Execute a command or callable on the cluster.
@@ -973,7 +933,6 @@ class Cluster(WorkspaceService):
973
933
  kwargs: Optional keyword arguments for the callable.
974
934
  env_keys: Optional environment variable names to pass.
975
935
  timeout: Optional timeout for execution.
976
- result_tag: Optional result tag for parsing output.
977
936
  context: ExecutionContext to run or create new one
978
937
 
979
938
  Returns:
@@ -987,7 +946,6 @@ class Cluster(WorkspaceService):
987
946
  kwargs=kwargs,
988
947
  env_keys=env_keys,
989
948
  timeout=timeout,
990
- result_tag=result_tag
991
949
  )
992
950
 
993
951
  # ------------------------------------------------------------------
@@ -1066,8 +1024,6 @@ class Cluster(WorkspaceService):
1066
1024
  env_keys=env_keys,
1067
1025
  env_variables=env_variables,
1068
1026
  timeout=timeout,
1069
- result_tag=result_tag,
1070
- **options
1071
1027
  )
1072
1028
 
1073
1029
  return wrapper
@@ -1084,7 +1040,6 @@ class Cluster(WorkspaceService):
1084
1040
  wait_timeout: Optional[dt.timedelta] = dt.timedelta(minutes=20),
1085
1041
  pip_settings: Optional[PipIndexSettings] = None,
1086
1042
  raise_error: bool = True,
1087
- restart: bool = True,
1088
1043
  ) -> "Cluster":
1089
1044
  """Install libraries on the cluster and optionally wait for completion.
1090
1045
 
@@ -1093,7 +1048,6 @@ class Cluster(WorkspaceService):
1093
1048
  wait_timeout: Optional timeout for installation.
1094
1049
  pip_settings: Optional pip index settings.
1095
1050
  raise_error: Whether to raise on install failure.
1096
- restart: Whether to restart the cluster after installation.
1097
1051
 
1098
1052
  Returns:
1099
1053
  The current Cluster instance.
@@ -1309,6 +1263,7 @@ class Cluster(WorkspaceService):
1309
1263
  value.startswith("datamanagement")
1310
1264
  or value.startswith("TSSecrets")
1311
1265
  or value.startswith("tgp_")
1266
+ or value.startswith("wma-data")
1312
1267
  ):
1313
1268
  repo = pip_settings.extra_index_url
1314
1269