digitalhub 0.13.3__py3-none-any.whl → 0.14.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 (116) hide show
  1. digitalhub/__init__.py +3 -8
  2. digitalhub/context/api.py +1 -5
  3. digitalhub/context/builder.py +1 -5
  4. digitalhub/context/context.py +15 -9
  5. digitalhub/entities/_base/_base/entity.py +0 -15
  6. digitalhub/entities/_base/context/entity.py +1 -1
  7. digitalhub/entities/_base/entity/builder.py +5 -5
  8. digitalhub/entities/_base/entity/entity.py +0 -8
  9. digitalhub/entities/_base/executable/entity.py +169 -79
  10. digitalhub/entities/_base/material/entity.py +6 -22
  11. digitalhub/entities/_base/material/utils.py +1 -4
  12. digitalhub/entities/_base/runtime_entity/builder.py +53 -18
  13. digitalhub/entities/_base/unversioned/entity.py +1 -1
  14. digitalhub/entities/_base/versioned/entity.py +1 -1
  15. digitalhub/entities/_commons/enums.py +1 -31
  16. digitalhub/entities/_commons/utils.py +83 -21
  17. digitalhub/entities/_constructors/_resources.py +151 -0
  18. digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
  19. digitalhub/entities/_processors/base/__init__.py +3 -0
  20. digitalhub/entities/_processors/{base.py → base/crud.py} +14 -226
  21. digitalhub/entities/_processors/base/import_export.py +123 -0
  22. digitalhub/entities/_processors/base/processor.py +302 -0
  23. digitalhub/entities/_processors/base/special_ops.py +108 -0
  24. digitalhub/entities/_processors/context/__init__.py +3 -0
  25. digitalhub/entities/_processors/context/crud.py +652 -0
  26. digitalhub/entities/_processors/context/import_export.py +242 -0
  27. digitalhub/entities/_processors/context/material.py +123 -0
  28. digitalhub/entities/_processors/context/processor.py +400 -0
  29. digitalhub/entities/_processors/context/special_ops.py +476 -0
  30. digitalhub/entities/_processors/processors.py +12 -0
  31. digitalhub/entities/_processors/utils.py +12 -11
  32. digitalhub/entities/artifact/crud.py +58 -22
  33. digitalhub/entities/artifact/utils.py +3 -3
  34. digitalhub/entities/dataitem/crud.py +63 -20
  35. digitalhub/entities/dataitem/table/entity.py +24 -22
  36. digitalhub/entities/dataitem/utils.py +15 -15
  37. digitalhub/entities/function/_base/entity.py +3 -3
  38. digitalhub/entities/function/crud.py +55 -24
  39. digitalhub/entities/model/_base/entity.py +62 -20
  40. digitalhub/entities/model/crud.py +58 -22
  41. digitalhub/entities/model/utils.py +3 -3
  42. digitalhub/entities/project/_base/entity.py +321 -152
  43. digitalhub/entities/project/crud.py +15 -23
  44. digitalhub/entities/run/_base/builder.py +0 -4
  45. digitalhub/entities/run/_base/entity.py +70 -63
  46. digitalhub/entities/run/crud.py +79 -26
  47. digitalhub/entities/secret/_base/entity.py +1 -5
  48. digitalhub/entities/secret/crud.py +29 -26
  49. digitalhub/entities/task/_base/builder.py +0 -4
  50. digitalhub/entities/task/_base/entity.py +5 -5
  51. digitalhub/entities/task/_base/models.py +13 -16
  52. digitalhub/entities/task/crud.py +61 -29
  53. digitalhub/entities/trigger/_base/entity.py +1 -5
  54. digitalhub/entities/trigger/crud.py +64 -24
  55. digitalhub/entities/workflow/_base/entity.py +3 -3
  56. digitalhub/entities/workflow/crud.py +55 -21
  57. digitalhub/factory/entity.py +283 -0
  58. digitalhub/factory/enums.py +18 -0
  59. digitalhub/factory/registry.py +197 -0
  60. digitalhub/factory/runtime.py +44 -0
  61. digitalhub/factory/utils.py +3 -54
  62. digitalhub/runtimes/_base.py +2 -2
  63. digitalhub/stores/client/_base/enums.py +39 -0
  64. digitalhub/stores/client/_base/key_builder.py +2 -2
  65. digitalhub/stores/client/_base/params_builder.py +48 -0
  66. digitalhub/stores/client/api.py +6 -10
  67. digitalhub/stores/client/builder.py +4 -4
  68. digitalhub/stores/client/dhcore/api_builder.py +2 -1
  69. digitalhub/stores/client/dhcore/client.py +85 -429
  70. digitalhub/stores/client/dhcore/configurator.py +109 -328
  71. digitalhub/stores/client/dhcore/enums.py +0 -16
  72. digitalhub/stores/client/dhcore/error_parser.py +0 -4
  73. digitalhub/stores/client/dhcore/header_manager.py +61 -0
  74. digitalhub/stores/client/dhcore/http_handler.py +133 -0
  75. digitalhub/stores/client/dhcore/params_builder.py +147 -134
  76. digitalhub/stores/client/dhcore/response_processor.py +102 -0
  77. digitalhub/stores/client/dhcore/utils.py +6 -72
  78. digitalhub/stores/client/local/api_builder.py +1 -1
  79. digitalhub/stores/client/local/client.py +79 -47
  80. digitalhub/stores/client/local/params_builder.py +18 -41
  81. digitalhub/stores/credentials/api.py +0 -4
  82. digitalhub/stores/credentials/configurator.py +2 -28
  83. digitalhub/stores/credentials/enums.py +3 -0
  84. digitalhub/stores/credentials/handler.py +0 -12
  85. digitalhub/stores/credentials/ini_module.py +0 -22
  86. digitalhub/stores/credentials/store.py +0 -4
  87. digitalhub/stores/data/_base/store.py +0 -16
  88. digitalhub/stores/data/builder.py +1 -5
  89. digitalhub/stores/data/local/store.py +0 -103
  90. digitalhub/stores/data/remote/store.py +0 -4
  91. digitalhub/stores/data/s3/configurator.py +60 -14
  92. digitalhub/stores/data/s3/store.py +49 -16
  93. digitalhub/stores/data/sql/configurator.py +0 -8
  94. digitalhub/stores/data/sql/store.py +21 -10
  95. digitalhub/stores/readers/data/factory.py +0 -8
  96. digitalhub/stores/readers/data/pandas/reader.py +0 -16
  97. digitalhub/utils/file_utils.py +0 -17
  98. digitalhub/utils/generic_utils.py +0 -12
  99. digitalhub/utils/git_utils.py +0 -8
  100. digitalhub/utils/io_utils.py +0 -12
  101. digitalhub/utils/store_utils.py +44 -0
  102. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/METADATA +3 -2
  103. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/RECORD +111 -95
  104. digitalhub/entities/_processors/context.py +0 -1450
  105. digitalhub/entities/task/_base/utils.py +0 -22
  106. digitalhub/factory/factory.py +0 -381
  107. digitalhub/stores/client/dhcore/models.py +0 -40
  108. digitalhub/stores/data/s3/utils.py +0 -78
  109. /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
  110. /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
  111. /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
  112. /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
  113. /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
  114. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/WHEEL +0 -0
  115. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/licenses/AUTHORS +0 -0
  116. {digitalhub-0.13.3.dist-info → digitalhub-0.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import typing
8
8
 
9
9
  from digitalhub.entities._commons.enums import EntityTypes
10
- from digitalhub.entities._processors.context import context_processor
10
+ from digitalhub.entities._processors.processors import context_processor
11
11
 
12
12
  if typing.TYPE_CHECKING:
13
13
  from digitalhub.entities.workflow._base.entity import Workflow
@@ -75,7 +75,6 @@ def get_workflow(
75
75
  identifier: str,
76
76
  project: str | None = None,
77
77
  entity_id: str | None = None,
78
- **kwargs,
79
78
  ) -> Workflow:
80
79
  """
81
80
  Get object from backend.
@@ -88,8 +87,6 @@ def get_workflow(
88
87
  Project name.
89
88
  entity_id : str
90
89
  Entity ID.
91
- **kwargs : dict
92
- Parameters to pass to the API call.
93
90
 
94
91
  Returns
95
92
  -------
@@ -107,18 +104,16 @@ def get_workflow(
107
104
  >>> entity_id="my-workflow-id")
108
105
  """
109
106
  return context_processor.read_context_entity(
110
- identifier,
107
+ identifier=identifier,
111
108
  entity_type=ENTITY_TYPE,
112
109
  project=project,
113
110
  entity_id=entity_id,
114
- **kwargs,
115
111
  )
116
112
 
117
113
 
118
114
  def get_workflow_versions(
119
115
  identifier: str,
120
116
  project: str | None = None,
121
- **kwargs,
122
117
  ) -> list[Workflow]:
123
118
  """
124
119
  Get object versions from backend.
@@ -129,8 +124,6 @@ def get_workflow_versions(
129
124
  Entity key (store://...) or entity name.
130
125
  project : str
131
126
  Project name.
132
- **kwargs : dict
133
- Parameters to pass to the API call.
134
127
 
135
128
  Returns
136
129
  -------
@@ -147,14 +140,23 @@ def get_workflow_versions(
147
140
  >>> project="my-project")
148
141
  """
149
142
  return context_processor.read_context_entity_versions(
150
- identifier,
143
+ identifier=identifier,
151
144
  entity_type=ENTITY_TYPE,
152
145
  project=project,
153
- **kwargs,
154
146
  )
155
147
 
156
148
 
157
- def list_workflows(project: str, **kwargs) -> list[Workflow]:
149
+ def list_workflows(
150
+ project: str,
151
+ q: str | None = None,
152
+ name: str | None = None,
153
+ kind: str | None = None,
154
+ user: str | None = None,
155
+ state: str | None = None,
156
+ created: str | None = None,
157
+ updated: str | None = None,
158
+ version: str | None = None,
159
+ ) -> list[Workflow]:
158
160
  """
159
161
  List all latest version objects from backend.
160
162
 
@@ -162,8 +164,22 @@ def list_workflows(project: str, **kwargs) -> list[Workflow]:
162
164
  ----------
163
165
  project : str
164
166
  Project name.
165
- **kwargs : dict
166
- Parameters to pass to the API call.
167
+ q : str
168
+ Query string to filter objects.
169
+ name : str
170
+ Object name.
171
+ kind : str
172
+ Kind of the object.
173
+ user : str
174
+ User that created the object.
175
+ state : str
176
+ Object state.
177
+ created : str
178
+ Creation date filter.
179
+ updated : str
180
+ Update date filter.
181
+ version : str
182
+ Object version, default is latest.
167
183
 
168
184
  Returns
169
185
  -------
@@ -177,18 +193,36 @@ def list_workflows(project: str, **kwargs) -> list[Workflow]:
177
193
  return context_processor.list_context_entities(
178
194
  project=project,
179
195
  entity_type=ENTITY_TYPE,
180
- **kwargs,
196
+ q=q,
197
+ name=name,
198
+ kind=kind,
199
+ user=user,
200
+ state=state,
201
+ created=created,
202
+ updated=updated,
203
+ version=version,
181
204
  )
182
205
 
183
206
 
184
- def import_workflow(file: str) -> Workflow:
207
+ def import_workflow(
208
+ file: str | None = None,
209
+ key: str | None = None,
210
+ reset_id: bool = False,
211
+ context: str | None = None,
212
+ ) -> Workflow:
185
213
  """
186
- Import object from a YAML file and create a new object into the backend.
214
+ Import an object from a YAML file or from a storage key.
187
215
 
188
216
  Parameters
189
217
  ----------
190
218
  file : str
191
- Path to YAML file.
219
+ Path to the YAML file.
220
+ key : str
221
+ Entity key (store://...).
222
+ reset_id : bool
223
+ Flag to determine if the ID of executable entities should be reset.
224
+ context : str
225
+ Project name to use for context resolution.
192
226
 
193
227
  Returns
194
228
  -------
@@ -199,7 +233,7 @@ def import_workflow(file: str) -> Workflow:
199
233
  --------
200
234
  >>> obj = import_workflow("my-workflow.yaml")
201
235
  """
202
- return context_processor.import_executable_entity(file)
236
+ return context_processor.import_executable_entity(file, key, reset_id, context)
203
237
 
204
238
 
205
239
  def load_workflow(file: str) -> Workflow:
@@ -269,7 +303,8 @@ def delete_workflow(
269
303
  entity_id : str
270
304
  Entity ID.
271
305
  delete_all_versions : bool
272
- Delete all versions of the named entity. If True, use entity name instead of entity key as identifier.
306
+ Delete all versions of the named entity.
307
+ If True, use entity name instead of entity key as identifier.
273
308
  cascade : bool
274
309
  Cascade delete.
275
310
  **kwargs : dict
@@ -297,5 +332,4 @@ def delete_workflow(
297
332
  entity_id=entity_id,
298
333
  delete_all_versions=delete_all_versions,
299
334
  cascade=cascade,
300
- **kwargs,
301
335
  )
@@ -0,0 +1,283 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ import typing
8
+
9
+ from digitalhub.factory.registry import registry
10
+ from digitalhub.utils.exceptions import BuilderError
11
+
12
+ if typing.TYPE_CHECKING:
13
+ from digitalhub.entities._base.entity.entity import Entity
14
+ from digitalhub.entities._base.entity.metadata import Metadata
15
+ from digitalhub.entities._base.entity.spec import Spec, SpecValidator
16
+ from digitalhub.entities._base.entity.status import Status
17
+
18
+
19
+ class EntityFactory:
20
+ """
21
+ Factory for creating and managing entity builders.
22
+
23
+ This class handles the creation of entities and their components
24
+ through their respective builders, using a centralized registry.
25
+ """
26
+
27
+ def _call_builder_method(self, kind: str, method_name: str, *args, **kwargs):
28
+ """
29
+ Helper method to get a builder and call a method on it.
30
+
31
+ Parameters
32
+ ----------
33
+ kind : str
34
+ The kind of builder to retrieve.
35
+ method_name : str
36
+ The name of the method to call on the builder.
37
+ *args
38
+ Positional arguments to pass to the method.
39
+ **kwargs
40
+ Keyword arguments to pass to the method.
41
+
42
+ Returns
43
+ -------
44
+ Any
45
+ The result of calling the method on the builder.
46
+ """
47
+ builder = registry.get_entity_builder(kind)
48
+ return getattr(builder, method_name)(*args, **kwargs)
49
+
50
+ def build_entity_from_params(self, **kwargs) -> Entity:
51
+ """
52
+ Build an entity from parameters.
53
+
54
+ Parameters
55
+ ----------
56
+ **kwargs
57
+ Entity parameters.
58
+
59
+ Returns
60
+ -------
61
+ Entity
62
+ Entity object.
63
+ """
64
+ kind = self._get_kind(**kwargs)
65
+ builder = registry.get_entity_builder(kind)
66
+ return builder.build(**kwargs)
67
+
68
+ def build_entity_from_dict(self, obj: dict) -> Entity:
69
+ """
70
+ Build an entity from a dictionary.
71
+
72
+ Parameters
73
+ ----------
74
+ obj : dict
75
+ Dictionary with entity data.
76
+
77
+ Returns
78
+ -------
79
+ Entity
80
+ Entity object.
81
+ """
82
+ kind = self._get_kind(**obj)
83
+ builder = registry.get_entity_builder(kind)
84
+ return builder.from_dict(obj)
85
+
86
+ def build_spec(self, kind_to_build_from: str, **kwargs) -> Spec:
87
+ """
88
+ Build an entity spec.
89
+
90
+ Parameters
91
+ ----------
92
+ kind_to_build_from : str
93
+ Entity type.
94
+ **kwargs
95
+ Additional spec parameters.
96
+
97
+ Returns
98
+ -------
99
+ Spec
100
+ Spec object.
101
+ """
102
+ return self._call_builder_method(kind_to_build_from, "build_spec", **kwargs)
103
+
104
+ def build_metadata(self, kind_to_build_from: str, **kwargs) -> Metadata:
105
+ """
106
+ Build an entity metadata.
107
+
108
+ Parameters
109
+ ----------
110
+ kind_to_build_from : str
111
+ Entity type.
112
+ **kwargs
113
+ Additional metadata parameters.
114
+
115
+ Returns
116
+ -------
117
+ Metadata
118
+ Metadata object.
119
+ """
120
+ return self._call_builder_method(kind_to_build_from, "build_metadata", **kwargs)
121
+
122
+ def build_status(self, kind_to_build_from: str, **kwargs) -> Status:
123
+ """
124
+ Build an entity status.
125
+
126
+ Parameters
127
+ ----------
128
+ kind_to_build_from : str
129
+ Entity type.
130
+ **kwargs
131
+ Additional status parameters.
132
+
133
+ Returns
134
+ -------
135
+ Status
136
+ Status object.
137
+ """
138
+ return self._call_builder_method(kind_to_build_from, "build_status", **kwargs)
139
+
140
+ def get_entity_type_from_kind(self, kind: str) -> str:
141
+ """
142
+ Get entity type from builder.
143
+
144
+ Parameters
145
+ ----------
146
+ kind : str
147
+ Entity type.
148
+
149
+ Returns
150
+ -------
151
+ str
152
+ Entity type.
153
+ """
154
+ return self._call_builder_method(kind, "get_entity_type")
155
+
156
+ def get_executable_kind(self, kind: str) -> str:
157
+ """
158
+ Get executable kind.
159
+
160
+ Parameters
161
+ ----------
162
+ kind : str
163
+ Kind.
164
+
165
+ Returns
166
+ -------
167
+ str
168
+ Executable kind.
169
+ """
170
+ return self._call_builder_method(kind, "get_executable_kind")
171
+
172
+ def get_action_from_task_kind(self, kind: str, task_kind: str) -> str:
173
+ """
174
+ Get action from task.
175
+
176
+ Parameters
177
+ ----------
178
+ kind : str
179
+ Kind.
180
+ task_kind : str
181
+ Task kind.
182
+
183
+ Returns
184
+ -------
185
+ str
186
+ Action.
187
+ """
188
+ return self._call_builder_method(kind, "get_action_from_task_kind", task_kind)
189
+
190
+ def get_task_kind_from_action(self, kind: str, action: str) -> list[str]:
191
+ """
192
+ Get task kinds from action.
193
+
194
+ Parameters
195
+ ----------
196
+ kind : str
197
+ Kind.
198
+ action : str
199
+ Action.
200
+
201
+ Returns
202
+ -------
203
+ list of str
204
+ Task kinds.
205
+ """
206
+ return self._call_builder_method(kind, "get_task_kind_from_action", action)
207
+
208
+ def get_run_kind_from_action(self, kind: str, action: str) -> str:
209
+ """
210
+ Get run kind.
211
+
212
+ Parameters
213
+ ----------
214
+ kind : str
215
+ Kind.
216
+
217
+ Returns
218
+ -------
219
+ str
220
+ Run kind.
221
+ """
222
+ return self._call_builder_method(kind, "get_run_kind_from_action", action)
223
+
224
+ def get_all_kinds(self, kind: str) -> list[str]:
225
+ """
226
+ Get all kinds.
227
+
228
+ Parameters
229
+ ----------
230
+ kind : str
231
+ Kind.
232
+
233
+ Returns
234
+ -------
235
+ list of str
236
+ All kinds.
237
+ """
238
+ return self._call_builder_method(kind, "get_all_kinds")
239
+
240
+ def get_spec_validator(self, kind: str) -> SpecValidator:
241
+ """
242
+ Get spec validators.
243
+
244
+ Parameters
245
+ ----------
246
+ kind : str
247
+ Kind.
248
+
249
+ Returns
250
+ -------
251
+ SpecValidator
252
+ Spec validator.
253
+ """
254
+ return self._call_builder_method(kind, "get_spec_validator")
255
+
256
+ @staticmethod
257
+ def _get_kind(**kwargs) -> str:
258
+ """
259
+ Extract the 'kind' from parameters.
260
+
261
+ Parameters
262
+ ----------
263
+ **kwargs
264
+ Entity parameters.
265
+
266
+ Returns
267
+ -------
268
+ str
269
+ The kind of the entity.
270
+
271
+ Raises
272
+ ------
273
+ BuilderError
274
+ If 'kind' is not found in parameters.
275
+ """
276
+ try:
277
+ return kwargs["kind"]
278
+ except KeyError:
279
+ raise BuilderError("Missing 'kind' parameter.")
280
+
281
+
282
+ # Global instance
283
+ entity_factory = EntityFactory()
@@ -0,0 +1,18 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+
9
+
10
+ class FactoryEnum(Enum):
11
+ """
12
+ Enumeration for factory.
13
+ """
14
+
15
+ RGX_RUNTIMES = r"digitalhub_runtime_.*"
16
+ REG_ENTITIES = "digitalhub.entities.builders"
17
+ REG_ENTITIES_VAR = "entity_builders"
18
+ REG_RUNTIME_VAR = "runtime_builders"
@@ -0,0 +1,197 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ import typing
8
+
9
+ from digitalhub.factory.enums import FactoryEnum
10
+ from digitalhub.factory.utils import import_module, list_runtimes
11
+ from digitalhub.utils.exceptions import BuilderError
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from digitalhub.entities._base.entity.builder import EntityBuilder
15
+ from digitalhub.entities._base.runtime_entity.builder import RuntimeEntityBuilder
16
+ from digitalhub.runtimes.builder import RuntimeBuilder
17
+
18
+
19
+ class BuilderRegistry:
20
+ """
21
+ Singleton registry for managing imported modules and builders.
22
+
23
+ This class centralizes the registration and retrieval of entity and runtime builders,
24
+ ensuring lazy loading and a single source of truth for available builders.
25
+ """
26
+
27
+ def __init__(self) -> None:
28
+ self._instance: BuilderRegistry | None = None
29
+ self._initialized = False
30
+ self._entity_builders: dict[str, EntityBuilder | RuntimeEntityBuilder] = {}
31
+ self._runtime_builders: dict[str, RuntimeBuilder] = {}
32
+ self._entities_registered = False
33
+ self._runtimes_registered = False
34
+
35
+ def add_entity_builder(
36
+ self,
37
+ name: str,
38
+ builder: type[EntityBuilder | RuntimeEntityBuilder],
39
+ ) -> None:
40
+ """
41
+ Register an entity builder.
42
+
43
+ Parameters
44
+ ----------
45
+ name : str
46
+ The unique identifier for the builder.
47
+ builder : type[EntityBuilder] | type[RuntimeEntityBuilder]
48
+ The builder class to register. It will be instantiated immediately.
49
+
50
+ Raises
51
+ ------
52
+ BuilderError
53
+ If a builder with the same name already exists.
54
+ """
55
+ if name in self._entity_builders:
56
+ raise BuilderError(f"Builder {name} already exists.")
57
+ self._entity_builders[name] = builder()
58
+
59
+ def add_runtime_builder(self, name: str, builder: type[RuntimeBuilder]) -> None:
60
+ """
61
+ Register a runtime builder.
62
+
63
+ Parameters
64
+ ----------
65
+ name : str
66
+ The unique identifier for the builder.
67
+ builder : type[RuntimeBuilder]
68
+ The builder class to register. It will be instantiated immediately.
69
+
70
+ Raises
71
+ ------
72
+ BuilderError
73
+ If a builder with the same name already exists.
74
+ """
75
+ if name in self._runtime_builders:
76
+ raise BuilderError(f"Builder {name} already exists.")
77
+ self._runtime_builders[name] = builder()
78
+
79
+ def get_entity_builder(self, kind: str) -> EntityBuilder | RuntimeEntityBuilder:
80
+ """
81
+ Retrieve the entity builder for the given kind, ensuring lazy registration.
82
+
83
+ Parameters
84
+ ----------
85
+ kind : str
86
+ The kind of entity builder to retrieve.
87
+
88
+ Returns
89
+ -------
90
+ EntityBuilder | RuntimeEntityBuilder
91
+ The builder instance.
92
+
93
+ Raises
94
+ ------
95
+ BuilderError
96
+ If no builder exists for the specified kind.
97
+ """
98
+ if not self._entities_registered:
99
+ self._ensure_entities_registered()
100
+ if kind not in self._entity_builders:
101
+ if not self._runtimes_registered:
102
+ self._ensure_runtimes_registered()
103
+ if kind not in self._entity_builders:
104
+ raise BuilderError(f"Entity builder for kind '{kind}' not found.")
105
+ return self._entity_builders[kind]
106
+
107
+ def get_runtime_builder(self, kind: str) -> RuntimeBuilder:
108
+ """
109
+ Retrieve the runtime builder for the given kind, ensuring lazy registration.
110
+
111
+ Parameters
112
+ ----------
113
+ kind : str
114
+ The kind of runtime builder to retrieve.
115
+
116
+ Returns
117
+ -------
118
+ RuntimeBuilder
119
+ The builder instance.
120
+
121
+ Raises
122
+ ------
123
+ BuilderError
124
+ If no builder exists for the specified kind.
125
+ """
126
+ if kind not in self._runtime_builders:
127
+ if not self._runtimes_registered:
128
+ self._ensure_runtimes_registered()
129
+ if kind not in self._runtime_builders:
130
+ raise BuilderError(f"Runtime builder for kind '{kind}' not found.")
131
+ return self._runtime_builders[kind]
132
+
133
+ def _ensure_entities_registered(self) -> None:
134
+ """
135
+ Ensure core entities are registered on-demand.
136
+ """
137
+ if self._entities_registered:
138
+ return
139
+ try:
140
+ self._register_entities()
141
+ self._entities_registered = True
142
+ except Exception as e:
143
+ raise BuilderError(f"Failed to register core entities: {e}")
144
+
145
+ def _register_entities(self) -> None:
146
+ """
147
+ Register core entity builders into the registry.
148
+
149
+ Imports the core entities module and registers all entity
150
+ builders with the registry.
151
+ """
152
+ try:
153
+ module = import_module(FactoryEnum.REG_ENTITIES.value)
154
+
155
+ # Register core entities
156
+ for k, b in getattr(module, FactoryEnum.REG_ENTITIES_VAR.value, []):
157
+ self.add_entity_builder(k, b)
158
+
159
+ except Exception as e:
160
+ raise RuntimeError("Error registering core entities.") from e
161
+
162
+ def _ensure_runtimes_registered(self) -> None:
163
+ """
164
+ Ensure runtime entities are registered on-demand.
165
+ """
166
+ if self._runtimes_registered:
167
+ return
168
+ try:
169
+ self._register_runtimes_entities()
170
+ self._runtimes_registered = True
171
+ except Exception as e:
172
+ raise BuilderError(f"Failed to register runtime entities: {e}")
173
+
174
+ def _register_runtimes_entities(self) -> None:
175
+ """
176
+ Register all runtime builders and their entities into the registry.
177
+
178
+ Imports each runtime package and registers its entity and runtime
179
+ builders with the registry.
180
+ """
181
+ try:
182
+ for package in list_runtimes():
183
+ module = import_module(package)
184
+
185
+ # Register workflows, functions, tasks and runs entities builders
186
+ for k, b in getattr(module, FactoryEnum.REG_ENTITIES_VAR.value, []):
187
+ self.add_entity_builder(k, b)
188
+
189
+ # Register runtime builders
190
+ for k, b in getattr(module, FactoryEnum.REG_RUNTIME_VAR.value, []):
191
+ self.add_runtime_builder(k, b)
192
+ except Exception as e:
193
+ raise RuntimeError("Error registering runtime entities.") from e
194
+
195
+
196
+ # Global singleton instance
197
+ registry = BuilderRegistry()
@@ -0,0 +1,44 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ import typing
8
+
9
+ from digitalhub.factory.registry import registry
10
+
11
+ if typing.TYPE_CHECKING:
12
+ from digitalhub.runtimes._base import Runtime
13
+
14
+
15
+ class RuntimeFactory:
16
+ """
17
+ Factory for creating and managing runtime builders.
18
+
19
+ This class handles the creation of runtimes through their respective builders,
20
+ using a centralized registry.
21
+ """
22
+
23
+ def build_runtime(self, kind_to_build_from: str, project: str) -> Runtime:
24
+ """
25
+ Build a runtime.
26
+
27
+ Parameters
28
+ ----------
29
+ kind_to_build_from : str
30
+ Runtime type.
31
+ project : str
32
+ Project name.
33
+
34
+ Returns
35
+ -------
36
+ Runtime
37
+ Runtime object.
38
+ """
39
+ builder = registry.get_runtime_builder(kind_to_build_from)
40
+ return builder.build(project=project)
41
+
42
+
43
+ # Global instance
44
+ runtime_factory = RuntimeFactory()