digitalhub 0.13.0b3__py3-none-any.whl → 0.14.9__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.
Files changed (139) hide show
  1. digitalhub/__init__.py +3 -8
  2. digitalhub/context/api.py +43 -6
  3. digitalhub/context/builder.py +1 -5
  4. digitalhub/context/context.py +28 -13
  5. digitalhub/entities/_base/_base/entity.py +0 -15
  6. digitalhub/entities/_base/context/entity.py +1 -4
  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 +195 -87
  10. digitalhub/entities/_base/material/entity.py +11 -23
  11. digitalhub/entities/_base/material/utils.py +28 -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/metrics.py +64 -30
  17. digitalhub/entities/_commons/utils.py +119 -30
  18. digitalhub/entities/_constructors/_resources.py +151 -0
  19. digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
  20. digitalhub/entities/_processors/base/crud.py +381 -0
  21. digitalhub/entities/_processors/base/import_export.py +118 -0
  22. digitalhub/entities/_processors/base/processor.py +299 -0
  23. digitalhub/entities/_processors/base/special_ops.py +104 -0
  24. digitalhub/entities/_processors/context/crud.py +652 -0
  25. digitalhub/entities/_processors/context/import_export.py +242 -0
  26. digitalhub/entities/_processors/context/material.py +123 -0
  27. digitalhub/entities/_processors/context/processor.py +400 -0
  28. digitalhub/entities/_processors/context/special_ops.py +476 -0
  29. digitalhub/entities/_processors/processors.py +12 -0
  30. digitalhub/entities/_processors/utils.py +38 -102
  31. digitalhub/entities/artifact/crud.py +58 -22
  32. digitalhub/entities/artifact/utils.py +28 -13
  33. digitalhub/entities/builders.py +2 -0
  34. digitalhub/entities/dataitem/crud.py +63 -20
  35. digitalhub/entities/dataitem/table/entity.py +27 -22
  36. digitalhub/entities/dataitem/utils.py +82 -32
  37. digitalhub/entities/function/_base/entity.py +3 -6
  38. digitalhub/entities/function/crud.py +55 -24
  39. digitalhub/entities/model/_base/entity.py +62 -20
  40. digitalhub/entities/model/crud.py +59 -23
  41. digitalhub/entities/model/mlflow/utils.py +29 -20
  42. digitalhub/entities/model/utils.py +28 -13
  43. digitalhub/entities/project/_base/builder.py +0 -6
  44. digitalhub/entities/project/_base/entity.py +337 -164
  45. digitalhub/entities/project/_base/spec.py +4 -4
  46. digitalhub/entities/project/crud.py +28 -71
  47. digitalhub/entities/project/utils.py +7 -3
  48. digitalhub/entities/run/_base/builder.py +0 -4
  49. digitalhub/entities/run/_base/entity.py +70 -63
  50. digitalhub/entities/run/crud.py +79 -26
  51. digitalhub/entities/secret/_base/entity.py +1 -5
  52. digitalhub/entities/secret/crud.py +31 -28
  53. digitalhub/entities/task/_base/builder.py +0 -4
  54. digitalhub/entities/task/_base/entity.py +5 -5
  55. digitalhub/entities/task/_base/models.py +13 -16
  56. digitalhub/entities/task/crud.py +61 -29
  57. digitalhub/entities/trigger/_base/entity.py +1 -5
  58. digitalhub/entities/trigger/crud.py +89 -30
  59. digitalhub/entities/workflow/_base/entity.py +3 -8
  60. digitalhub/entities/workflow/crud.py +55 -24
  61. digitalhub/factory/entity.py +283 -0
  62. digitalhub/factory/enums.py +18 -0
  63. digitalhub/factory/registry.py +197 -0
  64. digitalhub/factory/runtime.py +44 -0
  65. digitalhub/factory/utils.py +3 -54
  66. digitalhub/runtimes/_base.py +2 -2
  67. digitalhub/stores/client/{dhcore/api_builder.py → api_builder.py} +3 -3
  68. digitalhub/stores/client/builder.py +19 -31
  69. digitalhub/stores/client/client.py +322 -0
  70. digitalhub/stores/client/configurator.py +408 -0
  71. digitalhub/stores/client/enums.py +50 -0
  72. digitalhub/stores/client/{dhcore/error_parser.py → error_parser.py} +0 -4
  73. digitalhub/stores/client/header_manager.py +61 -0
  74. digitalhub/stores/client/http_handler.py +152 -0
  75. digitalhub/stores/client/{_base/key_builder.py → key_builder.py} +14 -14
  76. digitalhub/stores/client/params_builder.py +330 -0
  77. digitalhub/stores/client/response_processor.py +102 -0
  78. digitalhub/stores/client/utils.py +35 -0
  79. digitalhub/stores/{credentials → configurator}/api.py +5 -9
  80. digitalhub/stores/configurator/configurator.py +123 -0
  81. digitalhub/stores/{credentials → configurator}/enums.py +27 -10
  82. digitalhub/stores/configurator/handler.py +213 -0
  83. digitalhub/stores/{credentials → configurator}/ini_module.py +31 -22
  84. digitalhub/stores/data/_base/store.py +0 -20
  85. digitalhub/stores/data/api.py +5 -7
  86. digitalhub/stores/data/builder.py +53 -27
  87. digitalhub/stores/data/local/store.py +0 -103
  88. digitalhub/stores/data/remote/store.py +0 -4
  89. digitalhub/stores/data/s3/configurator.py +39 -77
  90. digitalhub/stores/data/s3/store.py +57 -37
  91. digitalhub/stores/data/sql/configurator.py +66 -46
  92. digitalhub/stores/data/sql/store.py +171 -104
  93. digitalhub/stores/readers/data/factory.py +0 -8
  94. digitalhub/stores/readers/data/pandas/reader.py +9 -19
  95. digitalhub/utils/file_utils.py +0 -17
  96. digitalhub/utils/generic_utils.py +1 -14
  97. digitalhub/utils/git_utils.py +0 -8
  98. digitalhub/utils/io_utils.py +0 -12
  99. digitalhub/utils/store_utils.py +44 -0
  100. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/METADATA +5 -4
  101. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/RECORD +112 -113
  102. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/WHEEL +1 -1
  103. digitalhub/entities/_commons/types.py +0 -9
  104. digitalhub/entities/_processors/base.py +0 -531
  105. digitalhub/entities/_processors/context.py +0 -1299
  106. digitalhub/entities/task/_base/utils.py +0 -22
  107. digitalhub/factory/factory.py +0 -381
  108. digitalhub/stores/client/_base/api_builder.py +0 -34
  109. digitalhub/stores/client/_base/client.py +0 -243
  110. digitalhub/stores/client/_base/params_builder.py +0 -34
  111. digitalhub/stores/client/api.py +0 -36
  112. digitalhub/stores/client/dhcore/client.py +0 -613
  113. digitalhub/stores/client/dhcore/configurator.py +0 -675
  114. digitalhub/stores/client/dhcore/enums.py +0 -34
  115. digitalhub/stores/client/dhcore/key_builder.py +0 -62
  116. digitalhub/stores/client/dhcore/models.py +0 -40
  117. digitalhub/stores/client/dhcore/params_builder.py +0 -278
  118. digitalhub/stores/client/dhcore/utils.py +0 -94
  119. digitalhub/stores/client/local/api_builder.py +0 -116
  120. digitalhub/stores/client/local/client.py +0 -573
  121. digitalhub/stores/client/local/enums.py +0 -15
  122. digitalhub/stores/client/local/key_builder.py +0 -62
  123. digitalhub/stores/client/local/params_builder.py +0 -120
  124. digitalhub/stores/credentials/__init__.py +0 -3
  125. digitalhub/stores/credentials/configurator.py +0 -210
  126. digitalhub/stores/credentials/handler.py +0 -176
  127. digitalhub/stores/credentials/store.py +0 -81
  128. digitalhub/stores/data/enums.py +0 -15
  129. digitalhub/stores/data/s3/utils.py +0 -78
  130. /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
  131. /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
  132. /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
  133. /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
  134. /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
  135. /digitalhub/{stores/client/_base → entities/_processors/base}/__init__.py +0 -0
  136. /digitalhub/{stores/client/dhcore → entities/_processors/context}/__init__.py +0 -0
  137. /digitalhub/stores/{client/local → configurator}/__init__.py +0 -0
  138. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/AUTHORS +0 -0
  139. {digitalhub-0.13.0b3.dist-info → digitalhub-0.14.9.dist-info}/licenses/LICENSE +0 -0
