spark-cell-runner 0.7.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.
- spark_cell_runner/__init__.py +10 -0
- spark_cell_runner/bootstrap.py +536 -0
- spark_cell_runner/bootstrap_builder.py +224 -0
- spark_cell_runner/cli.py +370 -0
- spark_cell_runner/execute_sql.py +197 -0
- spark_cell_runner/parser.py +178 -0
- spark_cell_runner/pool.py +276 -0
- spark_cell_runner/preview_table.py +86 -0
- spark_cell_runner/runtime_data.py +7 -0
- spark_cell_runner/session_driver.py +39 -0
- spark_cell_runner/table_formats.py +143 -0
- spark_cell_runner/uc_explore.py +111 -0
- spark_cell_runner/uc_sync.py +242 -0
- spark_cell_runner/warehouse_registry.py +30 -0
- spark_cell_runner-0.7.0.dist-info/METADATA +111 -0
- spark_cell_runner-0.7.0.dist-info/RECORD +19 -0
- spark_cell_runner-0.7.0.dist-info/WHEEL +5 -0
- spark_cell_runner-0.7.0.dist-info/entry_points.txt +2 -0
- spark_cell_runner-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""spark-cell-runner — headless engine for Databricks notebook cells and SQL.
|
|
2
|
+
|
|
3
|
+
Runs Databricks notebook-source .py cells and SQL against Databricks
|
|
4
|
+
(Databricks Connect) or a local Sail Spark pool, with Unity Catalog sync and
|
|
5
|
+
a local Delta / Iceberg warehouse. The VS Code extension of the same name
|
|
6
|
+
ships these same modules and spawns them by path; this package is the
|
|
7
|
+
pip-installable CLI form.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "0.7.0"
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# Databricks notebook source
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import shlex
|
|
7
|
+
import shutil
|
|
8
|
+
import io
|
|
9
|
+
import types
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
_dcr_workspace_path_mappings = globals().get("_dcr_workspace_path_mappings", {})
|
|
12
|
+
_dcr_secret_values = globals().get("_dcr_secret_values", {})
|
|
13
|
+
_dcr_widget_values = globals().get("_dcr_widget_values", {})
|
|
14
|
+
class DCRNotebookExit(Exception):
|
|
15
|
+
pass
|
|
16
|
+
def log_appinsights(*args, **kwargs):
|
|
17
|
+
message = kwargs.get("message") if isinstance(kwargs, dict) else None
|
|
18
|
+
if message is None and args:
|
|
19
|
+
message = args[0]
|
|
20
|
+
print(f"[spark-cell-runner] log_appinsights fallback: {message}")
|
|
21
|
+
def log_info(message, properties=None):
|
|
22
|
+
print(f"[spark-cell-runner] log_info: {message}")
|
|
23
|
+
def log_warning(message, properties=None):
|
|
24
|
+
print(f"[spark-cell-runner] log_warning: {message}")
|
|
25
|
+
def log_error(message, properties=None):
|
|
26
|
+
print(f"[spark-cell-runner] log_error: {message}")
|
|
27
|
+
def log_metric(name, value, properties=None):
|
|
28
|
+
print(f"[spark-cell-runner] log_metric: {name}={value}")
|
|
29
|
+
def display(*args, **kwargs):
|
|
30
|
+
if not args:
|
|
31
|
+
return None
|
|
32
|
+
obj = args[0]
|
|
33
|
+
show = getattr(obj, "show", None)
|
|
34
|
+
if callable(show):
|
|
35
|
+
try:
|
|
36
|
+
return show(20, truncate=False)
|
|
37
|
+
except TypeError:
|
|
38
|
+
return show()
|
|
39
|
+
to_string = getattr(obj, "to_string", None)
|
|
40
|
+
if callable(to_string):
|
|
41
|
+
print(to_string())
|
|
42
|
+
return None
|
|
43
|
+
print(obj)
|
|
44
|
+
return None
|
|
45
|
+
def displayHTML(html=""):
|
|
46
|
+
print(html)
|
|
47
|
+
for __dcr_df_module in ("pyspark.sql.connect.dataframe", "pyspark.sql.dataframe"):
|
|
48
|
+
try:
|
|
49
|
+
__dcr_df_mod = __import__(__dcr_df_module, fromlist=["DataFrame"])
|
|
50
|
+
__dcr_df_cls = getattr(__dcr_df_mod, "DataFrame", None)
|
|
51
|
+
if __dcr_df_cls is not None and not hasattr(__dcr_df_cls, "display"):
|
|
52
|
+
__dcr_df_cls.display = lambda self, *a, **k: display(self)
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
55
|
+
def __dcr_safe_callable(fn, name):
|
|
56
|
+
if not callable(fn):
|
|
57
|
+
return fn
|
|
58
|
+
if getattr(fn, "__dcr_safe_wrapper__", False):
|
|
59
|
+
return fn
|
|
60
|
+
def _wrapped(*args, **kwargs):
|
|
61
|
+
try:
|
|
62
|
+
return fn(*args, **kwargs)
|
|
63
|
+
except Exception as exc:
|
|
64
|
+
print(f"[spark-cell-runner] {name} failed: {exc}")
|
|
65
|
+
return None
|
|
66
|
+
_wrapped.__dcr_safe_wrapper__ = True
|
|
67
|
+
return _wrapped
|
|
68
|
+
def __dcr_wrap_logging_helpers():
|
|
69
|
+
for _name in ["log_appinsights", "log_info", "log_warning", "log_error", "log_metric"]:
|
|
70
|
+
if _name in globals():
|
|
71
|
+
globals()[_name] = __dcr_safe_callable(globals()[_name], _name)
|
|
72
|
+
def __dcr_wrap_hello__config_reader():
|
|
73
|
+
reader_cls = globals().get("AladdinConfigReader")
|
|
74
|
+
if reader_cls is None:
|
|
75
|
+
return
|
|
76
|
+
original = getattr(reader_cls, "get_config_variables", None)
|
|
77
|
+
if not callable(original) or getattr(original, "__dcr_safe_wrapper__", False):
|
|
78
|
+
return
|
|
79
|
+
def _wrapped(self, hello__file_id):
|
|
80
|
+
result = original(self, hello__file_id)
|
|
81
|
+
if result is not None:
|
|
82
|
+
return result
|
|
83
|
+
try:
|
|
84
|
+
full_table_name = getattr(self, "full_table_name", f"{getattr(self, 'catalog_name', '')}.{getattr(self, 'schema_name', 'common')}")
|
|
85
|
+
row = spark.sql(f"SELECT * from table1").collect()
|
|
86
|
+
if not row:
|
|
87
|
+
return None
|
|
88
|
+
row = row[0]
|
|
89
|
+
file_info = {
|
|
90
|
+
"config_path": row["ConfigPath"],
|
|
91
|
+
}
|
|
92
|
+
app_path = globals().get("app_storage_path", "")
|
|
93
|
+
full_config_path = f"{app_path.rstrip('/')}/{file_info['config_path'].lstrip('/')}"
|
|
94
|
+
except Exception as exc:
|
|
95
|
+
print(f"[spark-cell-runner] Aladdin config fallback failed: {exc}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
json_content = dbutils.fs.head(full_config_path)
|
|
100
|
+
except Exception:
|
|
101
|
+
json_content = dcr_read_text_via_spark(full_config_path)
|
|
102
|
+
|
|
103
|
+
# NOTE: the original hardcoded bootstrap ended here mid-function; _wrapped is
|
|
104
|
+
# intentionally left unattached until the Aladdin fallback is finished.
|
|
105
|
+
return None
|
|
106
|
+
def dcr_translate_workspace_path(value):
|
|
107
|
+
if not isinstance(value, str):
|
|
108
|
+
return value
|
|
109
|
+
normalized = value.replace("\\", "/")
|
|
110
|
+
for source_prefix, target_prefix in sorted(_dcr_workspace_path_mappings.items(), key=lambda item: len(item[0]), reverse=True):
|
|
111
|
+
source_norm = source_prefix.replace("\\", "/")
|
|
112
|
+
if normalized == source_norm or normalized.startswith(source_norm + "/"):
|
|
113
|
+
suffix = normalized[len(source_norm):].lstrip("/")
|
|
114
|
+
translated = os.path.join(target_prefix, *([part for part in suffix.split("/") if part] if suffix else []))
|
|
115
|
+
return translated
|
|
116
|
+
return value
|
|
117
|
+
def dcr_list_mapped_library_entries(translated):
|
|
118
|
+
if isinstance(translated, str) and translated.lower().endswith(".txt") and dcr_original_isfile(translated):
|
|
119
|
+
entries = []
|
|
120
|
+
with dcr_original_open(translated, "r", encoding="utf-8") as handle:
|
|
121
|
+
for raw_line in handle:
|
|
122
|
+
line = raw_line.strip()
|
|
123
|
+
if not line or line.startswith("#"):
|
|
124
|
+
continue
|
|
125
|
+
entries.append(line.split("=", 1)[0].strip())
|
|
126
|
+
return entries
|
|
127
|
+
return None
|
|
128
|
+
def dcr_wheel_to_requirement(token):
|
|
129
|
+
basename = os.path.basename(token)
|
|
130
|
+
match = re.match(r"(?P<name>.+?)-(?P<version>\d[^-]*)-", basename)
|
|
131
|
+
if match:
|
|
132
|
+
return f"{match.group('name')}=={match.group('version')}"
|
|
133
|
+
stem = re.sub(r"\.(whl|egg)$", "", basename, flags=re.IGNORECASE)
|
|
134
|
+
return stem
|
|
135
|
+
def dcr_translate_pip_command(command):
|
|
136
|
+
args = shlex.split(command)
|
|
137
|
+
translated_args = []
|
|
138
|
+
for arg in args:
|
|
139
|
+
translated = dcr_translate_workspace_path(arg)
|
|
140
|
+
if isinstance(arg, str) and arg.startswith("/Volumes/") and not os.path.exists(translated):
|
|
141
|
+
lowered = arg.lower()
|
|
142
|
+
if lowered.endswith(".whl") or lowered.endswith(".egg"):
|
|
143
|
+
translated_args.append(dcr_wheel_to_requirement(arg))
|
|
144
|
+
continue
|
|
145
|
+
translated_args.append(translated)
|
|
146
|
+
return translated_args
|
|
147
|
+
def dcr_read_text_via_spark(path_value, max_bytes=65536):
|
|
148
|
+
spark_session = globals().get("spark")
|
|
149
|
+
if spark_session is None:
|
|
150
|
+
raise FileNotFoundError(path_value)
|
|
151
|
+
rows = spark_session.read.text(path_value).collect()
|
|
152
|
+
text_value = "\n".join([row[0] for row in rows])
|
|
153
|
+
return text_value[:max_bytes]
|
|
154
|
+
def dcr_path_is_abfss(path_value):
|
|
155
|
+
return isinstance(path_value, str) and path_value.startswith("abfss://")
|
|
156
|
+
def dcr_list_via_spark(path_value):
|
|
157
|
+
spark_session = globals().get("spark")
|
|
158
|
+
if spark_session is None:
|
|
159
|
+
raise FileNotFoundError(path_value)
|
|
160
|
+
rows = spark_session.read.format("binaryFile").load(path_value.rstrip("/") + "/*").select("path").collect()
|
|
161
|
+
items = []
|
|
162
|
+
for row in rows:
|
|
163
|
+
item_path = row[0]
|
|
164
|
+
name = item_path.rsplit("/", 1)[-1]
|
|
165
|
+
items.append(DcrFileInfo(item_path, name, False))
|
|
166
|
+
return items
|
|
167
|
+
def dcr_write_text_via_spark(path_value, text_value):
|
|
168
|
+
spark_session = globals().get("spark")
|
|
169
|
+
if spark_session is None:
|
|
170
|
+
raise FileNotFoundError(path_value)
|
|
171
|
+
spark_session.createDataFrame([(text_value,)], ["value"]).coalesce(1).write.mode("overwrite").text(path_value)
|
|
172
|
+
return True
|
|
173
|
+
def dcr_rm_via_spark(path_value, recurse=False):
|
|
174
|
+
spark_session = globals().get("spark")
|
|
175
|
+
if spark_session is None:
|
|
176
|
+
raise FileNotFoundError(path_value)
|
|
177
|
+
sc = getattr(spark_session, "sparkContext", None)
|
|
178
|
+
jvm = getattr(sc, "_jvm", None) or getattr(spark_session, "_jvm", None)
|
|
179
|
+
if jvm is None or sc is None:
|
|
180
|
+
raise RuntimeError("JVM access unavailable for external rm")
|
|
181
|
+
hadoop_path = jvm.org.apache.hadoop.fs.Path(path_value)
|
|
182
|
+
hadoop_conf = sc._jsc.hadoopConfiguration()
|
|
183
|
+
fs = hadoop_path.getFileSystem(hadoop_conf)
|
|
184
|
+
return bool(fs.delete(hadoop_path, bool(recurse)))
|
|
185
|
+
dcr_original_open = getattr(builtins, "__dcr_original_open", builtins.open)
|
|
186
|
+
builtins.__dcr_original_open = dcr_original_open
|
|
187
|
+
def dcr_is_databricks_path(path_value):
|
|
188
|
+
if not isinstance(path_value, str):
|
|
189
|
+
return False
|
|
190
|
+
return (
|
|
191
|
+
path_value.startswith("/Volumes/")
|
|
192
|
+
or path_value.startswith("/dbfs/")
|
|
193
|
+
or path_value.startswith("dbfs:/")
|
|
194
|
+
or path_value.startswith("abfss://")
|
|
195
|
+
)
|
|
196
|
+
def dcr_read_binary_via_spark(path_value):
|
|
197
|
+
spark_session = globals().get("spark")
|
|
198
|
+
if spark_session is None:
|
|
199
|
+
raise FileNotFoundError(path_value)
|
|
200
|
+
rows = spark_session.read.format("binaryFile").load(path_value).select("content").collect()
|
|
201
|
+
if not rows:
|
|
202
|
+
raise FileNotFoundError(path_value)
|
|
203
|
+
return rows[0][0]
|
|
204
|
+
class DcrRawStream(io.RawIOBase):
|
|
205
|
+
def __init__(self, source):
|
|
206
|
+
self._source = source
|
|
207
|
+
def readable(self):
|
|
208
|
+
return True
|
|
209
|
+
def readinto(self, buffer):
|
|
210
|
+
chunk = self._source.read(len(buffer))
|
|
211
|
+
if not chunk:
|
|
212
|
+
return 0
|
|
213
|
+
count = len(chunk)
|
|
214
|
+
buffer[:count] = chunk
|
|
215
|
+
return count
|
|
216
|
+
def close(self):
|
|
217
|
+
try:
|
|
218
|
+
self._source.close()
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
221
|
+
super().close()
|
|
222
|
+
def dcr_open_databricks_stream(path_value, mode, kwargs):
|
|
223
|
+
raw_source = None
|
|
224
|
+
if path_value.startswith("/Volumes/"):
|
|
225
|
+
try:
|
|
226
|
+
from databricks.sdk import WorkspaceClient
|
|
227
|
+
client = globals().get("__dcr_workspace_client")
|
|
228
|
+
if client is None:
|
|
229
|
+
client = WorkspaceClient()
|
|
230
|
+
globals()["__dcr_workspace_client"] = client
|
|
231
|
+
raw_source = client.files.download(path_value).contents
|
|
232
|
+
except Exception:
|
|
233
|
+
raw_source = None
|
|
234
|
+
if raw_source is not None:
|
|
235
|
+
buffered = io.BufferedReader(DcrRawStream(raw_source))
|
|
236
|
+
else:
|
|
237
|
+
buffered = io.BytesIO(dcr_read_binary_via_spark(path_value))
|
|
238
|
+
if isinstance(mode, str) and "b" in mode:
|
|
239
|
+
return buffered
|
|
240
|
+
return io.TextIOWrapper(
|
|
241
|
+
buffered,
|
|
242
|
+
encoding=kwargs.get("encoding") or "utf-8",
|
|
243
|
+
errors=kwargs.get("errors"),
|
|
244
|
+
newline=kwargs.get("newline"),
|
|
245
|
+
)
|
|
246
|
+
def __dcr_open(file, *args, **kwargs):
|
|
247
|
+
translated = dcr_translate_workspace_path(file)
|
|
248
|
+
mode = args[0] if args else kwargs.get("mode", "r")
|
|
249
|
+
is_read = not isinstance(mode, str) or ("w" not in mode and "a" not in mode and "x" not in mode and "+" not in mode)
|
|
250
|
+
if is_read and dcr_is_databricks_path(translated) and not dcr_original_exists(translated):
|
|
251
|
+
try:
|
|
252
|
+
return dcr_open_databricks_stream(translated, mode, kwargs)
|
|
253
|
+
except Exception as exc:
|
|
254
|
+
print(f"[spark-cell-runner] could not read Databricks path {translated}: {exc}")
|
|
255
|
+
return dcr_original_open(translated, *args, **kwargs)
|
|
256
|
+
builtins.open = __dcr_open
|
|
257
|
+
dcr_original_exists = getattr(os.path, "__dcr_original_exists", os.path.exists)
|
|
258
|
+
os.path.__dcr_original_exists = dcr_original_exists
|
|
259
|
+
os.path.exists = lambda value: dcr_original_exists(dcr_translate_workspace_path(value))
|
|
260
|
+
dcr_original_isfile = getattr(os.path, "__dcr_original_isfile", os.path.isfile)
|
|
261
|
+
os.path.__dcr_original_isfile = dcr_original_isfile
|
|
262
|
+
os.path.isfile = lambda value: dcr_original_isfile(dcr_translate_workspace_path(value))
|
|
263
|
+
dcr_original_isdir = getattr(os.path, "__dcr_original_isdir", os.path.isdir)
|
|
264
|
+
os.path.__dcr_original_isdir = dcr_original_isdir
|
|
265
|
+
os.path.isdir = lambda value: (dcr_list_mapped_library_entries(dcr_translate_workspace_path(value)) is not None) or dcr_original_isdir(dcr_translate_workspace_path(value))
|
|
266
|
+
dcr_original_listdir = getattr(os, "__dcr_original_listdir", os.listdir)
|
|
267
|
+
os.__dcr_original_listdir = dcr_original_listdir
|
|
268
|
+
os.listdir = lambda value: (dcr_list_mapped_library_entries(dcr_translate_workspace_path(value)) if dcr_list_mapped_library_entries(dcr_translate_workspace_path(value)) is not None else dcr_original_listdir(dcr_translate_workspace_path(value)))
|
|
269
|
+
dcr_original_path_open = getattr(Path, "__dcr_original_path_open", Path.open)
|
|
270
|
+
Path.__dcr_original_path_open = dcr_original_path_open
|
|
271
|
+
def __dcr_path_open(self, *args, **kwargs):
|
|
272
|
+
return dcr_original_path_open(self, *args, **kwargs)
|
|
273
|
+
Path.open = __dcr_path_open
|
|
274
|
+
class DcrFileInfo:
|
|
275
|
+
def __init__(self, path_value, name, is_dir=False, size=0):
|
|
276
|
+
self.path = path_value
|
|
277
|
+
self.name = name
|
|
278
|
+
self.size = size
|
|
279
|
+
self._is_dir = bool(is_dir)
|
|
280
|
+
def isDir(self):
|
|
281
|
+
return self._is_dir
|
|
282
|
+
def isFile(self):
|
|
283
|
+
return not self._is_dir
|
|
284
|
+
@classmethod
|
|
285
|
+
def from_entry(cls, entry):
|
|
286
|
+
if isinstance(entry, cls):
|
|
287
|
+
return entry
|
|
288
|
+
path_value = getattr(entry, "path", None) or getattr(entry, "file_path", None) or ""
|
|
289
|
+
name = getattr(entry, "name", None) or (path_value.rstrip("/").rsplit("/", 1)[-1] if path_value else "")
|
|
290
|
+
size = getattr(entry, "size", 0)
|
|
291
|
+
is_dir = getattr(entry, "_is_dir", None)
|
|
292
|
+
if is_dir is None and hasattr(entry, "isDir"):
|
|
293
|
+
try:
|
|
294
|
+
candidate = getattr(entry, "isDir")
|
|
295
|
+
is_dir = candidate() if callable(candidate) else candidate
|
|
296
|
+
except Exception:
|
|
297
|
+
is_dir = None
|
|
298
|
+
if is_dir is None and hasattr(entry, "is_dir"):
|
|
299
|
+
try:
|
|
300
|
+
candidate = getattr(entry, "is_dir")
|
|
301
|
+
is_dir = candidate() if callable(candidate) else candidate
|
|
302
|
+
except Exception:
|
|
303
|
+
is_dir = None
|
|
304
|
+
if is_dir is None and path_value:
|
|
305
|
+
translated = dcr_translate_workspace_path(path_value)
|
|
306
|
+
is_dir = os.path.isdir(translated)
|
|
307
|
+
return cls(path_value, name, bool(is_dir), size)
|
|
308
|
+
def __repr__(self):
|
|
309
|
+
return f"DcrFileInfo(path={self.path!r}, name={self.name!r}, is_dir={self._is_dir!r}, size={self.size!r})"
|
|
310
|
+
class DcrFsProxy:
|
|
311
|
+
def __init__(self, wrapped):
|
|
312
|
+
self._wrapped = wrapped
|
|
313
|
+
def ls(self, value):
|
|
314
|
+
translated = dcr_translate_workspace_path(value)
|
|
315
|
+
mapped = dcr_list_mapped_library_entries(translated)
|
|
316
|
+
if mapped is not None:
|
|
317
|
+
return [DcrFileInfo(f"{str(value).rstrip('/')}/{name}", name, os.path.isdir(os.path.join(translated, name)), os.path.getsize(os.path.join(translated, name)) if os.path.isfile(os.path.join(translated, name)) else 0) for name in mapped]
|
|
318
|
+
if dcr_path_is_abfss(value):
|
|
319
|
+
return dcr_list_via_spark(value)
|
|
320
|
+
if os.path.isdir(translated):
|
|
321
|
+
return [DcrFileInfo(f"{str(value).rstrip('/')}/{name}", name, os.path.isdir(os.path.join(translated, name)), os.path.getsize(os.path.join(translated, name)) if os.path.isfile(os.path.join(translated, name)) else 0) for name in os.listdir(translated)]
|
|
322
|
+
if self._wrapped is not None:
|
|
323
|
+
return [DcrFileInfo.from_entry(entry) for entry in self._wrapped.fs.ls(value)]
|
|
324
|
+
raise FileNotFoundError(translated)
|
|
325
|
+
def head(self, value, max_bytes=65536):
|
|
326
|
+
translated = dcr_translate_workspace_path(value)
|
|
327
|
+
if isinstance(value, str) and value.startswith("abfss://"):
|
|
328
|
+
try:
|
|
329
|
+
return dcr_read_text_via_spark(value, max_bytes)
|
|
330
|
+
except Exception:
|
|
331
|
+
pass
|
|
332
|
+
if dcr_original_isfile(translated):
|
|
333
|
+
with dcr_original_open(translated, "r", encoding="utf-8") as handle:
|
|
334
|
+
return handle.read(max_bytes)
|
|
335
|
+
if self._wrapped is not None:
|
|
336
|
+
return self._wrapped.fs.head(value, max_bytes)
|
|
337
|
+
raise FileNotFoundError(translated)
|
|
338
|
+
def mkdirs(self, value):
|
|
339
|
+
if dcr_path_is_abfss(value):
|
|
340
|
+
return True
|
|
341
|
+
os.makedirs(dcr_translate_workspace_path(value), exist_ok=True)
|
|
342
|
+
return True
|
|
343
|
+
def put(self, value, contents, overwrite=False):
|
|
344
|
+
if dcr_path_is_abfss(value):
|
|
345
|
+
return dcr_write_text_via_spark(value, contents)
|
|
346
|
+
translated = dcr_translate_workspace_path(value)
|
|
347
|
+
parent = os.path.dirname(translated)
|
|
348
|
+
if parent:
|
|
349
|
+
os.makedirs(parent, exist_ok=True)
|
|
350
|
+
if not overwrite and os.path.exists(translated):
|
|
351
|
+
raise FileExistsError(translated)
|
|
352
|
+
with dcr_original_open(translated, "w", encoding="utf-8") as handle:
|
|
353
|
+
handle.write(contents)
|
|
354
|
+
return True
|
|
355
|
+
def mv(self, src, dst, recurse=False):
|
|
356
|
+
if (dcr_path_is_abfss(src) or dcr_path_is_abfss(dst)) and self._wrapped is not None:
|
|
357
|
+
return self._wrapped.fs.mv(src, dst, recurse)
|
|
358
|
+
translated_src = dcr_translate_workspace_path(src)
|
|
359
|
+
translated_dst = dcr_translate_workspace_path(dst)
|
|
360
|
+
parent = os.path.dirname(translated_dst)
|
|
361
|
+
if parent:
|
|
362
|
+
os.makedirs(parent, exist_ok=True)
|
|
363
|
+
shutil.move(translated_src, translated_dst)
|
|
364
|
+
return True
|
|
365
|
+
def rm(self, value, recurse=False):
|
|
366
|
+
if dcr_path_is_abfss(value):
|
|
367
|
+
try:
|
|
368
|
+
return dcr_rm_via_spark(value, recurse)
|
|
369
|
+
except Exception as exc:
|
|
370
|
+
if self._wrapped is not None:
|
|
371
|
+
try:
|
|
372
|
+
return self._wrapped.fs.rm(value, recurse)
|
|
373
|
+
except Exception:
|
|
374
|
+
pass
|
|
375
|
+
print("[spark-cell-runner] Skipping rm for external path %r (%s)" % (value, exc))
|
|
376
|
+
return True
|
|
377
|
+
translated = dcr_translate_workspace_path(value)
|
|
378
|
+
if os.path.isdir(translated):
|
|
379
|
+
shutil.rmtree(translated, ignore_errors=recurse)
|
|
380
|
+
elif os.path.exists(translated):
|
|
381
|
+
os.remove(translated)
|
|
382
|
+
return True
|
|
383
|
+
def cp(self, src, dst, recurse=False):
|
|
384
|
+
if (dcr_path_is_abfss(src) or dcr_path_is_abfss(dst)) and self._wrapped is not None:
|
|
385
|
+
return self._wrapped.fs.cp(src, dst, recurse)
|
|
386
|
+
translated_src = dcr_translate_workspace_path(src)
|
|
387
|
+
translated_dst = dcr_translate_workspace_path(dst)
|
|
388
|
+
parent = os.path.dirname(translated_dst)
|
|
389
|
+
if parent:
|
|
390
|
+
os.makedirs(parent, exist_ok=True)
|
|
391
|
+
if os.path.isdir(translated_src):
|
|
392
|
+
shutil.copytree(translated_src, translated_dst, dirs_exist_ok=True)
|
|
393
|
+
else:
|
|
394
|
+
shutil.copy2(translated_src, translated_dst)
|
|
395
|
+
return True
|
|
396
|
+
def __getattr__(self, name):
|
|
397
|
+
if self._wrapped is None:
|
|
398
|
+
raise AttributeError(name)
|
|
399
|
+
return getattr(self._wrapped.fs, name)
|
|
400
|
+
class DcrWidgetsProxy:
|
|
401
|
+
def __init__(self):
|
|
402
|
+
self._values = {}
|
|
403
|
+
_dcr_overrides = globals().get('__dcr_widget_values', {})
|
|
404
|
+
self._overrides = dict(_dcr_overrides) if isinstance(_dcr_overrides, dict) else {}
|
|
405
|
+
def _register(self, name, defaultValue=''):
|
|
406
|
+
self._values.setdefault(name, defaultValue)
|
|
407
|
+
def text(self, name, defaultValue='', label=None):
|
|
408
|
+
self._register(name, defaultValue)
|
|
409
|
+
def dropdown(self, name, defaultValue='', choices=None, label=None):
|
|
410
|
+
self._register(name, defaultValue)
|
|
411
|
+
def combobox(self, name, defaultValue='', choices=None, label=None):
|
|
412
|
+
self._register(name, defaultValue)
|
|
413
|
+
def multiselect(self, name, defaultValue='', choices=None, label=None):
|
|
414
|
+
self._register(name, defaultValue)
|
|
415
|
+
def get(self, name):
|
|
416
|
+
if name in self._overrides and self._overrides[name] not in (None, ''):
|
|
417
|
+
return self._overrides[name]
|
|
418
|
+
return self._values.get(name, '')
|
|
419
|
+
def getArgument(self, name, defaultValue=''):
|
|
420
|
+
value = self.get(name)
|
|
421
|
+
return value if value != '' else defaultValue
|
|
422
|
+
def removeAll(self):
|
|
423
|
+
self._values.clear()
|
|
424
|
+
def dcr_workspace_host():
|
|
425
|
+
client = globals().get('__dcr_workspace_client')
|
|
426
|
+
if client is None:
|
|
427
|
+
try:
|
|
428
|
+
from databricks.sdk import WorkspaceClient
|
|
429
|
+
client = WorkspaceClient()
|
|
430
|
+
globals()['__dcr_workspace_client'] = client
|
|
431
|
+
except Exception:
|
|
432
|
+
client = None
|
|
433
|
+
if client is not None:
|
|
434
|
+
try:
|
|
435
|
+
host = client.config.host
|
|
436
|
+
if host:
|
|
437
|
+
return host
|
|
438
|
+
except Exception:
|
|
439
|
+
pass
|
|
440
|
+
return os.environ.get('DATABRICKS_HOST', '')
|
|
441
|
+
class DcrScalaOption:
|
|
442
|
+
def __init__(self, value):
|
|
443
|
+
self._value = value
|
|
444
|
+
def get(self):
|
|
445
|
+
return self._value
|
|
446
|
+
def getOrElse(self, default):
|
|
447
|
+
return self._value if self._value not in (None, '') else default
|
|
448
|
+
def isDefined(self):
|
|
449
|
+
return self._value not in (None, '')
|
|
450
|
+
def isEmpty(self):
|
|
451
|
+
return self._value in (None, '')
|
|
452
|
+
def toString(self):
|
|
453
|
+
return str(self._value if self._value is not None else '')
|
|
454
|
+
class DcrTagsMap:
|
|
455
|
+
def __init__(self, data=None):
|
|
456
|
+
self._data = data or {}
|
|
457
|
+
def apply(self, key):
|
|
458
|
+
return self._data.get(key, '')
|
|
459
|
+
def get(self, key, default=None):
|
|
460
|
+
return DcrScalaOption(self._data.get(key))
|
|
461
|
+
def getOrElse(self, key, default):
|
|
462
|
+
return self._data.get(key, default)
|
|
463
|
+
class DcrNotebookContext:
|
|
464
|
+
def _bare_host(self):
|
|
465
|
+
return dcr_workspace_host().replace('https://', '').replace('http://', '').rstrip('/')
|
|
466
|
+
def apiUrl(self):
|
|
467
|
+
return DcrScalaOption(dcr_workspace_host())
|
|
468
|
+
def workspaceUrl(self):
|
|
469
|
+
return DcrScalaOption(self._bare_host())
|
|
470
|
+
def browserHostName(self):
|
|
471
|
+
return DcrScalaOption(self._bare_host())
|
|
472
|
+
def notebookPath(self):
|
|
473
|
+
return DcrScalaOption(globals().get('__dcr_notebook_path', ''))
|
|
474
|
+
def tags(self):
|
|
475
|
+
return DcrTagsMap()
|
|
476
|
+
def __getattr__(self, name):
|
|
477
|
+
return lambda *args, **kwargs: DcrScalaOption(None)
|
|
478
|
+
class DcrNotebookContextHolder:
|
|
479
|
+
def getContext(self):
|
|
480
|
+
return DcrNotebookContext()
|
|
481
|
+
class DcrEntryPointDbutils:
|
|
482
|
+
def notebook(self):
|
|
483
|
+
return DcrNotebookContextHolder()
|
|
484
|
+
class DcrEntryPoint:
|
|
485
|
+
def getDbutils(self):
|
|
486
|
+
return DcrEntryPointDbutils()
|
|
487
|
+
class DcrNotebookProxy:
|
|
488
|
+
def __init__(self):
|
|
489
|
+
self.entry_point = DcrEntryPoint()
|
|
490
|
+
def exit(self, value=None):
|
|
491
|
+
raise DcrNotebookExit(value)
|
|
492
|
+
def run(self, path=None, timeout_seconds=None, arguments=None, *args, **kwargs):
|
|
493
|
+
print('[spark-cell-runner] Skipping dbutils.notebook.run(%r) in local execution' % (path,))
|
|
494
|
+
return ''
|
|
495
|
+
def getContext(self):
|
|
496
|
+
return DcrNotebookContext()
|
|
497
|
+
class DcrSecretsProxy:
|
|
498
|
+
def get(self, scope=None, key=None):
|
|
499
|
+
_dcr_secrets = globals().get('__dcr_secret_values', {})
|
|
500
|
+
scoped_key = f'{scope}/{key}' if scope and key else None
|
|
501
|
+
if scoped_key and scoped_key in _dcr_secrets:
|
|
502
|
+
return _dcr_secrets[scoped_key]
|
|
503
|
+
if key and key in _dcr_secrets:
|
|
504
|
+
return _dcr_secrets[key]
|
|
505
|
+
env_key = key or scoped_key or ''
|
|
506
|
+
env_key = env_key.replace('-', '_').replace('/', '_').upper()
|
|
507
|
+
if env_key and env_key in os.environ:
|
|
508
|
+
return os.environ[env_key]
|
|
509
|
+
return ''
|
|
510
|
+
class DcrLibraryProxy:
|
|
511
|
+
def __init__(self, wrapped):
|
|
512
|
+
self._wrapped = wrapped
|
|
513
|
+
def restartPython(self):
|
|
514
|
+
print('[spark-cell-runner] Skipping dbutils.library.restartPython() in local execution')
|
|
515
|
+
def __getattr__(self, name):
|
|
516
|
+
if self._wrapped is None:
|
|
517
|
+
raise AttributeError(name)
|
|
518
|
+
return getattr(self._wrapped.library, name)
|
|
519
|
+
class DcrDbutilsProxy:
|
|
520
|
+
def __init__(self, wrapped):
|
|
521
|
+
self._wrapped = wrapped
|
|
522
|
+
self.fs = DcrFsProxy(wrapped)
|
|
523
|
+
self.widgets = DcrWidgetsProxy()
|
|
524
|
+
self.notebook = DcrNotebookProxy()
|
|
525
|
+
self.secrets = DcrSecretsProxy()
|
|
526
|
+
self.library = DcrLibraryProxy(wrapped)
|
|
527
|
+
def __getattr__(self, name):
|
|
528
|
+
if self._wrapped is None:
|
|
529
|
+
raise AttributeError(name)
|
|
530
|
+
return getattr(self._wrapped, name)
|
|
531
|
+
try:
|
|
532
|
+
from databricks.sdk.runtime import dbutils as runtime_dbutils
|
|
533
|
+
except Exception:
|
|
534
|
+
runtime_dbutils = None
|
|
535
|
+
dbutils = globals().get('dbutils') or DcrDbutilsProxy(runtime_dbutils)
|
|
536
|
+
__SCR_CONNECTION_BLOCK__
|