digitalhub 0.9.2__py3-none-any.whl → 0.10.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 (121) hide show
  1. digitalhub/__init__.py +2 -3
  2. digitalhub/client/_base/api_builder.py +1 -1
  3. digitalhub/client/_base/client.py +25 -2
  4. digitalhub/client/_base/params_builder.py +16 -0
  5. digitalhub/client/dhcore/api_builder.py +9 -3
  6. digitalhub/client/dhcore/client.py +30 -398
  7. digitalhub/client/dhcore/configurator.py +361 -0
  8. digitalhub/client/dhcore/error_parser.py +107 -0
  9. digitalhub/client/dhcore/models.py +13 -23
  10. digitalhub/client/dhcore/params_builder.py +178 -0
  11. digitalhub/client/dhcore/utils.py +4 -44
  12. digitalhub/client/local/api_builder.py +13 -18
  13. digitalhub/client/local/client.py +18 -2
  14. digitalhub/client/local/enums.py +11 -0
  15. digitalhub/client/local/params_builder.py +116 -0
  16. digitalhub/configurator/api.py +31 -0
  17. digitalhub/configurator/configurator.py +195 -0
  18. digitalhub/configurator/credentials_store.py +65 -0
  19. digitalhub/configurator/ini_module.py +74 -0
  20. digitalhub/entities/_base/_base/entity.py +2 -2
  21. digitalhub/entities/_base/context/entity.py +4 -4
  22. digitalhub/entities/_base/entity/builder.py +5 -5
  23. digitalhub/entities/_base/executable/entity.py +2 -2
  24. digitalhub/entities/_base/material/entity.py +12 -12
  25. digitalhub/entities/_base/material/status.py +1 -1
  26. digitalhub/entities/_base/material/utils.py +2 -2
  27. digitalhub/entities/_base/unversioned/entity.py +2 -2
  28. digitalhub/entities/_base/versioned/entity.py +2 -2
  29. digitalhub/entities/_commons/enums.py +2 -0
  30. digitalhub/entities/_commons/metrics.py +164 -0
  31. digitalhub/entities/_commons/types.py +5 -0
  32. digitalhub/entities/_commons/utils.py +2 -2
  33. digitalhub/entities/_processors/base.py +527 -0
  34. digitalhub/entities/{_operations/processor.py → _processors/context.py} +212 -837
  35. digitalhub/entities/_processors/utils.py +158 -0
  36. digitalhub/entities/artifact/artifact/spec.py +3 -1
  37. digitalhub/entities/artifact/crud.py +13 -12
  38. digitalhub/entities/artifact/utils.py +1 -1
  39. digitalhub/entities/builders.py +6 -18
  40. digitalhub/entities/dataitem/_base/entity.py +0 -41
  41. digitalhub/entities/dataitem/crud.py +27 -15
  42. digitalhub/entities/dataitem/table/entity.py +49 -35
  43. digitalhub/entities/dataitem/table/models.py +4 -3
  44. digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +46 -54
  45. digitalhub/entities/dataitem/utils.py +58 -10
  46. digitalhub/entities/function/crud.py +9 -9
  47. digitalhub/entities/model/_base/entity.py +120 -0
  48. digitalhub/entities/model/_base/spec.py +6 -17
  49. digitalhub/entities/model/_base/status.py +10 -0
  50. digitalhub/entities/model/crud.py +13 -12
  51. digitalhub/entities/model/huggingface/spec.py +9 -4
  52. digitalhub/entities/model/mlflow/models.py +2 -2
  53. digitalhub/entities/model/mlflow/spec.py +7 -7
  54. digitalhub/entities/model/mlflow/utils.py +44 -5
  55. digitalhub/entities/project/_base/entity.py +317 -9
  56. digitalhub/entities/project/_base/spec.py +8 -6
  57. digitalhub/entities/project/crud.py +12 -11
  58. digitalhub/entities/run/_base/entity.py +103 -6
  59. digitalhub/entities/run/_base/spec.py +4 -2
  60. digitalhub/entities/run/_base/status.py +12 -0
  61. digitalhub/entities/run/crud.py +8 -8
  62. digitalhub/entities/secret/_base/entity.py +3 -3
  63. digitalhub/entities/secret/_base/spec.py +4 -2
  64. digitalhub/entities/secret/crud.py +11 -9
  65. digitalhub/entities/task/_base/entity.py +4 -4
  66. digitalhub/entities/task/_base/models.py +51 -40
  67. digitalhub/entities/task/_base/spec.py +2 -0
  68. digitalhub/entities/task/_base/utils.py +2 -2
  69. digitalhub/entities/task/crud.py +12 -8
  70. digitalhub/entities/workflow/crud.py +9 -9
  71. digitalhub/factory/utils.py +9 -9
  72. digitalhub/readers/{_base → data/_base}/builder.py +1 -1
  73. digitalhub/readers/{_base → data/_base}/reader.py +16 -4
  74. digitalhub/readers/{api.py → data/api.py} +2 -2
  75. digitalhub/readers/{factory.py → data/factory.py} +3 -3
  76. digitalhub/readers/{pandas → data/pandas}/builder.py +2 -2
  77. digitalhub/readers/{pandas → data/pandas}/reader.py +110 -30
  78. digitalhub/readers/query/__init__.py +0 -0
  79. digitalhub/stores/_base/store.py +59 -69
  80. digitalhub/stores/api.py +8 -33
  81. digitalhub/stores/builder.py +44 -161
  82. digitalhub/stores/local/store.py +106 -89
  83. digitalhub/stores/remote/store.py +86 -11
  84. digitalhub/stores/s3/configurator.py +108 -0
  85. digitalhub/stores/s3/enums.py +17 -0
  86. digitalhub/stores/s3/models.py +21 -0
  87. digitalhub/stores/s3/store.py +154 -70
  88. digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
  89. digitalhub/stores/sql/configurator.py +88 -0
  90. digitalhub/stores/sql/enums.py +16 -0
  91. digitalhub/stores/sql/models.py +24 -0
  92. digitalhub/stores/sql/store.py +106 -85
  93. digitalhub/{readers/_commons → utils}/enums.py +5 -1
  94. digitalhub/utils/exceptions.py +6 -0
  95. digitalhub/utils/file_utils.py +8 -7
  96. digitalhub/utils/generic_utils.py +28 -15
  97. digitalhub/utils/git_utils.py +16 -9
  98. digitalhub/utils/types.py +5 -0
  99. digitalhub/utils/uri_utils.py +2 -2
  100. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/METADATA +25 -31
  101. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/RECORD +108 -99
  102. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/WHEEL +1 -2
  103. digitalhub/client/dhcore/env.py +0 -23
  104. digitalhub/entities/_base/project/entity.py +0 -341
  105. digitalhub-0.9.2.dist-info/top_level.txt +0 -2
  106. test/local/CRUD/test_artifacts.py +0 -96
  107. test/local/CRUD/test_dataitems.py +0 -96
  108. test/local/CRUD/test_models.py +0 -95
  109. test/local/imports/test_imports.py +0 -66
  110. test/local/instances/test_validate.py +0 -55
  111. test/test_crud_functions.py +0 -109
  112. test/test_crud_runs.py +0 -86
  113. test/test_crud_tasks.py +0 -81
  114. test/testkfp.py +0 -37
  115. test/testkfp_pipeline.py +0 -22
  116. /digitalhub/{entities/_base/project → configurator}/__init__.py +0 -0
  117. /digitalhub/entities/{_operations → _processors}/__init__.py +0 -0
  118. /digitalhub/readers/{_base → data}/__init__.py +0 -0
  119. /digitalhub/readers/{_commons → data/_base}/__init__.py +0 -0
  120. /digitalhub/readers/{pandas → data/pandas}/__init__.py +0 -0
  121. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info/licenses}/LICENSE.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Optional