@@ -4,6 +4,7 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ from digitalhub.entities._commons.utils import KindAction
7
8
  from digitalhub.utils.exceptions import EntityError
8
9
 
9
10
 
@@ -13,16 +14,40 @@ class RuntimeEntityBuilder:
13
14
  """
14
15
 
15
16
  EXECUTABLE_KIND: str = None
16
- TASKS_KINDS: dict = None
17
- RUN_KIND: str = None
17
+ TASKS_KINDS: list[KindAction] = None
18
+ RUN_KINDS: list[KindAction] = None
18
19
 
19
20
  def __init__(self) -> None:
20
- if self.EXECUTABLE_KIND is None:
21
- raise EntityError("EXECUTABLE_KIND must be set")
22
- if self.TASKS_KINDS is None:
23
- raise EntityError("TASKS_KINDS must be set")
24
- if self.RUN_KIND is None:
25
- raise EntityError("RUN_KIND must be set")
21
+ self._validate()
22
+
23
+ def _validate(self) -> None:
24
+ """
25
+ Validate the entity.
26
+ """
27
+ for attr_name in ["EXECUTABLE_KIND", "TASKS_KINDS", "RUN_KINDS"]:
28
+ value = getattr(self, attr_name)
29
+ if value is None:
30
+ raise EntityError(f"{attr_name} must be set")
31
+
32
+ for attr_name in ["TASKS_KINDS", "RUN_KINDS"]:
33
+ self._instance_validation(getattr(self, attr_name))
34
+
35
+ def _instance_validation(self, attribute: list[KindAction]) -> None:
36
+ """
37
+ Validate if the attribute is a list of KindAction.
38
+
39
+ Parameters
40
+ ----------
41
+ attribute : list[KindAction]
42
+ Attribute to validate.
43
+ """
44
+ if not isinstance(attribute, list):
45
+ raise EntityError(f"{attribute} must be a list")
46
+ for i in attribute:
47
+ if not isinstance(i, KindAction):
48
+ raise EntityError(f"{attribute} must be a list of KindAction")
49
+ if i.kind is None:
50
+ raise EntityError(f"{attribute} must be a list of KindAction with kind set")
26
51
 
27
52
  def get_action_from_task_kind(self, task_kind: str) -> str:
28
53
  """
