digitalhub 0.10.0b5__py3-none-any.whl → 0.10.0b6__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 (49) hide show
  1. digitalhub/client/_base/api_builder.py +1 -1
  2. digitalhub/client/_base/client.py +22 -0
  3. digitalhub/client/_base/params_builder.py +16 -0
  4. digitalhub/client/dhcore/api_builder.py +4 -3
  5. digitalhub/client/dhcore/client.py +4 -0
  6. digitalhub/client/dhcore/configurator.py +22 -0
  7. digitalhub/client/dhcore/params_builder.py +174 -0
  8. digitalhub/client/local/api_builder.py +4 -1
  9. digitalhub/client/local/client.py +6 -0
  10. digitalhub/client/local/params_builder.py +116 -0
  11. digitalhub/entities/_base/context/entity.py +4 -4
  12. digitalhub/entities/_base/executable/entity.py +2 -2
  13. digitalhub/entities/_base/material/entity.py +3 -3
  14. digitalhub/entities/_base/unversioned/entity.py +2 -2
  15. digitalhub/entities/_base/versioned/entity.py +2 -2
  16. digitalhub/entities/_commons/enums.py +1 -0
  17. digitalhub/entities/_commons/metrics.py +164 -0
  18. digitalhub/entities/_commons/utils.py +0 -26
  19. digitalhub/entities/_processors/base.py +527 -0
  20. digitalhub/entities/{_operations/processor.py → _processors/context.py} +85 -739
  21. digitalhub/entities/_processors/utils.py +158 -0
  22. digitalhub/entities/artifact/crud.py +10 -10
  23. digitalhub/entities/dataitem/crud.py +10 -10
  24. digitalhub/entities/function/crud.py +9 -9
  25. digitalhub/entities/model/_base/entity.py +26 -78
  26. digitalhub/entities/model/_base/status.py +1 -1
  27. digitalhub/entities/model/crud.py +10 -10
  28. digitalhub/entities/project/_base/entity.py +317 -9
  29. digitalhub/entities/project/crud.py +10 -9
  30. digitalhub/entities/run/_base/entity.py +32 -84
  31. digitalhub/entities/run/_base/status.py +1 -1
  32. digitalhub/entities/run/crud.py +8 -8
  33. digitalhub/entities/secret/_base/entity.py +3 -3
  34. digitalhub/entities/secret/crud.py +9 -9
  35. digitalhub/entities/task/_base/entity.py +4 -4
  36. digitalhub/entities/task/_base/models.py +10 -0
  37. digitalhub/entities/task/crud.py +8 -8
  38. digitalhub/entities/workflow/crud.py +9 -9
  39. digitalhub/stores/s3/enums.py +7 -7
  40. digitalhub/stores/sql/enums.py +6 -6
  41. digitalhub/utils/git_utils.py +16 -9
  42. {digitalhub-0.10.0b5.dist-info → digitalhub-0.10.0b6.dist-info}/METADATA +1 -4
  43. {digitalhub-0.10.0b5.dist-info → digitalhub-0.10.0b6.dist-info}/RECORD +46 -43
  44. digitalhub/entities/_base/project/entity.py +0 -341
  45. digitalhub/entities/_commons/models.py +0 -13
  46. digitalhub/entities/_operations/__init__.py +0 -0
  47. /digitalhub/entities/{_base/project → _processors}/__init__.py +0 -0
  48. {digitalhub-0.10.0b5.dist-info → digitalhub-0.10.0b6.dist-info}/WHEEL +0 -0
  49. {digitalhub-0.10.0b5.dist-info → digitalhub-0.10.0b6.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Union
4
+
5
+ from pydantic import BaseModel, ValidationError
6
+
7
+ MetricType = Union[float, int, list[Union[float, int]]]
8
+
9
+
10
+ class Metric(BaseModel):
11
+ """
12
+ Metric.
13
+ """
14
+
15
+ value: MetricType
16
+
17
+
18
+ def validate_metric_value(value: Any) -> MetricType:
19
+ """
20
+ Validate metric value.
21
+
22
+ Parameters
23
+ ----------
24
+ value : Any
25
+ The value to validate.
26
+
27
+ Returns
28
+ -------
29
+ MetricType
30
+ The validated value.
31
+ """
32
+ try:
33
+ return Metric(value=value).value
34
+ except ValidationError as e:
35
+ raise ValueError("Invalid metric value. Must be a list of floats or ints or a float or an int.") from e
36
+
37
+
38
+ def set_metrics(
39
+ metrics: dict[str, MetricType],
40
+ key: str,
41
+ value: Any,
42
+ overwrite: bool,
43
+ single_value: bool,
44
+ ) -> dict[str, MetricType]:
45
+ """
46
+ Set metric value.
47
+
48
+ Parameters
49
+ ----------
50
+ metrics : dict[str, MetricType]
51
+ The metrics dictionary.
52
+ key : str
53
+ The key of the entity.
54
+ value : Any
55
+ The value to set.
56
+ overwrite : bool
57
+ Whether to overwrite the metric.
58
+
59
+ Returns
60
+ -------
61
+ dict[str, MetricType]
62
+ The metrics dictionary.
63
+ """
64
+ if isinstance(value, list):
65
+ return handle_metric_list(metrics, key, value, overwrite)
66
+ elif single_value:
67
+ return handle_metric_single(metrics, key, value, overwrite)
68
+ return handle_metric_list_append(metrics, key, value, overwrite)
69
+
70
+
71
+ def handle_metric_single(
72
+ metrics: dict[str, MetricType],
73
+ key: str,
74
+ value: float | int,
75
+ overwrite: bool,
76
+ ) -> dict:
77
+ """
78
+ Handle metric single value.
79
+
80
+ Parameters
81
+ ----------
82
+ metrics : dict[str, MetricType]
83
+ Metrics dictionary.
84
+ key : str
85
+ Key of the metric.
86
+ value : float
87
+ Value of the metric.
88
+ overwrite : bool
89
+ If True, overwrite existing metric.
90
+
91
+ Returns
92
+ -------
93
+ dict
94
+ Metrics dictionary.
95
+ """
96
+ if key not in metrics or overwrite:
97
+ metrics[key] = value
98
+ return metrics
99
+
100
+
101
+ def handle_metric_list_append(
102
+ metrics: dict[str, MetricType],
103
+ key: str,
104
+ value: float | int,
105
+ overwrite: bool,
106
+ ) -> dict:
107
+ """
108
+ Handle metric list append.
109
+
110
+ Parameters
111
+ ----------
112
+ metrics : dict[str, MetricType]
113
+ Metrics dictionary.
114
+ key : str
115
+ Key of the metric.
116
+ value : float
117
+ Value of the metric.
118
+ overwrite : bool
119
+ If True, overwrite existing metric.
120
+
121
+ Returns
122
+ -------
123
+ dict
124
+ Metrics dictionary.
125
+ """
126
+ if key not in metrics or overwrite:
127
+ metrics[key] = [value]
128
+ elif isinstance(metrics[key], list):
129
+ metrics[key].append(value)
130
+ else:
131
+ metrics[key] = [metrics[key], value]
132
+ return metrics
133
+
134
+
135
+ def handle_metric_list(
136
+ metrics: dict[str, MetricType],
137
+ key: str,
138
+ value: list[int | float],
139
+ overwrite: bool,
140
+ ) -> dict:
141
+ """
142
+ Handle metric list.
143
+
144
+ Parameters
145
+ ----------
146
+ metrics : dict[str, MetricType]
147
+ Metrics dictionary.
148
+ key : str
149
+ Key of the metric.
150
+ value : list[int | float]
151
+ Value of the metric.
152
+ overwrite : bool
153
+ If True, overwrite existing metric.
154
+
155
+ Returns
156
+ -------
157
+ dict
158
+ Metrics dictionary.
159
+ """
160
+ if key not in metrics or overwrite:
161
+ metrics[key] = value
162
+ else:
163
+ metrics[key].extend(value)
164
+ return metrics
@@ -1,12 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any
4
-
5
- from pydantic import ValidationError
6
-
7
3
  from digitalhub.entities._commons.enums import EntityTypes
8
- from digitalhub.entities._commons.models import Metric
9
- from digitalhub.entities._commons.types import MetricType
10
4
 
11
5
 
12
6
  def parse_entity_key(key: str) -> tuple[str, str, str, str | None, str]:
@@ -87,23 +81,3 @@ def get_project_from_key(key: str) -> str:
87
81
  """
88
82
  project, _, _, _, _ = parse_entity_key(key)
89
83
  return project
90
-
91
-
92
- def validate_metric_value(value: Any) -> MetricType:
93
- """
94
- Validate metric value.
95
-
96
- Parameters
97
- ----------
98
- value : Any
99
- The value to validate.
100
-
101
- Returns
102
- -------
103
- MetricType
104
- The validated value.
105
- """
106
- try:
107
- return Metric(value=value).value
108
- except ValidationError as e:
109
- raise ValueError("Invalid metric value. Must be a list of floats or ints or a float or an int.") from e
@@ -0,0 +1,527 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+
5
+ from digitalhub.client.api import get_client
6
+ from digitalhub.context.api import delete_context
7
+ from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
8
+ from digitalhub.factory.api import build_entity_from_dict, build_entity_from_params
9
+ from digitalhub.utils.exceptions import EntityAlreadyExistsError, EntityError, EntityNotExistsError
10
+ from digitalhub.utils.io_utils import read_yaml
11
+
12
+ if typing.TYPE_CHECKING:
13
+ from digitalhub.client._base.client import Client
14
+ from digitalhub.entities.project._base.entity import Project
15
+
16
+
17
+ class BaseEntityOperationsProcessor:
18
+ """
19
+ Processor for Base Entity operations.
20
+
21
+ This object interacts with the context, check the category of the object,
22
+ and then calls the appropriate method to perform the requested operation.
23
+ Operations can be CRUD, search, list, etc.
24
+ """
25
+
26
+ ##############################
27
+ # CRUD base entity
28
+ ##############################
29
+
30
+ def _create_base_entity(
31
+ self,
32
+ client: Client,
33
+ entity_type: str,
34
+ entity_dict: dict,
35
+ **kwargs,
36
+ ) -> dict:
37
+ """
38
+ Create object in backend.
39
+
40
+ Parameters
41
+ ----------
42
+ client : Client
43
+ Client instance.
44
+ entity_type : str
45
+ Entity type.
46
+ entity_dict : dict
47
+ Object instance.
48
+ **kwargs : dict
49
+ Parameters to pass to the API call.
50
+
51
+ Returns
52
+ -------
53
+ dict
54
+ Object instance.
55
+ """
56
+ api = client.build_api(
57
+ ApiCategories.BASE.value,
58
+ BackendOperations.CREATE.value,
59
+ entity_type=entity_type,
60
+ )
61
+ return client.create_object(api, entity_dict, **kwargs)
62
+
63
+ def create_project_entity(
64
+ self,
65
+ _entity: Project | None = None,
66
+ **kwargs,
67
+ ) -> Project:
68
+ """
69
+ Create object in backend.
70
+
71
+ Parameters
72
+ ----------
73
+ _entity : Project
74
+ Object instance.
75
+ **kwargs : dict
76
+ Parameters to pass to entity builder.
77
+
78
+ Returns
79
+ -------
80
+ Project
81
+ Object instance.
82
+ """
83
+ if _entity is not None:
84
+ client = _entity._client
85
+ obj = _entity
86
+ else:
87
+ client = get_client(kwargs.get("local"), kwargs.pop("config", None))
88
+ obj = build_entity_from_params(**kwargs)
89
+ ent = self._create_base_entity(client, obj.ENTITY_TYPE, obj.to_dict())
90
+ ent["local"] = client.is_local()
91
+ return build_entity_from_dict(ent)
92
+
93
+ def _read_base_entity(
94
+ self,
95
+ client: Client,
96
+ entity_type: str,
97
+ entity_name: str,
98
+ **kwargs,
99
+ ) -> dict:
100
+ """
101
+ Read object from backend.
102
+
103
+ Parameters
104
+ ----------
105
+ client : Client
106
+ Client instance.
107
+ entity_type : str
108
+ Entity type.
109
+ entity_name : str
110
+ Entity name.
111
+ **kwargs : dict
112
+ Parameters to pass to the API call.
113
+
114
+ Returns
115
+ -------
116
+ dict
117
+ Object instance.
118
+ """
119
+ api = client.build_api(
120
+ ApiCategories.BASE.value,
121
+ BackendOperations.READ.value,
122
+ entity_type=entity_type,
123
+ entity_name=entity_name,
124
+ )
125
+ return client.read_object(api, **kwargs)
126
+
127
+ def read_project_entity(
128
+ self,
129
+ entity_type: str,
130
+ entity_name: str,
131
+ **kwargs,
132
+ ) -> Project:
133
+ """
134
+ Read object from backend.
135
+
136
+ Parameters
137
+ ----------
138
+ entity_type : str
139
+ Entity type.
140
+ entity_name : str
141
+ Entity name.
142
+ **kwargs : dict
143
+ Parameters to pass to entity builder.
144
+
145
+ Returns
146
+ -------
147
+ Project
148
+ Object instance.
149
+ """
150
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
151
+ obj = self._read_base_entity(client, entity_type, entity_name, **kwargs)
152
+ obj["local"] = client.is_local()
153
+ return build_entity_from_dict(obj)
154
+
155
+ def import_project_entity(
156
+ self,
157
+ file: str,
158
+ **kwargs,
159
+ ) -> Project:
160
+ """
161
+ Import object from a YAML file and create a new object into the backend.
162
+
163
+ Parameters
164
+ ----------
165
+ file : str
166
+ Path to YAML file.
167
+ **kwargs : dict
168
+ Additional keyword arguments.
169
+
170
+ Returns
171
+ -------
172
+ Project
173
+ Object instance.
174
+ """
175
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
176
+ obj: dict = read_yaml(file)
177
+ obj["status"] = {}
178
+ obj["local"] = client.is_local()
179
+ ent: Project = build_entity_from_dict(obj)
180
+
181
+ try:
182
+ self._create_base_entity(ent._client, ent.ENTITY_TYPE, ent.to_dict())
183
+ except EntityAlreadyExistsError:
184
+ raise EntityError(f"Entity {ent.name} already exists. If you want to update it, use load instead.")
185
+
186
+ # Import related entities
187
+ ent._import_entities(obj)
188
+ ent.refresh()
189
+ return ent
190
+
191
+ def load_project_entity(
192
+ self,
193
+ file: str,
194
+ **kwargs,
195
+ ) -> Project:
196
+ """
197
+ Load object from a YAML file and update an existing object into the backend.
198
+
199
+ Parameters
200
+ ----------
201
+ file : str
202
+ Path to YAML file.
203
+ **kwargs : dict
204
+ Additional keyword arguments.
205
+
206
+ Returns
207
+ -------
208
+ Project
209
+ Object instance.
210
+ """
211
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
212
+ obj: dict = read_yaml(file)
213
+ obj["local"] = client.is_local()
214
+ ent: Project = build_entity_from_dict(obj)
215
+
216
+ try:
217
+ self._update_base_entity(ent._client, ent.ENTITY_TYPE, ent.name, ent.to_dict())
218
+ except EntityNotExistsError:
219
+ self._create_base_entity(ent._client, ent.ENTITY_TYPE, ent.to_dict())
220
+
221
+ # Load related entities
222
+ ent._load_entities(obj)
223
+ ent.refresh()
224
+ return ent
225
+
226
+ def _list_base_entities(
227
+ self,
228
+ client: Client,
229
+ entity_type: str,
230
+ **kwargs,
231
+ ) -> list[dict]:
232
+ """
233
+ List objects from backend.
234
+
235
+ Parameters
236
+ ----------
237
+ client : Client
238
+ Client instance.
239
+ entity_type : str
240
+ Entity type.
241
+ **kwargs : dict
242
+ Parameters to pass to the API call.
243
+
244
+ Returns
245
+ -------
246
+ list[dict]
247
+ List of objects.
248
+ """
249
+ api = client.build_api(
250
+ ApiCategories.BASE.value,
251
+ BackendOperations.LIST.value,
252
+ entity_type=entity_type,
253
+ )
254
+ return client.list_objects(api, **kwargs)
255
+
256
+ def list_project_entities(
257
+ self,
258
+ entity_type: str,
259
+ **kwargs,
260
+ ) -> list[Project]:
261
+ """
262
+ List objects from backend.
263
+
264
+ Parameters
265
+ ----------
266
+ entity_type : str
267
+ Entity type.
268
+ **kwargs : dict
269
+ Parameters to pass to API call.
270
+
271
+ Returns
272
+ -------
273
+ list[Project]
274
+ List of objects.
275
+ """
276
+ client = get_client(kwargs.pop("local", False))
277
+ objs = self._list_base_entities(client, entity_type, **kwargs)
278
+ entities = []
279
+ for obj in objs:
280
+ obj["local"] = client.is_local()
281
+ ent = build_entity_from_dict(obj)
282
+ entities.append(ent)
283
+ return entities
284
+
285
+ def _update_base_entity(
286
+ self,
287
+ client: Client,
288
+ entity_type: str,
289
+ entity_name: str,
290
+ entity_dict: dict,
291
+ **kwargs,
292
+ ) -> dict:
293
+ """
294
+ Update object method.
295
+
296
+ Parameters
297
+ ----------
298
+ client : Client
299
+ Client instance.
300
+ entity_type : str
301
+ Entity type.
302
+ entity_name : str
303
+ Entity name.
304
+ entity_dict : dict
305
+ Object instance.
306
+ **kwargs : dict
307
+ Parameters to pass to the API call.
308
+
309
+ Returns
310
+ -------
311
+ dict
312
+ Object instance.
313
+ """
314
+ api = client.build_api(
315
+ ApiCategories.BASE.value,
316
+ BackendOperations.UPDATE.value,
317
+ entity_type=entity_type,
318
+ entity_name=entity_name,
319
+ )
320
+ return client.update_object(api, entity_dict, **kwargs)
321
+
322
+ def update_project_entity(
323
+ self,
324
+ entity_type: str,
325
+ entity_name: str,
326
+ entity_dict: dict,
327
+ **kwargs,
328
+ ) -> Project:
329
+ """
330
+ Update object method.
331
+
332
+ Parameters
333
+ ----------
334
+ entity_type : str
335
+ Entity type.
336
+ entity_name : str
337
+ Entity name.
338
+ entity_dict : dict
339
+ Object instance.
340
+ **kwargs : dict
341
+ Parameters to pass to entity builder.
342
+
343
+ Returns
344
+ -------
345
+ Project
346
+ Object instance.
347
+ """
348
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
349
+ obj = self._update_base_entity(client, entity_type, entity_name, entity_dict, **kwargs)
350
+ obj["local"] = client.is_local()
351
+ return build_entity_from_dict(obj)
352
+
353
+ def _delete_base_entity(
354
+ self,
355
+ client: Client,
356
+ entity_type: str,
357
+ entity_name: str,
358
+ **kwargs,
359
+ ) -> dict:
360
+ """
361
+ Delete object method.
362
+
363
+ Parameters
364
+ ----------
365
+ client : Client
366
+ Client instance.
367
+ entity_type : str
368
+ Entity type.
369
+ entity_name : str
370
+ Entity name.
371
+ **kwargs : dict
372
+ Parameters to pass to the API call.
373
+
374
+ Returns
375
+ -------
376
+ dict
377
+ Response from backend.
378
+ """
379
+ kwargs = client.build_parameters(
380
+ ApiCategories.BASE.value,
381
+ BackendOperations.DELETE.value,
382
+ **kwargs,
383
+ )
384
+ api = client.build_api(
385
+ ApiCategories.BASE.value,
386
+ BackendOperations.DELETE.value,
387
+ entity_type=entity_type,
388
+ entity_name=entity_name,
389
+ )
390
+ return client.delete_object(api, **kwargs)
391
+
392
+ def delete_project_entity(
393
+ self,
394
+ entity_type: str,
395
+ entity_name: str,
396
+ **kwargs,
397
+ ) -> dict:
398
+ """
399
+ Delete object method.
400
+
401
+ Parameters
402
+ ----------
403
+ entity_type : str
404
+ Entity type.
405
+ entity_name : str
406
+ Entity name.
407
+ **kwargs : dict
408
+ Parameters to pass to entity builder.
409
+
410
+ Returns
411
+ -------
412
+ dict
413
+ Response from backend.
414
+ """
415
+ if kwargs.pop("clean_context", True):
416
+ delete_context(entity_name)
417
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
418
+ return self._delete_base_entity(
419
+ client,
420
+ entity_type,
421
+ entity_name,
422
+ **kwargs,
423
+ )
424
+
425
+ ##############################
426
+ # Base entity operations
427
+ ##############################
428
+
429
+ def _build_base_entity_key(
430
+ self,
431
+ client: Client,
432
+ entity_id: str,
433
+ ) -> str:
434
+ """
435
+ Build object key.
436
+
437
+ Parameters
438
+ ----------
439
+ client : Client
440
+ Client instance.
441
+ entity_id : str
442
+ Entity ID.
443
+
444
+ Returns
445
+ -------
446
+ str
447
+ Object key.
448
+ """
449
+ return client.build_key(ApiCategories.BASE.value, entity_id)
450
+
451
+ def build_project_key(
452
+ self,
453
+ entity_id: str,
454
+ **kwargs,
455
+ ) -> str:
456
+ """
457
+ Build object key.
458
+
459
+ Parameters
460
+ ----------
461
+ entity_id : str
462
+ Entity ID.
463
+ **kwargs : dict
464
+ Parameters to pass to entity builder.
465
+
466
+ Returns
467
+ -------
468
+ str
469
+ Object key.
470
+ """
471
+ client = get_client(kwargs.pop("local", False))
472
+ return self._build_base_entity_key(client, entity_id)
473
+
474
+ def share_project_entity(
475
+ self,
476
+ entity_type: str,
477
+ entity_name: str,
478
+ **kwargs,
479
+ ) -> None:
480
+ """
481
+ Share object method.
482
+
483
+ Parameters
484
+ ----------
485
+ entity_type : str
486
+ Entity type.
487
+ entity_name : str
488
+ Entity name.
489
+ **kwargs : dict
490
+ Parameters to pass to entity builder.
491
+
492
+ Returns
493
+ -------
494
+ None
495
+ """
496
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
497
+ api = client.build_api(
498
+ ApiCategories.BASE.value,
499
+ BackendOperations.SHARE.value,
500
+ entity_type=entity_type,
501
+ entity_name=entity_name,
502
+ )
503
+
504
+ user = kwargs.pop("user", None)
505
+ if unshare := kwargs.pop("unshare", False):
506
+ users = client.read_object(api, **kwargs)
507
+ for u in users:
508
+ if u["user"] == user:
509
+ kwargs["id"] = u["id"]
510
+ break
511
+ else:
512
+ raise ValueError(f"User '{user}' does not have access to project.")
513
+
514
+ kwargs = client.build_parameters(
515
+ ApiCategories.BASE.value,
516
+ BackendOperations.SHARE.value,
517
+ unshare=unshare,
518
+ user=user,
519
+ **kwargs,
520
+ )
521
+ if unshare:
522
+ client.delete_object(api, **kwargs)
523
+ return
524
+ client.create_object(api, obj={}, **kwargs)
525
+
526
+
527
+ base_processor = BaseEntityOperationsProcessor()