digitalhub 0.8.1__py3-none-any.whl → 0.9.0b0__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 (128) hide show
  1. digitalhub/__init__.py +19 -2
  2. digitalhub/client/_base/api_builder.py +16 -0
  3. digitalhub/client/_base/client.py +31 -0
  4. digitalhub/client/api.py +2 -38
  5. digitalhub/client/dhcore/api_builder.py +100 -0
  6. digitalhub/client/dhcore/client.py +77 -24
  7. digitalhub/client/dhcore/enums.py +26 -0
  8. digitalhub/client/dhcore/env.py +2 -2
  9. digitalhub/client/dhcore/utils.py +17 -17
  10. digitalhub/client/local/api_builder.py +100 -0
  11. digitalhub/client/local/client.py +20 -0
  12. digitalhub/context/api.py +3 -38
  13. digitalhub/context/builder.py +10 -23
  14. digitalhub/context/context.py +20 -92
  15. digitalhub/entities/_base/context/entity.py +30 -22
  16. digitalhub/entities/_base/entity/_constructors/metadata.py +12 -1
  17. digitalhub/entities/_base/entity/_constructors/name.py +1 -1
  18. digitalhub/entities/_base/entity/_constructors/spec.py +1 -1
  19. digitalhub/entities/_base/entity/_constructors/status.py +3 -2
  20. digitalhub/entities/_base/entity/builder.py +6 -1
  21. digitalhub/entities/_base/entity/entity.py +30 -10
  22. digitalhub/entities/_base/entity/metadata.py +22 -0
  23. digitalhub/entities/_base/entity/spec.py +7 -2
  24. digitalhub/entities/_base/executable/entity.py +8 -8
  25. digitalhub/entities/_base/material/entity.py +48 -16
  26. digitalhub/entities/_base/material/status.py +0 -31
  27. digitalhub/entities/_base/material/utils.py +106 -0
  28. digitalhub/entities/_base/project/entity.py +341 -0
  29. digitalhub/entities/_base/unversioned/entity.py +1 -23
  30. digitalhub/entities/_base/versioned/entity.py +0 -25
  31. digitalhub/entities/_commons/enums.py +103 -0
  32. digitalhub/entities/_commons/utils.py +83 -0
  33. digitalhub/entities/_operations/processor.py +1747 -0
  34. digitalhub/entities/artifact/_base/builder.py +1 -1
  35. digitalhub/entities/artifact/_base/entity.py +1 -1
  36. digitalhub/entities/artifact/artifact/builder.py +2 -1
  37. digitalhub/entities/artifact/crud.py +46 -29
  38. digitalhub/entities/artifact/utils.py +62 -0
  39. digitalhub/entities/dataitem/_base/builder.py +1 -1
  40. digitalhub/entities/dataitem/_base/entity.py +6 -6
  41. digitalhub/entities/dataitem/crud.py +50 -66
  42. digitalhub/entities/dataitem/dataitem/builder.py +2 -1
  43. digitalhub/entities/dataitem/iceberg/builder.py +2 -1
  44. digitalhub/entities/dataitem/table/builder.py +2 -1
  45. digitalhub/entities/dataitem/table/entity.py +5 -10
  46. digitalhub/entities/dataitem/table/models.py +4 -5
  47. digitalhub/entities/dataitem/utils.py +137 -0
  48. digitalhub/entities/function/_base/builder.py +1 -1
  49. digitalhub/entities/function/_base/entity.py +5 -1
  50. digitalhub/entities/function/crud.py +36 -17
  51. digitalhub/entities/model/_base/builder.py +1 -1
  52. digitalhub/entities/model/_base/entity.py +1 -1
  53. digitalhub/entities/model/crud.py +46 -29
  54. digitalhub/entities/model/huggingface/builder.py +2 -1
  55. digitalhub/entities/model/huggingface/spec.py +4 -2
  56. digitalhub/entities/model/mlflow/builder.py +2 -1
  57. digitalhub/entities/model/mlflow/models.py +17 -9
  58. digitalhub/entities/model/mlflow/spec.py +6 -1
  59. digitalhub/entities/model/mlflow/utils.py +4 -2
  60. digitalhub/entities/model/model/builder.py +2 -1
  61. digitalhub/entities/model/sklearn/builder.py +2 -1
  62. digitalhub/entities/model/utils.py +62 -0
  63. digitalhub/entities/project/_base/builder.py +2 -2
  64. digitalhub/entities/project/_base/entity.py +82 -272
  65. digitalhub/entities/project/crud.py +110 -91
  66. digitalhub/entities/project/utils.py +35 -0
  67. digitalhub/entities/run/_base/builder.py +3 -1
  68. digitalhub/entities/run/_base/entity.py +52 -54
  69. digitalhub/entities/run/_base/spec.py +11 -7
  70. digitalhub/entities/run/crud.py +35 -17
  71. digitalhub/entities/secret/_base/builder.py +2 -2
  72. digitalhub/entities/secret/_base/entity.py +4 -10
  73. digitalhub/entities/secret/crud.py +36 -21
  74. digitalhub/entities/task/_base/builder.py +14 -14
  75. digitalhub/entities/task/_base/entity.py +6 -6
  76. digitalhub/entities/task/_base/models.py +29 -6
  77. digitalhub/entities/task/_base/spec.py +44 -13
  78. digitalhub/entities/task/_base/utils.py +18 -0
  79. digitalhub/entities/task/crud.py +35 -15
  80. digitalhub/entities/workflow/_base/builder.py +1 -1
  81. digitalhub/entities/workflow/_base/entity.py +14 -6
  82. digitalhub/entities/workflow/crud.py +36 -17
  83. digitalhub/factory/utils.py +1 -1
  84. digitalhub/readers/_base/reader.py +2 -2
  85. digitalhub/readers/_commons/enums.py +13 -0
  86. digitalhub/readers/api.py +3 -2
  87. digitalhub/readers/factory.py +12 -6
  88. digitalhub/readers/pandas/reader.py +20 -8
  89. digitalhub/runtimes/_base.py +0 -7
  90. digitalhub/stores/_base/store.py +53 -9
  91. digitalhub/stores/builder.py +5 -5
  92. digitalhub/stores/local/store.py +37 -2
  93. digitalhub/stores/remote/store.py +25 -3
  94. digitalhub/stores/s3/store.py +34 -7
  95. digitalhub/stores/sql/store.py +112 -45
  96. digitalhub/utils/exceptions.py +6 -0
  97. digitalhub/utils/file_utils.py +60 -2
  98. digitalhub/utils/generic_utils.py +45 -4
  99. digitalhub/utils/io_utils.py +18 -0
  100. digitalhub/utils/uri_utils.py +153 -15
  101. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/METADATA +2 -2
  102. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/RECORD +110 -113
  103. test/testkfp.py +4 -1
  104. digitalhub/datastores/_base/datastore.py +0 -85
  105. digitalhub/datastores/api.py +0 -37
  106. digitalhub/datastores/builder.py +0 -110
  107. digitalhub/datastores/local/datastore.py +0 -50
  108. digitalhub/datastores/remote/__init__.py +0 -0
  109. digitalhub/datastores/remote/datastore.py +0 -31
  110. digitalhub/datastores/s3/__init__.py +0 -0
  111. digitalhub/datastores/s3/datastore.py +0 -46
  112. digitalhub/datastores/sql/__init__.py +0 -0
  113. digitalhub/datastores/sql/datastore.py +0 -68
  114. digitalhub/entities/_base/api_utils.py +0 -620
  115. digitalhub/entities/_base/crud.py +0 -468
  116. digitalhub/entities/function/_base/models.py +0 -118
  117. digitalhub/entities/utils/__init__.py +0 -0
  118. digitalhub/entities/utils/api.py +0 -346
  119. digitalhub/entities/utils/entity_types.py +0 -19
  120. digitalhub/entities/utils/state.py +0 -31
  121. digitalhub/entities/utils/utils.py +0 -202
  122. /digitalhub/{context → entities/_base/project}/__init__.py +0 -0
  123. /digitalhub/{datastores → entities/_commons}/__init__.py +0 -0
  124. /digitalhub/{datastores/_base → entities/_operations}/__init__.py +0 -0
  125. /digitalhub/{datastores/local → readers/_commons}/__init__.py +0 -0
  126. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/LICENSE.txt +0 -0
  127. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/WHEEL +0 -0
  128. {digitalhub-0.8.1.dist-info → digitalhub-0.9.0b0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1747 @@
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, get_context
7
+ from digitalhub.entities._commons.enums import ApiCategories, BackendOperations, EntityTypes, Relationship
8
+ from digitalhub.entities._commons.utils import get_project_from_key, parse_entity_key
9
+ from digitalhub.factory.api import build_entity_from_dict, build_entity_from_params
10
+ from digitalhub.utils.exceptions import ContextError, EntityAlreadyExistsError, EntityError, EntityNotExistsError
11
+ from digitalhub.utils.io_utils import read_yaml
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from digitalhub.client._base.client import Client
15
+ from digitalhub.context.context import Context
16
+ from digitalhub.entities._base.context.entity import ContextEntity
17
+ from digitalhub.entities._base.executable.entity import ExecutableEntity
18
+ from digitalhub.entities._base.material.entity import MaterialEntity
19
+ from digitalhub.entities._base.project.entity import ProjectEntity
20
+ from digitalhub.entities._base.unversioned.entity import UnversionedEntity
21
+
22
+
23
+ class OperationsProcessor:
24
+ """
25
+ Processor for Entity operations.
26
+
27
+ This object interacts with the context, check the category of the object,
28
+ and then calls the appropriate method to perform the requested operation.
29
+ Operations can be CRUD, search, list, etc.
30
+ """
31
+
32
+ ##############################
33
+ # CRUD base entity
34
+ ##############################
35
+
36
+ def _create_base_entity(
37
+ self,
38
+ client: Client,
39
+ entity_type: str,
40
+ entity_dict: dict,
41
+ **kwargs,
42
+ ) -> dict:
43
+ """
44
+ Create object in backend.
45
+
46
+ Parameters
47
+ ----------
48
+ client : Client
49
+ Client instance.
50
+ entity_type : str
51
+ Entity type.
52
+ entity_dict : dict
53
+ Object instance.
54
+ **kwargs : dict
55
+ Parameters to pass to the API call.
56
+
57
+ Returns
58
+ -------
59
+ dict
60
+ Object instance.
61
+ """
62
+ api = client.build_api(
63
+ ApiCategories.BASE.value,
64
+ BackendOperations.CREATE.value,
65
+ entity_type=entity_type,
66
+ )
67
+ return client.create_object(api, entity_dict, **kwargs)
68
+
69
+ def create_project_entity(
70
+ self,
71
+ _entity: ProjectEntity | None = None,
72
+ **kwargs,
73
+ ) -> ProjectEntity:
74
+ """
75
+ Create object in backend.
76
+
77
+ Parameters
78
+ ----------
79
+ _entity : ProjectEntity
80
+ Object instance.
81
+ **kwargs : dict
82
+ Parameters to pass to entity builder.
83
+
84
+ Returns
85
+ -------
86
+ Project
87
+ Object instance.
88
+ """
89
+ if _entity is not None:
90
+ client = _entity._client
91
+ obj = _entity
92
+ else:
93
+ client = get_client(kwargs.get("local"), kwargs.pop("config", None))
94
+ obj = build_entity_from_params(**kwargs)
95
+ ent = self._create_base_entity(client, obj.ENTITY_TYPE, obj.to_dict())
96
+ ent["local"] = client.is_local()
97
+ return build_entity_from_dict(ent)
98
+
99
+ def _read_base_entity(
100
+ self,
101
+ client: Client,
102
+ entity_type: str,
103
+ entity_name: str,
104
+ **kwargs,
105
+ ) -> dict:
106
+ """
107
+ Read object from backend.
108
+
109
+ Parameters
110
+ ----------
111
+ client : Client
112
+ Client instance.
113
+ entity_type : str
114
+ Entity type.
115
+ entity_name : str
116
+ Entity name.
117
+ **kwargs : dict
118
+ Parameters to pass to the API call.
119
+
120
+ Returns
121
+ -------
122
+ dict
123
+ Object instance.
124
+ """
125
+ api = client.build_api(
126
+ ApiCategories.BASE.value,
127
+ BackendOperations.READ.value,
128
+ entity_type=entity_type,
129
+ entity_name=entity_name,
130
+ )
131
+ return client.read_object(api, **kwargs)
132
+
133
+ def read_project_entity(
134
+ self,
135
+ entity_type: str,
136
+ entity_name: str,
137
+ **kwargs,
138
+ ) -> ProjectEntity:
139
+ """
140
+ Read object from backend.
141
+
142
+ Parameters
143
+ ----------
144
+ entity_type : str
145
+ Entity type.
146
+ entity_name : str
147
+ Entity name.
148
+ **kwargs : dict
149
+ Parameters to pass to entity builder.
150
+
151
+ Returns
152
+ -------
153
+ ProjectEntity
154
+ Object instance.
155
+ """
156
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
157
+ obj = self._read_base_entity(client, entity_type, entity_name, **kwargs)
158
+ obj["local"] = client.is_local()
159
+ return build_entity_from_dict(obj)
160
+
161
+ def import_project_entity(
162
+ self,
163
+ file: str,
164
+ **kwargs,
165
+ ) -> ProjectEntity:
166
+ """
167
+ Import object from a YAML file and create a new object into the backend.
168
+
169
+ Parameters
170
+ ----------
171
+ file : str
172
+ Path to YAML file.
173
+ **kwargs : dict
174
+ Additional keyword arguments.
175
+
176
+ Returns
177
+ -------
178
+ ProjectEntity
179
+ Object instance.
180
+ """
181
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
182
+ obj: dict = read_yaml(file)
183
+ obj["status"] = {}
184
+ obj["local"] = client.is_local()
185
+ ent: ProjectEntity = build_entity_from_dict(obj)
186
+
187
+ try:
188
+ self._create_base_entity(ent._client, ent.ENTITY_TYPE, ent.to_dict())
189
+ except EntityAlreadyExistsError:
190
+ raise EntityError(f"Entity {ent.name} already exists. If you want to update it, use load instead.")
191
+
192
+ # Import related entities
193
+ ent._import_entities(obj)
194
+ ent.refresh()
195
+ return ent
196
+
197
+ def load_project_entity(
198
+ self,
199
+ file: str,
200
+ **kwargs,
201
+ ) -> ProjectEntity:
202
+ """
203
+ Load object from a YAML file and update an existing object into the backend.
204
+
205
+ Parameters
206
+ ----------
207
+ file : str
208
+ Path to YAML file.
209
+ **kwargs : dict
210
+ Additional keyword arguments.
211
+
212
+ Returns
213
+ -------
214
+ ProjectEntity
215
+ Object instance.
216
+ """
217
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
218
+ obj: dict = read_yaml(file)
219
+ obj["local"] = client.is_local()
220
+ ent: ProjectEntity = build_entity_from_dict(obj)
221
+
222
+ try:
223
+ self._update_base_entity(ent._client, ent.ENTITY_TYPE, ent.name, ent.to_dict())
224
+ except EntityNotExistsError:
225
+ self._create_base_entity(ent._client, ent.ENTITY_TYPE, ent.to_dict())
226
+
227
+ # Load related entities
228
+ ent._load_entities(obj)
229
+ ent.refresh()
230
+ return ent
231
+
232
+ def _list_base_entities(
233
+ self,
234
+ client: Client,
235
+ entity_type: str,
236
+ **kwargs,
237
+ ) -> list[dict]:
238
+ """
239
+ List objects from backend.
240
+
241
+ Parameters
242
+ ----------
243
+ client : Client
244
+ Client instance.
245
+ entity_type : str
246
+ Entity type.
247
+ **kwargs : dict
248
+ Parameters to pass to the API call.
249
+
250
+ Returns
251
+ -------
252
+ list[dict]
253
+ List of objects.
254
+ """
255
+ api = client.build_api(
256
+ ApiCategories.BASE.value,
257
+ BackendOperations.LIST.value,
258
+ entity_type=entity_type,
259
+ )
260
+ return client.list_objects(api, **kwargs)
261
+
262
+ def list_project_entities(
263
+ self,
264
+ entity_type: str,
265
+ **kwargs,
266
+ ) -> list[ProjectEntity]:
267
+ """
268
+ List objects from backend.
269
+
270
+ Parameters
271
+ ----------
272
+ entity_type : str
273
+ Entity type.
274
+ **kwargs : dict
275
+ Parameters to pass to API call.
276
+
277
+ Returns
278
+ -------
279
+ list[ProjectEntity]
280
+ List of objects.
281
+ """
282
+ client = get_client(kwargs.pop("local", False))
283
+ objs = self._list_base_entities(client, entity_type, **kwargs)
284
+ entities = []
285
+ for obj in objs:
286
+ obj["local"] = client.is_local()
287
+ ent = build_entity_from_dict(obj)
288
+ entities.append(ent)
289
+ return entities
290
+
291
+ def _update_base_entity(
292
+ self,
293
+ client: Client,
294
+ entity_type: str,
295
+ entity_name: str,
296
+ entity_dict: dict,
297
+ **kwargs,
298
+ ) -> dict:
299
+ """
300
+ Update object method.
301
+
302
+ Parameters
303
+ ----------
304
+ client : Client
305
+ Client instance.
306
+ entity_type : str
307
+ Entity type.
308
+ entity_name : str
309
+ Entity name.
310
+ entity_dict : dict
311
+ Object instance.
312
+ **kwargs : dict
313
+ Parameters to pass to the API call.
314
+
315
+ Returns
316
+ -------
317
+ dict
318
+ Object instance.
319
+ """
320
+ api = client.build_api(
321
+ ApiCategories.BASE.value,
322
+ BackendOperations.UPDATE.value,
323
+ entity_type=entity_type,
324
+ entity_name=entity_name,
325
+ )
326
+ return client.update_object(api, entity_dict, **kwargs)
327
+
328
+ def update_project_entity(
329
+ self,
330
+ entity_type: str,
331
+ entity_name: str,
332
+ entity_dict: dict,
333
+ **kwargs,
334
+ ) -> ProjectEntity:
335
+ """
336
+ Update object method.
337
+
338
+ Parameters
339
+ ----------
340
+ entity_type : str
341
+ Entity type.
342
+ entity_name : str
343
+ Entity name.
344
+ entity_dict : dict
345
+ Object instance.
346
+ **kwargs : dict
347
+ Parameters to pass to entity builder.
348
+
349
+ Returns
350
+ -------
351
+ ProjectEntity
352
+ Object instance.
353
+ """
354
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
355
+ obj = self._update_base_entity(client, entity_type, entity_name, entity_dict, **kwargs)
356
+ obj["local"] = client.is_local()
357
+ return build_entity_from_dict(obj)
358
+
359
+ def _delete_base_entity(
360
+ self,
361
+ client: Client,
362
+ entity_type: str,
363
+ entity_name: str,
364
+ **kwargs,
365
+ ) -> dict:
366
+ """
367
+ Delete object method.
368
+
369
+ Parameters
370
+ ----------
371
+ client : Client
372
+ Client instance.
373
+ entity_type : str
374
+ Entity type.
375
+ entity_name : str
376
+ Entity name.
377
+ **kwargs : dict
378
+ Parameters to pass to the API call.
379
+
380
+ Returns
381
+ -------
382
+ dict
383
+ Response from backend.
384
+ """
385
+ api = client.build_api(
386
+ ApiCategories.BASE.value,
387
+ BackendOperations.DELETE.value,
388
+ entity_type=entity_type,
389
+ entity_name=entity_name,
390
+ )
391
+ return client.delete_object(api, **kwargs)
392
+
393
+ def delete_project_entity(
394
+ self,
395
+ entity_type: str,
396
+ entity_name: str,
397
+ **kwargs,
398
+ ) -> dict:
399
+ """
400
+ Delete object method.
401
+
402
+ Parameters
403
+ ----------
404
+ entity_type : str
405
+ Entity type.
406
+ entity_name : str
407
+ Entity name.
408
+ **kwargs : dict
409
+ Parameters to pass to entity builder.
410
+
411
+ Returns
412
+ -------
413
+ dict
414
+ Response from backend.
415
+ """
416
+ kwargs = self._set_params(**kwargs)
417
+ if cascade := kwargs.pop("cascade", None) is not None:
418
+ kwargs["params"]["cascade"] = str(cascade).lower()
419
+ if kwargs.pop("clean_context", True):
420
+ delete_context(entity_name)
421
+
422
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
423
+ return self._delete_base_entity(
424
+ client,
425
+ entity_type,
426
+ entity_name,
427
+ **kwargs,
428
+ )
429
+
430
+ def share_project_entity(
431
+ self,
432
+ entity_type: str,
433
+ entity_name: str,
434
+ **kwargs,
435
+ ) -> None:
436
+ """
437
+ Share object method.
438
+
439
+ Parameters
440
+ ----------
441
+ entity_type : str
442
+ Entity type.
443
+ entity_name : str
444
+ Entity name.
445
+ **kwargs : dict
446
+ Parameters to pass to entity builder.
447
+
448
+ Returns
449
+ -------
450
+ None
451
+ """
452
+ client = get_client(kwargs.pop("local", False), kwargs.pop("config", None))
453
+ api = client.build_api(
454
+ ApiCategories.BASE.value,
455
+ BackendOperations.SHARE.value,
456
+ entity_type=entity_type,
457
+ entity_name=entity_name,
458
+ )
459
+ user = kwargs.pop("user")
460
+ unshare = kwargs.pop("unshare", False)
461
+ kwargs = self._set_params(**kwargs)
462
+
463
+ # Unshare
464
+ if unshare:
465
+ users = client.read_object(api, **kwargs)
466
+ for u in users:
467
+ if u["user"] == user:
468
+ kwargs["params"]["id"] = u["id"]
469
+ client.delete_object(api, **kwargs)
470
+ break
471
+ return
472
+
473
+ # Share
474
+ kwargs["params"]["user"] = user
475
+ client.create_object(api, obj={}, **kwargs)
476
+
477
+ ##############################
478
+ # CRUD context entity
479
+ ##############################
480
+
481
+ def _create_context_entity(
482
+ self,
483
+ context: Context,
484
+ entity_type: str,
485
+ entity_dict: dict,
486
+ ) -> dict:
487
+ """
488
+ Create object in backend.
489
+
490
+ Parameters
491
+ ----------
492
+ context : Context
493
+ Context instance.
494
+ project : str
495
+ Project name.
496
+ entity_type : str
497
+ Entity type.
498
+ entity_dict : dict
499
+ Object instance.
500
+
501
+ Returns
502
+ -------
503
+ dict
504
+ Object instance.
505
+ """
506
+ api = context.client.build_api(
507
+ ApiCategories.CONTEXT.value,
508
+ BackendOperations.CREATE.value,
509
+ project=context.name,
510
+ entity_type=entity_type,
511
+ )
512
+ return context.client.create_object(api, entity_dict)
513
+
514
+ def create_context_entity(
515
+ self,
516
+ _entity: ContextEntity | None = None,
517
+ **kwargs,
518
+ ) -> dict:
519
+ """
520
+ Create object in backend.
521
+
522
+ Parameters
523
+ ----------
524
+ **kwargs : dict
525
+ Parameters to pass to entity builder.
526
+
527
+ Returns
528
+ -------
529
+ dict
530
+ Object instance.
531
+ """
532
+ if _entity is not None:
533
+ context = _entity._context()
534
+ obj = _entity
535
+ else:
536
+ context = self._get_context(kwargs["project"])
537
+ obj: ContextEntity = build_entity_from_params(**kwargs)
538
+ new_obj = self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
539
+ return build_entity_from_dict(new_obj)
540
+
541
+ def log_material_entity(self, **kwargs,) -> MaterialEntity:
542
+ """
543
+ Create object in backend and upload file.
544
+
545
+ Parameters
546
+ ----------
547
+ **kwargs : dict
548
+ Parameters to pass to entity builder.
549
+
550
+ Returns
551
+ -------
552
+ MaterialEntity
553
+ Object instance.
554
+ """
555
+ source = kwargs.pop("source")
556
+ context = self._get_context(kwargs["project"])
557
+ obj = build_entity_from_params(**kwargs)
558
+ if context.is_running:
559
+ obj.add_relationship(Relationship.PRODUCEDBY.value, obj.key, context.get_run_ctx())
560
+
561
+ new_obj: MaterialEntity = self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
562
+ new_obj = build_entity_from_dict(new_obj)
563
+ new_obj.upload(source)
564
+ return new_obj
565
+
566
+ def _read_context_entity(
567
+ self,
568
+ context: Context,
569
+ identifier: str,
570
+ entity_type: str | None = None,
571
+ project: str | None = None,
572
+ entity_id: str | None = None,
573
+ **kwargs,
574
+ ) -> dict:
575
+ """
576
+ Read object from backend.
577
+
578
+ Parameters
579
+ ----------
580
+ context : Context
581
+ Context instance.
582
+ identifier : str
583
+ Entity key (store://...) or entity name.
584
+ entity_type : str
585
+ Entity type.
586
+ project : str
587
+ Project name.
588
+ entity_id : str
589
+ Entity ID.
590
+ **kwargs : dict
591
+ Parameters to pass to the API call.
592
+
593
+ Returns
594
+ -------
595
+ dict
596
+ Object instance.
597
+ """
598
+ if not identifier.startswith("store://"):
599
+ if project is None or entity_type is None:
600
+ raise ValueError("Project and entity type must be specified.")
601
+ entity_name = identifier
602
+ else:
603
+ project, entity_type, _, entity_name, entity_id = parse_entity_key(identifier)
604
+
605
+ kwargs = self._set_params(**kwargs)
606
+
607
+ if entity_id is None:
608
+ kwargs["params"]["name"] = entity_name
609
+ api = context.client.build_api(
610
+ ApiCategories.CONTEXT.value,
611
+ BackendOperations.LIST.value,
612
+ project=context.name,
613
+ entity_type=entity_type,
614
+ )
615
+ return context.client.list_first_object(api, **kwargs)
616
+
617
+ api = context.client.build_api(
618
+ ApiCategories.CONTEXT.value,
619
+ BackendOperations.READ.value,
620
+ project=context.name,
621
+ entity_type=entity_type,
622
+ entity_id=entity_id,
623
+ )
624
+ return context.client.read_object(api, **kwargs)
625
+
626
+ def read_context_entity(
627
+ self,
628
+ identifier: str,
629
+ entity_type: str | None = None,
630
+ project: str | None = None,
631
+ entity_id: str | None = None,
632
+ **kwargs,
633
+ ) -> ContextEntity:
634
+ """
635
+ Read object from backend.
636
+
637
+ Parameters
638
+ ----------
639
+ identifier : str
640
+ Entity key (store://...) or entity name.
641
+ entity_type : str
642
+ Entity type.
643
+ project : str
644
+ Project name.
645
+ entity_id : str
646
+ Entity ID.
647
+ **kwargs : dict
648
+ Parameters to pass to the API call.
649
+
650
+ Returns
651
+ -------
652
+ VersionedEntity
653
+ Object instance.
654
+ """
655
+ context = self._get_context_from_identifier(identifier, project)
656
+ obj = self._read_context_entity(
657
+ context,
658
+ identifier,
659
+ entity_type=entity_type,
660
+ project=project,
661
+ entity_id=entity_id,
662
+ **kwargs,
663
+ )
664
+ return build_entity_from_dict(obj)
665
+
666
+ def read_material_entity(
667
+ self,
668
+ identifier: str,
669
+ entity_type: str | None = None,
670
+ project: str | None = None,
671
+ entity_id: str | None = None,
672
+ **kwargs,
673
+ ) -> MaterialEntity:
674
+ """
675
+ Read object from backend.
676
+
677
+ Parameters
678
+ ----------
679
+ identifier : str
680
+ Entity key (store://...) or entity name.
681
+ entity_type : str
682
+ Entity type.
683
+ project : str
684
+ Project name.
685
+ entity_id : str
686
+ Entity ID.
687
+ **kwargs : dict
688
+ Parameters to pass to the API call.
689
+
690
+ Returns
691
+ -------
692
+ MaterialEntity
693
+ Object instance.
694
+ """
695
+ obj: MaterialEntity = self.read_context_entity(
696
+ identifier,
697
+ entity_type=entity_type,
698
+ project=project,
699
+ entity_id=entity_id,
700
+ **kwargs,
701
+ )
702
+ obj._get_files_info()
703
+ return obj
704
+
705
+ def read_unversioned_entity(
706
+ self,
707
+ identifier: str,
708
+ entity_type: str | None = None,
709
+ project: str | None = None,
710
+ entity_id: str | None = None,
711
+ **kwargs,
712
+ ) -> UnversionedEntity:
713
+ """
714
+ Read object from backend.
715
+
716
+ Parameters
717
+ ----------
718
+ identifier : str
719
+ Entity key (store://...) or entity name.
720
+ entity_type : str
721
+ Entity type.
722
+ project : str
723
+ Project name.
724
+ entity_id : str
725
+ Entity ID.
726
+ **kwargs : dict
727
+ Parameters to pass to the API call.
728
+
729
+ Returns
730
+ -------
731
+ UnversionedEntity
732
+ Object instance.
733
+ """
734
+ if not identifier.startswith("store://"):
735
+ entity_id = identifier
736
+ return self.read_context_entity(
737
+ identifier,
738
+ entity_type=entity_type,
739
+ project=project,
740
+ entity_id=entity_id,
741
+ **kwargs,
742
+ )
743
+
744
+ def import_context_entity(
745
+ self,
746
+ file: str,
747
+ ) -> ContextEntity:
748
+ """
749
+ Import object from a YAML file and create a new object into the backend.
750
+
751
+ Parameters
752
+ ----------
753
+ file : str
754
+ Path to YAML file.
755
+
756
+ Returns
757
+ -------
758
+ ContextEntity
759
+ Object instance.
760
+ """
761
+ dict_obj: dict = read_yaml(file)
762
+ dict_obj["status"] = {}
763
+ context = self._get_context(dict_obj["project"])
764
+ obj = build_entity_from_dict(dict_obj)
765
+ try:
766
+ self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
767
+ except EntityAlreadyExistsError:
768
+ raise EntityError(f"Entity {obj.name} already exists. If you want to update it, use load instead.")
769
+ return obj
770
+
771
+ def import_executable_entity(
772
+ self,
773
+ file: str,
774
+ ) -> ExecutableEntity:
775
+ """
776
+ Import object from a YAML file and create a new object into the backend.
777
+
778
+ Parameters
779
+ ----------
780
+ file : str
781
+ Path to YAML file.
782
+
783
+ Returns
784
+ -------
785
+ ExecutableEntity
786
+ Object instance.
787
+ """
788
+ dict_obj: dict | list[dict] = read_yaml(file)
789
+ if isinstance(dict_obj, list):
790
+ exec_dict = dict_obj[0]
791
+ exec_dict["status"] = {}
792
+ tsk_dicts = []
793
+ for i in dict_obj[1:]:
794
+ i["status"] = {}
795
+ tsk_dicts.append(i)
796
+ else:
797
+ exec_dict = dict_obj
798
+ tsk_dicts = []
799
+
800
+ context = self._get_context(exec_dict["project"])
801
+ obj: ExecutableEntity = build_entity_from_dict(exec_dict)
802
+ try:
803
+ self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
804
+ except EntityAlreadyExistsError:
805
+ raise EntityError(f"Entity {obj.name} already exists. If you want to update it, use load instead.")
806
+
807
+ obj.import_tasks(tsk_dicts)
808
+
809
+ return obj
810
+
811
+ def load_context_entity(
812
+ self,
813
+ file: str,
814
+ ) -> ContextEntity:
815
+ """
816
+ Load object from a YAML file and update an existing object into the backend.
817
+
818
+ Parameters
819
+ ----------
820
+ file : str
821
+ Path to YAML file.
822
+
823
+ Returns
824
+ -------
825
+ ContextEntity
826
+ Object instance.
827
+ """
828
+ dict_obj: dict = read_yaml(file)
829
+ context = self._get_context(dict_obj["project"])
830
+ obj: ContextEntity = build_entity_from_dict(dict_obj)
831
+ try:
832
+ self._update_context_entity(context, obj.ENTITY_TYPE, obj.id, obj.to_dict())
833
+ except EntityNotExistsError:
834
+ self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
835
+ return obj
836
+
837
+ def load_executable_entity(
838
+ self,
839
+ file: str,
840
+ ) -> ExecutableEntity:
841
+ """
842
+ Load object from a YAML file and update an existing object into the backend.
843
+
844
+ Parameters
845
+ ----------
846
+ file : str
847
+ Path to YAML file.
848
+
849
+ Returns
850
+ -------
851
+ ExecutableEntity
852
+ Object instance.
853
+ """
854
+ dict_obj: dict | list[dict] = read_yaml(file)
855
+ if isinstance(dict_obj, list):
856
+ exec_dict = dict_obj[0]
857
+ tsk_dicts = dict_obj[1:]
858
+ else:
859
+ exec_dict = dict_obj
860
+ tsk_dicts = []
861
+
862
+ context = self._get_context(exec_dict["project"])
863
+ obj: ExecutableEntity = build_entity_from_dict(exec_dict)
864
+
865
+ try:
866
+ self._update_context_entity(context, obj.ENTITY_TYPE, obj.id, obj.to_dict())
867
+ except EntityNotExistsError:
868
+ self._create_context_entity(context, obj.ENTITY_TYPE, obj.to_dict())
869
+ obj.import_tasks(tsk_dicts)
870
+ return obj
871
+
872
+ def _read_context_entity_versions(
873
+ self,
874
+ context: Context,
875
+ identifier: str,
876
+ entity_type: str | None = None,
877
+ project: str | None = None,
878
+ **kwargs,
879
+ ) -> list[dict]:
880
+ """
881
+ Get all versions object from backend.
882
+
883
+ Parameters
884
+ ----------
885
+ context : Context
886
+ Context instance.
887
+ identifier : str
888
+ Entity key (store://...) or entity name.
889
+ entity_type : str
890
+ Entity type.
891
+ project : str
892
+ Project name.
893
+ **kwargs : dict
894
+ Parameters to pass to the API call.
895
+
896
+ Returns
897
+ -------
898
+ list[dict]
899
+ Object instances.
900
+ """
901
+ if not identifier.startswith("store://"):
902
+ if project is None or entity_type is None:
903
+ raise ValueError("Project and entity type must be specified.")
904
+ entity_name = identifier
905
+ else:
906
+ project, entity_type, _, entity_name, _ = parse_entity_key(identifier)
907
+
908
+ kwargs = self._set_params(**kwargs)
909
+ kwargs["params"]["name"] = entity_name
910
+ kwargs["params"]["versions"] = "all"
911
+
912
+ api = context.client.build_api(
913
+ ApiCategories.CONTEXT.value,
914
+ BackendOperations.LIST.value,
915
+ project=context.name,
916
+ entity_type=entity_type,
917
+ )
918
+ return context.client.list_objects(api, **kwargs)
919
+
920
+ def read_context_entity_versions(
921
+ self,
922
+ identifier: str,
923
+ entity_type: str | None = None,
924
+ project: str | None = None,
925
+ **kwargs,
926
+ ) -> list[ContextEntity]:
927
+ """
928
+ Read object versions from backend.
929
+
930
+ Parameters
931
+ ----------
932
+ identifier : str
933
+ Entity key (store://...) or entity name.
934
+ entity_type : str
935
+ Entity type.
936
+ project : str
937
+ Project name.
938
+ **kwargs : dict
939
+ Parameters to pass to the API call.
940
+
941
+ Returns
942
+ -------
943
+ list[ContextEntity]
944
+ List of object instances.
945
+ """
946
+ context = self._get_context_from_identifier(identifier, project)
947
+ obj = self._read_context_entity_versions(
948
+ context,
949
+ identifier,
950
+ entity_type=entity_type,
951
+ project=project,
952
+ **kwargs,
953
+ )
954
+ return [build_entity_from_dict(o) for o in obj]
955
+
956
+ def read_material_entity_versions(
957
+ self,
958
+ identifier: str,
959
+ entity_type: str | None = None,
960
+ project: str | None = None,
961
+ **kwargs,
962
+ ) -> list[MaterialEntity]:
963
+ """
964
+ Read object versions from backend.
965
+
966
+ Parameters
967
+ ----------
968
+ identifier : str
969
+ Entity key (store://...) or entity name.
970
+ entity_type : str
971
+ Entity type.
972
+ project : str
973
+ Project name.
974
+ **kwargs : dict
975
+ Parameters to pass to the API call.
976
+
977
+ Returns
978
+ -------
979
+ list[MaterialEntity]
980
+ List of object instances.
981
+ """
982
+ context = self._get_context_from_identifier(identifier, project)
983
+ objs = self._read_context_entity_versions(
984
+ context,
985
+ identifier,
986
+ entity_type=entity_type,
987
+ project=project,
988
+ **kwargs,
989
+ )
990
+ objects = []
991
+ for o in objs:
992
+ entity: MaterialEntity = build_entity_from_dict(o)
993
+ entity._get_files_info()
994
+ objects.append(entity)
995
+ return objects
996
+
997
+ def _list_context_entities(
998
+ self,
999
+ context: Context,
1000
+ entity_type: str,
1001
+ **kwargs,
1002
+ ) -> list[dict]:
1003
+ """
1004
+ List objects from backend.
1005
+
1006
+ Parameters
1007
+ ----------
1008
+ context : Context
1009
+ Context instance.
1010
+ entity_type : str
1011
+ Entity type.
1012
+ **kwargs : dict
1013
+ Parameters to pass to the API call.
1014
+
1015
+ Returns
1016
+ -------
1017
+ list[dict]
1018
+ List of objects.
1019
+ """
1020
+ api = context.client.build_api(
1021
+ ApiCategories.CONTEXT.value,
1022
+ BackendOperations.LIST.value,
1023
+ project=context.name,
1024
+ entity_type=entity_type,
1025
+ )
1026
+ return context.client.list_objects(api, **kwargs)
1027
+
1028
+ def list_context_entities(
1029
+ self,
1030
+ project: str,
1031
+ entity_type: str,
1032
+ **kwargs,
1033
+ ) -> list[ContextEntity]:
1034
+ """
1035
+ List all latest version objects from backend.
1036
+
1037
+ Parameters
1038
+ ----------
1039
+ project : str
1040
+ Project name.
1041
+ entity_type : str
1042
+ Entity type.
1043
+ **kwargs : dict
1044
+ Parameters to pass to the API call.
1045
+
1046
+ Returns
1047
+ -------
1048
+ list[ContextEntity]
1049
+ List of object instances.
1050
+ """
1051
+ context = self._get_context(project)
1052
+ objs = self._list_context_entities(context, entity_type, **kwargs)
1053
+ return [build_entity_from_dict(obj) for obj in objs]
1054
+
1055
+ def list_material_entities(
1056
+ self,
1057
+ project: str,
1058
+ entity_type: str,
1059
+ **kwargs,
1060
+ ) -> list[MaterialEntity]:
1061
+ """
1062
+ List all latest version objects from backend.
1063
+
1064
+ Parameters
1065
+ ----------
1066
+ project : str
1067
+ Project name.
1068
+ entity_type : str
1069
+ Entity type.
1070
+ **kwargs : dict
1071
+ Parameters to pass to the API call.
1072
+
1073
+ Returns
1074
+ -------
1075
+ list[MaterialEntity]
1076
+ List of object instances.
1077
+ """
1078
+ context = self._get_context(project)
1079
+ objs = self._list_context_entities(context, entity_type, **kwargs)
1080
+ objects = []
1081
+ for o in objs:
1082
+ entity: MaterialEntity = build_entity_from_dict(o)
1083
+ entity._get_files_info()
1084
+ objects.append(entity)
1085
+ return objects
1086
+
1087
+ def _update_context_entity(
1088
+ self,
1089
+ context: Context,
1090
+ entity_type: str,
1091
+ entity_id: str,
1092
+ entity_dict: dict,
1093
+ **kwargs,
1094
+ ) -> dict:
1095
+ """
1096
+ Update object. Note that object spec are immutable.
1097
+
1098
+ Parameters
1099
+ ----------
1100
+ context : Context
1101
+ Context instance.
1102
+ entity_type : str
1103
+ Entity type.
1104
+ entity_id : str
1105
+ Entity ID.
1106
+ entity_dict : dict
1107
+ Entity dictionary.
1108
+ **kwargs : dict
1109
+ Parameters to pass to the API call.
1110
+
1111
+ Returns
1112
+ -------
1113
+ dict
1114
+ Response from backend.
1115
+ """
1116
+ api = context.client.build_api(
1117
+ ApiCategories.CONTEXT.value,
1118
+ BackendOperations.UPDATE.value,
1119
+ project=context.name,
1120
+ entity_type=entity_type,
1121
+ entity_id=entity_id,
1122
+ )
1123
+ return context.client.update_object(api, entity_dict, **kwargs)
1124
+
1125
+ def update_context_entity(
1126
+ self,
1127
+ project: str,
1128
+ entity_type: str,
1129
+ entity_id: str,
1130
+ entity_dict: dict,
1131
+ **kwargs,
1132
+ ) -> ContextEntity:
1133
+ """
1134
+ Update object. Note that object spec are immutable.
1135
+
1136
+ Parameters
1137
+ ----------
1138
+ project : str
1139
+ Project name.
1140
+ entity_type : str
1141
+ Entity type.
1142
+ entity_id : str
1143
+ Entity ID.
1144
+ entity_dict : dict
1145
+ Entity dictionary.
1146
+ **kwargs : dict
1147
+ Parameters to pass to the API call.
1148
+
1149
+ Returns
1150
+ -------
1151
+ ContextEntity
1152
+ Object instance.
1153
+ """
1154
+ context = self._get_context(project)
1155
+ obj = self._update_context_entity(
1156
+ context,
1157
+ entity_type,
1158
+ entity_id,
1159
+ entity_dict,
1160
+ **kwargs,
1161
+ )
1162
+ return build_entity_from_dict(obj)
1163
+
1164
+ def _delete_context_entity(
1165
+ self,
1166
+ context: Context,
1167
+ identifier: str,
1168
+ entity_type: str | None = None,
1169
+ project: str | None = None,
1170
+ entity_id: str | None = None,
1171
+ **kwargs,
1172
+ ) -> dict:
1173
+ """
1174
+ Delete object from backend.
1175
+
1176
+ Parameters
1177
+ ----------
1178
+ context : Context
1179
+ Context instance.
1180
+ identifier : str
1181
+ Entity key (store://...) or entity name.
1182
+ entity_type : str
1183
+ Entity type.
1184
+ project : str
1185
+ Project name.
1186
+ entity_id : str
1187
+ Entity ID.
1188
+ **kwargs : dict
1189
+ Parameters to pass to the API call.
1190
+
1191
+ Returns
1192
+ -------
1193
+ dict
1194
+ Response from backend.
1195
+ """
1196
+ if not identifier.startswith("store://"):
1197
+ if project is None or entity_type is None:
1198
+ raise ValueError("Project must be provided.")
1199
+ entity_name = identifier
1200
+ else:
1201
+ project, _, _, entity_name, entity_id = parse_entity_key(identifier)
1202
+
1203
+ kwargs = self._set_params(**kwargs)
1204
+ if cascade := kwargs.pop("cascade", None) is not None:
1205
+ kwargs["params"]["cascade"] = str(cascade).lower()
1206
+
1207
+ delete_all_versions: bool = kwargs.pop("delete_all_versions", False)
1208
+
1209
+ if not delete_all_versions and entity_id is None:
1210
+ raise ValueError(
1211
+ "If `delete_all_versions` is False, `entity_id` must be provided, either as an argument or in key `identifier`."
1212
+ )
1213
+
1214
+ if delete_all_versions:
1215
+ api = context.client.build_api(
1216
+ ApiCategories.CONTEXT.value,
1217
+ BackendOperations.LIST.value,
1218
+ project=context.name,
1219
+ entity_type=entity_type,
1220
+ )
1221
+ kwargs["params"]["name"] = entity_name
1222
+ else:
1223
+ api = context.client.build_api(
1224
+ ApiCategories.CONTEXT.value,
1225
+ BackendOperations.DELETE.value,
1226
+ project=context.name,
1227
+ entity_type=entity_type,
1228
+ entity_id=entity_id,
1229
+ )
1230
+ return context.client.delete_object(api, **kwargs)
1231
+
1232
+ def delete_context_entity(
1233
+ self,
1234
+ identifier: str,
1235
+ project: str | None = None,
1236
+ entity_type: str | None = None,
1237
+ entity_id: str | None = None,
1238
+ **kwargs,
1239
+ ) -> dict:
1240
+ """
1241
+ Delete object from backend.
1242
+
1243
+ Parameters
1244
+ ----------
1245
+ identifier : str
1246
+ Entity key (store://...) or entity name.
1247
+ project : str
1248
+ Project name.
1249
+ entity_type : str
1250
+ Entity type.
1251
+ entity_id : str
1252
+ Entity ID.
1253
+ **kwargs : dict
1254
+ Parameters to pass to the API call.
1255
+
1256
+ Returns
1257
+ -------
1258
+ dict
1259
+ Response from backend.
1260
+ """
1261
+ context = self._get_context_from_identifier(identifier, project)
1262
+ return self._delete_context_entity(
1263
+ context,
1264
+ identifier,
1265
+ entity_type,
1266
+ context.name,
1267
+ entity_id,
1268
+ **kwargs,
1269
+ )
1270
+
1271
+ ##############################
1272
+ # Context entity operations
1273
+ ##############################
1274
+
1275
+ def read_secret_data(
1276
+ self,
1277
+ project: str,
1278
+ entity_type: str,
1279
+ **kwargs,
1280
+ ) -> dict:
1281
+ """
1282
+ Get data from backend.
1283
+
1284
+ Parameters
1285
+ ----------
1286
+ project : str
1287
+ Project name.
1288
+ entity_type : str
1289
+ Entity type.
1290
+ **kwargs : dict
1291
+ Parameters to pass to the API call.
1292
+
1293
+ Returns
1294
+ -------
1295
+ dict
1296
+ Response from backend.
1297
+ """
1298
+ context = self._get_context(project)
1299
+ api = context.client.build_api(
1300
+ ApiCategories.CONTEXT.value,
1301
+ BackendOperations.DATA.value,
1302
+ project=context.name,
1303
+ entity_type=entity_type,
1304
+ )
1305
+ return context.client.read_object(api, **kwargs)
1306
+
1307
+ def update_secret_data(
1308
+ self,
1309
+ project: str,
1310
+ entity_type: str,
1311
+ data: dict,
1312
+ **kwargs,
1313
+ ) -> None:
1314
+ """
1315
+ Set data in backend.
1316
+
1317
+ Parameters
1318
+ ----------
1319
+ project : str
1320
+ Project name.
1321
+ entity_type : str
1322
+ Entity type.
1323
+ data : dict
1324
+ Data dictionary.
1325
+ **kwargs : dict
1326
+ Parameters to pass to the API call.
1327
+
1328
+ Returns
1329
+ -------
1330
+ None
1331
+ """
1332
+ context = self._get_context(project)
1333
+ api = context.client.build_api(
1334
+ ApiCategories.CONTEXT.value,
1335
+ BackendOperations.DATA.value,
1336
+ project=context.name,
1337
+ entity_type=entity_type,
1338
+ )
1339
+ return context.client.update_object(api, data, **kwargs)
1340
+
1341
+ def read_run_logs(
1342
+ self,
1343
+ project: str,
1344
+ entity_type: str,
1345
+ entity_id: str,
1346
+ **kwargs,
1347
+ ) -> dict:
1348
+ """
1349
+ Get logs from backend.
1350
+
1351
+ Parameters
1352
+ ----------
1353
+ project : str
1354
+ Project name.
1355
+ entity_type : str
1356
+ Entity type.
1357
+ entity_id : str
1358
+ Entity ID.
1359
+ **kwargs : dict
1360
+ Parameters to pass to the API call.
1361
+
1362
+ Returns
1363
+ -------
1364
+ dict
1365
+ Response from backend.
1366
+ """
1367
+ context = self._get_context(project)
1368
+ api = context.client.build_api(
1369
+ ApiCategories.CONTEXT.value,
1370
+ BackendOperations.LOGS.value,
1371
+ project=context.name,
1372
+ entity_type=entity_type,
1373
+ entity_id=entity_id,
1374
+ )
1375
+ return context.client.read_object(api, **kwargs)
1376
+
1377
+ def stop_run(
1378
+ self,
1379
+ project: str,
1380
+ entity_type: str,
1381
+ entity_id: str,
1382
+ **kwargs,
1383
+ ) -> None:
1384
+ """
1385
+ Stop object in backend.
1386
+
1387
+ Parameters
1388
+ ----------
1389
+ project : str
1390
+ Project name.
1391
+ entity_type : str
1392
+ Entity type.
1393
+ entity_id : str
1394
+ Entity ID.
1395
+ **kwargs : dict
1396
+ Parameters to pass to the API call.
1397
+
1398
+ Returns
1399
+ -------
1400
+ None
1401
+ """
1402
+ context = self._get_context(project)
1403
+ api = context.client.build_api(
1404
+ ApiCategories.CONTEXT.value,
1405
+ BackendOperations.STOP.value,
1406
+ project=context.name,
1407
+ entity_type=entity_type,
1408
+ entity_id=entity_id,
1409
+ )
1410
+ return context.client.create_object(api, **kwargs)
1411
+
1412
+ def resume_run(
1413
+ self,
1414
+ project: str,
1415
+ entity_type: str,
1416
+ entity_id: str,
1417
+ **kwargs,
1418
+ ) -> None:
1419
+ """
1420
+ Resume object in backend.
1421
+
1422
+ Parameters
1423
+ ----------
1424
+ project : str
1425
+ Project name.
1426
+ entity_type : str
1427
+ Entity type.
1428
+ entity_id : str
1429
+ Entity ID.
1430
+ **kwargs : dict
1431
+ Parameters to pass to the API call.
1432
+
1433
+ Returns
1434
+ -------
1435
+ None
1436
+ """
1437
+ context = self._get_context(project)
1438
+ api = context.client.build_api(
1439
+ ApiCategories.CONTEXT.value,
1440
+ BackendOperations.RESUME.value,
1441
+ project=context.name,
1442
+ entity_type=entity_type,
1443
+ entity_id=entity_id,
1444
+ )
1445
+ return context.client.create_object(api, **kwargs)
1446
+
1447
+ def read_files_info(
1448
+ self,
1449
+ project: str,
1450
+ entity_type: str,
1451
+ entity_id: str,
1452
+ **kwargs,
1453
+ ) -> list[dict]:
1454
+ """
1455
+ Get files info from backend.
1456
+
1457
+ Parameters
1458
+ ----------
1459
+ project : str
1460
+ Project name.
1461
+ entity_type : str
1462
+ Entity type.
1463
+ entity_id : str
1464
+ Entity ID.
1465
+ **kwargs : dict
1466
+ Parameters to pass to the API call.
1467
+
1468
+ Returns
1469
+ -------
1470
+ list[dict]
1471
+ Response from backend.
1472
+ """
1473
+ context = self._get_context(project)
1474
+ api = context.client.build_api(
1475
+ ApiCategories.CONTEXT.value,
1476
+ BackendOperations.FILES.value,
1477
+ project=context.name,
1478
+ entity_type=entity_type,
1479
+ entity_id=entity_id,
1480
+ )
1481
+ return context.client.read_object(api, **kwargs)
1482
+
1483
+ def update_files_info(
1484
+ self,
1485
+ project: str,
1486
+ entity_type: str,
1487
+ entity_id: str,
1488
+ entity_list: list[dict],
1489
+ **kwargs,
1490
+ ) -> None:
1491
+ """
1492
+ Get files info from backend.
1493
+
1494
+ Parameters
1495
+ ----------
1496
+ project : str
1497
+ Project name.
1498
+ entity_type : str
1499
+ Entity type.
1500
+ entity_id : str
1501
+ Entity ID.
1502
+ entity_list : list[dict]
1503
+ Entity list.
1504
+ **kwargs : dict
1505
+ Parameters to pass to the API call.
1506
+
1507
+ Returns
1508
+ -------
1509
+ None
1510
+ """
1511
+ context = self._get_context(project)
1512
+ api = context.client.build_api(
1513
+ ApiCategories.CONTEXT.value,
1514
+ BackendOperations.FILES.value,
1515
+ project=context.name,
1516
+ entity_type=entity_type,
1517
+ entity_id=entity_id,
1518
+ )
1519
+ return context.client.update_object(api, entity_list, **kwargs)
1520
+
1521
+ def _search(
1522
+ self,
1523
+ project: str,
1524
+ **kwargs,
1525
+ ) -> dict:
1526
+ """
1527
+ Search in backend.
1528
+
1529
+ Parameters
1530
+ ----------
1531
+ project : str
1532
+ Project name.
1533
+ **kwargs : dict
1534
+ Parameters to pass to the API call.
1535
+
1536
+ Returns
1537
+ -------
1538
+ dict
1539
+ Response from backend.
1540
+ """
1541
+ context = self._get_context(project)
1542
+ api = context.client.build_api(
1543
+ ApiCategories.CONTEXT.value,
1544
+ BackendOperations.SEARCH.value,
1545
+ project=context.name,
1546
+ )
1547
+ return context.client.read_object(api, **kwargs)
1548
+
1549
+ def search_entity(
1550
+ self,
1551
+ project: str,
1552
+ query: str | None = None,
1553
+ entity_types: list[str] | None = None,
1554
+ name: str | None = None,
1555
+ kind: str | None = None,
1556
+ created: str | None = None,
1557
+ updated: str | None = None,
1558
+ description: str | None = None,
1559
+ labels: list[str] | None = None,
1560
+ **kwargs,
1561
+ ) -> list[ContextEntity]:
1562
+ """
1563
+ Search objects from backend.
1564
+
1565
+ Parameters
1566
+ ----------
1567
+ project : str
1568
+ Project name.
1569
+ query : str
1570
+ Search query.
1571
+ entity_types : list[str]
1572
+ Entity types.
1573
+ name : str
1574
+ Entity name.
1575
+ kind : str
1576
+ Entity kind.
1577
+ created : str
1578
+ Entity creation date.
1579
+ updated : str
1580
+ Entity update date.
1581
+ description : str
1582
+ Entity description.
1583
+ labels : list[str]
1584
+ Entity labels.
1585
+ **kwargs : dict
1586
+ Parameters to pass to the API call.
1587
+
1588
+ Returns
1589
+ -------
1590
+ list[ContextEntity]
1591
+ List of object instances.
1592
+ """
1593
+ context = self._get_context(project)
1594
+
1595
+ kwargs = self._set_params(**kwargs)
1596
+
1597
+ # Add search query
1598
+ if query is not None:
1599
+ kwargs["params"]["q"] = query
1600
+
1601
+ # Add search filters
1602
+ fq = []
1603
+
1604
+ # Entity types
1605
+ if entity_types is not None:
1606
+ if len(entity_types) == 1:
1607
+ entity_types = entity_types[0]
1608
+ else:
1609
+ entity_types = " OR ".join(entity_types)
1610
+ fq.append(f"type:({entity_types})")
1611
+
1612
+ # Name
1613
+ if name is not None:
1614
+ fq.append(f'metadata.name:"{name}"')
1615
+
1616
+ # Kind
1617
+ if kind is not None:
1618
+ fq.append(f'kind:"{kind}"')
1619
+
1620
+ # Time
1621
+ created = created if created is not None else "*"
1622
+ updated = updated if updated is not None else "*"
1623
+ fq.append(f"metadata.updated:[{created} TO {updated}]")
1624
+
1625
+ # Description
1626
+ if description is not None:
1627
+ fq.append(f'metadata.description:"{description}"')
1628
+
1629
+ # Labels
1630
+ if labels is not None:
1631
+ if len(labels) == 1:
1632
+ labels = labels[0]
1633
+ else:
1634
+ labels = " AND ".join(labels)
1635
+ fq.append(f"metadata.labels:({labels})")
1636
+
1637
+ # Add filters
1638
+ kwargs["params"]["fq"] = fq
1639
+
1640
+ objs = self._search(context, **kwargs)
1641
+ return objs
1642
+ return [build_entity_from_dict(obj) for obj in objs]
1643
+
1644
+ ##############################
1645
+ # Helpers
1646
+ ##############################
1647
+
1648
+ @staticmethod
1649
+ def _set_params(**kwargs) -> dict:
1650
+ """
1651
+ Format params parameter.
1652
+
1653
+ Parameters
1654
+ ----------
1655
+ **kwargs : dict
1656
+ Keyword arguments.
1657
+
1658
+ Returns
1659
+ -------
1660
+ dict
1661
+ Parameters with initialized params.
1662
+ """
1663
+ if not kwargs:
1664
+ kwargs = {}
1665
+ if "params" not in kwargs:
1666
+ kwargs["params"] = {}
1667
+ return kwargs
1668
+
1669
+ def _get_context_from_identifier(
1670
+ self,
1671
+ identifier: str,
1672
+ project: str | None = None,
1673
+ ) -> Context:
1674
+ """
1675
+ Get context from project.
1676
+
1677
+ Parameters
1678
+ ----------
1679
+ identifier : str
1680
+ Entity key (store://...) or entity name.
1681
+ project : str
1682
+ Project name.
1683
+
1684
+ Returns
1685
+ -------
1686
+ Context
1687
+ Context.
1688
+ """
1689
+ if not identifier.startswith("store://"):
1690
+ if project is None:
1691
+ raise EntityError("Specify project if you do not specify entity key.")
1692
+ else:
1693
+ project = get_project_from_key(identifier)
1694
+
1695
+ return self._get_context(project)
1696
+
1697
+ def _get_context(
1698
+ self,
1699
+ project: str,
1700
+ ) -> Context:
1701
+ """
1702
+ Check if the given project is in the context.
1703
+ Otherwise try to get the project from remote.
1704
+ Finally return the client.
1705
+
1706
+ Parameters
1707
+ ----------
1708
+ project : str
1709
+ Project name.
1710
+
1711
+ Returns
1712
+ -------
1713
+ Context
1714
+ Context.
1715
+ """
1716
+ try:
1717
+ return get_context(project)
1718
+ except ContextError:
1719
+ return self._get_context_from_remote(project)
1720
+
1721
+ def _get_context_from_remote(
1722
+ self,
1723
+ project: str,
1724
+ ) -> Client:
1725
+ """
1726
+ Get context from remote.
1727
+
1728
+ Parameters
1729
+ ----------
1730
+ project : str
1731
+ Project name.
1732
+
1733
+ Returns
1734
+ -------
1735
+ Client
1736
+ Client.
1737
+ """
1738
+ try:
1739
+ client = get_client()
1740
+ obj = self._read_base_entity(client, EntityTypes.PROJECT.value, project)
1741
+ build_entity_from_dict(obj)
1742
+ return get_context(project)
1743
+ except EntityNotExistsError:
1744
+ raise ContextError(f"Project '{project}' not found.")
1745
+
1746
+
1747
+ processor = OperationsProcessor()