@@ -39,8 +64,8 @@ class RuntimeEntityBuilder:
39
64
  Action.
40
65
  """
41
66
  for task in self.TASKS_KINDS:
42
- if task["kind"] == task_kind:
43
- return task["action"]
67
+ if task.kind == task_kind:
68
+ return task.action
44
69
  msg = f"Task kind {task_kind} not allowed."
45
70
  raise EntityError(msg)
46
71
 
@@ -59,21 +84,30 @@ class RuntimeEntityBuilder:
59
84
  Task kinds.
60
85
  """
61
86
  for task in self.TASKS_KINDS:
62
- if task["action"] == action:
63
- return task["kind"]
87
+ if task.action == action:
88
+ return task.kind
64
89
  msg = f"Action {action} not allowed."
65
90
  raise EntityError(msg)
66
91
 
67
- def get_run_kind(self) -> str:
92
+ def get_run_kind_from_action(self, action: str) -> str:
68
93
  """
69
- Get run kind.
94
+ Get run kind from action.
95
+
96
+ Parameters
97
+ ----------
98
+ action : str
99
+ Action.
70
100
 
71
101
  Returns
72
102
  -------
73
103
  str
74
104
  Run kind.
75
105
  """
76
- return self.RUN_KIND
106
+ for run in self.RUN_KINDS:
107
+ if run.action == action:
108
+ return run.kind
109
+ msg = f"Action {action} not allowed."
110
+ raise EntityError(msg)
77
111
 