4
+
3
5
  from pydantic import Field
4
6
 
5
7
  from digitalhub.entities.model._base.spec import ModelSpec, ModelValidator
@@ -15,13 +17,13 @@ class ModelSpecHuggingface(ModelSpec):
15
17
  path: str,
16
18
  framework: str | None = None,
17
19
  algorithm: str | None = None,
18
- base_model: str | None = None,
19
20
  parameters: dict | None = None,
20
- metrics: dict | None = None,
21
+ base_model: str | None = None,
21
22
  model_id: str | None = None,
22
- model_revision: str = None,
23
+ model_revision: str | None = None,
23
24
  ) -> None:
24
- super().__init__(path, framework, algorithm, base_model, parameters, metrics)
25
+ super().__init__(path, framework, algorithm, parameters)
26
+ self.base_model = base_model
25
27
  self.model_id = model_id
26
28
  self.model_revision = model_revision
27
29
 
@@ -31,6 +33,9 @@ class ModelValidatorHuggingface(ModelValidator):
31
33
  ModelValidatorHuggingface validator.
32
34
  """
33
35
 
36
+ base_model: Optional[str] = None
37
+ """Base model."""
38
+
34
39
  placeholder_model_id: str = Field(default=None, alias="model_id")
35
40
  """Huggingface model id. If not specified, the model is loaded from the model path."""
36
41
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Optional
4
4
 
5
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel
6
6
 
7
7
 
8
8
  class Signature(BaseModel):
@@ -26,7 +26,7 @@ class Dataset(BaseModel):
26
26
  name: Optional[str] = None
27
27
  digest: Optional[str] = None
28
28
  profile: Optional[str] = None
29
- schema_: Optional[str] = Field(default=None, alias="schema")
29
+ dataset_schema: Optional[str] = None
30
30
  source: Optional[str] = None
31
31
  source_type: Optional[str] = None
32
32
 
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Optional
4
+
3
5
  from pydantic import Field
4
6
 
5
7
  from digitalhub.entities.model._base.spec import ModelSpec, ModelValidator
@@ -16,15 +18,13 @@ class ModelSpecMlflow(ModelSpec):
16
18
  path: str,
17
19
  framework: str | None = None,
18
20
  algorithm: str | None = None,
19
- base_model: str | None = None,
20
21
  parameters: dict | None = None,
21
- metrics: dict | None = None,
22
22
  flavor: str | None = None,
23
23
  model_config: dict | None = None,
24
24
  input_datasets: list[Dataset] | None = None,
25
- signature: Signature = None,
25
+ signature: Signature | None = None,
26
26
  ) -> None:
27
- super().__init__(path, framework, algorithm, base_model, parameters, metrics)
27
+ super().__init__(path, framework, algorithm, parameters)
28
28
  self.flavor = flavor
29
29
  self.model_config = model_config
30
30
  self.input_datasets = input_datasets
@@ -36,14 +36,14 @@ class ModelValidatorMlflow(ModelValidator):
36
36
  ModelValidatorMlflow validator.
37
37
  """
