digitalhub 0.8.0b0__py3-none-any.whl → 0.8.0b2__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 digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +62 -94
- digitalhub/client/__init__.py +0 -0
- digitalhub/client/builder.py +105 -0
- digitalhub/client/objects/__init__.py +0 -0
- digitalhub/client/objects/base.py +56 -0
- digitalhub/client/objects/dhcore.py +681 -0
- digitalhub/client/objects/local.py +533 -0
- digitalhub/context/__init__.py +0 -0
- digitalhub/context/builder.py +178 -0
- digitalhub/context/context.py +136 -0
- digitalhub/datastores/__init__.py +0 -0
- digitalhub/datastores/builder.py +134 -0
- digitalhub/datastores/objects/__init__.py +0 -0
- digitalhub/datastores/objects/base.py +85 -0
- digitalhub/datastores/objects/local.py +42 -0
- digitalhub/datastores/objects/remote.py +23 -0
- digitalhub/datastores/objects/s3.py +38 -0
- digitalhub/datastores/objects/sql.py +60 -0
- digitalhub/entities/__init__.py +0 -0
- digitalhub/entities/_base/__init__.py +0 -0
- digitalhub/entities/_base/api.py +346 -0
- digitalhub/entities/_base/base.py +82 -0
- digitalhub/entities/_base/crud.py +610 -0
- digitalhub/entities/_base/entity/__init__.py +0 -0
- digitalhub/entities/_base/entity/base.py +132 -0
- digitalhub/entities/_base/entity/context.py +118 -0
- digitalhub/entities/_base/entity/executable.py +380 -0
- digitalhub/entities/_base/entity/material.py +214 -0
- digitalhub/entities/_base/entity/unversioned.py +87 -0
- digitalhub/entities/_base/entity/versioned.py +94 -0
- digitalhub/entities/_base/metadata.py +59 -0
- digitalhub/entities/_base/spec/__init__.py +0 -0
- digitalhub/entities/_base/spec/base.py +58 -0
- digitalhub/entities/_base/spec/material.py +22 -0
- digitalhub/entities/_base/state.py +31 -0
- digitalhub/entities/_base/status/__init__.py +0 -0
- digitalhub/entities/_base/status/base.py +32 -0
- digitalhub/entities/_base/status/material.py +49 -0
- digitalhub/entities/_builders/__init__.py +0 -0
- digitalhub/entities/_builders/metadata.py +60 -0
- digitalhub/entities/_builders/name.py +31 -0
- digitalhub/entities/_builders/spec.py +43 -0
- digitalhub/entities/_builders/status.py +62 -0
- digitalhub/entities/_builders/uuid.py +33 -0
- digitalhub/entities/artifact/__init__.py +0 -0
- digitalhub/entities/artifact/builder.py +133 -0
- digitalhub/entities/artifact/crud.py +358 -0
- digitalhub/entities/artifact/entity/__init__.py +0 -0
- digitalhub/entities/artifact/entity/_base.py +39 -0
- digitalhub/entities/artifact/entity/artifact.py +9 -0
- digitalhub/entities/artifact/spec.py +39 -0
- digitalhub/entities/artifact/status.py +15 -0
- digitalhub/entities/dataitem/__init__.py +0 -0
- digitalhub/entities/dataitem/builder.py +144 -0
- digitalhub/entities/dataitem/crud.py +395 -0
- digitalhub/entities/dataitem/entity/__init__.py +0 -0
- digitalhub/entities/dataitem/entity/_base.py +75 -0
- digitalhub/entities/dataitem/entity/dataitem.py +9 -0
- digitalhub/entities/dataitem/entity/iceberg.py +7 -0
- digitalhub/entities/dataitem/entity/table.py +125 -0
- digitalhub/entities/dataitem/models.py +62 -0
- digitalhub/entities/dataitem/spec.py +61 -0
- digitalhub/entities/dataitem/status.py +38 -0
- digitalhub/entities/entity_types.py +19 -0
- digitalhub/entities/function/__init__.py +0 -0
- digitalhub/entities/function/builder.py +86 -0
- digitalhub/entities/function/crud.py +305 -0
- digitalhub/entities/function/entity.py +101 -0
- digitalhub/entities/function/models.py +118 -0
- digitalhub/entities/function/spec.py +81 -0
- digitalhub/entities/function/status.py +9 -0
- digitalhub/entities/model/__init__.py +0 -0
- digitalhub/entities/model/builder.py +152 -0
- digitalhub/entities/model/crud.py +358 -0
- digitalhub/entities/model/entity/__init__.py +0 -0
- digitalhub/entities/model/entity/_base.py +34 -0
- digitalhub/entities/model/entity/huggingface.py +9 -0
- digitalhub/entities/model/entity/mlflow.py +90 -0
- digitalhub/entities/model/entity/model.py +9 -0
- digitalhub/entities/model/entity/sklearn.py +9 -0
- digitalhub/entities/model/models.py +26 -0
- digitalhub/entities/model/spec.py +146 -0
- digitalhub/entities/model/status.py +33 -0
- digitalhub/entities/project/__init__.py +0 -0
- digitalhub/entities/project/builder.py +82 -0
- digitalhub/entities/project/crud.py +350 -0
- digitalhub/entities/project/entity.py +2060 -0
- digitalhub/entities/project/spec.py +50 -0
- digitalhub/entities/project/status.py +9 -0
- digitalhub/entities/registries.py +48 -0
- digitalhub/entities/run/__init__.py +0 -0
- digitalhub/entities/run/builder.py +77 -0
- digitalhub/entities/run/crud.py +232 -0
- digitalhub/entities/run/entity.py +461 -0
- digitalhub/entities/run/spec.py +153 -0
- digitalhub/entities/run/status.py +114 -0
- digitalhub/entities/secret/__init__.py +0 -0
- digitalhub/entities/secret/builder.py +93 -0
- digitalhub/entities/secret/crud.py +294 -0
- digitalhub/entities/secret/entity.py +73 -0
- digitalhub/entities/secret/spec.py +35 -0
- digitalhub/entities/secret/status.py +9 -0
- digitalhub/entities/task/__init__.py +0 -0
- digitalhub/entities/task/builder.py +74 -0
- digitalhub/entities/task/crud.py +241 -0
- digitalhub/entities/task/entity.py +135 -0
- digitalhub/entities/task/models.py +199 -0
- digitalhub/entities/task/spec.py +51 -0
- digitalhub/entities/task/status.py +9 -0
- digitalhub/entities/utils.py +184 -0
- digitalhub/entities/workflow/__init__.py +0 -0
- digitalhub/entities/workflow/builder.py +91 -0
- digitalhub/entities/workflow/crud.py +304 -0
- digitalhub/entities/workflow/entity.py +77 -0
- digitalhub/entities/workflow/spec.py +15 -0
- digitalhub/entities/workflow/status.py +9 -0
- digitalhub/readers/__init__.py +0 -0
- digitalhub/readers/builder.py +54 -0
- digitalhub/readers/objects/__init__.py +0 -0
- digitalhub/readers/objects/base.py +70 -0
- digitalhub/readers/objects/pandas.py +207 -0
- digitalhub/readers/registry.py +15 -0
- digitalhub/registry/__init__.py +0 -0
- digitalhub/registry/models.py +87 -0
- digitalhub/registry/registry.py +74 -0
- digitalhub/registry/utils.py +150 -0
- digitalhub/runtimes/__init__.py +0 -0
- digitalhub/runtimes/base.py +164 -0
- digitalhub/runtimes/builder.py +53 -0
- digitalhub/runtimes/kind_registry.py +170 -0
- digitalhub/stores/__init__.py +0 -0
- digitalhub/stores/builder.py +257 -0
- digitalhub/stores/objects/__init__.py +0 -0
- digitalhub/stores/objects/base.py +189 -0
- digitalhub/stores/objects/local.py +230 -0
- digitalhub/stores/objects/remote.py +143 -0
- digitalhub/stores/objects/s3.py +563 -0
- digitalhub/stores/objects/sql.py +328 -0
- digitalhub/utils/__init__.py +0 -0
- digitalhub/utils/data_utils.py +127 -0
- digitalhub/utils/env_utils.py +123 -0
- digitalhub/utils/exceptions.py +55 -0
- digitalhub/utils/file_utils.py +204 -0
- digitalhub/utils/generic_utils.py +207 -0
- digitalhub/utils/git_utils.py +148 -0
- digitalhub/utils/io_utils.py +79 -0
- digitalhub/utils/logger.py +17 -0
- digitalhub/utils/uri_utils.py +56 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b2.dist-info}/METADATA +27 -12
- digitalhub-0.8.0b2.dist-info/RECORD +161 -0
- test/test_crud_artifacts.py +1 -1
- test/test_crud_dataitems.py +1 -1
- test/test_crud_functions.py +1 -1
- test/test_crud_runs.py +1 -1
- test/test_crud_tasks.py +1 -1
- digitalhub-0.8.0b0.dist-info/RECORD +0 -14
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b2.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b2.dist-info}/WHEEL +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import typing
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from digitalhub.entities._base.crud import (
|
|
10
|
+
list_entity_api_base,
|
|
11
|
+
list_entity_api_ctx,
|
|
12
|
+
logs_api,
|
|
13
|
+
read_entity_api_ctx,
|
|
14
|
+
resume_api,
|
|
15
|
+
stop_api,
|
|
16
|
+
)
|
|
17
|
+
from digitalhub.entities._base.entity.unversioned import UnversionedEntity
|
|
18
|
+
from digitalhub.entities._base.state import State
|
|
19
|
+
from digitalhub.entities._builders.spec import build_spec
|
|
20
|
+
from digitalhub.entities._builders.status import build_status
|
|
21
|
+
from digitalhub.entities.entity_types import EntityTypes
|
|
22
|
+
from digitalhub.registry.registry import registry
|
|
23
|
+
from digitalhub.runtimes.builder import build_runtime
|
|
24
|
+
from digitalhub.utils.exceptions import EntityError
|
|
25
|
+
from digitalhub.utils.logger import LOGGER
|
|
26
|
+
|
|
27
|
+
if typing.TYPE_CHECKING:
|
|
28
|
+
from digitalhub.entities._base.entity.material import MaterialEntity
|
|
29
|
+
from digitalhub.entities._base.metadata import Metadata
|
|
30
|
+
from digitalhub.entities.run.spec import RunSpec
|
|
31
|
+
from digitalhub.entities.run.status import RunStatus
|
|
32
|
+
from digitalhub.runtimes.base import Runtime
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Run(UnversionedEntity):
|
|
36
|
+
"""
|
|
37
|
+
A class representing a run.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
ENTITY_TYPE = EntityTypes.RUN.value
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
project: str,
|
|
45
|
+
uuid: str,
|
|
46
|
+
kind: str,
|
|
47
|
+
metadata: Metadata,
|
|
48
|
+
spec: RunSpec,
|
|
49
|
+
status: RunStatus,
|
|
50
|
+
user: str | None = None,
|
|
51
|
+
) -> None:
|
|
52
|
+
super().__init__(project, uuid, kind, metadata, spec, status, user)
|
|
53
|
+
|
|
54
|
+
self.spec: RunSpec
|
|
55
|
+
self.status: RunStatus
|
|
56
|
+
|
|
57
|
+
##############################
|
|
58
|
+
# Run Methods
|
|
59
|
+
##############################
|
|
60
|
+
|
|
61
|
+
def build(self) -> None:
|
|
62
|
+
"""
|
|
63
|
+
Build run.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
None
|
|
68
|
+
"""
|
|
69
|
+
runtime = self._get_runtime()
|
|
70
|
+
executable = self._get_executable(runtime)
|
|
71
|
+
task = self._get_task(runtime)
|
|
72
|
+
new_spec = runtime.build(executable, task, self.to_dict())
|
|
73
|
+
self.spec = build_spec(
|
|
74
|
+
self.kind,
|
|
75
|
+
**new_spec,
|
|
76
|
+
)
|
|
77
|
+
self._set_state(State.BUILT.value)
|
|
78
|
+
self.save()
|
|
79
|
+
|
|
80
|
+
def run(self) -> Run:
|
|
81
|
+
"""
|
|
82
|
+
Run run.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Run
|
|
87
|
+
Run object.
|
|
88
|
+
"""
|
|
89
|
+
self.refresh()
|
|
90
|
+
if self.spec.local_execution:
|
|
91
|
+
if not self._is_ready_to_run():
|
|
92
|
+
raise EntityError("Run is not in a state to run.")
|
|
93
|
+
self._set_state(State.RUNNING.value)
|
|
94
|
+
self.save(update=True)
|
|
95
|
+
|
|
96
|
+
# Try to get inputs if they exist
|
|
97
|
+
try:
|
|
98
|
+
self.spec.inputs = self.inputs(as_dict=True)
|
|
99
|
+
except EntityError:
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
status = self._get_runtime().run(self.to_dict())
|
|
104
|
+
except Exception as e:
|
|
105
|
+
self.refresh()
|
|
106
|
+
if self.spec.local_execution:
|
|
107
|
+
self._set_state(State.ERROR.value)
|
|
108
|
+
self._set_message(str(e))
|
|
109
|
+
self.save(update=True)
|
|
110
|
+
raise e
|
|
111
|
+
|
|
112
|
+
self.refresh()
|
|
113
|
+
if not self.spec.local_execution:
|
|
114
|
+
status.pop("state", None)
|
|
115
|
+
new_status = {**self.status.to_dict(), **status}
|
|
116
|
+
self._set_status(new_status)
|
|
117
|
+
self.save(update=True)
|
|
118
|
+
return self
|
|
119
|
+
|
|
120
|
+
def wait(self, log_info: bool = True) -> Run:
|
|
121
|
+
"""
|
|
122
|
+
Wait for run to finish.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
log_info : bool
|
|
127
|
+
If True, log information.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
Run
|
|
132
|
+
Run object.
|
|
133
|
+
"""
|
|
134
|
+
start = time.time()
|
|
135
|
+
while True:
|
|
136
|
+
if log_info:
|
|
137
|
+
LOGGER.info(f"Waiting for run {self.id} to finish...")
|
|
138
|
+
self.refresh()
|
|
139
|
+
time.sleep(5)
|
|
140
|
+
if self.status.state in [
|
|
141
|
+
State.STOPPED.value,
|
|
142
|
+
State.ERROR.value,
|
|
143
|
+
State.COMPLETED.value,
|
|
144
|
+
]:
|
|
145
|
+
if log_info:
|
|
146
|
+
current = time.time() - start
|
|
147
|
+
LOGGER.info(f"Run {self.id} finished in {current:.2f} seconds.")
|
|
148
|
+
return self
|
|
149
|
+
|
|
150
|
+
def inputs(self, as_dict: bool = False) -> list[dict]:
|
|
151
|
+
"""
|
|
152
|
+
Get inputs passed in spec as objects or as dictionaries.
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
as_dict : bool
|
|
157
|
+
If True, return inputs as dictionaries.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
list[dict]
|
|
162
|
+
List of input objects.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
return self.spec.get_inputs(as_dict=as_dict)
|
|
166
|
+
except AttributeError:
|
|
167
|
+
msg = f"Run of type {self.kind} has no inputs."
|
|
168
|
+
raise EntityError(msg)
|
|
169
|
+
|
|
170
|
+
def results(self) -> dict:
|
|
171
|
+
"""
|
|
172
|
+
Get results from runtime execution.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
dict
|
|
177
|
+
Results.
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
return self.status.get_results()
|
|
181
|
+
except AttributeError:
|
|
182
|
+
msg = f"Run of type {self.kind} has no results."
|
|
183
|
+
raise EntityError(msg)
|
|
184
|
+
|
|
185
|
+
def result(self, key: str) -> Any:
|
|
186
|
+
"""
|
|
187
|
+
Get result from runtime execution by key.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
key : str
|
|
192
|
+
Key of the result.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
Any
|
|
197
|
+
Result.
|
|
198
|
+
"""
|
|
199
|
+
return self.results().get(key)
|
|
200
|
+
|
|
201
|
+
def outputs(self, as_key: bool = False, as_dict: bool = False) -> dict:
|
|
202
|
+
"""
|
|
203
|
+
Get run objects results.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
as_key : bool
|
|
208
|
+
If True, return results as keys.
|
|
209
|
+
as_dict : bool
|
|
210
|
+
If True, return results as dictionaries.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
dict
|
|
215
|
+
List of output objects.
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
return self.status.get_outputs(as_key=as_key, as_dict=as_dict)
|
|
219
|
+
except AttributeError:
|
|
220
|
+
msg = f"Run of type {self.kind} has no outputs."
|
|
221
|
+
raise EntityError(msg)
|
|
222
|
+
|
|
223
|
+
def output(self, key: str, as_key: bool = False, as_dict: bool = False) -> MaterialEntity | dict | str | None:
|
|
224
|
+
"""
|
|
225
|
+
Get run object result by key.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
key : str
|
|
230
|
+
Key of the result.
|
|
231
|
+
as_key : bool
|
|
232
|
+
If True, return result as key.
|
|
233
|
+
as_dict : bool
|
|
234
|
+
If True, return result as dictionary.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
Entity | dict | str | None
|
|
239
|
+
Result.
|
|
240
|
+
"""
|
|
241
|
+
return self.outputs(as_key=as_key, as_dict=as_dict).get(key)
|
|
242
|
+
|
|
243
|
+
def values(self) -> dict:
|
|
244
|
+
"""
|
|
245
|
+
Get values from runtime execution.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
dict
|
|
250
|
+
Values from backend.
|
|
251
|
+
"""
|
|
252
|
+
try:
|
|
253
|
+
value_list = getattr(self.spec, "values", [])
|
|
254
|
+
value_list = value_list if value_list is not None else []
|
|
255
|
+
return self.status.get_values(value_list)
|
|
256
|
+
except AttributeError:
|
|
257
|
+
msg = f"Run of type {self.kind} has no values."
|
|
258
|
+
raise EntityError(msg)
|
|
259
|
+
|
|
260
|
+
def logs(self) -> dict:
|
|
261
|
+
"""
|
|
262
|
+
Get object from backend.
|
|
263
|
+
Returns empty dictionary if context is local.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
dict
|
|
268
|
+
Logs from backend.
|
|
269
|
+
"""
|
|
270
|
+
if self._context().local:
|
|
271
|
+
return {}
|
|
272
|
+
return logs_api(self.project, self.ENTITY_TYPE, self.id)
|
|
273
|
+
|
|
274
|
+
def stop(self) -> None:
|
|
275
|
+
"""
|
|
276
|
+
Stop run.
|
|
277
|
+
|
|
278
|
+
Returns
|
|
279
|
+
-------
|
|
280
|
+
None
|
|
281
|
+
"""
|
|
282
|
+
if not self._context().local and not self.spec.local_execution:
|
|
283
|
+
return stop_api(self.project, self.ENTITY_TYPE, self.id)
|
|
284
|
+
try:
|
|
285
|
+
self.status.stop()
|
|
286
|
+
except AttributeError:
|
|
287
|
+
raise EntityError("Stop is not supported in local execution.")
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
def resume(self) -> None:
|
|
291
|
+
"""
|
|
292
|
+
Resume run.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
None
|
|
297
|
+
"""
|
|
298
|
+
if not self._context().local and not self.spec.local_execution:
|
|
299
|
+
return resume_api(self.project, self.ENTITY_TYPE, self.id)
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
self.status.resume()
|
|
303
|
+
except AttributeError:
|
|
304
|
+
raise EntityError("Resume is not supported in local execution.")
|
|
305
|
+
return
|
|
306
|
+
# re-run
|
|
307
|
+
# TODO verify the logic and order
|
|
308
|
+
self.run()
|
|
309
|
+
|
|
310
|
+
def invoke(self, **kwargs) -> requests.Response:
|
|
311
|
+
"""
|
|
312
|
+
Invoke run.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
kwargs
|
|
317
|
+
Keyword arguments to pass to the request.
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
requests.Response
|
|
322
|
+
Response from service.
|
|
323
|
+
"""
|
|
324
|
+
try:
|
|
325
|
+
if not self._context().local and not self.spec.local_execution:
|
|
326
|
+
local = False
|
|
327
|
+
else:
|
|
328
|
+
local = True
|
|
329
|
+
if kwargs is None:
|
|
330
|
+
kwargs = {}
|
|
331
|
+
return self.status.invoke(local, **kwargs)
|
|
332
|
+
except AttributeError:
|
|
333
|
+
msg = f"Run of type {self.kind} has no invoke operation."
|
|
334
|
+
raise EntityError(msg)
|
|
335
|
+
|
|
336
|
+
##############################
|
|
337
|
+
# Helpers
|
|
338
|
+
##############################
|
|
339
|
+
|
|
340
|
+
def _is_ready_to_run(self) -> bool:
|
|
341
|
+
"""
|
|
342
|
+
Check if run is in a state ready for running (BUILT or STOPPED).
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
bool
|
|
347
|
+
True if run is in runnable state, False otherwise.
|
|
348
|
+
"""
|
|
349
|
+
return (self.status.state == State.BUILT.value) or (self.status.state == State.STOPPED.value)
|
|
350
|
+
|
|
351
|
+
def _set_status(self, status: dict) -> None:
|
|
352
|
+
"""
|
|
353
|
+
Set run status.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
status : dict
|
|
358
|
+
Status to set.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
None
|
|
363
|
+
"""
|
|
364
|
+
self.status: RunStatus = build_status(self.kind, **status)
|
|
365
|
+
|
|
366
|
+
def _set_state(self, state: str) -> None:
|
|
367
|
+
"""
|
|
368
|
+
Update run state.
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
state : str
|
|
373
|
+
State to set.
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
None
|
|
378
|
+
"""
|
|
379
|
+
self.status.state = state
|
|
380
|
+
|
|
381
|
+
def _set_message(self, message: str) -> None:
|
|
382
|
+
"""
|
|
383
|
+
Update run message.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
message : str
|
|
388
|
+
Message to set.
|
|
389
|
+
|
|
390
|
+
Returns
|
|
391
|
+
-------
|
|
392
|
+
None
|
|
393
|
+
"""
|
|
394
|
+
self.status.message = message
|
|
395
|
+
|
|
396
|
+
def _get_runtime(self) -> Runtime:
|
|
397
|
+
"""
|
|
398
|
+
Build runtime to build run or execute it.
|
|
399
|
+
|
|
400
|
+
Returns
|
|
401
|
+
-------
|
|
402
|
+
Runtime
|
|
403
|
+
Runtime object.
|
|
404
|
+
"""
|
|
405
|
+
return build_runtime(self.kind, self.project)
|
|
406
|
+
|
|
407
|
+
def _get_executable(self, runtime: Runtime) -> dict:
|
|
408
|
+
"""
|
|
409
|
+
Get object from backend. Reimplemented to avoid circular imports.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
runtime : Runtime
|
|
414
|
+
Runtime object.
|
|
415
|
+
|
|
416
|
+
Returns
|
|
417
|
+
-------
|
|
418
|
+
dict
|
|
419
|
+
Executable (function or workflow) from backend.
|
|
420
|
+
"""
|
|
421
|
+
exec_kind = runtime.get_executable_kind()
|
|
422
|
+
entity_type = registry.get_entity_type(exec_kind)
|
|
423
|
+
splitted = self.spec.task.split("/")
|
|
424
|
+
exec_name = splitted[-1].split(":")[0]
|
|
425
|
+
exec_id = splitted[-1].split(":")[1]
|
|
426
|
+
return read_entity_api_ctx(
|
|
427
|
+
exec_name,
|
|
428
|
+
entity_type=entity_type,
|
|
429
|
+
project=self.project,
|
|
430
|
+
entity_id=exec_id,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def _get_task(self, runtime: Runtime) -> dict:
|
|
434
|
+
"""
|
|
435
|
+
Get object from backend. Reimplemented to avoid circular imports.
|
|
436
|
+
|
|
437
|
+
Parameters
|
|
438
|
+
----------
|
|
439
|
+
runtime : Runtime
|
|
440
|
+
Runtime object.
|
|
441
|
+
|
|
442
|
+
Returns
|
|
443
|
+
-------
|
|
444
|
+
dict
|
|
445
|
+
Task from backend.
|
|
446
|
+
"""
|
|
447
|
+
executable_kind = runtime.get_executable_kind()
|
|
448
|
+
exec_string = f"{executable_kind}://{self.spec.task.split('://')[1]}"
|
|
449
|
+
|
|
450
|
+
# Local backend
|
|
451
|
+
if self._context().local:
|
|
452
|
+
tasks = list_entity_api_base(self._context().client, EntityTypes.TASK.value)
|
|
453
|
+
for i in tasks:
|
|
454
|
+
if i.get("spec").get("function") == exec_string:
|
|
455
|
+
return i
|
|
456
|
+
raise EntityError("Task not found.")
|
|
457
|
+
|
|
458
|
+
# Remote backend
|
|
459
|
+
task_kind = self.spec.task.split("://")[0]
|
|
460
|
+
params = {"function": exec_string, "kind": task_kind}
|
|
461
|
+
return list_entity_api_ctx(self.project, EntityTypes.TASK.value, params=params)[0]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from digitalhub.entities._base.spec.base import Spec, SpecParams
|
|
6
|
+
from digitalhub.entities.artifact.crud import get_artifact
|
|
7
|
+
from digitalhub.entities.dataitem.crud import get_dataitem
|
|
8
|
+
from digitalhub.entities.entity_types import EntityTypes
|
|
9
|
+
from digitalhub.entities.model.crud import get_model
|
|
10
|
+
from digitalhub.entities.task.models import K8s
|
|
11
|
+
from digitalhub.entities.utils import parse_entity_key
|
|
12
|
+
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from digitalhub.entities._base.entity.base import Entity
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
ENTITY_FUNC = {
|
|
18
|
+
EntityTypes.ARTIFACT.value: get_artifact,
|
|
19
|
+
EntityTypes.DATAITEM.value: get_dataitem,
|
|
20
|
+
EntityTypes.MODEL.value: get_model,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class RunSpec(Spec):
|
|
25
|
+
"""Run specification."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
task: str,
|
|
30
|
+
local_execution: bool = False,
|
|
31
|
+
function: str | None = None,
|
|
32
|
+
node_selector: dict | None = None,
|
|
33
|
+
volumes: list | None = None,
|
|
34
|
+
resources: dict | None = None,
|
|
35
|
+
affinity: dict | None = None,
|
|
36
|
+
tolerations: list | None = None,
|
|
37
|
+
envs: list | None = None,
|
|
38
|
+
secrets: list | None = None,
|
|
39
|
+
profile: str | None = None,
|
|
40
|
+
**kwargs,
|
|
41
|
+
) -> None:
|
|
42
|
+
self.task = task
|
|
43
|
+
self.local_execution = local_execution
|
|
44
|
+
self.function = function
|
|
45
|
+
self.node_selector = node_selector
|
|
46
|
+
self.volumes = volumes
|
|
47
|
+
self.resources = resources
|
|
48
|
+
self.affinity = affinity
|
|
49
|
+
self.tolerations = tolerations
|
|
50
|
+
self.envs = envs
|
|
51
|
+
self.secrets = secrets
|
|
52
|
+
self.profile = profile
|
|
53
|
+
|
|
54
|
+
def get_inputs(self, as_dict: bool = False) -> dict:
|
|
55
|
+
"""
|
|
56
|
+
Get inputs.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
dict
|
|
61
|
+
The inputs.
|
|
62
|
+
"""
|
|
63
|
+
inputs = {}
|
|
64
|
+
if not hasattr(self, "inputs") or self.inputs is None:
|
|
65
|
+
return inputs
|
|
66
|
+
|
|
67
|
+
for parameter, item in self.inputs.items():
|
|
68
|
+
parameter_type = self._parse_parameter(parameter)
|
|
69
|
+
|
|
70
|
+
# Get entity from key
|
|
71
|
+
if parameter_type == "key":
|
|
72
|
+
key = self._collect_key(item)
|
|
73
|
+
entity = self._collect_entity(key)
|
|
74
|
+
if as_dict:
|
|
75
|
+
entity = entity.to_dict()
|
|
76
|
+
inputs[parameter] = entity
|
|
77
|
+
|
|
78
|
+
# Create entity from parameter
|
|
79
|
+
elif parameter_type == "create":
|
|
80
|
+
raise NotImplementedError
|
|
81
|
+
|
|
82
|
+
return inputs
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _parse_parameter(parameter: str) -> str:
|
|
86
|
+
"""
|
|
87
|
+
Parse parameter.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
parameter : str
|
|
92
|
+
Parameter.
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
str
|
|
97
|
+
The parsed parameter.
|
|
98
|
+
"""
|
|
99
|
+
if len(parameter.split(":")) == 1:
|
|
100
|
+
return "key"
|
|
101
|
+
return "create"
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _collect_key(item: str | dict) -> str:
|
|
105
|
+
"""
|
|
106
|
+
Collect key from item.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
item : str | dict
|
|
111
|
+
Key or dict representation of the entity.
|
|
112
|
+
|
|
113
|
+
Returns
|
|
114
|
+
-------
|
|
115
|
+
str
|
|
116
|
+
The key.
|
|
117
|
+
"""
|
|
118
|
+
if isinstance(item, str):
|
|
119
|
+
return item
|
|
120
|
+
return item.get("key")
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _collect_entity(key: str) -> Entity:
|
|
124
|
+
"""
|
|
125
|
+
Collect entity from key.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
key : str
|
|
130
|
+
Key of the entity.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
Entity
|
|
135
|
+
The entity.
|
|
136
|
+
"""
|
|
137
|
+
_, entity_type, _, _, _ = parse_entity_key(key)
|
|
138
|
+
return ENTITY_FUNC[entity_type](key)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class RunParams(SpecParams, K8s):
|
|
142
|
+
"""
|
|
143
|
+
Run parameters.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
function: str = None
|
|
147
|
+
"""The function associated with the run."""
|
|
148
|
+
|
|
149
|
+
task: str = None
|
|
150
|
+
"""The task string associated with the run."""
|
|
151
|
+
|
|
152
|
+
local_execution: bool = False
|
|
153
|
+
"""Flag to indicate if the run will be executed locally."""
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from digitalhub.entities._base.status.base import Status
|
|
4
|
+
from digitalhub.entities.artifact.crud import get_artifact
|
|
5
|
+
from digitalhub.entities.dataitem.crud import get_dataitem
|
|
6
|
+
from digitalhub.entities.entity_types import EntityTypes
|
|
7
|
+
from digitalhub.entities.model.crud import get_model
|
|
8
|
+
from digitalhub.entities.utils import parse_entity_key
|
|
9
|
+
|
|
10
|
+
ENTITY_FUNC = {
|
|
11
|
+
EntityTypes.ARTIFACT.value: get_artifact,
|
|
12
|
+
EntityTypes.DATAITEM.value: get_dataitem,
|
|
13
|
+
EntityTypes.MODEL.value: get_model,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RunStatus(Status):
|
|
18
|
+
"""
|
|
19
|
+
Status class for run entities.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
state: str,
|
|
25
|
+
message: str | None = None,
|
|
26
|
+
outputs: list | None = None,
|
|
27
|
+
results: dict | None = None,
|
|
28
|
+
**kwargs,
|
|
29
|
+
) -> None:
|
|
30
|
+
super().__init__(state, message)
|
|
31
|
+
self.outputs = outputs
|
|
32
|
+
self.results = results
|
|
33
|
+
|
|
34
|
+
self._any_setter(**kwargs)
|
|
35
|
+
|
|
36
|
+
def get_results(self) -> dict:
|
|
37
|
+
"""
|
|
38
|
+
Get results.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
dict
|
|
43
|
+
The results.
|
|
44
|
+
"""
|
|
45
|
+
if not hasattr(self, "results") or self.results is None:
|
|
46
|
+
return {}
|
|
47
|
+
return self.results
|
|
48
|
+
|
|
49
|
+
def get_outputs(self, as_key: bool = False, as_dict: bool = False) -> dict:
|
|
50
|
+
"""
|
|
51
|
+
Get outputs.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
as_key : bool
|
|
56
|
+
If True, return outputs as keys.
|
|
57
|
+
as_dict : bool
|
|
58
|
+
If True, return outputs as dictionaries.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
dict
|
|
63
|
+
The outputs.
|
|
64
|
+
"""
|
|
65
|
+
outputs = {}
|
|
66
|
+
if not hasattr(self, "outputs") or self.outputs is None:
|
|
67
|
+
return outputs
|
|
68
|
+
|
|
69
|
+
for parameter, key in self.outputs.items():
|
|
70
|
+
entity_type = self._get_entity_type(key)
|
|
71
|
+
entity = ENTITY_FUNC[entity_type](key)
|
|
72
|
+
if as_key:
|
|
73
|
+
entity = entity.key
|
|
74
|
+
if as_dict:
|
|
75
|
+
entity = entity.to_dict()
|
|
76
|
+
outputs[parameter] = entity
|
|
77
|
+
|
|
78
|
+
return outputs
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _get_entity_type(key: str) -> str:
|
|
82
|
+
"""
|
|
83
|
+
Get entity type.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
key : str
|
|
88
|
+
The key of the entity.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
str
|
|
93
|
+
The entity type.
|
|
94
|
+
"""
|
|
95
|
+
_, entity_type, _, _, _ = parse_entity_key(key)
|
|
96
|
+
return entity_type
|
|
97
|
+
|
|
98
|
+
def get_values(self, values_list: list) -> dict:
|
|
99
|
+
"""
|
|
100
|
+
Get values.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
values_list : list
|
|
105
|
+
The values list to search in.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
dict
|
|
110
|
+
The values.
|
|
111
|
+
"""
|
|
112
|
+
if not hasattr(self, "results") or self.results is None:
|
|
113
|
+
return {}
|
|
114
|
+
return {k: v for k, v in self.get_results().items() if k in values_list}
|