78
112
  def get_executable_kind(self) -> str:
79
113
  """
@@ -95,8 +129,9 @@ class RuntimeEntityBuilder:
95
129
  list[str]
96
130
  All kinds.
97
131
  """
98
- task_kinds = [i["kind"] for i in self.TASKS_KINDS]
99
- return [self.EXECUTABLE_KIND, self.RUN_KIND, *task_kinds]
132
+ task_kinds = [i.kind for i in self.TASKS_KINDS]
133
+ run_kinds = [i.kind for i in self.RUN_KINDS]
134
+ return [self.EXECUTABLE_KIND, *run_kinds, *task_kinds]
100
135
 
101
136
  def get_all_actions(self) -> list[str]:
102
137
  """
@@ -107,4 +142,4 @@ class RuntimeEntityBuilder:
107
142
  list[str]
108
143
  All actions.
109
144
  """
110
- return [i["action"] for i in self.TASKS_KINDS]
145
+ return [i.action for i in self.TASKS_KINDS]
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import typing
8
8
 
9
9
  from digitalhub.entities._base.context.entity import ContextEntity
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._base.entity.metadata import Metadata
@@ -7,7 +7,7 @@ from __future__ import annotations
7
7
  import typing
8
8
 
9
9
  from digitalhub.entities._base.context.entity import ContextEntity
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._base.entity.metadata import Metadata
@@ -32,6 +32,7 @@ class Relationship(Enum):
32
32
  PRODUCEDBY = "produced_by"
33
33
  CONSUMES = "consumes"
34
34
  RUN_OF = "run_of"
35
+ STEP_OF = "step_of"
35
36
 
36
37
 
37
38
  class State(Enum):
@@ -63,37 +64,6 @@ class State(Enum):
63
64
  UPLOADING = "UPLOADING"
64
65
 
65
66
 
66
- class ApiCategories(Enum):
67
- """
68
- Entity categories.
69
- """
70
-
71
- BASE = "base"
72
- CONTEXT = "context"
73
-
74
-
75
- class BackendOperations(Enum):
76
- """
77
- Backend operations.
78
- """
79
-
80
- CREATE = "create"
81
- READ = "read"
82
- READ_ALL_VERSIONS = "read_all_versions"
83
- UPDATE = "update"
84
- DELETE = "delete"
85
- LIST = "list"
86
- LIST_FIRST = "list_first"
87
- STOP = "stop"
88
- RESUME = "resume"
89
- DATA = "data"
90
- FILES = "files"
91
- LOGS = "logs"
92
- SEARCH = "search"
93
- SHARE = "share"
94
- METRICS = "metrics"
95
-
96
-
97
67
  class EntityKinds(Enum):
98
68
  """
99
69
  Entity kinds.
@@ -13,7 +13,15 @@ MetricType = Union[float, int, list[Union[float, int]]]
13
13
 
14
14
  class Metric(BaseModel):
15
15
  """
16
- Metric.
16
+ Pydantic model for validating metric values.
17
+
18
+ This model ensures that metric values are of the correct type,
19
+ accepting single numeric values or lists of numeric values.
20
+
21
+ Attributes
22
+ ----------
23
+ value : MetricType
24
+ The metric value, which can be a float, int, or list of floats/ints.
17
25
  """
18
26
 
19
27
  value: MetricType
@@ -21,17 +29,25 @@ class Metric(BaseModel):
21
29
 
22
30
  def validate_metric_value(value: Any) -> MetricType:
23
31
  """
24
- Validate metric value.
32
+ Validate and convert a value to a proper metric type.
33
+
34
+ Uses Pydantic validation to ensure the input value conforms to
35
+ the MetricType specification (float, int, or list of floats/ints).
25
36
 
