11from attrs import field , frozen
22from datetime import datetime
3- from metafold .api import asdatetime , asdict , optional
3+ from metafold .api import asdatetime , asdict , optional , optional_datetime
44from metafold .assets import Asset
55from metafold .client import Client
66from metafold .exceptions import PollTimeout
77from requests import Response
8- from typing import Any , Optional , Union
9- import time
8+ from typing import Any , Optional , TypeAlias , TypedDict , Union
109
1110
1211def _assets (v : list [Union [dict [str , Any ], Asset ]]) -> list [Asset ]:
1312 return [a if isinstance (a , Asset ) else Asset (** a ) for a in v ]
1413
1514
15+ AssetDict : TypeAlias = dict [str , Union [dict [str , Any ], Asset ]]
16+
17+
18+ def _assets_dict (v : AssetDict ) -> dict [str , Asset ]:
19+ return dict (
20+ (k , a if isinstance (a , Asset ) else Asset (** a ))
21+ for k , a in v .items ()
22+ )
23+
24+
25+ class IODict (TypedDict ):
26+ params : Optional [dict [str , Any ]]
27+ assets : Optional [dict [str , AssetDict ]]
28+
29+
30+ @frozen (kw_only = True )
31+ class IO :
32+ """Job input/output.
33+
34+ Attributes:
35+ params: JSON-encoded parameter values.
36+ assets: Related assets.
37+ """
38+ params : Optional [dict [str , Any ]] = None
39+ assets : Optional [dict [str , Asset ]] = field (
40+ converter = optional (_assets_dict ), default = None )
41+
42+ @staticmethod
43+ def from_dict (d : IODict ) -> "IO" :
44+ return IO (params = d .get ("params" ), assets = d .get ("assets" ))
45+
46+
1647@frozen (kw_only = True )
1748class Job :
1849 """Job resource.
@@ -21,27 +52,31 @@ class Job:
2152 id: Job ID.
2253 name: Job name.
2354 type: Job type.
24- parameters: Job parameters.
55+ state: Job state. May be one of: pending, started, success, failure, or
56+ canceled.
2557 created: Job creation datetime.
58+ started: Job started datetime.
2659 finished: Job finished datetime.
27- state: Job state. May be one of: pending, started, success, or failure.
28- assets: List of generated asset resources.
29- meta: Additional metadata generated by the job.
60+ error: Error message for failed jobs.
61+ inputs: Input assets and parameters.
62+ outputs: Output assets and parameters.
63+ assets: (Deprecated) List of generated asset resources.
64+ parameters: (Deprecated) Job parameters.
65+ meta: (Deprecated) Additional metadata generated by the job.
3066 """
3167 id : str
3268 name : Optional [str ] = None
3369 type : str
34- parameters : dict [str , Any ]
35- created : datetime = field (converter = asdatetime )
36- finished : Optional [datetime ] = field (
37- converter = lambda v : optional (asdatetime )(v ),
38- default = None ,
39- )
4070 state : str
41- assets : Optional [list [Asset ]] = field (
42- converter = lambda v : optional (_assets )(v ),
43- default = None ,
44- )
71+ created : datetime = field (converter = asdatetime )
72+ started : Optional [datetime ] = field (converter = optional_datetime , default = None )
73+ finished : Optional [datetime ] = field (converter = optional_datetime , default = None )
74+ error : Optional [str ] = None
75+ inputs : IO = field (converter = lambda v : v if isinstance (v , IO ) else IO .from_dict (v ))
76+ outputs : IO = field (converter = lambda v : v if isinstance (v , IO ) else IO .from_dict (v ))
77+ # NOTE(ryan): Deprecated
78+ assets : Optional [list [Asset ]] = field (converter = optional (_assets ), default = None )
79+ parameters : dict [str , Any ]
4580 meta : dict [str , Any ]
4681
4782
@@ -61,7 +96,8 @@ def list(
6196
6297 Args:
6398 sort: Sort string. For details on syntax see the Metafold API docs.
64- Supported sorting fields are: "id", "name", or "created".
99+ Supported sorting fields are: "id", "name", "created", "started", or
100+ "finished".
65101 q: Query string. For details on syntax see the Metafold API docs.
66102 Supported search fields are: "id", "name", "type", and "state".
67103 project_id: Job project ID.
@@ -142,31 +178,8 @@ def run_status(
142178 r : Response = self ._client .post (f"/projects/{ project_id } /jobs" , json = payload )
143179 return r .json ()["link" ]
144180
145- def poll (
146- self , url : str ,
147- timeout : Union [int , float ] = 120 ,
148- every : Union [int , float ] = 1 ,
149- ) -> Response :
150- """Poll the given URL in regular intervals.
151-
152- Helpful for waiting on job results given a status URL.
153-
154- Args:
155- timeout: Time in seconds to wait for a result.
156- every: Frequency in seconds.
157-
158- Returns:
159- HTTP response.
160- """
161- t0 = time .monotonic ()
162- r = self ._client .get (url )
163- while r .status_code == 202 :
164- elapsed = time .monotonic () - t0
165- if elapsed >= timeout :
166- raise PollTimeout ("Job timed out" )
167- time .sleep (1 )
168- r = self ._client .get (url )
169- return r
181+ def poll (self , * args , ** kwargs ):
182+ return self ._client .poll (* args , ** kwargs )
170183
171184 def update (
172185 self , job_id : str ,
@@ -188,3 +201,14 @@ def update(
188201 payload = asdict (name = name )
189202 r : Response = self ._client .patch (url , data = payload )
190203 return Job (** r .json ())
204+
205+ def delete (self , job_id : str , project_id : Optional [str ] = None ):
206+ """Delete a job.
207+
208+ Args:
209+ job_id: ID of job to delete.
210+ project_id: Job project ID.
211+ """
212+ project_id = self ._client .project_id (project_id )
213+ url = f"/projects/{ project_id } /jobs/{ job_id } "
214+ self ._client .delete (url )
0 commit comments