38
38
 
39
- flavor: str = None
39
+ flavor: Optional[str] = None
40
40
  """Mlflow model flavor."""
41
41
 
42
42
  placeholder_cfg_: dict = Field(default=None, alias="model_config")
43
43
  """Mlflow model config."""
44
44
 
45
- input_datasets: list[Dataset] = None
45
+ input_datasets: Optional[list[Dataset]] = None
46
46
  """Mlflow input datasets."""
47
47
 
48
- signature: Signature = None
48
+ signature: Optional[Signature] = None
49
49
  """Mlflow model signature."""
@@ -1,11 +1,32 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import typing
3
4
  from urllib.parse import urlparse
4
5
 
5
6
  import mlflow
6
7
 
7
8
  from digitalhub.entities.model.mlflow.models import Dataset, Signature
8
9
 
10
+ if typing.TYPE_CHECKING:
11
+ from mlflow.entities import Run
12
+
13
+
14
+ def get_mlflow_run(run_id: str) -> Run:
15
+ """
16
+ Get MLFlow run.
17
+
18
+ Parameters
19
+ ----------
20
+ run_id : str
21
+ The id of the mlflow run.
22
+
23
+ Returns
24
+ -------
25
+ Run
26
+ The extracted run.
27
+ """
28
+ return mlflow.MlflowClient().get_run(run_id)
29
+
9
30
 