26
37
  Parameters
27
38
  ----------
28
39
  value : Any
29
- The value to validate.
40
+ The value to validate and convert.
30
41
 
31
42
  Returns
32
43
  -------
33
44
  MetricType
34
- The validated value.
45
+ The validated metric value.
46
+
47
+ Raises
48
+ ------
49
+ ValueError
50
+ If the value cannot be converted to a valid metric type.
35
51
  """
36
52
  try:
37
53
  return Metric(value=value).value
@@ -47,23 +63,30 @@ def set_metrics(
47
63
  single_value: bool,
48
64
  ) -> dict[str, MetricType]:
49
65
  """
50
- Set metric value.
66
+ Set or update a metric value in the metrics dictionary.
67
+
68
+ This function routes to appropriate handling based on the value type
69
+ and the single_value flag. It can handle single values, lists, and
70
+ appending to existing metrics.
51
71
 
52
72
  Parameters
53
73
  ----------
54
74
  metrics : dict[str, MetricType]
55
- The metrics dictionary.
75
+ The metrics dictionary to update.
56
76
  key : str
57
- The key of the entity.
77
+ The metric key to set or update.
58
78
  value : Any
59
- The value to set.
79
+ The value to set for the metric.
60
80
  overwrite : bool
61
- Whether to overwrite the metric.
81
+ Whether to overwrite existing metrics.
82
+ single_value : bool
83
+ Whether to treat the value as a single metric rather than
84
+ appending to a list.
62
85
 
63
86
  Returns
64
87
  -------
65
88
  dict[str, MetricType]
66
- The metrics dictionary.
89
+ The updated metrics dictionary.
67
90
  """
68
91
  if isinstance(value, list):
69
92
  return handle_metric_list(metrics, key, value, overwrite)
@@ -79,23 +102,26 @@ def handle_metric_single(
79
102
  overwrite: bool,
80
103
  ) -> dict:
81
104
  """
82
- Handle metric single value.
105
+ Handle setting a single metric value.
106
+
107
+ Sets or overwrites a metric with a single numeric value. If the key
108
+ already exists and overwrite is False, the existing value is preserved.
83
109
 
84
110
  Parameters
85
111
  ----------
86
112
  metrics : dict[str, MetricType]
87
- Metrics dictionary.
113
+ The metrics dictionary to update.
88
114
  key : str
89
- Key of the metric.
90
- value : float
91
- Value of the metric.
115
+ The metric key to set.
116
+ value : float | int
117
+ The single numeric value to set.
92
118
  overwrite : bool
93
- If True, overwrite existing metric.
119
+ Whether to overwrite an existing metric with the same key.
94
120
 
95
121
  Returns
96
122
  -------
97
123
  dict
98
- Metrics dictionary.
124
+ The updated metrics dictionary.
99
125
  """
100
126
  if key not in metrics or overwrite:
101
127
  metrics[key] = value
@@ -109,23 +135,27 @@ def handle_metric_list_append(
109
135
  overwrite: bool,
110
136
  ) -> dict:
111
137
  """
112
- Handle metric list append.
138
+ Handle appending a single value to a metric list.
139
+
140
+ If the metric doesn't exist or overwrite is True, creates a new list
141
+ with the single value. If the metric exists as a list, appends to it.
142
+ If the metric exists as a single value, converts it to a list and appends.
113
143
 
114
144
  Parameters
115
145
  ----------
116
146
  metrics : dict[str, MetricType]
117
- Metrics dictionary.
147
+ The metrics dictionary to update.
118
148
  key : str
119
- Key of the metric.
120
- value : float
121
- Value of the metric.
149
+ The metric key to append to.
150
+ value : float | int
151
+ The numeric value to append.
122
152
  overwrite : bool
123
- If True, overwrite existing metric.
153
+ Whether to overwrite an existing metric instead of appending.
124
154
 
125
155
  Returns
126
156
  -------
127
157
  dict
