ygg 0.1.44__py3-none-any.whl → 0.1.46__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.
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/METADATA +1 -1
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/RECORD +14 -13
- yggdrasil/databricks/compute/cluster.py +20 -16
- yggdrasil/databricks/compute/execution_context.py +46 -64
- yggdrasil/databricks/sql/engine.py +5 -2
- yggdrasil/databricks/sql/warehouse.py +355 -0
- yggdrasil/databricks/workspaces/workspace.py +19 -9
- yggdrasil/pyutils/callable_serde.py +296 -308
- yggdrasil/pyutils/expiring_dict.py +114 -25
- yggdrasil/version.py +1 -1
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/WHEEL +0 -0
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/entry_points.txt +0 -0
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/licenses/LICENSE +0 -0
- {ygg-0.1.44.dist-info → ygg-0.1.46.dist-info}/top_level.txt +0 -0
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
ygg-0.1.
|
|
1
|
+
ygg-0.1.46.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
2
2
|
yggdrasil/__init__.py,sha256=PfH7Xwt6uue6oqe6S5V8NhDJcVQClkKrBE1KXhdelZc,117
|
|
3
|
-
yggdrasil/version.py,sha256=
|
|
3
|
+
yggdrasil/version.py,sha256=Jfjdb6r4PUMCYLyBcF_gpjjUzE1bkfL7XTvU1GuAtdA,22
|
|
4
4
|
yggdrasil/databricks/__init__.py,sha256=skctY2c8W-hI81upx9F_PWRe5ishL3hrdiTuizgDjdw,152
|
|
5
5
|
yggdrasil/databricks/compute/__init__.py,sha256=NvdzmaJSNYY1uJthv1hHdBuNu3bD_-Z65DWnaJt9yXg,289
|
|
6
|
-
yggdrasil/databricks/compute/cluster.py,sha256=
|
|
7
|
-
yggdrasil/databricks/compute/execution_context.py,sha256=
|
|
6
|
+
yggdrasil/databricks/compute/cluster.py,sha256=HI9811oBCpWeo4V921FVAlRUXKXM4XO7HS9DQVOuzpM,41340
|
|
7
|
+
yggdrasil/databricks/compute/execution_context.py,sha256=SMFjgWqp9CEgruJszxTAmqg3iSREb7OG0nYTUn2H3ec,21946
|
|
8
8
|
yggdrasil/databricks/compute/remote.py,sha256=nEN_Fr1Ouul_iKOf4B5QjEGscYAcl7nHjGsl2toRzrU,2874
|
|
9
9
|
yggdrasil/databricks/jobs/__init__.py,sha256=snxGSJb0M5I39v0y3IR-uEeSlZR248cQ_4DJ1sYs-h8,154
|
|
10
10
|
yggdrasil/databricks/jobs/config.py,sha256=9LGeHD04hbfy0xt8_6oobC4moKJh4_DTjZiK4Q2Tqjk,11557
|
|
11
11
|
yggdrasil/databricks/sql/__init__.py,sha256=y1n5yg-drZ8QVZbEgznsRG24kdJSnFis9l2YfYCsaCM,234
|
|
12
|
-
yggdrasil/databricks/sql/engine.py,sha256=
|
|
12
|
+
yggdrasil/databricks/sql/engine.py,sha256=K5WmGKpXU78JA3UdK8dLxBD_GXKidZJFe7hytuC5UHg,41029
|
|
13
13
|
yggdrasil/databricks/sql/exceptions.py,sha256=Jqd_gT_VyPL8klJEHYEzpv5eHtmdY43WiQ7HZBaEqSk,53
|
|
14
14
|
yggdrasil/databricks/sql/statement_result.py,sha256=_mBolHae0AASfe1Tlld1KTXs-K4-oy9dniHDyR2ILYc,16736
|
|
15
15
|
yggdrasil/databricks/sql/types.py,sha256=5G-BM9_eOsRKEMzeDTWUsWW5g4Idvs-czVCpOCrMhdA,6412
|
|
16
|
+
yggdrasil/databricks/sql/warehouse.py,sha256=1J0dyQLJb-OS1_1xU1eAVZ4CoL2-FhFeowKSvU3RzFc,9773
|
|
16
17
|
yggdrasil/databricks/workspaces/__init__.py,sha256=Ti1I99JTC3koYJaCy8WYvkAox4KdcuMRk8b2rHroWCY,133
|
|
17
18
|
yggdrasil/databricks/workspaces/filesytem.py,sha256=Z8JXU7_XUEbw9fpTQT1avRQKi-IAP2KemXBMPkUoY4w,9805
|
|
18
19
|
yggdrasil/databricks/workspaces/io.py,sha256=Tdde4LaGNJNT50R11OkEYZyNacyIW9QrOXMAicAlIr4,32208
|
|
19
20
|
yggdrasil/databricks/workspaces/path.py,sha256=-XnCD9p42who3DAwnITVE1KyrZUSoXDKHA8iZi-7wk4,47743
|
|
20
21
|
yggdrasil/databricks/workspaces/path_kind.py,sha256=Xc319NysH8_6E9C0Q8nCxDHYG07_SnzyUVKHe0dNdDQ,305
|
|
21
|
-
yggdrasil/databricks/workspaces/workspace.py,sha256=
|
|
22
|
+
yggdrasil/databricks/workspaces/workspace.py,sha256=c6CBBun2BskEnsP74pbLVOe_TKXZs4L4r4gPQtIzlQE,23821
|
|
22
23
|
yggdrasil/dataclasses/__init__.py,sha256=6SdfIyTsoM4AuVw5TW4Q-UWXz41EyfsMcpD30cmjbSM,125
|
|
23
24
|
yggdrasil/dataclasses/dataclass.py,sha256=fKokFUnqe4CmXXGMTdF4XDWbCUl_c_-se-UD48L5s1E,6594
|
|
24
25
|
yggdrasil/libs/__init__.py,sha256=ulzk-ZkFUI2Pfo93YKtO8MBsEWtRZzLos7HAxN74R0w,168
|
|
@@ -30,10 +31,10 @@ yggdrasil/libs/extensions/__init__.py,sha256=mcXW5Li3Cbprbs4Ci-b5A0Ju0wmLcfvEiFu
|
|
|
30
31
|
yggdrasil/libs/extensions/polars_extensions.py,sha256=RTkGi8llhPJjX7x9egix7-yXWo2X24zIAPSKXV37SSA,12397
|
|
31
32
|
yggdrasil/libs/extensions/spark_extensions.py,sha256=E64n-3SFTDgMuXwWitX6vOYP9ln2lpGKb0htoBLEZgc,16745
|
|
32
33
|
yggdrasil/pyutils/__init__.py,sha256=tl-LapAc71TV7RMgf2ftKwrzr8iiLOGHeJgA3RvO93w,293
|
|
33
|
-
yggdrasil/pyutils/callable_serde.py,sha256=
|
|
34
|
+
yggdrasil/pyutils/callable_serde.py,sha256=UNxLfhkE1xunbGdos4BF7TOMZbBxDRNMlj8lkZx3xq0,23197
|
|
34
35
|
yggdrasil/pyutils/equality.py,sha256=Xyf8D1dLUCm3spDEir8Zyj7O4US_fBJwEylJCfJ9slI,3080
|
|
35
36
|
yggdrasil/pyutils/exceptions.py,sha256=ssKNm-rjhavHUOZmGA7_1Gq9tSHDrb2EFI-cnBuWgng,3388
|
|
36
|
-
yggdrasil/pyutils/expiring_dict.py,sha256=
|
|
37
|
+
yggdrasil/pyutils/expiring_dict.py,sha256=pr2u25LGwPVbLfsLptiHGovUtYRRo0AMjaJtCtJl7nQ,8477
|
|
37
38
|
yggdrasil/pyutils/modules.py,sha256=B7IP99YqUMW6-DIESFzBx8-09V1d0a8qrIJUDFhhL2g,11424
|
|
38
39
|
yggdrasil/pyutils/parallel.py,sha256=ubuq2m9dJzWYUyKCga4Y_9bpaeMYUrleYxdp49CHr44,6781
|
|
39
40
|
yggdrasil/pyutils/python_env.py,sha256=tuglnjdqHQjNh18qDladVoSEOjCD0RcnMEPYJ0tArOs,50985
|
|
@@ -55,8 +56,8 @@ yggdrasil/types/cast/registry.py,sha256=_zdFGmUBB7P-e_LIcJlOxMcxAkXoA-UXB6HqLMgT
|
|
|
55
56
|
yggdrasil/types/cast/spark_cast.py,sha256=_KAsl1DqmKMSfWxqhVE7gosjYdgiL1C5bDQv6eP3HtA,24926
|
|
56
57
|
yggdrasil/types/cast/spark_pandas_cast.py,sha256=BuTiWrdCANZCdD_p2MAytqm74eq-rdRXd-LGojBRrfU,5023
|
|
57
58
|
yggdrasil/types/cast/spark_polars_cast.py,sha256=btmZNHXn2NSt3fUuB4xg7coaE0RezIBdZD92H8NK0Jw,9073
|
|
58
|
-
ygg-0.1.
|
|
59
|
-
ygg-0.1.
|
|
60
|
-
ygg-0.1.
|
|
61
|
-
ygg-0.1.
|
|
62
|
-
ygg-0.1.
|
|
59
|
+
ygg-0.1.46.dist-info/METADATA,sha256=V8VQXfyZROSBPEU21Rv-xykPqQkg3hhvG4k2OdP8gNc,19204
|
|
60
|
+
ygg-0.1.46.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
61
|
+
ygg-0.1.46.dist-info/entry_points.txt,sha256=6q-vpWG3kvw2dhctQ0LALdatoeefkN855Ev02I1dKGY,70
|
|
62
|
+
ygg-0.1.46.dist-info/top_level.txt,sha256=iBe9Kk4VIVbLpgv_p8OZUIfxgj4dgJ5wBg6vO3rigso,10
|
|
63
|
+
ygg-0.1.46.dist-info/RECORD,,
|
|
@@ -22,7 +22,7 @@ from typing import Any, Iterator, Optional, Union, List, Callable, Dict, ClassVa
|
|
|
22
22
|
|
|
23
23
|
from .execution_context import ExecutionContext
|
|
24
24
|
from ..workspaces.workspace import WorkspaceService, Workspace
|
|
25
|
-
from ... import
|
|
25
|
+
from ... import CallableSerde
|
|
26
26
|
from ...libs.databrickslib import databricks_sdk
|
|
27
27
|
from ...pyutils.equality import dicts_equal, dict_diff
|
|
28
28
|
from ...pyutils.expiring_dict import ExpiringDict
|
|
@@ -47,6 +47,7 @@ else: # pragma: no cover - runtime fallback when SDK is missing
|
|
|
47
47
|
__all__ = ["Cluster"]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
LOGGER = logging.getLogger(__name__)
|
|
50
51
|
NAME_ID_CACHE: dict[str, ExpiringDict] = {}
|
|
51
52
|
|
|
52
53
|
|
|
@@ -72,9 +73,6 @@ def get_cached_cluster_id(
|
|
|
72
73
|
return existing.get(cluster_name) if existing else None
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
logger = logging.getLogger(__name__)
|
|
76
|
-
|
|
77
|
-
|
|
78
76
|
# module-level mapping Databricks Runtime -> (major, minor) Python version
|
|
79
77
|
_PYTHON_BY_DBR: dict[str, tuple[int, int]] = {
|
|
80
78
|
"10.4": (3, 8),
|
|
@@ -363,7 +361,8 @@ class Cluster(WorkspaceService):
|
|
|
363
361
|
tick: float = 0.5,
|
|
364
362
|
timeout: Union[float, dt.timedelta] = 600,
|
|
365
363
|
backoff: int = 2,
|
|
366
|
-
max_sleep_time: float = 15
|
|
364
|
+
max_sleep_time: float = 15,
|
|
365
|
+
wait_libraries: bool = True
|
|
367
366
|
):
|
|
368
367
|
"""Wait for the cluster to exit pending states.
|
|
369
368
|
|
|
@@ -372,6 +371,7 @@ class Cluster(WorkspaceService):
|
|
|
372
371
|
timeout: Max seconds to wait before timing out.
|
|
373
372
|
backoff: Backoff multiplier for the sleep interval.
|
|
374
373
|
max_sleep_time: Maximum sleep interval in seconds.
|
|
374
|
+
wait_libraries: Wait libraries to install fully
|
|
375
375
|
|
|
376
376
|
Returns:
|
|
377
377
|
The current Cluster instance.
|
|
@@ -390,7 +390,8 @@ class Cluster(WorkspaceService):
|
|
|
390
390
|
|
|
391
391
|
sleep_time = min(max_sleep_time, sleep_time * backoff)
|
|
392
392
|
|
|
393
|
-
|
|
393
|
+
if wait_libraries:
|
|
394
|
+
self.wait_installed_libraries()
|
|
394
395
|
|
|
395
396
|
self.raise_for_status()
|
|
396
397
|
|
|
@@ -638,7 +639,7 @@ class Cluster(WorkspaceService):
|
|
|
638
639
|
if k in _CREATE_ARG_NAMES
|
|
639
640
|
}
|
|
640
641
|
|
|
641
|
-
|
|
642
|
+
LOGGER.debug(
|
|
642
643
|
"Creating Databricks cluster %s with %s",
|
|
643
644
|
update_details["cluster_name"],
|
|
644
645
|
update_details,
|
|
@@ -646,7 +647,7 @@ class Cluster(WorkspaceService):
|
|
|
646
647
|
|
|
647
648
|
self.details = self.clusters_client().create_and_wait(**update_details)
|
|
648
649
|
|
|
649
|
-
|
|
650
|
+
LOGGER.info(
|
|
650
651
|
"Created %s",
|
|
651
652
|
self
|
|
652
653
|
)
|
|
@@ -699,7 +700,7 @@ class Cluster(WorkspaceService):
|
|
|
699
700
|
for k, v in dict_diff(existing_details, update_details, keys=_EDIT_ARG_NAMES).items()
|
|
700
701
|
}
|
|
701
702
|
|
|
702
|
-
|
|
703
|
+
LOGGER.debug(
|
|
703
704
|
"Updating %s with %s",
|
|
704
705
|
self, diff
|
|
705
706
|
)
|
|
@@ -707,7 +708,7 @@ class Cluster(WorkspaceService):
|
|
|
707
708
|
self.wait_for_status()
|
|
708
709
|
self.clusters_client().edit(**update_details)
|
|
709
710
|
|
|
710
|
-
|
|
711
|
+
LOGGER.info(
|
|
711
712
|
"Updated %s",
|
|
712
713
|
self
|
|
713
714
|
)
|
|
@@ -811,7 +812,7 @@ class Cluster(WorkspaceService):
|
|
|
811
812
|
self.wait_for_status()
|
|
812
813
|
|
|
813
814
|
if not self.is_running:
|
|
814
|
-
|
|
815
|
+
LOGGER.debug("Starting %s", self)
|
|
815
816
|
|
|
816
817
|
if wait_timeout:
|
|
817
818
|
self.clusters_client().start(cluster_id=self.cluster_id)
|
|
@@ -819,6 +820,8 @@ class Cluster(WorkspaceService):
|
|
|
819
820
|
else:
|
|
820
821
|
self.clusters_client().start(cluster_id=self.cluster_id)
|
|
821
822
|
|
|
823
|
+
LOGGER.info("Started %s", self)
|
|
824
|
+
|
|
822
825
|
return self
|
|
823
826
|
|
|
824
827
|
def restart(
|
|
@@ -832,7 +835,6 @@ class Cluster(WorkspaceService):
|
|
|
832
835
|
self.wait_for_status()
|
|
833
836
|
|
|
834
837
|
if self.is_running:
|
|
835
|
-
logger.info("Restarting %s", self)
|
|
836
838
|
self.details = self.clusters_client().restart_and_wait(cluster_id=self.cluster_id)
|
|
837
839
|
return self.wait_for_status()
|
|
838
840
|
|
|
@@ -846,8 +848,10 @@ class Cluster(WorkspaceService):
|
|
|
846
848
|
Returns:
|
|
847
849
|
The SDK delete response.
|
|
848
850
|
"""
|
|
849
|
-
|
|
850
|
-
|
|
851
|
+
if self.cluster_id:
|
|
852
|
+
LOGGER.debug("Deleting %s", self)
|
|
853
|
+
self.clusters_client().delete(cluster_id=self.cluster_id)
|
|
854
|
+
LOGGER.info("Deleted %s", self)
|
|
851
855
|
|
|
852
856
|
def context(
|
|
853
857
|
self,
|
|
@@ -1150,7 +1154,7 @@ class Cluster(WorkspaceService):
|
|
|
1150
1154
|
if raise_error:
|
|
1151
1155
|
raise DatabricksError("Libraries %s in %s failed to install" % (failed, self))
|
|
1152
1156
|
|
|
1153
|
-
|
|
1157
|
+
LOGGER.exception(
|
|
1154
1158
|
"Libraries %s in %s failed to install",
|
|
1155
1159
|
failed, self
|
|
1156
1160
|
)
|
|
@@ -1187,7 +1191,7 @@ class Cluster(WorkspaceService):
|
|
|
1187
1191
|
Returns:
|
|
1188
1192
|
The uploaded library argument(s).
|
|
1189
1193
|
"""
|
|
1190
|
-
return self.
|
|
1194
|
+
return self.system_context.install_temporary_libraries(libraries=libraries)
|
|
1191
1195
|
|
|
1192
1196
|
def _check_library(
|
|
1193
1197
|
self,
|
|
@@ -12,11 +12,13 @@ import re
|
|
|
12
12
|
import sys
|
|
13
13
|
import threading
|
|
14
14
|
import zipfile
|
|
15
|
+
from threading import Thread
|
|
15
16
|
from types import ModuleType
|
|
16
17
|
from typing import TYPE_CHECKING, Optional, Any, Callable, List, Dict, Union, Iterable, Tuple
|
|
17
18
|
|
|
18
19
|
from ...libs.databrickslib import databricks_sdk
|
|
19
20
|
from ...pyutils.exceptions import raise_parsed_traceback
|
|
21
|
+
from ...pyutils.expiring_dict import ExpiringDict
|
|
20
22
|
from ...pyutils.modules import resolve_local_lib_path
|
|
21
23
|
from ...pyutils.callable_serde import CallableSerde
|
|
22
24
|
|
|
@@ -30,7 +32,7 @@ __all__ = [
|
|
|
30
32
|
"ExecutionContext"
|
|
31
33
|
]
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
LOGGER = logging.getLogger(__name__)
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
@dc.dataclass
|
|
@@ -38,7 +40,6 @@ class RemoteMetadata:
|
|
|
38
40
|
"""Metadata describing the remote cluster execution environment."""
|
|
39
41
|
site_packages_path: Optional[str] = dc.field(default=None)
|
|
40
42
|
os_env: Dict[str, str] = dc.field(default_factory=dict)
|
|
41
|
-
requirements: Optional[str] = dc.field(default=None)
|
|
42
43
|
version_info: Tuple[int, int, int] = dc.field(default=(0, 0, 0))
|
|
43
44
|
|
|
44
45
|
def os_env_diff(
|
|
@@ -80,6 +81,7 @@ class ExecutionContext:
|
|
|
80
81
|
|
|
81
82
|
_was_connected: Optional[bool] = dc.field(default=None, repr=False)
|
|
82
83
|
_remote_metadata: Optional[RemoteMetadata] = dc.field(default=None, repr=False)
|
|
84
|
+
_uploaded_package_roots: Optional[ExpiringDict] = dc.field(default_factory=ExpiringDict, repr=False)
|
|
83
85
|
|
|
84
86
|
_lock: threading.RLock = dc.field(default_factory=threading.RLock, init=False, repr=False)
|
|
85
87
|
|
|
@@ -113,7 +115,11 @@ class ExecutionContext:
|
|
|
113
115
|
|
|
114
116
|
def __del__(self):
|
|
115
117
|
"""Best-effort cleanup for the remote execution context."""
|
|
116
|
-
self.
|
|
118
|
+
if self.context_id:
|
|
119
|
+
try:
|
|
120
|
+
Thread(target=self.close).start()
|
|
121
|
+
except BaseException:
|
|
122
|
+
pass
|
|
117
123
|
|
|
118
124
|
@property
|
|
119
125
|
def remote_metadata(self) -> RemoteMetadata:
|
|
@@ -127,9 +133,7 @@ class ExecutionContext:
|
|
|
127
133
|
with self._lock:
|
|
128
134
|
# double-check after acquiring lock
|
|
129
135
|
if self._remote_metadata is None:
|
|
130
|
-
cmd = r"""import glob
|
|
131
|
-
import json
|
|
132
|
-
import os
|
|
136
|
+
cmd = r"""import glob, json, os
|
|
133
137
|
from yggdrasil.pyutils.python_env import PythonEnv
|
|
134
138
|
|
|
135
139
|
current_env = PythonEnv.get_current()
|
|
@@ -144,7 +148,6 @@ os_env = meta["os_env"] = {}
|
|
|
144
148
|
for k, v in os.environ.items():
|
|
145
149
|
os_env[k] = v
|
|
146
150
|
|
|
147
|
-
meta["requirements"] = current_env.requirements()
|
|
148
151
|
meta["version_info"] = current_env.version_info
|
|
149
152
|
|
|
150
153
|
print(json.dumps(meta))"""
|
|
@@ -191,7 +194,7 @@ print(json.dumps(meta))"""
|
|
|
191
194
|
"""
|
|
192
195
|
self.cluster.ensure_running()
|
|
193
196
|
|
|
194
|
-
|
|
197
|
+
LOGGER.debug(
|
|
195
198
|
"Creating Databricks command execution context for %s",
|
|
196
199
|
self.cluster
|
|
197
200
|
)
|
|
@@ -217,7 +220,7 @@ print(json.dumps(meta))"""
|
|
|
217
220
|
The connected ExecutionContext instance.
|
|
218
221
|
"""
|
|
219
222
|
if self.context_id is not None:
|
|
220
|
-
|
|
223
|
+
LOGGER.debug(
|
|
221
224
|
"Execution context already open for %s",
|
|
222
225
|
self
|
|
223
226
|
)
|
|
@@ -235,7 +238,7 @@ print(json.dumps(meta))"""
|
|
|
235
238
|
raise RuntimeError("Failed to create command execution context")
|
|
236
239
|
|
|
237
240
|
self.context_id = context_id
|
|
238
|
-
|
|
241
|
+
LOGGER.info(
|
|
239
242
|
"Opened execution context for %s",
|
|
240
243
|
self
|
|
241
244
|
)
|
|
@@ -247,13 +250,9 @@ print(json.dumps(meta))"""
|
|
|
247
250
|
Returns:
|
|
248
251
|
None.
|
|
249
252
|
"""
|
|
250
|
-
if self.context_id
|
|
253
|
+
if not self.context_id:
|
|
251
254
|
return
|
|
252
255
|
|
|
253
|
-
logger.debug(
|
|
254
|
-
"Closing execution context for %s",
|
|
255
|
-
self
|
|
256
|
-
)
|
|
257
256
|
try:
|
|
258
257
|
self._workspace_client().command_execution.destroy(
|
|
259
258
|
cluster_id=self.cluster.cluster_id,
|
|
@@ -349,7 +348,7 @@ print(json.dumps(meta))"""
|
|
|
349
348
|
|
|
350
349
|
self.connect(language=Language.PYTHON)
|
|
351
350
|
|
|
352
|
-
|
|
351
|
+
LOGGER.debug(
|
|
353
352
|
"Executing callable %s with %s",
|
|
354
353
|
getattr(func, "__name__", type(func)),
|
|
355
354
|
self,
|
|
@@ -386,7 +385,11 @@ print(json.dumps(meta))"""
|
|
|
386
385
|
)
|
|
387
386
|
|
|
388
387
|
try:
|
|
389
|
-
result = serialized.parse_command_result(
|
|
388
|
+
result = serialized.parse_command_result(
|
|
389
|
+
raw_result,
|
|
390
|
+
result_tag=result_tag,
|
|
391
|
+
workspace=self.cluster.workspace
|
|
392
|
+
)
|
|
390
393
|
except ModuleNotFoundError as remote_module_error:
|
|
391
394
|
_MOD_NOT_FOUND_RE = re.compile(r"No module named ['\"]([^'\"]+)['\"]")
|
|
392
395
|
module_name = _MOD_NOT_FOUND_RE.search(str(remote_module_error))
|
|
@@ -394,12 +397,18 @@ print(json.dumps(meta))"""
|
|
|
394
397
|
module_name = module_name.split(".")[0]
|
|
395
398
|
|
|
396
399
|
if module_name and "yggdrasil" not in module_name:
|
|
397
|
-
|
|
400
|
+
LOGGER.debug(
|
|
401
|
+
"Installing missing module %s from local environment",
|
|
402
|
+
module_name,
|
|
403
|
+
)
|
|
398
404
|
|
|
399
|
-
self.
|
|
405
|
+
self.install_temporary_libraries(
|
|
400
406
|
libraries=[module_name],
|
|
401
|
-
|
|
402
|
-
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
LOGGER.warning(
|
|
410
|
+
"Installed missing module %s from local environment",
|
|
411
|
+
module_name,
|
|
403
412
|
)
|
|
404
413
|
|
|
405
414
|
return self.execute_callable(
|
|
@@ -412,6 +421,7 @@ print(json.dumps(meta))"""
|
|
|
412
421
|
timeout=timeout,
|
|
413
422
|
command=command,
|
|
414
423
|
)
|
|
424
|
+
|
|
415
425
|
raise remote_module_error
|
|
416
426
|
|
|
417
427
|
return result
|
|
@@ -446,29 +456,7 @@ print(json.dumps(meta))"""
|
|
|
446
456
|
timeout=timeout or dt.timedelta(minutes=20)
|
|
447
457
|
)
|
|
448
458
|
|
|
449
|
-
|
|
450
|
-
return self._decode_result(result, result_tag=result_tag, print_stdout=print_stdout)
|
|
451
|
-
except ModuleNotFoundError as remote_module_error:
|
|
452
|
-
_MOD_NOT_FOUND_RE = re.compile(r"No module named ['\"]([^'\"]+)['\"]")
|
|
453
|
-
module_name = _MOD_NOT_FOUND_RE.search(str(remote_module_error))
|
|
454
|
-
module_name = module_name.group(1) if module_name else None
|
|
455
|
-
module_name = module_name.split(".")[0]
|
|
456
|
-
|
|
457
|
-
if module_name and "yggdrasil" not in module_name:
|
|
458
|
-
self.close()
|
|
459
|
-
self.cluster.install_libraries(
|
|
460
|
-
libraries=[module_name],
|
|
461
|
-
raise_error=True,
|
|
462
|
-
restart=True
|
|
463
|
-
)
|
|
464
|
-
|
|
465
|
-
return self.execute_command(
|
|
466
|
-
command=command,
|
|
467
|
-
timeout=timeout,
|
|
468
|
-
result_tag=result_tag,
|
|
469
|
-
print_stdout=print_stdout
|
|
470
|
-
)
|
|
471
|
-
raise remote_module_error
|
|
459
|
+
return self._decode_result(result, result_tag=result_tag, print_stdout=print_stdout)
|
|
472
460
|
|
|
473
461
|
# ------------------------------------------------------------------
|
|
474
462
|
# generic local → remote uploader, via remote python
|
|
@@ -589,16 +577,22 @@ with zipfile.ZipFile(buf, "r") as zf:
|
|
|
589
577
|
]
|
|
590
578
|
|
|
591
579
|
resolved = resolve_local_lib_path(libraries)
|
|
580
|
+
str_resolved = str(resolved)
|
|
581
|
+
existing = self._uploaded_package_roots.get(str_resolved)
|
|
592
582
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
# site-packages/<package_name>/
|
|
596
|
-
remote_target = posixpath.join(remote_site_packages_path, resolved.name)
|
|
597
|
-
else:
|
|
598
|
-
# site-packages/<module_file>
|
|
599
|
-
remote_target = posixpath.join(remote_site_packages_path, resolved.name)
|
|
583
|
+
if not existing:
|
|
584
|
+
remote_site_packages_path = self.remote_metadata.site_packages_path
|
|
600
585
|
|
|
601
|
-
|
|
586
|
+
if resolved.is_dir():
|
|
587
|
+
# site-packages/<package_name>/
|
|
588
|
+
remote_target = posixpath.join(remote_site_packages_path, resolved.name)
|
|
589
|
+
else:
|
|
590
|
+
# site-packages/<module_file>
|
|
591
|
+
remote_target = posixpath.join(remote_site_packages_path, resolved.name)
|
|
592
|
+
|
|
593
|
+
self.upload_local_path(resolved, remote_target)
|
|
594
|
+
|
|
595
|
+
self._uploaded_package_roots[str_resolved] = remote_target
|
|
602
596
|
|
|
603
597
|
return libraries
|
|
604
598
|
|
|
@@ -649,16 +643,4 @@ with zipfile.ZipFile(buf, "r") as zf:
|
|
|
649
643
|
else:
|
|
650
644
|
output = ""
|
|
651
645
|
|
|
652
|
-
# result_tag slicing
|
|
653
|
-
if result_tag:
|
|
654
|
-
start = output.find(result_tag)
|
|
655
|
-
if start != -1:
|
|
656
|
-
content_start = start + len(result_tag)
|
|
657
|
-
end = output.find(result_tag, content_start)
|
|
658
|
-
if end != -1:
|
|
659
|
-
before = output[:start].strip()
|
|
660
|
-
if before and print_stdout:
|
|
661
|
-
print(before)
|
|
662
|
-
return output[content_start:end]
|
|
663
|
-
|
|
664
646
|
return output
|
|
@@ -130,7 +130,10 @@ class SQLEngine(WorkspaceService):
|
|
|
130
130
|
schema_name = schema_name or self.schema_name
|
|
131
131
|
return catalog_name, schema_name, table_name
|
|
132
132
|
|
|
133
|
-
def _default_warehouse(
|
|
133
|
+
def _default_warehouse(
|
|
134
|
+
self,
|
|
135
|
+
cluster_size: str = "Small"
|
|
136
|
+
):
|
|
134
137
|
"""Pick a default SQL warehouse (best-effort) matching the desired size.
|
|
135
138
|
|
|
136
139
|
Args:
|
|
@@ -951,7 +954,7 @@ FROM parquet.`{temp_volume_path}`"""
|
|
|
951
954
|
"""Convert an Arrow Field to a Databricks SQL column DDL fragment."""
|
|
952
955
|
name = field.name
|
|
953
956
|
nullable_str = " NOT NULL" if put_not_null and not field.nullable else ""
|
|
954
|
-
name_str = f"{name} " if put_name else ""
|
|
957
|
+
name_str = f"`{name}` " if put_name else ""
|
|
955
958
|
|
|
956
959
|
comment_str = ""
|
|
957
960
|
if put_comment and field.metadata and b"comment" in field.metadata:
|