digitalhub 0.7.0b2__py3-none-any.whl → 0.8.0__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.

Files changed (232) hide show
  1. digitalhub/__init__.py +63 -93
  2. digitalhub/client/__init__.py +0 -0
  3. digitalhub/client/_base/__init__.py +0 -0
  4. digitalhub/client/_base/client.py +56 -0
  5. digitalhub/client/api.py +63 -0
  6. digitalhub/client/builder.py +50 -0
  7. digitalhub/client/dhcore/__init__.py +0 -0
  8. digitalhub/client/dhcore/client.py +669 -0
  9. digitalhub/client/dhcore/env.py +21 -0
  10. digitalhub/client/dhcore/models.py +46 -0
  11. digitalhub/client/dhcore/utils.py +111 -0
  12. digitalhub/client/local/__init__.py +0 -0
  13. digitalhub/client/local/client.py +533 -0
  14. digitalhub/context/__init__.py +0 -0
  15. digitalhub/context/api.py +93 -0
  16. digitalhub/context/builder.py +94 -0
  17. digitalhub/context/context.py +136 -0
  18. digitalhub/datastores/__init__.py +0 -0
  19. digitalhub/datastores/_base/__init__.py +0 -0
  20. digitalhub/datastores/_base/datastore.py +85 -0
  21. digitalhub/datastores/api.py +37 -0
  22. digitalhub/datastores/builder.py +110 -0
  23. digitalhub/datastores/local/__init__.py +0 -0
  24. digitalhub/datastores/local/datastore.py +50 -0
  25. digitalhub/datastores/remote/__init__.py +0 -0
  26. digitalhub/datastores/remote/datastore.py +31 -0
  27. digitalhub/datastores/s3/__init__.py +0 -0
  28. digitalhub/datastores/s3/datastore.py +46 -0
  29. digitalhub/datastores/sql/__init__.py +0 -0
  30. digitalhub/datastores/sql/datastore.py +68 -0
  31. digitalhub/entities/__init__.py +0 -0
  32. digitalhub/entities/_base/__init__.py +0 -0
  33. digitalhub/entities/_base/_base/__init__.py +0 -0
  34. digitalhub/entities/_base/_base/entity.py +82 -0
  35. digitalhub/entities/_base/api_utils.py +620 -0
  36. digitalhub/entities/_base/context/__init__.py +0 -0
  37. digitalhub/entities/_base/context/entity.py +118 -0
  38. digitalhub/entities/_base/crud.py +468 -0
  39. digitalhub/entities/_base/entity/__init__.py +0 -0
  40. digitalhub/entities/_base/entity/_constructors/__init__.py +0 -0
  41. digitalhub/entities/_base/entity/_constructors/metadata.py +44 -0
  42. digitalhub/entities/_base/entity/_constructors/name.py +31 -0
  43. digitalhub/entities/_base/entity/_constructors/spec.py +33 -0
  44. digitalhub/entities/_base/entity/_constructors/status.py +52 -0
  45. digitalhub/entities/_base/entity/_constructors/uuid.py +26 -0
  46. digitalhub/entities/_base/entity/builder.py +175 -0
  47. digitalhub/entities/_base/entity/entity.py +106 -0
  48. digitalhub/entities/_base/entity/metadata.py +59 -0
  49. digitalhub/entities/_base/entity/spec.py +58 -0
  50. digitalhub/entities/_base/entity/status.py +43 -0
  51. digitalhub/entities/_base/executable/__init__.py +0 -0
  52. digitalhub/entities/_base/executable/entity.py +405 -0
  53. digitalhub/entities/_base/material/__init__.py +0 -0
  54. digitalhub/entities/_base/material/entity.py +214 -0
  55. digitalhub/entities/_base/material/spec.py +22 -0
  56. digitalhub/entities/_base/material/status.py +49 -0
  57. digitalhub/entities/_base/runtime_entity/__init__.py +0 -0
  58. digitalhub/entities/_base/runtime_entity/builder.py +106 -0
  59. digitalhub/entities/_base/unversioned/__init__.py +0 -0
  60. digitalhub/entities/_base/unversioned/builder.py +66 -0
  61. digitalhub/entities/_base/unversioned/entity.py +49 -0
  62. digitalhub/entities/_base/versioned/__init__.py +0 -0
  63. digitalhub/entities/_base/versioned/builder.py +68 -0
  64. digitalhub/entities/_base/versioned/entity.py +53 -0
  65. digitalhub/entities/artifact/__init__.py +0 -0
  66. digitalhub/entities/artifact/_base/__init__.py +0 -0
  67. digitalhub/entities/artifact/_base/builder.py +86 -0
  68. digitalhub/entities/artifact/_base/entity.py +39 -0
  69. digitalhub/entities/artifact/_base/spec.py +15 -0
  70. digitalhub/entities/artifact/_base/status.py +9 -0
  71. digitalhub/entities/artifact/artifact/__init__.py +0 -0
  72. digitalhub/entities/artifact/artifact/builder.py +18 -0
  73. digitalhub/entities/artifact/artifact/entity.py +32 -0
  74. digitalhub/entities/artifact/artifact/spec.py +27 -0
  75. digitalhub/entities/artifact/artifact/status.py +15 -0
  76. digitalhub/entities/artifact/crud.py +332 -0
  77. digitalhub/entities/builders.py +63 -0
  78. digitalhub/entities/dataitem/__init__.py +0 -0
  79. digitalhub/entities/dataitem/_base/__init__.py +0 -0
  80. digitalhub/entities/dataitem/_base/builder.py +86 -0
  81. digitalhub/entities/dataitem/_base/entity.py +75 -0
  82. digitalhub/entities/dataitem/_base/spec.py +15 -0
  83. digitalhub/entities/dataitem/_base/status.py +20 -0
  84. digitalhub/entities/dataitem/crud.py +372 -0
  85. digitalhub/entities/dataitem/dataitem/__init__.py +0 -0
  86. digitalhub/entities/dataitem/dataitem/builder.py +18 -0
  87. digitalhub/entities/dataitem/dataitem/entity.py +32 -0
  88. digitalhub/entities/dataitem/dataitem/spec.py +15 -0
  89. digitalhub/entities/dataitem/dataitem/status.py +9 -0
  90. digitalhub/entities/dataitem/iceberg/__init__.py +0 -0
  91. digitalhub/entities/dataitem/iceberg/builder.py +18 -0
  92. digitalhub/entities/dataitem/iceberg/entity.py +32 -0
  93. digitalhub/entities/dataitem/iceberg/spec.py +15 -0
  94. digitalhub/entities/dataitem/iceberg/status.py +9 -0
  95. digitalhub/entities/dataitem/table/__init__.py +0 -0
  96. digitalhub/entities/dataitem/table/builder.py +18 -0
  97. digitalhub/entities/dataitem/table/entity.py +146 -0
  98. digitalhub/entities/dataitem/table/models.py +62 -0
  99. digitalhub/entities/dataitem/table/spec.py +25 -0
  100. digitalhub/entities/dataitem/table/status.py +9 -0
  101. digitalhub/entities/function/__init__.py +0 -0
  102. digitalhub/entities/function/_base/__init__.py +0 -0
  103. digitalhub/entities/function/_base/builder.py +79 -0
  104. digitalhub/entities/function/_base/entity.py +98 -0
  105. digitalhub/entities/function/_base/models.py +118 -0
  106. digitalhub/entities/function/_base/spec.py +15 -0
  107. digitalhub/entities/function/_base/status.py +9 -0
  108. digitalhub/entities/function/crud.py +279 -0
  109. digitalhub/entities/model/__init__.py +0 -0
  110. digitalhub/entities/model/_base/__init__.py +0 -0
  111. digitalhub/entities/model/_base/builder.py +86 -0
  112. digitalhub/entities/model/_base/entity.py +34 -0
  113. digitalhub/entities/model/_base/spec.py +49 -0
  114. digitalhub/entities/model/_base/status.py +9 -0
  115. digitalhub/entities/model/crud.py +331 -0
  116. digitalhub/entities/model/huggingface/__init__.py +0 -0
  117. digitalhub/entities/model/huggingface/builder.py +18 -0
  118. digitalhub/entities/model/huggingface/entity.py +32 -0
  119. digitalhub/entities/model/huggingface/spec.py +36 -0
  120. digitalhub/entities/model/huggingface/status.py +9 -0
  121. digitalhub/entities/model/mlflow/__init__.py +0 -0
  122. digitalhub/entities/model/mlflow/builder.py +18 -0
  123. digitalhub/entities/model/mlflow/entity.py +32 -0
  124. digitalhub/entities/model/mlflow/models.py +26 -0
  125. digitalhub/entities/model/mlflow/spec.py +44 -0
  126. digitalhub/entities/model/mlflow/status.py +9 -0
  127. digitalhub/entities/model/mlflow/utils.py +81 -0
  128. digitalhub/entities/model/model/__init__.py +0 -0
  129. digitalhub/entities/model/model/builder.py +18 -0
  130. digitalhub/entities/model/model/entity.py +32 -0
  131. digitalhub/entities/model/model/spec.py +15 -0
  132. digitalhub/entities/model/model/status.py +9 -0
  133. digitalhub/entities/model/sklearn/__init__.py +0 -0
  134. digitalhub/entities/model/sklearn/builder.py +18 -0
  135. digitalhub/entities/model/sklearn/entity.py +32 -0
  136. digitalhub/entities/model/sklearn/spec.py +15 -0
  137. digitalhub/entities/model/sklearn/status.py +9 -0
  138. digitalhub/entities/project/__init__.py +0 -0
  139. digitalhub/entities/project/_base/__init__.py +0 -0
  140. digitalhub/entities/project/_base/builder.py +128 -0
  141. digitalhub/entities/project/_base/entity.py +2078 -0
  142. digitalhub/entities/project/_base/spec.py +50 -0
  143. digitalhub/entities/project/_base/status.py +9 -0
  144. digitalhub/entities/project/crud.py +357 -0
  145. digitalhub/entities/run/__init__.py +0 -0
  146. digitalhub/entities/run/_base/__init__.py +0 -0
  147. digitalhub/entities/run/_base/builder.py +94 -0
  148. digitalhub/entities/run/_base/entity.py +307 -0
  149. digitalhub/entities/run/_base/spec.py +50 -0
  150. digitalhub/entities/run/_base/status.py +9 -0
  151. digitalhub/entities/run/crud.py +219 -0
  152. digitalhub/entities/secret/__init__.py +0 -0
  153. digitalhub/entities/secret/_base/__init__.py +0 -0
  154. digitalhub/entities/secret/_base/builder.py +81 -0
  155. digitalhub/entities/secret/_base/entity.py +74 -0
  156. digitalhub/entities/secret/_base/spec.py +35 -0
  157. digitalhub/entities/secret/_base/status.py +9 -0
  158. digitalhub/entities/secret/crud.py +290 -0
  159. digitalhub/entities/task/__init__.py +0 -0
  160. digitalhub/entities/task/_base/__init__.py +0 -0
  161. digitalhub/entities/task/_base/builder.py +91 -0
  162. digitalhub/entities/task/_base/entity.py +136 -0
  163. digitalhub/entities/task/_base/models.py +208 -0
  164. digitalhub/entities/task/_base/spec.py +53 -0
  165. digitalhub/entities/task/_base/status.py +9 -0
  166. digitalhub/entities/task/crud.py +228 -0
  167. digitalhub/entities/utils/__init__.py +0 -0
  168. digitalhub/entities/utils/api.py +346 -0
  169. digitalhub/entities/utils/entity_types.py +19 -0
  170. digitalhub/entities/utils/state.py +31 -0
  171. digitalhub/entities/utils/utils.py +202 -0
  172. digitalhub/entities/workflow/__init__.py +0 -0
  173. digitalhub/entities/workflow/_base/__init__.py +0 -0
  174. digitalhub/entities/workflow/_base/builder.py +79 -0
  175. digitalhub/entities/workflow/_base/entity.py +74 -0
  176. digitalhub/entities/workflow/_base/spec.py +15 -0
  177. digitalhub/entities/workflow/_base/status.py +9 -0
  178. digitalhub/entities/workflow/crud.py +278 -0
  179. digitalhub/factory/__init__.py +0 -0
  180. digitalhub/factory/api.py +277 -0
  181. digitalhub/factory/factory.py +268 -0
  182. digitalhub/factory/utils.py +90 -0
  183. digitalhub/readers/__init__.py +0 -0
  184. digitalhub/readers/_base/__init__.py +0 -0
  185. digitalhub/readers/_base/builder.py +26 -0
  186. digitalhub/readers/_base/reader.py +70 -0
  187. digitalhub/readers/api.py +80 -0
  188. digitalhub/readers/factory.py +133 -0
  189. digitalhub/readers/pandas/__init__.py +0 -0
  190. digitalhub/readers/pandas/builder.py +29 -0
  191. digitalhub/readers/pandas/reader.py +207 -0
  192. digitalhub/runtimes/__init__.py +0 -0
  193. digitalhub/runtimes/_base.py +102 -0
  194. digitalhub/runtimes/builder.py +32 -0
  195. digitalhub/stores/__init__.py +0 -0
  196. digitalhub/stores/_base/__init__.py +0 -0
  197. digitalhub/stores/_base/store.py +189 -0
  198. digitalhub/stores/api.py +54 -0
  199. digitalhub/stores/builder.py +211 -0
  200. digitalhub/stores/local/__init__.py +0 -0
  201. digitalhub/stores/local/store.py +230 -0
  202. digitalhub/stores/remote/__init__.py +0 -0
  203. digitalhub/stores/remote/store.py +143 -0
  204. digitalhub/stores/s3/__init__.py +0 -0
  205. digitalhub/stores/s3/store.py +563 -0
  206. digitalhub/stores/sql/__init__.py +0 -0
  207. digitalhub/stores/sql/store.py +328 -0
  208. digitalhub/utils/__init__.py +0 -0
  209. digitalhub/utils/data_utils.py +127 -0
  210. digitalhub/utils/exceptions.py +67 -0
  211. digitalhub/utils/file_utils.py +204 -0
  212. digitalhub/utils/generic_utils.py +183 -0
  213. digitalhub/utils/git_utils.py +148 -0
  214. digitalhub/utils/io_utils.py +116 -0
  215. digitalhub/utils/logger.py +17 -0
  216. digitalhub/utils/s3_utils.py +58 -0
  217. digitalhub/utils/uri_utils.py +56 -0
  218. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/METADATA +30 -13
  219. digitalhub-0.8.0.dist-info/RECORD +231 -0
  220. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/WHEEL +1 -1
  221. test/local/CRUD/test_artifacts.py +96 -0
  222. test/local/CRUD/test_dataitems.py +96 -0
  223. test/local/CRUD/test_models.py +95 -0
  224. test/test_crud_functions.py +1 -1
  225. test/test_crud_runs.py +1 -1
  226. test/test_crud_tasks.py +1 -1
  227. digitalhub-0.7.0b2.dist-info/RECORD +0 -14
  228. test/test_crud_artifacts.py +0 -96
  229. test/test_crud_dataitems.py +0 -96
  230. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/LICENSE.txt +0 -0
  231. {digitalhub-0.7.0b2.dist-info → digitalhub-0.8.0.dist-info}/top_level.txt +0 -0
  232. /test/{test_imports.py → local/imports/test_imports.py} +0 -0