10
31
  def from_mlflow_run(run_id: str) -> dict:
11
32
  """
@@ -23,13 +44,11 @@ def from_mlflow_run(run_id: str) -> dict:
23
44
  """
24
45
 
25
46
  # Get MLFlow run
26
- client = mlflow.MlflowClient()
27
- run = client.get_run(run_id)
47
+ run = get_mlflow_run(run_id)
28
48
 
29
49
  # Extract spec
30
50
  data = run.data
31
51
  parameters = data.params
32
- metrics = data.metrics
33
52
  source_path = urlparse(run.info.artifact_uri).path
34
53
  model_uri = f"runs:/{run_id}/model"
35
54
  model = mlflow.pyfunc.load_model(model_uri=model_uri)
@@ -56,7 +75,7 @@ def from_mlflow_run(run_id: str) -> dict:
56
75
  name=d.dataset.name,
57
76
  digest=d.dataset.digest,
58
77
  profile=d.dataset.profile,
59
- schema=d.dataset.schema,
78
+ dataset_schema=d.dataset.schema,
60
79
  source=d.dataset.source,
61
80
  source_type=d.dataset.source_type,
62
81
  ).to_dict()
@@ -72,7 +91,6 @@ def from_mlflow_run(run_id: str) -> dict:
72
91
  # common properties
73
92
  model_params["framework"] = flavor
74
93
  model_params["parameters"] = parameters
75
- model_params["metrics"] = metrics
76
94
 
77
95
  # specific to MLFlow
78
96
  model_params["flavor"] = flavor
@@ -81,3 +99,24 @@ def from_mlflow_run(run_id: str) -> dict:
81
99
  model_params["signature"] = signature
82
100
 
83
101
  return model_params
102
+
103
+
104
+ def get_mlflow_model_metrics(run_id: str) -> dict:
105
+ """
106
+ Get MLFlow model metrics for a given run id.
107
+
108
+ Parameters
109
+ ----------
110
+ run_id : str
111
+ The id of the mlflow run.
112
+
113
+ Returns
114
+ -------
115
+ dict
116
+ The extracted metrics.
117
+ """
118
+ # Get MLFlow run
119
+ run = get_mlflow_run(run_id)
120
+
121
+ # Extract metrics
122
+ return run.data.metrics
@@ -1,11 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import typing
4
+ from pathlib import Path
4
5
  from typing import Any
5
6
 
6
- from digitalhub.entities._base.project.entity import ProjectEntity
7
+ from digitalhub.client.api import get_client
8
+ from digitalhub.context.api import build_context
9
+ from digitalhub.entities._base.entity.entity import Entity
7
10
  from digitalhub.entities._commons.enums import EntityTypes
8
- from digitalhub.entities._operations.processor import processor
11
+ from digitalhub.entities._processors.base import base_processor
12
+ from digitalhub.entities._processors.context import context_processor
9
13
  from digitalhub.entities.artifact.crud import (
10
14
  delete_artifact,
11
15
  get_artifact,
@@ -64,9 +68,14 @@ from digitalhub.entities.workflow.crud import (
64
68
  new_workflow,
65
69
  update_workflow,
66
70
  )
67
- from digitalhub.utils.exceptions import EntityError
71
+ from digitalhub.factory.api import build_entity_from_dict
72
+ from digitalhub.utils.exceptions import BackendError, EntityAlreadyExistsError, EntityError
73
+ from digitalhub.utils.generic_utils import get_timestamp
74
+ from digitalhub.utils.io_utils import write_yaml
75
+ from digitalhub.utils.uri_utils import has_local_scheme
68
76
 
69
77
  if typing.TYPE_CHECKING:
78
+ from digitalhub.entities._base.context.entity import ContextEntity
70
79
  from digitalhub.entities._base.entity.metadata import Metadata
71
80
  from digitalhub.entities.artifact._base.entity import Artifact
72
81
  from digitalhub.entities.dataitem._base.entity import Dataitem
@@ -79,7 +88,7 @@ if typing.TYPE_CHECKING:
79
88
  from digitalhub.entities.workflow._base.entity import Workflow
80
89
 
81
90
 
82
- class Project(ProjectEntity):
91
+ class Project(Entity):
83
92
  """
84
93
  A class representing a project.
85
94
  """
@@ -96,9 +105,308 @@ class Project(ProjectEntity):
96
105
  user: str | None = None,
97
106
  local: bool = False,
98
107
  ) -> None:
