sutro 0.1.17__py3-none-any.whl → 0.1.18__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 sutro might be problematic. Click here for more details.
- sutro/sdk.py +96 -12
- {sutro-0.1.17.dist-info → sutro-0.1.18.dist-info}/METADATA +1 -1
- sutro-0.1.18.dist-info/RECORD +8 -0
- sutro-0.1.17.dist-info/RECORD +0 -8
- {sutro-0.1.17.dist-info → sutro-0.1.18.dist-info}/WHEEL +0 -0
- {sutro-0.1.17.dist-info → sutro-0.1.18.dist-info}/entry_points.txt +0 -0
- {sutro-0.1.17.dist-info → sutro-0.1.18.dist-info}/licenses/LICENSE +0 -0
sutro/sdk.py
CHANGED
|
@@ -266,11 +266,6 @@ class Sutro:
|
|
|
266
266
|
"random_seed_per_input": random_seed_per_input,
|
|
267
267
|
"truncate_rows": truncate_rows
|
|
268
268
|
}
|
|
269
|
-
if dry_run:
|
|
270
|
-
spinner_text = to_colored_text("Retrieving cost estimates...")
|
|
271
|
-
else:
|
|
272
|
-
t = f"Creating priority {job_priority} job"
|
|
273
|
-
spinner_text = to_colored_text(t)
|
|
274
269
|
|
|
275
270
|
# There are two gotchas with yaspin:
|
|
276
271
|
# 1. Can't use print while in spinner is running
|
|
@@ -279,6 +274,8 @@ class Sutro:
|
|
|
279
274
|
# Terminal size {self._terminal_width} is too small to display spinner with the given settings.
|
|
280
275
|
# https://github.com/pavdmyt/yaspin/blob/9c7430b499ab4611888ece39783a870e4a05fa45/yaspin/core.py#L568-L571
|
|
281
276
|
job_id = None
|
|
277
|
+
t = f"Creating {'[dry run] ' if dry_run else ''}priority {job_priority} job"
|
|
278
|
+
spinner_text = to_colored_text(t)
|
|
282
279
|
with yaspin(SPINNER, text=spinner_text, color=YASPIN_COLOR) as spinner:
|
|
283
280
|
response = requests.post(
|
|
284
281
|
endpoint, data=json.dumps(payload), headers=headers
|
|
@@ -292,13 +289,19 @@ class Sutro:
|
|
|
292
289
|
print(to_colored_text(response.json(), state="fail"))
|
|
293
290
|
return None
|
|
294
291
|
else:
|
|
292
|
+
job_id = response_data["results"]
|
|
295
293
|
if dry_run:
|
|
296
294
|
spinner.write(
|
|
297
|
-
to_colored_text("
|
|
295
|
+
to_colored_text(f"Awaiting cost estimates with job ID: {job_id}. You can safely detach and retrieve the cost estimates later.", state="info")
|
|
296
|
+
)
|
|
297
|
+
spinner.stop()
|
|
298
|
+
self.await_job_completion(job_id, obtain_results=False)
|
|
299
|
+
cost_estimate = self._get_job_cost_estimate(job_id)
|
|
300
|
+
spinner.write(
|
|
301
|
+
to_colored_text(f"✔ Cost estimates retrieved for job {job_id}: ${cost_estimate}", state="success")
|
|
298
302
|
)
|
|
299
|
-
return
|
|
303
|
+
return job_id
|
|
300
304
|
else:
|
|
301
|
-
job_id = response_data["results"]
|
|
302
305
|
spinner.write(
|
|
303
306
|
to_colored_text(
|
|
304
307
|
f"🛠️ Priority {job_priority} Job created with ID: {job_id}",
|
|
@@ -315,6 +318,13 @@ class Sutro:
|
|
|
315
318
|
|
|
316
319
|
success = False
|
|
317
320
|
if stay_attached and job_id is not None:
|
|
321
|
+
spinner.write(to_colored_text("Awaiting job start...", "info"))
|
|
322
|
+
started = self._await_job_start(job_id)
|
|
323
|
+
if not started:
|
|
324
|
+
failure_reason = self._get_failure_reason(job_id)
|
|
325
|
+
spinner.write(to_colored_text(f"Failure reason: {failure_reason['message']}", "fail"))
|
|
326
|
+
return None
|
|
327
|
+
|
|
318
328
|
s = requests.Session()
|
|
319
329
|
payload = {
|
|
320
330
|
"job_id": job_id,
|
|
@@ -719,6 +729,40 @@ class Sutro:
|
|
|
719
729
|
return
|
|
720
730
|
return response.json()["jobs"]
|
|
721
731
|
|
|
732
|
+
def _list_jobs_helper(self):
|
|
733
|
+
"""
|
|
734
|
+
Helper function to list jobs.
|
|
735
|
+
"""
|
|
736
|
+
endpoint = f"{self.base_url}/list-jobs"
|
|
737
|
+
headers = {
|
|
738
|
+
"Authorization": f"Key {self.api_key}",
|
|
739
|
+
"Content-Type": "application/json",
|
|
740
|
+
}
|
|
741
|
+
response = requests.get(endpoint, headers=headers)
|
|
742
|
+
if response.status_code != 200:
|
|
743
|
+
return None
|
|
744
|
+
return response.json()["jobs"]
|
|
745
|
+
|
|
746
|
+
def _get_job_cost_estimate(self, job_id: str):
|
|
747
|
+
"""
|
|
748
|
+
Get the cost estimate for a job.
|
|
749
|
+
"""
|
|
750
|
+
all_jobs = self._list_jobs_helper()
|
|
751
|
+
for job in all_jobs:
|
|
752
|
+
if job["job_id"] == job_id:
|
|
753
|
+
return job["cost_estimate"]
|
|
754
|
+
return None
|
|
755
|
+
|
|
756
|
+
def _get_failure_reason(self, job_id: str):
|
|
757
|
+
"""
|
|
758
|
+
Get the failure reason for a job.
|
|
759
|
+
"""
|
|
760
|
+
all_jobs = self._list_jobs_helper()
|
|
761
|
+
for job in all_jobs:
|
|
762
|
+
if job["job_id"] == job_id:
|
|
763
|
+
return job["failure_reason"]
|
|
764
|
+
return None
|
|
765
|
+
|
|
722
766
|
def _fetch_job_status(self, job_id: str):
|
|
723
767
|
"""
|
|
724
768
|
Core logic to fetch job status from the API.
|
|
@@ -1162,7 +1206,7 @@ class Sutro:
|
|
|
1162
1206
|
return
|
|
1163
1207
|
return response.json()["quotas"]
|
|
1164
1208
|
|
|
1165
|
-
def await_job_completion(self, job_id: str, timeout: Optional[int] = 7200) -> list | None:
|
|
1209
|
+
def await_job_completion(self, job_id: str, timeout: Optional[int] = 7200, obtain_results: bool = True) -> list | None:
|
|
1166
1210
|
"""
|
|
1167
1211
|
Waits for job completion to occur and then returns the results upon
|
|
1168
1212
|
a successful completion.
|
|
@@ -1181,7 +1225,7 @@ class Sutro:
|
|
|
1181
1225
|
results = None
|
|
1182
1226
|
start_time = time.time()
|
|
1183
1227
|
with yaspin(
|
|
1184
|
-
|
|
1228
|
+
SPINNER, text=to_colored_text("Awaiting job completion"), color=YASPIN_COLOR
|
|
1185
1229
|
) as spinner:
|
|
1186
1230
|
while (time.time() - start_time) < timeout:
|
|
1187
1231
|
try:
|
|
@@ -1201,7 +1245,8 @@ class Sutro:
|
|
|
1201
1245
|
if status == JobStatus.SUCCEEDED:
|
|
1202
1246
|
spinner.write(to_colored_text("Job completed! Retrieving results...", "success"))
|
|
1203
1247
|
spinner.stop() # Stop this spinner as `get_job_results` has its own spinner text
|
|
1204
|
-
|
|
1248
|
+
if obtain_results:
|
|
1249
|
+
results = self.get_job_results(job_id)
|
|
1205
1250
|
break
|
|
1206
1251
|
if status == JobStatus.FAILED:
|
|
1207
1252
|
spinner.write(to_colored_text("Job has failed", "fail"))
|
|
@@ -1213,4 +1258,43 @@ class Sutro:
|
|
|
1213
1258
|
|
|
1214
1259
|
time.sleep(POLL_INTERVAL)
|
|
1215
1260
|
|
|
1216
|
-
return results
|
|
1261
|
+
return results
|
|
1262
|
+
|
|
1263
|
+
def _await_job_start(self, job_id: str, timeout: Optional[int] = 7200):
|
|
1264
|
+
"""
|
|
1265
|
+
Waits for job start to occur and then returns the results upon
|
|
1266
|
+
a successful start.
|
|
1267
|
+
|
|
1268
|
+
"""
|
|
1269
|
+
POLL_INTERVAL = 5
|
|
1270
|
+
|
|
1271
|
+
start_time = time.time()
|
|
1272
|
+
with yaspin(
|
|
1273
|
+
SPINNER, text=to_colored_text("Awaiting job completion"), color=YASPIN_COLOR
|
|
1274
|
+
) as spinner:
|
|
1275
|
+
while (time.time() - start_time) < timeout:
|
|
1276
|
+
try:
|
|
1277
|
+
status = self._fetch_job_status(job_id)
|
|
1278
|
+
except requests.HTTPError as e:
|
|
1279
|
+
spinner.write(
|
|
1280
|
+
to_colored_text(
|
|
1281
|
+
f"Bad status code: {e.response.status_code}", state="fail"
|
|
1282
|
+
)
|
|
1283
|
+
)
|
|
1284
|
+
spinner.stop()
|
|
1285
|
+
print(to_colored_text(e.response.json(), state="fail"))
|
|
1286
|
+
return None
|
|
1287
|
+
|
|
1288
|
+
spinner.text = to_colored_text(f"Job status is {status} for {job_id}")
|
|
1289
|
+
|
|
1290
|
+
if status == JobStatus.RUNNING or status == JobStatus.STARTING:
|
|
1291
|
+
return True
|
|
1292
|
+
if status == JobStatus.FAILED:
|
|
1293
|
+
return False
|
|
1294
|
+
if status == JobStatus.CANCELLED:
|
|
1295
|
+
return False
|
|
1296
|
+
|
|
1297
|
+
time.sleep(POLL_INTERVAL)
|
|
1298
|
+
|
|
1299
|
+
return False
|
|
1300
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
sutro/__init__.py,sha256=yUiVwcZ8QamSqDdRHgzoANyTZ-x3cPzlt2Fs5OllR_w,402
|
|
2
|
+
sutro/cli.py,sha256=6Qy9Vwaaho92HeO8YA_z1De4zp1dEFkSX3bEnLvdbkE,13203
|
|
3
|
+
sutro/sdk.py,sha256=Jjv6FQjRyHVF0_6qYHaP-qDOzdx8FlGLKIUod4g7sZU,49687
|
|
4
|
+
sutro-0.1.18.dist-info/METADATA,sha256=DpPBKOfef-Nlou5uN8AacVUtCZeWfq_iCIH8JBLRUNE,669
|
|
5
|
+
sutro-0.1.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
sutro-0.1.18.dist-info/entry_points.txt,sha256=eXvr4dvMV4UmZgR0zmrY8KOmNpo64cJkhNDywiadRFM,40
|
|
7
|
+
sutro-0.1.18.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
+
sutro-0.1.18.dist-info/RECORD,,
|
sutro-0.1.17.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
sutro/__init__.py,sha256=yUiVwcZ8QamSqDdRHgzoANyTZ-x3cPzlt2Fs5OllR_w,402
|
|
2
|
-
sutro/cli.py,sha256=6Qy9Vwaaho92HeO8YA_z1De4zp1dEFkSX3bEnLvdbkE,13203
|
|
3
|
-
sutro/sdk.py,sha256=z3cvU9zyAi7EJF_rFIb9mQMjE9zuh576-p8KuP_F1PM,46500
|
|
4
|
-
sutro-0.1.17.dist-info/METADATA,sha256=UDHp7-xr8tdKmDl_GbxxRbVVC6nx62yr6-nka4vBwOY,669
|
|
5
|
-
sutro-0.1.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
sutro-0.1.17.dist-info/entry_points.txt,sha256=eXvr4dvMV4UmZgR0zmrY8KOmNpo64cJkhNDywiadRFM,40
|
|
7
|
-
sutro-0.1.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
-
sutro-0.1.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|