128
- Metrics dictionary.
158
+ The updated metrics dictionary.
129
159
  """
130
160
  if key not in metrics or overwrite:
131
161
  metrics[key] = [value]
@@ -143,23 +173,27 @@ def handle_metric_list(
143
173
  overwrite: bool,
144
174
  ) -> dict:
145
175
  """
146
- Handle metric list.
176
+ Handle setting or extending a metric with a list of values.
177
+
178
+ If the metric doesn't exist or overwrite is True, sets the metric to
179
+ the provided list. If the metric exists and overwrite is False, extends
180
+ the existing list with the new values.
147
181
 
148
182
  Parameters
149
183
  ----------
150
184
  metrics : dict[str, MetricType]
151
- Metrics dictionary.
185
+ The metrics dictionary to update.
152
186
  key : str
153
- Key of the metric.
187
+ The metric key to set or extend.
154
188
  value : list[int | float]
155
- Value of the metric.
189
+ The list of numeric values to set or extend with.
156
190
  overwrite : bool
157
- If True, overwrite existing metric.
191
+ Whether to overwrite an existing metric instead of extending it.
158
192
 
159
193
  Returns
160
194
  -------
161
195
  dict
162
- Metrics dictionary.
196
+ The updated metrics dictionary.
163
197
  """
164
198
  if key not in metrics or overwrite:
165
199
  metrics[key] = value
@@ -4,66 +4,130 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import re
8
+ from collections import namedtuple
9
+
7
10
  from digitalhub.entities._commons.enums import EntityTypes
8
11
 
12
+ KindAction = namedtuple("KindAction", ["kind", "action"])
13
+
14
+
15
+ KEY_PATTERN_WITH_ID = "store://([^/]+)/([^/]+)/([^/]+)/([^:]+):(.+)"
16
+ KEY_PATTERN_NO_ID = "store://([^/]+)/([^/]+)/([^/]+)/([^:]+)"
17
+
18
+
19
+ def is_valid_key(key: str) -> bool:
20
+ """
21
+ Check if an entity key is valid.
22
+
23
+ Parameters
24
+ ----------
25
+ key : str
26
+ The entity key to validate.
27
+
28
+ Returns
29
+ -------
30
+ bool
31
+ True if the key is valid, False otherwise.
32
+ """
33
+ return bool(re.fullmatch(KEY_PATTERN_WITH_ID, key) or re.fullmatch(KEY_PATTERN_NO_ID, key))
34
+
35
+
36
+ def sanitize_unversioned_key(key: str) -> str:
37
+ """
38
+ Sanitize an unversioned entity key (from name:id to id).
39
+
40
+ Parameters
41
+ ----------
42
+ key : str
43
+ The unversioned entity key.
44
+
45
+ Returns
46
+ -------
47
+ str
48
+ The sanitized entity key with version.
49
+ """
50
+ splt = key.split("/")[2:]
51
+ ent_id = splt[-1].split(":")[0]
52
+ return "store://" + "/".join(splt[:-1] + [ent_id])
53
+
9
54
 
10
55
  def parse_entity_key(key: str) -> tuple[str, str, str, str | None, str]:
11
56
  """
12
- Parse the entity key. Returns project, entity type, kind, name and uuid.
57
+ Parse an entity key into its constituent components.
58
+
59
+ Extracts project name, entity type, kind, name, and UUID from a
60
+ standardized entity key format. Handles special cases for tasks
61
+ and runs which don't have name components.
13
62
 
14
63
  Parameters
15
64
  ----------
16
65
  key : str
17
- The entity key.
66
+ The entity key in format "store://project/type/kind/name:uuid"
67
+ or "store://project/type/kind/uuid" for tasks and runs.
18
68
 
19
69
  Returns
20
70
  -------
21
71
  tuple[str, str, str, str | None, str]
