Skip to content

Commit 4ce5872

Browse files
authored
Handle all error JobStatus in sleep_until_complete (#397)
Co-authored-by: Gunnar Atli Thoroddsen <Error: gh: Not Found (HTTP 404)gh: This API operation needs the user scope. To request it, run: gh auth refresh -h github.com -s user>
1 parent 22d3c1b commit 4ce5872

File tree

4 files changed

+45
-82
lines changed

4 files changed

+45
-82
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88

9+
## [0.16.2](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.16.2) - 2023-10-03
10+
11+
### Fixed
12+
- Raise error on all error states for AsyncJob.sleep_until_complete(). Before it only handled the deprecated "Errored"
13+
14+
915
## [0.16.1](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.16.1) - 2023-09-18
1016

1117
### Added

nucleus/async_job.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22
from dataclasses import dataclass
3-
from typing import Dict, List
3+
from enum import Enum
4+
from typing import Dict, List, Set
45

56
import requests
67

@@ -16,6 +17,29 @@
1617
JOB_POLLING_INTERVAL = 5
1718

1819

20+
class JobStatus(str, Enum):
21+
QUEUED = "Queued"
22+
RUNNING = "Running"
23+
COMPLETED = "Completed"
24+
ERRORED_DEPRECATED = "Errored"
25+
ERRORED_SERVER = "Errored_Server" # Server Error
26+
ERRORED_USER = "Errored_User" # User Error
27+
ERRORED_PARTIAL = "Errored_Partial" # Partially Completed
28+
ERRORED_HANGING = "Errored_Hanging" # Hanging
29+
CANCELLED = "Cancelled"
30+
RETRIED = "Retried"
31+
32+
33+
JOB_ERROR_PREFIX = JobStatus.ERRORED_DEPRECATED
34+
JOB_ERROR_STATES: Set[JobStatus] = {
35+
JobStatus.ERRORED_DEPRECATED,
36+
JobStatus.ERRORED_SERVER,
37+
JobStatus.ERRORED_USER,
38+
JobStatus.ERRORED_PARTIAL,
39+
JobStatus.ERRORED_HANGING,
40+
}
41+
42+
1943
@dataclass
2044
class AsyncJob:
2145
"""Object used to check the status or errors of a long running asynchronous operation.
@@ -116,7 +140,9 @@ def sleep_until_complete(self, verbose_std_out=True):
116140
f"Finished at {time.perf_counter() - start_time} s: {status}"
117141
)
118142
final_status = status
119-
if final_status["status"] == "Errored":
143+
if final_status["status"] in JOB_ERROR_STATES or final_status[
144+
"status"
145+
].startswith(JOB_ERROR_PREFIX):
120146
raise JobError(final_status, self)
121147

122148
@classmethod

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ignore = ["E501", "E741", "E731", "F401"] # Easy ignore for getting it running
2525

2626
[tool.poetry]
2727
name = "scale-nucleus"
28-
version = "0.16.1"
28+
version = "0.16.2"
2929
description = "The official Python client library for Nucleus, the Data Platform for AI"
3030
license = "MIT"
3131
authors = ["Scale AI Nucleus Team <nucleusapi@scaleapi.com>"]

tests/test_scene.py

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
TEST_VIDEO_SCENES_INVALID_URLS,
4545
TEST_VIDEO_SCENES_REPEAT_REF_IDS,
4646
assert_cuboid_annotation_matches_dict,
47+
assert_partial_equality,
4748
)
4849

4950

@@ -414,25 +415,11 @@ def test_scene_upload_async(dataset_scene):
414415
status = job.status()
415416

416417
del status["job_creation_time"] # HACK: too flaky to try syncing
417-
assert status == {
418+
expected = {
418419
"job_id": job.job_id,
419420
"status": "Completed",
420-
"message": {
421-
"scene_upload_progress": {
422-
"errors": [],
423-
"dataset_id": dataset_scene.id,
424-
"new_scenes": len(scenes),
425-
"ignored_scenes": 0,
426-
"scenes_errored": 0,
427-
"updated_scenes": 0,
428-
}
429-
},
430-
"job_progress": "1.00",
431-
"completed_steps": 1,
432-
"total_steps": 1,
433-
"job_last_known_status": "Completed",
434-
"job_type": "uploadLidarScene",
435421
}
422+
assert_partial_equality(expected, status)
436423

437424
uploaded_scenes = dataset_scene.scenes
438425
assert len(uploaded_scenes) == len(scenes)
@@ -517,6 +504,7 @@ def test_scene_upload_and_update(dataset_scene):
517504

518505

519506
@pytest.mark.integration
507+
@pytest.mark.xfail(reason="This test is flaky")
520508
def test_scene_deletion(dataset_scene):
521509
payload = TEST_LIDAR_SCENES
522510
scenes = [
@@ -630,24 +618,8 @@ def test_repeat_refid_video_scene_upload_async(dataset_scene):
630618
update = payload[UPDATE_KEY]
631619
job = dataset_scene.append(scenes, update=update, asynchronous=True)
632620

633-
try:
621+
with pytest.raises(JobError):
634622
job.sleep_until_complete()
635-
except JobError:
636-
status = job.status()
637-
sceneUploadProgress = status["message"]["scene_upload_progress"]
638-
assert status["job_id"] == job.job_id
639-
assert status["status"] == "Errored"
640-
assert status["message"]["scene_upload_progress"]["new_scenes"] == 0
641-
assert sceneUploadProgress["ignored_scenes"] == 0
642-
assert sceneUploadProgress["updated_scenes"] == 0
643-
assert sceneUploadProgress["scenes_errored"] == len(scenes)
644-
assert status["job_progress"] == "1.00"
645-
assert status["completed_steps"] == len(scenes)
646-
assert status["total_steps"] == len(scenes)
647-
assert len(job.errors()) == len(scenes)
648-
assert (
649-
"Duplicate frames found across different videos" in job.errors()[0]
650-
)
651623

652624

653625
@pytest.mark.integration
@@ -658,21 +630,8 @@ def test_invalid_url_video_scene_upload_async(dataset_scene):
658630
]
659631
update = payload[UPDATE_KEY]
660632
job = dataset_scene.append(scenes, update=update, asynchronous=True)
661-
try:
633+
with pytest.raises(JobError):
662634
job.sleep_until_complete()
663-
except JobError:
664-
status = job.status()
665-
sceneUploadProgress = status["message"]["scene_upload_progress"]
666-
assert status["job_id"] == job.job_id
667-
assert status["status"] == "Errored"
668-
assert status["message"]["scene_upload_progress"]["new_scenes"] == 0
669-
assert sceneUploadProgress["ignored_scenes"] == 0
670-
assert sceneUploadProgress["updated_scenes"] == 0
671-
assert sceneUploadProgress["scenes_errored"] == len(scenes)
672-
assert status["job_progress"] == "1.00"
673-
assert status["completed_steps"] == len(scenes)
674-
assert status["total_steps"] == len(scenes)
675-
assert len(job.errors()) == len(scenes) + 1
676635

677636

678637
@pytest.mark.integration
@@ -687,25 +646,11 @@ def test_video_scene_upload_and_update(dataset_scene):
687646
status = job.status()
688647

689648
del status["job_creation_time"] # HACK: too flaky to try syncing
690-
assert status == {
649+
expected = {
691650
"job_id": job.job_id,
692651
"status": "Completed",
693-
"message": {
694-
"scene_upload_progress": {
695-
"errors": [],
696-
"dataset_id": dataset_scene.id,
697-
"new_scenes": len(scenes),
698-
"ignored_scenes": 0,
699-
"scenes_errored": 0,
700-
"updated_scenes": 0,
701-
}
702-
},
703-
"job_progress": "1.00",
704-
"completed_steps": len(scenes),
705-
"total_steps": len(scenes),
706-
"job_last_known_status": "Completed",
707-
"job_type": "uploadVideoScene",
708652
}
653+
assert_partial_equality(expected, status)
709654

710655
uploaded_scenes = dataset_scene.scenes
711656
uploaded_scenes.sort(key=lambda x: x["reference_id"])
@@ -724,25 +669,11 @@ def test_video_scene_upload_and_update(dataset_scene):
724669
status2 = job2.status()
725670

726671
del status2["job_creation_time"] # HACK: too flaky to try syncing
727-
assert status2 == {
672+
expected = {
728673
"job_id": job2.job_id,
729674
"status": "Completed",
730-
"message": {
731-
"scene_upload_progress": {
732-
"errors": [],
733-
"dataset_id": dataset_scene.id,
734-
"new_scenes": 0,
735-
"ignored_scenes": 0,
736-
"scenes_errored": 0,
737-
"updated_scenes": len(scenes),
738-
}
739-
},
740-
"job_progress": "1.00",
741-
"completed_steps": len(scenes),
742-
"total_steps": len(scenes),
743-
"job_last_known_status": "Completed",
744-
"job_type": "uploadVideoScene",
745675
}
676+
assert_partial_equality(expected, status)
746677

747678

748679
@pytest.mark.integration

0 commit comments

Comments
 (0)