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
@@ -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.
@@ -4,8 +4,53 @@
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
  """
@@ -32,34 +77,34 @@ def parse_entity_key(key: str) -> tuple[str, str, str, str | None, str]:
32
77
  ValueError
33
78
  If the key format is invalid or cannot be parsed.
34
79
  """
35
- try:
36
- # Remove "store://" from the key
37
- key = key.replace("store://", "")
80
+ if not is_valid_key(key):
81
+ raise ValueError("Invalid entity key format.")
38
82
 
39
- # Split the key into parts
40
- parts = key.split("/")
83
+ # Remove "store://" from the key
84
+ key = key.replace("store://", "")
41
85
 
42
- # The project is the first part
43
- project = parts[0]
86
+ # Split the key into parts
87
+ parts = key.split("/")
44
88
 
45
- # The entity type is the second part
46
- entity_type = parts[1]
89
+ # The project is the first part
90
+ project = parts[0]
47
91
 
48
- # The kind is the third part
49
- kind = parts[2]
92
+ # The entity type is the second part
93
+ entity_type = parts[1]
50
94
 
51
- # Tasks and runs have no name and uuid
52
- if entity_type in (EntityTypes.TASK.value, EntityTypes.RUN.value):
53
- name = None
54
- uuid = parts[3]
95
+ # The kind is the third part
96
+ kind = parts[2]
55
97
 
56
- # The name and uuid are separated by a colon in the last part
57
- else:
58
- name, uuid = parts[3].split(":")
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]
59
102
 
60
- return project, entity_type, kind, name, uuid
61
- except Exception as e:
62
- raise ValueError("Invalid key format.") from e
103
+ # The name and uuid are separated by a colon in the last part
104
+ else:
105
+ name, uuid = parts[3].split(":")
106
+
107
+ return project, entity_type, kind, name, uuid
63
108
 
64
109
 
65
110
  def get_entity_type_from_key(key: str) -> str:
@@ -112,3 +157,20 @@ def get_project_from_key(key: str) -> str:
112
157
  """
113
158
  project, _, _, _, _ = parse_entity_key(key)
114
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]
@@ -0,0 +1,151 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ ANIMALS = [
8
+ "ant",
9
+ "bear",
10
+ "bee",
11
+ "bison",
12
+ "cat",
13
+ "cheetah",
14
+ "cougar",
15
+ "crane",
16
+ "deer",
17
+ "dolphin",
18
+ "duck",
19
+ "eagle",
20
+ "falcon",
21
+ "finch",
22
+ "fox",
23
+ "frog",
24
+ "gecko",
25
+ "hawk",
26
+ "heron",
27
+ "horse",
28
+ "jaguar",
29
+ "koala",
30
+ "leopard",
31
+ "lion",
32
+ "llama",
33
+ "lynx",
34
+ "mole",
35
+ "moose",
36
+ "otter",
37
+ "owl",
38
+ "panda",
39
+ "penguin",
40
+ "rabbit",
41
+ "robin",
42
+ "seal",
43
+ "shark",
44
+ "sparrow",
45
+ "tiger",
46
+ "tortoise",
47
+ "wolf",
48
+ ]
49
+
50
+ ADJECTIVES = [
51
+ "able",
52
+ "agile",
53
+ "alert",
54
+ "alpha",
55
+ "amber",
56
+ "apex",
57
+ "arctic",
58
+ "atomic",
59
+ "balanced",
60
+ "basic",
61
+ "bold",
62
+ "bright",
63
+ "brisk",
64
+ "calm",
65
+ "clever",
66
+ "clear",
67
+ "cloudy",
68
+ "crisp",
69
+ "curious",
70
+ "cyber",
71
+ "daring",
72
+ "deep",
73
+ "delta",
74
+ "digital",
75
+ "dynamic",
76
+ "early",
77
+ "easy",
78
+ "electric",
79
+ "epic",
80
+ "fast",
81
+ "firm",
82
+ "fluent",
83
+ "focused",
84
+ "fresh",
85
+ "friendly",
86
+ "full",
87
+ "future",
88
+ "gentle",
89
+ "global",
90
+ "golden",
91
+ "grand",
92
+ "great",
93
+ "green",
94
+ "happy",
95
+ "high",
96
+ "honest",
97
+ "hyper",
98
+ "ideal",
99
+ "infinite",
100
+ "inner",
101
+ "instant",
102
+ "keen",
103
+ "kind",
104
+ "known",
105
+ "large",
106
+ "light",
107
+ "linear",
108
+ "logical",
109
+ "lucky",
110
+ "lunar",
111
+ "magic",
112
+ "major",
113
+ "mega",
114
+ "mild",
115
+ "modern",
116
+ "neat",
117
+ "noble",
118
+ "open",
119
+ "outer",
120
+ "peak",
121
+ "perfect",
122
+ "plain",
123
+ "prime",
124
+ "pure",
125
+ "quick",
126
+ "quiet",
127
+ "rapid",
128
+ "rare",
129
+ "ready",
130
+ "real",
131
+ "red",
132
+ "right",
133
+ "safe",
134
+ "sharp",
135
+ "silent",
136
+ "simple",
137
+ "smart",
138
+ "solid",
139
+ "solar",
140
+ "speedy",
141
+ "stable",
142
+ "steady",
143
+ "stellar",
144
+ "strong",
145
+ "swift",
146
+ "true",
147
+ "ultra",
148
+ "vast",
149
+ "vital",
150
+ "wise",
151
+ ]
@@ -4,8 +4,12 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import random
8
+
7
9
  from pydantic import BaseModel, Field
8
10
 
11
+ from digitalhub.entities._constructors._resources import ADJECTIVES, ANIMALS
12
+
9
13
  NAME_REGEX = r"^[a-zA-Z0-9._+-]+$"
10
14
 
11
15
 
@@ -33,3 +37,17 @@ def build_name(name: str) -> str:
33
37
  """
34
38
  NameValidator(name=name)
35
39
  return name
40
+
41
+
42
+ def random_name() -> str:
43
+ """
44
+ Generate a random name.
45
+
46
+ Returns
47
+ -------
48
+ str
49
+ The random name.
50
+ """
51
+ adjective = random.choice(ADJECTIVES)
52
+ animal = random.choice(ANIMALS)
53
+ return f"{adjective}-{animal}"
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0