22
- Project, entity type, kind, name and uuid.
72
+ A tuple containing (project, entity_type, kind, name, uuid).
73
+ The name component is None for tasks and runs.
74
+
75
+ Raises
76
+ ------
77
+ ValueError
78
+ If the key format is invalid or cannot be parsed.
23
79
  """
24
- try:
25
- # Remove "store://" from the key
26
- key = key.replace("store://", "")
80
+ if not is_valid_key(key):
81
+ raise ValueError("Invalid entity key format.")
82
+
83
+ # Remove "store://" from the key
84
+ key = key.replace("store://", "")
27
85
 
28
- # Split the key into parts
29
- parts = key.split("/")
86
+ # Split the key into parts
87
+ parts = key.split("/")
30
88
 
31
- # The project is the first part
32
- project = parts[0]
89
+ # The project is the first part
90
+ project = parts[0]
33
91
 
34
- # The entity type is the second part
35
- entity_type = parts[1]
92
+ # The entity type is the second part
93
+ entity_type = parts[1]
36
94
 
37
- # The kind is the third part
38
- kind = parts[2]
95
+ # The kind is the third part
96
+ kind = parts[2]
39
97
 
40
- # Tasks and runs have no name and uuid
41
- if entity_type in (EntityTypes.TASK.value, EntityTypes.RUN.value):
42
- name = None
43
- uuid = parts[3]
98
+ # Tasks and runs have no name and uuid
99
+ if entity_type in (EntityTypes.TASK.value, EntityTypes.RUN.value):
100
+ name = None
101
+ uuid = parts[3]
44
102
 
45
- # The name and uuid are separated by a colon in the last part
46
- else:
47
- name, uuid = parts[3].split(":")
103
+ # The name and uuid are separated by a colon in the last part
104
+ else:
105
+ name, uuid = parts[3].split(":")
48
106
 
49
- return project, entity_type, kind, name, uuid
50
- except Exception as e:
51
- raise ValueError("Invalid key format.") from e
107
+ return project, entity_type, kind, name, uuid
52
108
 
53
109
 
54
110
  def get_entity_type_from_key(key: str) -> str:
55
111
  """
56
- Get entity type from key.
112
+ Extract the entity type from an entity key.
113
+
114
+ Parses the entity key and returns only the entity type component,
115
+ which indicates the kind of entity (artifact, function, run, etc.).
57
116
 
58
117
  Parameters
59
118
  ----------
60
119
  key : str
61
- The key of the entity.
120
+ The entity key in standardized format.
62
121
 
63
122
  Returns
64
123
  -------
65
124
  str
66
- The entity type.
125
+ The entity type extracted from the key.
126
+
127
+ Raises
128
+ ------
129
+ ValueError
130
+ If the key format is invalid or cannot be parsed.
67
131
  """
68
132
  _, entity_type, _, _, _ = parse_entity_key(key)
69
133
  return entity_type
@@ -71,17 +135,42 @@ def get_entity_type_from_key(key: str) -> str:
71
135
 
72
136
  def get_project_from_key(key: str) -> str:
73
137
  """
74
- Get project from key.
138
+ Extract the project name from an entity key.
139
+
140
+ Parses the entity key and returns only the project component,
141
+ which identifies the project context the entity belongs to.
75
142
 
76
143
  Parameters
77
144
  ----------
78
145
  key : str
79
- The key of the entity.
146
+ The entity key in standardized format.
80
147
 
81
148
  Returns
82
149
  -------
83
150
  str
84
- The project.
151
+ The project name extracted from the key.
152
+
153
+ Raises
154
+ ------
155
+ ValueError
156
+ If the key format is invalid or cannot be parsed.
85
157
  """
86
158
  project, _, _, _, _ = parse_entity_key(key)
87
159
  return project
160
+
161
+
162
+ def map_actions(kind_action_list: list[tuple[str, str]]) -> list[KindAction]:
163
+ """
164
+ Build task actions as KindAction namedtuples.
165
+
166
+ Parameters
167
+ ----------
168
+ kind_action_list : list[tuple[str, str]]
169
+ List of kind-action couples.
170
+
171
+ Returns
172
+ -------
173
+ list[KindAction]
174
+ Returns the task actions as KindAction namedtuples.
175
+ """
176
+ return [KindAction(kind, action) for (kind, action) in kind_action_list]