99
- super().__init__(name, kind, metadata, spec, status, user, local)
100
- self.spec: ProjectSpec
101
- self.status: ProjectStatus
108
+ super().__init__(kind, metadata, spec, status, user)
109
+ self.id = name
110
+ self.name = name
111
+ self.key = base_processor.build_project_key(self.name, local=local)
112
+
113
+ self._obj_attr.extend(["id", "name"])
114
+
115
+ # Set client
116
+ self._client = get_client(local)
117
+
118
+ # Set context
119
+ build_context(self)
120
+
121
+ ##############################
122
+ # Save / Refresh / Export
123
+ ##############################
124
+
125
+ def save(self, update: bool = False) -> Project:
126
+ """
127
+ Save entity into backend.
128
+
129
+ Parameters
130
+ ----------
131
+ update : bool
132
+ If True, the object will be updated.
133
+
134
+ Returns
135
+ -------
136
+ Project
137
+ Entity saved.
138
+ """
139
+ if update:
140
+ if self._client.is_local():
141
+ self.metadata.updated = get_timestamp()
142
+ new_obj = base_processor.update_project_entity(
143
+ entity_type=self.ENTITY_TYPE,
144
+ entity_name=self.name,
145
+ entity_dict=self.to_dict(),
146
+ local=self._client.is_local(),
147
+ )
148
+ else:
149
+ new_obj = base_processor.create_project_entity(_entity=self)
150
+ self._update_attributes(new_obj)
151
+ return self
152
+
153
+ def refresh(self) -> Project:
154
+ """
155
+ Refresh object from backend.
156
+
157
+ Returns
158
+ -------
159
+ Project
160
+ Project object.
161
+ """
162
+ new_obj = base_processor.read_project_entity(
163
+ entity_type=self.ENTITY_TYPE,
164
+ entity_name=self.name,
165
+ local=self._client.is_local(),
166
+ )
167
+ self._update_attributes(new_obj)
168
+ return self
169
+
170
+ def search_entity(
171
+ self,
172
+ query: str | None = None,
173
+ entity_types: list[str] | None = None,
174
+ name: str | None = None,
175
+ kind: str | None = None,
176
+ created: str | None = None,
177
+ updated: str | None = None,
178
+ description: str | None = None,
179
+ labels: list[str] | None = None,
180
+ **kwargs,
181
+ ) -> list[ContextEntity]:
182
+ """
183
+ Search objects from backend.
184
+
185
+ Parameters
186
+ ----------
187
+ query : str
188
+ Search query.
189
+ entity_types : list[str]
190
+ Entity types.
191
+ name : str
192
+ Entity name.
193
+ kind : str
194
+ Entity kind.
195
+ created : str
196
+ Entity creation date.
197
+ updated : str
198
+ Entity update date.
199
+ description : str
200
+ Entity description.
201
+ labels : list[str]
202
+ Entity labels.
203
+ **kwargs : dict
204
+ Parameters to pass to the API call.
205
+
206
+ Returns
207
+ -------
208
+ list[ContextEntity]
209
+ List of object instances.
210
+ """
211
+ objs = context_processor.search_entity(
212
+ self.name,
213
+ query=query,
214
+ entity_types=entity_types,
215
+ name=name,
216
+ kind=kind,
217
+ created=created,
218
+ updated=updated,
219
+ description=description,
220
+ labels=labels,
221
+ **kwargs,
222
+ )
223
+ self.refresh()
224
+ return objs
225
+
226
+ def export(self) -> str:
227
+ """
228
+ Export object as a YAML file in the context folder.
229
+ If the objects are not embedded, the objects are exported as a YAML file.
230
+
231
+ Returns
232
+ -------
233
+ str
234
+ Exported filepath.
235
+ """
236
+ obj = self._refresh_to_dict()
237
+ pth = Path(self.spec.context) / f"{self.ENTITY_TYPE}s-{self.name}.yaml"
238
+ obj = self._export_not_embedded(obj)
239
+ write_yaml(pth, obj)
240
+ return str(pth)
241
+
242
+ def _refresh_to_dict(self) -> dict:
243
+ """
244
+ Try to refresh object to collect entities related to project.
245
+
246
+ Returns
247
+ -------
248
+ dict
249
+ Entity object in dictionary format.
250
+ """
251
+ try:
252
+ return self.refresh().to_dict()
253
+ except BackendError:
254
+ return self.to_dict()
255
+
256
+ def _export_not_embedded(self, obj: dict) -> dict:
257
+ """
258
+ Export project objects if not embedded.
259
+
260
+ Parameters
261
+ ----------
262
+ obj : dict
263
+ Project object in dictionary format.
264
+
265
+ Returns
266
+ -------
267
+ dict
268
+ Updatated project object in dictionary format with referenced entities.
269
+ """
270
+ # Cycle over entity types
271
+ for entity_type in self._get_entity_types():
272
+ # Entity types are stored as a list of entities
273
+ for idx, entity in enumerate(obj.get("spec", {}).get(entity_type, [])):
274
+ # Export entity if not embedded is in metadata, else do nothing
275
+ if not self._is_embedded(entity):
276
+ # Get entity object from backend
277
+ ent = context_processor.read_context_entity(entity["key"])
278
+
279
+ # Export and store ref in object metadata inside project
280
+ pth = ent.export()
281
+ obj["spec"][entity_type][idx]["metadata"]["ref"] = pth
282
+
283
+ # Return updated object
284
+ return obj
285
+
286
+ def _import_entities(self, obj: dict) -> None:
287
+ """
288
+ Import project entities.
289
+
290
+ Parameters
291
+ ----------
292
+ obj : dict
293
+ Project object in dictionary format.
294
+
295
+ Returns
296
+ -------
297
+ None
298
+ """
299
+ entity_types = self._get_entity_types()
300
+
301
+ # Cycle over entity types
302
+ for entity_type in entity_types:
303
+ # Entity types are stored as a list of entities
304
+ for entity in obj.get("spec", {}).get(entity_type, []):
305
+ embedded = self._is_embedded(entity)
306
+ ref = entity["metadata"].get("ref")
307
+
308
+ # Import entity if not embedded and there is a ref
309
+ if not embedded and ref is not None:
310
+ # Import entity from local ref
311
+ if has_local_scheme(ref):
312
+ try:
313
+ # Artifacts, Dataitems and Models
314
+ if entity_type in entity_types[:3]:
315
+ context_processor.import_context_entity(ref)
316
+
317
+ # Functions and Workflows
318
+ elif entity_type in entity_types[3:]:
319
+ context_processor.import_executable_entity(ref)
320
+
321
+ except FileNotFoundError:
322
+ msg = f"File not found: {ref}."
323
+ raise EntityError(msg)
324
+
325
+ # If entity is embedded, create it and try to save
326
+ elif embedded:
327
+ # It's possible that embedded field in metadata is not shown
328
+ if entity["metadata"].get("embedded") is None:
329
+ entity["metadata"]["embedded"] = True
330
+
331
+ try:
332
+ build_entity_from_dict(entity).save()
333
+ except EntityAlreadyExistsError:
334
+ pass
335
+
336
+ def _load_entities(self, obj: dict) -> None:
337
+ """
338
+ Load project entities.
339
+
340
+ Parameters
341
+ ----------
342
+ obj : dict
343
+ Project object in dictionary format.
344
+
345
+ Returns
346
+ -------
347
+ None
348
+ """
349
+ entity_types = self._get_entity_types()
350
+
351
+ # Cycle over entity types
352
+ for entity_type in entity_types:
353
+ # Entity types are stored as a list of entities
354
+ for entity in obj.get("spec", {}).get(entity_type, []):
355
+ embedded = self._is_embedded(entity)
356
+ ref = entity["metadata"].get("ref")
357
+
358
+ # Load entity if not embedded and there is a ref
359
+ if not embedded and ref is not None:
360
+ # Load entity from local ref
361
+ if has_local_scheme(ref):
362
+ try:
363
+ # Artifacts, Dataitems and Models
364
+ if entity_type in entity_types[:3]:
365
+ context_processor.load_context_entity(ref)
366
+
367
+ # Functions and Workflows
368
+ elif entity_type in entity_types[3:]:
369
+ context_processor.load_executable_entity(ref)
370
+
371
+ except FileNotFoundError:
372
+ msg = f"File not found: {ref}."
373
+ raise EntityError(msg)
374
+
375
+ def _is_embedded(self, entity: dict) -> bool:
376
+ """
377
+ Check if entity is embedded.
378
+
379
+ Parameters
380
+ ----------
381
+ entity : dict
382
+ Entity in dictionary format.
383
+
384
+ Returns
385
+ -------
386
+ bool
387
+ True if entity is embedded.
388
+ """
389
+ metadata_embedded = entity["metadata"].get("embedded", False)
390
+ no_status = entity.get("status", None) is None
391
+ no_spec = entity.get("spec", None) is None
392
+ return metadata_embedded or not (no_status and no_spec)
393
+
394
+ def _get_entity_types(self) -> list[str]:
395
+ """
396
+ Get entity types.
397
+
398
+ Returns
399
+ -------
400
+ list
401
+ Entity types.
402
+ """
403
+ return [
404
+ f"{EntityTypes.ARTIFACT.value}s",
405
+ f"{EntityTypes.DATAITEM.value}s",
406
+ f"{EntityTypes.MODEL.value}s",
407
+ f"{EntityTypes.FUNCTION.value}s",
408
+ f"{EntityTypes.WORKFLOW.value}s",
409
+ ]
102
410
 