@@ -0,0 +1,405 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ from digitalhub.entities._base.api_utils import list_entity_api_ctx
6
+ from digitalhub.entities._base.versioned.entity import VersionedEntity
7
+ from digitalhub.entities.run.crud import delete_run, get_run, list_runs
8
+ from digitalhub.entities.task.crud import delete_task
9
+ from digitalhub.entities.utils.entity_types import EntityTypes
10
+ from digitalhub.factory.api import build_entity_from_dict, build_entity_from_params
11
+ from digitalhub.utils.exceptions import EntityAlreadyExistsError, EntityError
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from digitalhub.entities._base.entity.metadata import Metadata
15
+ from digitalhub.entities._base.entity.spec import Spec
16
+ from digitalhub.entities._base.entity.status import Status
17
+ from digitalhub.entities.run._base.entity import Run
18
+ from digitalhub.entities.task._base.entity import Task
19
+
20
+
21
+ class ExecutableEntity(VersionedEntity):
22
+ """
23
+ A class representing an entity that can be executed.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ project: str,
29
+ name: str,
30
+ uuid: str,
31
+ kind: str,
32
+ metadata: Metadata,
33
+ spec: Spec,
34
+ status: Status,
35
+ user: str | None = None,
36
+ ) -> None:
37
+ super().__init__(project, name, uuid, kind, metadata, spec, status, user)
38
+
39
+ # Initialize tasks
40
+ self._tasks: dict[str, Task] = {}
41
+
42
+ ##############################
43
+ # Helpers
44
+ ##############################
45
+
46
+ def _get_executable_string(self) -> str:
47
+ """
48
+ Get executable string.
49
+
50
+ Returns
51
+ -------
52
+ str
53
+ Executable string.
54
+ """
55
+ return f"{self.kind}://{self.project}/{self.name}:{self.id}"
56
+
57
+ ##############################
58
+ # Tasks
59
+ ##############################
60
+
61
+ def _get_or_create_task(self, kind: str) -> Task:
62
+ """
63
+ Get or create task.
64
+
65
+ Parameters
66
+ ----------
67
+ kind : str
68
+ Kind the object.
69
+
70
+ Returns
71
+ -------
72
+ Task
73
+ Task.
74
+ """
75
+ if self._tasks.get(kind) is None:
76
+ try:
77
+ self._tasks[kind] = self.get_task(kind)
78
+ except EntityError:
79
+ self._tasks[kind] = self.new_task(kind)
80
+ return self._tasks[kind]
81
+
82
+ def import_tasks(self, tasks: list[dict]) -> None:
83
+ """
84
+ Import tasks from yaml.
85
+
86
+ Parameters
87
+ ----------
88
+ tasks : list[dict]
89
+ List of tasks to import.
90
+
91
+ Returns
92
+ -------
93
+ None
94
+ """
95
+ # Loop over tasks list, in the case where the function
96
+ # is imported from local file.
97
+ for task in tasks:
98
+ # If task is not a dictionary, skip it
99
+ if not isinstance(task, dict):
100
+ continue
101
+
102
+ # Create a new object from dictionary.
103
+ # the form in which tasks are stored in function
104
+ # status
105
+ task_obj = build_entity_from_dict(task)
106
+
107
+ # Try to save it in backend to been able to use
108
+ # it for launching runs. In fact, tasks must be
109
+ # persisted in backend to be able to launch runs.
110
+ # Ignore if task already exists
111
+ try:
112
+ task_obj.save()
113
+ except EntityAlreadyExistsError:
114
+ pass
115
+
116
+ # Set task if function is the same. Overwrite
117
+ # status task dict with the new task object
118
+ if task_obj.spec.function == self._get_executable_string():
119
+ self._tasks[task_obj.kind] = task_obj
120
+
121
+ def new_task(self, task_kind: str, **kwargs) -> Task:
122
+ """
123
+ Create new task. If the task already exists, update it.
124
+
125
+ Parameters
126
+ ----------
127
+ task_kind : str
128
+ Kind the object.
129
+ **kwargs : dict
130
+ Keyword arguments.
131
+
132
+ Returns
133
+ -------
134
+ Task
135
+ New task.
136
+ """
137
+ self._raise_if_exists(task_kind)
138
+
139
+ if kwargs is None:
140
+ kwargs = {}
141
+
142
+ # Override kwargs
143
+ kwargs["project"] = self.project
144
+ kwargs["function"] = self._get_executable_string()
145
+ kwargs["kind"] = task_kind
146
+
147
+ # Create object instance
148
+ task = build_entity_from_params(**kwargs)
149
+ task.save()
150
+
151
+ self._tasks[task_kind] = task
152
+ return task
153
+
154
+ def get_task(self, kind: str) -> Task:
155
+ """
156
+ Get task.
157
+
158
+ Parameters
159
+ ----------
160
+ kind : str
161
+ Kind the object.
162
+
163
+ Returns
164
+ -------
165
+ Task
166
+ Task.
167
+
168
+ Raises
169
+ ------
170
+ EntityError
171
+ If task is not created.
172
+ """
173
+ try:
174
+ return self._tasks[kind]
175
+ except KeyError:
176
+ resp = self._get_task_from_backend(kind)
177
+ if not resp:
178
+ raise EntityError(f"Task {kind} is not created")
179
+ self._tasks[kind] = build_entity_from_dict(resp[0])
180
+ return self._tasks[kind]
181
+
182
+ def update_task(self, kind: str, **kwargs) -> Task:
183
+ """
184
+ Update task.
185
+
186
+ Parameters
187
+ ----------
188
+ kind : str
189
+ Kind the object.
190
+ **kwargs : dict
191
+ Keyword arguments.
192
+
193
+ Returns
194
+ -------
195
+ Task
196
+ Task.
197
+ """
198
+ self._raise_if_not_exists(kind)
199
+
200
+ if kwargs is None:
201
+ kwargs = {}
202
+
203
+ # Update kwargs
204
+ kwargs["project"] = self.project
205
+ kwargs["kind"] = kind
206
+ kwargs["function"] = self._get_executable_string()
207
+ kwargs["uuid"] = self._tasks[kind].id
208
+
209
+ # Update task
210
+ task = build_entity_from_params(**kwargs)
211
+ task.save(update=True)
212
+ self._tasks[kind] = task
213
+ return task
214
+
215
+ def delete_task(self, kind: str, cascade: bool = True) -> dict:
216
+ """
217
+ Delete task.
218
+
219
+ Parameters
220
+ ----------
221
+ kind : str
222
+ Kind the object.
223
+ cascade : bool
224
+ Flag to determine if cascade deletion must be performed.
225
+
226
+ Returns
227
+ -------
228
+ dict
229
+ Response from backend.
230
+ """
231
+ resp = delete_task(self._tasks[kind].key, cascade=cascade)
232
+ self._tasks.pop(kind, None)
233
+ return resp
234
+
235
+ def _get_task_from_backend(self, kind: str) -> list:
236
+ """
237
+ List tasks from backend filtered by function and kind.
238
+
239
+ Parameters
240
+ ----------
241
+ kind : str
242
+ Kind the object.
243
+
244
+ Returns
245
+ -------
246
+ list
247
+ Response from backend.
248
+ """
249
+ params = {"function": self._get_executable_string(), "kind": kind}
250
+ return list_entity_api_ctx(self.project, EntityTypes.TASK.value, params=params)
251
+
252
+ def _check_task_in_backend(self, kind: str) -> bool:
253
+ """
254
+ Check if task exists in backend.
255
+
256
+ Parameters
257
+ ----------
258
+ kind : str
259
+ Kind the object.
260
+
261
+ Returns
262
+ -------
263
+ bool
264
+ Flag to determine if task exists in backend.
265
+ """
266
+ resp = self._get_task_from_backend(kind)
267
+ if not resp:
268
+ return False
269
+ return True
270
+
271
+ def _raise_if_exists(self, kind: str) -> None:
272
+ """
273
+ Raise error if task is created.
274
+
275
+ Parameters
276
+ ----------
277
+ kind : str
278
+ Kind the object.
279
+
280
+ Returns
281
+ -------
282
+ None
283
+
284
+ Raises
285
+ ------
286
+ EntityError
287
+ If task already exists.
288
+ """
289
+ if self._check_task_in_backend(kind):
290
+ raise EntityError(f"Task '{kind}' already exists.")
291
+
292
+ def _raise_if_not_exists(self, kind: str) -> None:
293
+ """
294
+ Raise error if task is not created.
295
+
296
+ Parameters
297
+ ----------
298
+ kind : str
299
+ Kind the object.
300
+
301
+ Returns
302
+ -------
303
+ None
304
+
305
+ Raises
306
+ ------
307
+ EntityError
308
+ If task does not exist.
309
+ """
310
+ if self._tasks.get(kind) is None:
311
+ raise EntityError(f"Task '{kind}' does not exist.")
312
+
313
+ ##############################
314
+ # Runs
315
+ ##############################
316
+
317
+ def get_run(
318
+ self,
319
+ identifier: str,
320
+ **kwargs,
321
+ ) -> Run:
322
+ """
323
+ Get object from backend.
324
+
325
+ Parameters
326
+ ----------
327
+ identifier : str
328
+ Entity key (store://...) or entity ID.
329
+ **kwargs : dict
330
+ Parameters to pass to the API call.
331
+
332
+ Returns
333
+ -------
334
+ Run
335
+ Object instance.
336
+
337
+ Examples
338
+ --------
339
+ Using entity key:
340
+ >>> obj = executable.get_run("store://my-secret-key")
341
+
342
+ Using entity ID:
343
+ >>> obj = executable.get_run("123")
344
+ """
345
+ obj = get_run(
346
+ identifier=identifier,
347
+ project=self.project,
348
+ **kwargs,
349
+ )
350
+ self.refresh()
351
+ return obj
352
+
353
+ def list_runs(self, **kwargs) -> list[Run]:
354
+ """
355
+ List all runs from backend.
356
+
357
+ Parameters
358
+ ----------
359
+ **kwargs : dict
360
+ Parameters to pass to the API call.
361
+
362
+ Returns
363
+ -------
364
+ list[Run]
365
+ List of object instances.
366
+
367
+ Examples
368
+ --------
369
+ >>> objs = executable.list_runs()
370
+ """
371
+ if kwargs is None:
372
+ kwargs = {}
373
+ kwargs["params"] = {"function": self._get_executable_string()}
374
+ return list_runs(self.project, **kwargs)
375
+
376
+ def delete_run(
377
+ self,
378
+ identifier: str,
379
+ **kwargs,
380
+ ) -> None:
381
+ """
382
+ Delete run from backend.
383
+
384
+ Parameters
385
+ ----------
386
+ identifier : str
387
+ Entity key (store://...) or entity ID.
388
+ **kwargs : dict
389
+ Parameters to pass to the API call.
390
+
391
+ Returns
392
+ -------
393
+ dict
394
+ Response from backend.
395
+
396
+ Examples
397
+ --------
398
+ >>> executable.delete_run("store://my-run-key")
399
+
400
+ """
401
+ delete_run(
402
+ identifier=identifier,
403
+ project=self.project,
404
+ **kwargs,
405
+ )
File without changes
@@ -0,0 +1,214 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+ from pathlib import Path
5
+
6
+ from digitalhub.entities._base.api_utils import files_info_get_api, files_info_put_api
7
+ from digitalhub.entities._base.versioned.entity import VersionedEntity
8
+ from digitalhub.stores.api import get_store
9
+
10
+ if typing.TYPE_CHECKING:
11
+ from digitalhub.entities._base.entity.metadata import Metadata
12
+ from digitalhub.entities._base.material.spec import MaterialSpec
13
+ from digitalhub.entities._base.material.status import MaterialStatus
14
+
15
+
16
+ class MaterialEntity(VersionedEntity):
17
+ """
18
+ A class representing an entity that can be materialized
19
+ as file(s).
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ project: str,
25
+ name: str,
26
+ uuid: str,
27
+ kind: str,
28
+ metadata: Metadata,
29
+ spec: MaterialSpec,
30
+ status: MaterialStatus,
31
+ user: str | None = None,
32
+ ) -> None:
33
+ super().__init__(project, name, uuid, kind, metadata, spec, status, user)
34
+ self.spec: MaterialSpec
35
+ self.status: MaterialStatus
36
+
37
+ def save(self, update: bool = False) -> MaterialEntity:
38
+ """
39
+ Save entity into backend.
40
+
41
+ Parameters
42
+ ----------
43
+ update : bool
44
+ Flag to indicate update.
45
+
46
+ Returns
47
+ -------
48
+ MaterialEntity
49
+ Entity saved.
50
+ """
51
+ obj = self.to_dict()
52
+
53
+ files = None
54
+ if self.status.files is not None and len(self.status.files) > 5 and not self._context().local:
55
+ files = obj["status"].pop("files")
56
+
57
+ if not update:
58
+ new_obj: MaterialEntity = self._save(obj)
59
+ else:
60
+ new_obj: MaterialEntity = self._update(obj)
61
+
62
+ # Handle files info
63
+ if files is not None:
64
+ files_info_put_api(self.project, self.ENTITY_TYPE, self.id, files)
65
+ self.status.add_files_info(files)
66
+
67
+ return new_obj
68
+
69
+ ##############################
70
+ # I/O Methods
71
+ ##############################
72
+
73
+ def as_file(self) -> list[str]:
74
+ """
75
+ Get object as file(s). It downloads the object from storage in
76
+ a temporary folder and returns the list of downloaded files paths.
77
+
78
+ Returns
79
+ -------
80
+ list[str]
81
+ List of file paths.
82
+ """
83
+ store = get_store(self.spec.path)
84
+ paths = self.status.get_file_paths()
85
+ dst = store._build_temp()
86
+ return store.download(self.spec.path, dst=dst, src=paths)
87
+
88
+ def download(
89
+ self,
90
+ destination: str | None = None,
91
+ overwrite: bool = False,
92
+ ) -> str:
93
+ """
94
+ This function downloads one or more file from storage on local
95
+ machine.
96
+ It looks inside the object's status for the file(s) path under
97
+ files attribute. If it does not find it, it will try to download
98
+ what it can from spec.path.
99
+ The files are downloaded into a destination folder. If the destination
100
+ is not specified, it will set by default under the context path
101
+ as '<ctx-root>/<entity_type>', e.g. './dataitem'.
102
+ The overwrite flag allows to overwrite existing file(s) in the
103
+ destination folder.
104
+
105
+ Parameters
106
+ ----------
107
+ destination : str
108
+ Destination path as filename or directory.
109
+ overwrite : bool
110
+ Specify if overwrite existing file(s). If file(s) already
111
+ exist and overwrite is False, it will raise an error.
112
+
113
+ Returns
114
+ -------
115
+ str
116
+ Downloaded path.
117
+
118
+ Examples
119
+ --------
120
+ Download a single file:
121
+
122
+ >>> entity.status.files[0]
123
+ {
124
+ "path ": "data.csv",
125
+ "name ": "data.csv",
126
+ "content_type ": "text/csv;charset=utf-8 "
127
+ }
128
+ >>> path = entity.download()
129
+ >>> print(path)
130
+ dataitem/data.csv
131
+ """
132
+ store = get_store(self.spec.path)
133
+ paths = self.status.get_file_paths()
134
+
135
+ if destination is None:
136
+ dst = self._context().root / self.ENTITY_TYPE
137
+ else:
138
+ dst = Path(destination)
139
+
140
+ return store.download(self.spec.path, dst=dst, src=paths, overwrite=overwrite)
141
+
142
+ def upload(self, source: str | list[str]) -> None:
143
+ """
144
+ Upload object from given local path to spec path destination.
145
+ Source must be a local path. If the path is a folder, destination
146
+ path (object's spec path) must be a folder or a partition ending
147
+ with '/' (s3).
148
+
149
+ Parameters
150
+ ----------
151
+ source : str | list[str]
152
+ Local filepath, directory or list of filepaths.
153
+
154
+ Returns
155
+ -------
156
+ None
157
+
158
+ Examples
159
+ --------
160
+ Upload a single file:
161
+
162
+ >>> entity.spec.path = 's3://bucket/data.csv'
163
+ >>> entity.upload('./data.csv')
164
+
165
+ Upload a folder:
166
+ >>> entity.spec.path = 's3://bucket/data/'
167
+ >>> entity.upload('./data')
168
+ """
169
+ # Get store and upload object
170
+ store = get_store(self.spec.path)
171
+ paths = store.upload(source, self.spec.path)
172
+
173
+ # Update files info
174
+ files_info = store.get_file_info(paths)
175
+ self._update_files_info(files_info)
176
+
177
+ ##############################
178
+ # Private Helpers
179
+ ##############################
180
+
181
+ def _update_files_info(self, files_info: list[dict] | None = None) -> None:
182
+ """
183
+ Update files info.
184
+
185
+ Parameters
186
+ ----------
187
+ files_info : list[dict] | None
188
+ Files info.
189
+
190
+ Returns
191
+ -------
192
+ None
193
+ """
194
+ if files_info is None:
195
+ return
196
+ self.refresh()
197
+ self.status.add_files_info(files_info)
198
+ self.save(update=True)
199
+
200
+ def _get_files_info(self) -> None:
201
+ """
202
+ Get files info from backend.
203
+
204
+ Returns
205
+ -------
206
+ None
207
+ """
208
+ if not self._context().local and not self.status.files:
209
+ files = files_info_get_api(
210
+ project=self.project,
211
+ entity_type=self.ENTITY_TYPE,
212
+ entity_id=self.id,
213
+ )
214
+ self.status.add_files_info(files)
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub.entities._base.entity.spec import Spec, SpecValidator
4
+
5
+
6
+ class MaterialSpec(Spec):
7
+ """
8
+ Material specifications.class.
9
+ """
10
+
11
+ def __init__(self, path: str, **kwargs) -> None:
12
+ super().__init__(**kwargs)
13
+ self.path = path
14
+
15
+
16
+ class MaterialValidator(SpecValidator):
17
+ """
18
+ Material parameters class.
19
+ """
20
+
21
+ path: str
22
+ """Target path to file(s)"""
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub.entities._base.entity.status import Status
4
+
5
+
6
+ class MaterialStatus(Status):
7
+ """
8
+ Material Status class.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ state: str,
14
+ message: str | None = None,
15
+ files: list[dict] | None = None,
16
+ ) -> None:
17
+ super().__init__(state, message)
18
+ if files is None:
19
+ files = []
20
+ self.files = files
21
+
22
+ def add_files_info(self, files: list[dict]) -> None:
23
+ """
24
+ Add a file to the status.
25
+
26
+ Parameters
27
+ ----------
28
+ files : list[dict]
29
+ Files to add.
30
+
31
+ Returns
32
+ -------
33
+ None
34
+ """
35
+ path_list = self.get_file_paths()
36
+ for f in files:
37
+ if f.get("path") not in path_list:
38
+ self.files.append(f)
39
+
40
+ def get_file_paths(self) -> list[str]:
41
+ """
42
+ Get the paths of the files in the status.
43
+
44
+ Returns
45
+ -------
46
+ list[str]
47
+ Paths of the files in the status.
48
+ """
49
+ return [f.get("path") for f in self.files]
File without changes