1+ from gemd .enumeration .base_enumeration import BaseEnumeration
12from logging import getLogger
23from time import time , sleep
34from typing import Union
45from uuid import UUID
6+ from warnings import warn
57
68from citrine ._rest .resource import Resource
79from citrine ._serialization .properties import Set as PropertySet , String , Object
@@ -23,6 +25,16 @@ class JobSubmissionResponse(Resource['JobSubmissionResponse']):
2325 """:UUID: job id of the job submission request"""
2426
2527
28+ class JobStatus (BaseEnumeration ):
29+ """The valid status codes for a job."""
30+
31+ SUBMITTED = "Submitted"
32+ PENDING = "Pending"
33+ RUNNING = "Running"
34+ SUCCESS = "Success"
35+ FAILURE = "Failure"
36+
37+
2638class TaskNode (Resource ['TaskNode' ]):
2739 """Individual task status.
2840
@@ -33,14 +45,29 @@ class TaskNode(Resource['TaskNode']):
3345 """:str: unique identification number for the job task"""
3446 task_type = properties .String ("task_type" )
3547 """:str: the type of task running"""
36- status = properties .String ("status" )
37- """:str: The last reported status of this particular task.
38- One of "Submitted", "Pending", "Running", "Success", or "Failure"."""
48+ _status = properties .String ("status" )
3949 dependencies = PropertySet (String (), "dependencies" )
4050 """:Set[str]: all the tasks that this task is dependent on"""
4151 failure_reason = properties .Optional (String (), "failure_reason" )
4252 """:str: if a task has failed, the failure reason will be in this parameter"""
4353
54+ @property
55+ def status (self ) -> Union [JobStatus , str ]:
56+ """The last reported status of this particular task."""
57+ if resolved := JobStatus .from_str (self ._status , exception = False ):
58+ return resolved
59+ else :
60+ return self ._status
61+
62+ @status .setter
63+ def status (self , value ):
64+ if JobStatus .from_str (value , exception = False ) is None :
65+ warn (
66+ f"{ value } is not a recognized JobStatus; this will become an error as of v4.0.0." ,
67+ DeprecationWarning
68+ )
69+ self ._status = value
70+
4471
4572class JobStatusResponse (Resource ['JobStatusResponse' ]):
4673 """A response to a job status check.
@@ -50,13 +77,37 @@ class JobStatusResponse(Resource['JobStatusResponse']):
5077
5178 job_type = properties .String ("job_type" )
5279 """:str: the type of job for this status report"""
53- status = properties .String ("status" )
80+ _status = properties .String ("status" )
5481 """:str: The status of the job. One of "Running", "Success", or "Failure"."""
5582 tasks = properties .List (Object (TaskNode ), "tasks" )
5683 """:List[TaskNode]: all of the constituent task required to complete this job"""
5784 output = properties .Optional (properties .Mapping (String , String ), 'output' )
5885 """:Optional[dict[str, str]]: job output properties and results"""
5986
87+ @property
88+ def status (self ) -> Union [JobStatus , str ]:
89+ """The last reported status of this particular task."""
90+ if resolved := JobStatus .from_str (self ._status , exception = False ):
91+ return resolved
92+ else :
93+ return self ._status
94+
95+ @status .setter
96+ def status (self , value ):
97+ if resolved := JobStatus .from_str (value , exception = False ):
98+ if resolved not in [JobStatus .RUNNING , JobStatus .SUCCESS , JobStatus .FAILURE ]:
99+ warn (
100+ f"{ value } is not a valid JobStatus for a JobStatusResponse; "
101+ f"this will become an error as of v4.0.0." ,
102+ DeprecationWarning
103+ )
104+ else :
105+ warn (
106+ f"{ value } is not a recognized JobStatus; this will become an error as of v4.0.0." ,
107+ DeprecationWarning
108+ )
109+ self ._status = value
110+
60111
61112def _poll_for_job_completion (session : Session ,
62113 job : Union [JobSubmissionResponse , UUID , str ],
@@ -102,7 +153,7 @@ def _poll_for_job_completion(session: Session,
102153 while True :
103154 response = session .get_resource (path = path , params = params )
104155 status : JobStatusResponse = JobStatusResponse .build (response )
105- if status .status in ['Success' , 'Failure' ]:
156+ if status .status in [JobStatus . SUCCESS , JobStatus . FAILURE ]:
106157 break
107158 elif time () - start_time < timeout :
108159 logger .info (
@@ -115,12 +166,12 @@ def _poll_for_job_completion(session: Session,
115166 f'Note job on server is unaffected by this timeout.' )
116167 logger .debug ('Last status: {}' .format (status .dump ()))
117168 raise PollingTimeoutError ('Job {} timed out.' .format (job_id ))
118- if status .status == 'Failure' :
169+ if status .status == JobStatus . FAILURE :
119170 logger .debug (f'Job terminated with Failure status: { status .dump ()} ' )
120171 if raise_errors :
121172 failure_reasons = []
122173 for task in status .tasks :
123- if task .status == 'Failure' :
174+ if task .status == JobStatus . FAILURE :
124175 logger .error (f'Task { task .id } failed with reason "{ task .failure_reason } "' )
125176 failure_reasons .append (task .failure_reason )
126177 raise JobFailureError (
0 commit comments