103
411
  ##############################
104
412
  # Artifacts
@@ -1859,7 +2167,7 @@ class Project(ProjectEntity):
1859
2167
  -------
1860
2168
  None
1861
2169
  """
1862
- return processor.share_project_entity(
2170
+ return base_processor.share_project_entity(
1863
2171
  entity_type=self.ENTITY_TYPE,
1864
2172
  entity_name=self.name,
1865
2173
  user=user,
@@ -1879,7 +2187,7 @@ class Project(ProjectEntity):
1879
2187
  -------
1880
2188
  None
1881
2189
  """
1882
- return processor.share_project_entity(
2190
+ return base_processor.share_project_entity(
1883
2191
  entity_type=self.ENTITY_TYPE,
1884
2192
  entity_name=self.name,
1885
2193
  user=user,
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Optional
4
+
3
5
  from digitalhub.entities._base.entity.spec import Spec, SpecValidator
4
6
 
5
7
 
@@ -31,20 +33,20 @@ class ProjectValidator(SpecValidator):
31
33
  ProjectValidator validator.
32
34
  """
33
35
 
34
- context: str = None
36
+ context: Optional[str] = None
35
37
  """The project's context."""
36
38
 
37
- functions: list = None
39
+ functions: Optional[list] = None
38
40
  """List of project's functions."""
39
41
 
40
- artifacts: list = None
42
+ artifacts: Optional[list] = None
41
43
  """List of project's artifacts."""
42
44
 
43
- workflows: list = None
45
+ workflows: Optional[list] = None
44
46
  """List of project's workflows."""
45
47
 
46
- dataitems: list = None
48
+ dataitems: Optional[list] = None
47
49
  """List of project's dataitems."""
48
50
 
49
- models: list = None
51
+ models: Optional[list] = None
50
52
  """List of project's models."""
@@ -3,7 +3,8 @@ from __future__ import annotations
3
3
  import typing
4
4
 
5
5
  from digitalhub.entities._commons.enums import EntityTypes
6
- from digitalhub.entities._operations.processor import processor
6
+ from digitalhub.entities._processors.base import base_processor
7
+ from digitalhub.entities._processors.context import context_processor
7
8
  from digitalhub.entities.project.utils import setup_project
8
9
  from digitalhub.utils.exceptions import BackendError
9
10
 
@@ -58,7 +59,7 @@ def new_project(
58
59
  """
59
60
  if context is None:
60
61
  context = "./"
61
- obj = processor.create_project_entity(
62
+ obj = base_processor.create_project_entity(
62
63
  name=name,
63
64
  kind="project",
64
65
  description=description,
@@ -103,7 +104,7 @@ def get_project(
103
104
  --------
104
105
  >>> obj = get_project("my-project")
105
106
  """
106
- obj = processor.read_project_entity(
107
+ obj = base_processor.read_project_entity(
107
108
  entity_type=ENTITY_TYPE,
108
109
  entity_name=name,
109
110
  local=local,
@@ -142,7 +143,7 @@ def import_project(
142
143
  --------
143
144
  >>> obj = import_project("my-project.yaml")
144
145
  """
145
- obj = processor.import_project_entity(file=file, local=local, config=config)
146
+ obj = base_processor.import_project_entity(file=file, local=local, config=config)
146
147
  return setup_project(obj, setup_kwargs)
147
148
 
148
149
 
@@ -175,7 +176,7 @@ def load_project(
175
176
  --------
176
177
  >>> obj = load_project("my-project.yaml")
177
178
  """
178
- obj = processor.load_project_entity(file=file, local=local, config=config)
179
+ obj = base_processor.load_project_entity(file=file, local=local, config=config)
179
180
  return setup_project(obj, setup_kwargs)
180
181
 
181
182
 
@@ -195,7 +196,7 @@ def list_projects(local: bool = False, **kwargs) -> list[Project]:
195
196
  list
196
197
  List of objects.
197
198
  """
198
- return processor.list_project_entities(local=local, **kwargs)
199
+ return base_processor.list_project_entities(local=local, **kwargs)
199
200
 
200
201
 
201
202
  def get_or_create_project(
@@ -268,11 +269,11 @@ def update_project(entity: Project, **kwargs) -> Project:
268
269
  --------
269
270
  >>> obj = update_project(obj)
270
271
  """
271
- return processor.update_project_entity(
272
+ return base_processor.update_project_entity(
272
273
  entity_type=entity.ENTITY_TYPE,
273
274
  entity_name=entity.name,
274
275
  entity_dict=entity.to_dict(),
275
- local=entity.local,
276
+ local=entity._client.is_local(),
276
277
  **kwargs,
277
278
  )
278
279
 
@@ -283,7 +284,7 @@ def delete_project(
283
284
  clean_context: bool = True,
284
285
  local: bool = False,
285
286
  **kwargs,
286
- ) -> list[dict]:
287
+ ) -> dict:
287
288
  """
288
289
  Delete a project.
289
290
 
@@ -309,7 +310,7 @@ def delete_project(
309
310
  --------
310
311
  >>> delete_project("my-project")
311
312
  """
312
- return processor.delete_project_entity(
313
+ return base_processor.delete_project_entity(
313
314
  entity_type=ENTITY_TYPE,
314
315
  entity_name=name,
315
316
  local=local,
@@ -362,7 +363,7 @@ def search_entity(
362
363
  list[ContextEntity]
363
364
  List of object instances.
364
365
  """
365
- return processor.search_entity(
366
+ return context_processor.search_entity(
366
367
  project_name,
367
368
  query=query,
368
369
  entity_types=entity_types,