digitalhub 0.11.0b7__py3-none-any.whl → 0.13.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 (272) hide show
  1. digitalhub/__init__.py +4 -1
  2. digitalhub/context/api.py +9 -5
  3. digitalhub/context/builder.py +7 -5
  4. digitalhub/context/context.py +13 -1
  5. digitalhub/entities/__init__.py +3 -0
  6. digitalhub/entities/_base/__init__.py +3 -0
  7. digitalhub/entities/_base/_base/__init__.py +3 -0
  8. digitalhub/entities/_base/_base/entity.py +4 -0
  9. digitalhub/entities/_base/context/__init__.py +3 -0
  10. digitalhub/entities/_base/context/entity.py +4 -0
  11. digitalhub/entities/_base/entity/__init__.py +3 -0
  12. digitalhub/entities/_base/entity/_constructors/__init__.py +3 -0
  13. digitalhub/entities/_base/entity/_constructors/metadata.py +4 -0
  14. digitalhub/entities/_base/entity/_constructors/name.py +4 -0
  15. digitalhub/entities/_base/entity/_constructors/spec.py +4 -0
  16. digitalhub/entities/_base/entity/_constructors/status.py +4 -0
  17. digitalhub/entities/_base/entity/_constructors/uuid.py +4 -0
  18. digitalhub/entities/_base/entity/builder.py +4 -0
  19. digitalhub/entities/_base/entity/entity.py +4 -0
  20. digitalhub/entities/_base/entity/metadata.py +4 -0
  21. digitalhub/entities/_base/entity/spec.py +4 -0
  22. digitalhub/entities/_base/entity/status.py +4 -0
  23. digitalhub/entities/_base/executable/__init__.py +3 -0
  24. digitalhub/entities/_base/executable/entity.py +109 -57
  25. digitalhub/entities/_base/material/__init__.py +3 -0
  26. digitalhub/entities/_base/material/entity.py +15 -18
  27. digitalhub/entities/_base/material/spec.py +4 -0
  28. digitalhub/entities/_base/material/status.py +4 -0
  29. digitalhub/entities/_base/material/utils.py +5 -1
  30. digitalhub/entities/_base/runtime_entity/__init__.py +3 -0
  31. digitalhub/entities/_base/runtime_entity/builder.py +4 -0
  32. digitalhub/entities/_base/unversioned/__init__.py +3 -0
  33. digitalhub/entities/_base/unversioned/builder.py +4 -0
  34. digitalhub/entities/_base/unversioned/entity.py +4 -0
  35. digitalhub/entities/_base/versioned/__init__.py +3 -0
  36. digitalhub/entities/_base/versioned/builder.py +4 -0
  37. digitalhub/entities/_base/versioned/entity.py +4 -0
  38. digitalhub/entities/_commons/__init__.py +3 -0
  39. digitalhub/entities/_commons/enums.py +4 -0
  40. digitalhub/entities/_commons/metrics.py +68 -30
  41. digitalhub/entities/_commons/utils.py +40 -9
  42. digitalhub/entities/_processors/__init__.py +3 -0
  43. digitalhub/entities/_processors/base.py +154 -79
  44. digitalhub/entities/_processors/context.py +370 -215
  45. digitalhub/entities/_processors/utils.py +78 -30
  46. digitalhub/entities/artifact/__init__.py +3 -0
  47. digitalhub/entities/artifact/_base/__init__.py +3 -0
  48. digitalhub/entities/artifact/_base/builder.py +4 -0
  49. digitalhub/entities/artifact/_base/entity.py +4 -0
  50. digitalhub/entities/artifact/_base/spec.py +4 -0
  51. digitalhub/entities/artifact/_base/status.py +4 -0
  52. digitalhub/entities/artifact/artifact/__init__.py +3 -0
  53. digitalhub/entities/artifact/artifact/builder.py +4 -0
  54. digitalhub/entities/artifact/artifact/entity.py +4 -0
  55. digitalhub/entities/artifact/artifact/spec.py +4 -0
  56. digitalhub/entities/artifact/artifact/status.py +4 -0
  57. digitalhub/entities/artifact/crud.py +8 -0
  58. digitalhub/entities/artifact/utils.py +32 -13
  59. digitalhub/entities/builders.py +4 -0
  60. digitalhub/entities/dataitem/__init__.py +3 -0
  61. digitalhub/entities/dataitem/_base/__init__.py +3 -0
  62. digitalhub/entities/dataitem/_base/builder.py +4 -0
  63. digitalhub/entities/dataitem/_base/entity.py +4 -0
  64. digitalhub/entities/dataitem/_base/spec.py +4 -0
  65. digitalhub/entities/dataitem/_base/status.py +4 -0
  66. digitalhub/entities/dataitem/crud.py +18 -2
  67. digitalhub/entities/dataitem/dataitem/__init__.py +3 -0
  68. digitalhub/entities/dataitem/dataitem/builder.py +4 -0
  69. digitalhub/entities/dataitem/dataitem/entity.py +4 -0
  70. digitalhub/entities/dataitem/dataitem/spec.py +4 -0
  71. digitalhub/entities/dataitem/dataitem/status.py +4 -0
  72. digitalhub/entities/dataitem/iceberg/__init__.py +3 -0
  73. digitalhub/entities/dataitem/iceberg/builder.py +4 -0
  74. digitalhub/entities/dataitem/iceberg/entity.py +4 -0
  75. digitalhub/entities/dataitem/iceberg/spec.py +4 -0
  76. digitalhub/entities/dataitem/iceberg/status.py +4 -0
  77. digitalhub/entities/dataitem/table/__init__.py +3 -0
  78. digitalhub/entities/dataitem/table/builder.py +4 -0
  79. digitalhub/entities/dataitem/table/entity.py +7 -3
  80. digitalhub/entities/dataitem/table/models.py +4 -0
  81. digitalhub/entities/dataitem/table/spec.py +4 -0
  82. digitalhub/entities/dataitem/table/status.py +4 -0
  83. digitalhub/entities/dataitem/table/utils.py +4 -0
  84. digitalhub/entities/dataitem/utils.py +88 -35
  85. digitalhub/entities/function/__init__.py +3 -0
  86. digitalhub/entities/function/_base/__init__.py +3 -0
  87. digitalhub/entities/function/_base/builder.py +4 -0
  88. digitalhub/entities/function/_base/entity.py +4 -0
  89. digitalhub/entities/function/_base/spec.py +4 -0
  90. digitalhub/entities/function/_base/status.py +4 -0
  91. digitalhub/entities/function/crud.py +4 -0
  92. digitalhub/entities/model/__init__.py +3 -0
  93. digitalhub/entities/model/_base/__init__.py +3 -0
  94. digitalhub/entities/model/_base/builder.py +4 -0
  95. digitalhub/entities/model/_base/entity.py +4 -0
  96. digitalhub/entities/model/_base/spec.py +4 -0
  97. digitalhub/entities/model/_base/status.py +4 -0
  98. digitalhub/entities/model/crud.py +8 -0
  99. digitalhub/entities/model/huggingface/__init__.py +3 -0
  100. digitalhub/entities/model/huggingface/builder.py +4 -0
  101. digitalhub/entities/model/huggingface/entity.py +4 -0
  102. digitalhub/entities/model/huggingface/spec.py +4 -0
  103. digitalhub/entities/model/huggingface/status.py +4 -0
  104. digitalhub/entities/model/mlflow/__init__.py +3 -0
  105. digitalhub/entities/model/mlflow/builder.py +4 -0
  106. digitalhub/entities/model/mlflow/entity.py +4 -0
  107. digitalhub/entities/model/mlflow/models.py +4 -0
  108. digitalhub/entities/model/mlflow/spec.py +4 -0
  109. digitalhub/entities/model/mlflow/status.py +4 -0
  110. digitalhub/entities/model/mlflow/utils.py +4 -0
  111. digitalhub/entities/model/model/__init__.py +3 -0
  112. digitalhub/entities/model/model/builder.py +4 -0
  113. digitalhub/entities/model/model/entity.py +4 -0
  114. digitalhub/entities/model/model/spec.py +4 -0
  115. digitalhub/entities/model/model/status.py +4 -0
  116. digitalhub/entities/model/sklearn/__init__.py +3 -0
  117. digitalhub/entities/model/sklearn/builder.py +4 -0
  118. digitalhub/entities/model/sklearn/entity.py +4 -0
  119. digitalhub/entities/model/sklearn/spec.py +4 -0
  120. digitalhub/entities/model/sklearn/status.py +4 -0
  121. digitalhub/entities/model/utils.py +32 -13
  122. digitalhub/entities/project/__init__.py +3 -0
  123. digitalhub/entities/project/_base/__init__.py +3 -0
  124. digitalhub/entities/project/_base/builder.py +4 -0
  125. digitalhub/entities/project/_base/entity.py +4 -2
  126. digitalhub/entities/project/_base/models.py +4 -0
  127. digitalhub/entities/project/_base/spec.py +4 -0
  128. digitalhub/entities/project/_base/status.py +4 -0
  129. digitalhub/entities/project/crud.py +4 -0
  130. digitalhub/entities/project/utils.py +4 -0
  131. digitalhub/entities/run/__init__.py +3 -0
  132. digitalhub/entities/run/_base/__init__.py +3 -0
  133. digitalhub/entities/run/_base/builder.py +4 -0
  134. digitalhub/entities/run/_base/entity.py +6 -2
  135. digitalhub/entities/run/_base/spec.py +4 -0
  136. digitalhub/entities/run/_base/status.py +4 -0
  137. digitalhub/entities/run/crud.py +4 -0
  138. digitalhub/entities/secret/__init__.py +3 -0
  139. digitalhub/entities/secret/_base/__init__.py +3 -0
  140. digitalhub/entities/secret/_base/builder.py +4 -0
  141. digitalhub/entities/secret/_base/entity.py +4 -0
  142. digitalhub/entities/secret/_base/spec.py +4 -0
  143. digitalhub/entities/secret/_base/status.py +4 -0
  144. digitalhub/entities/secret/crud.py +4 -0
  145. digitalhub/entities/task/__init__.py +3 -0
  146. digitalhub/entities/task/_base/__init__.py +3 -0
  147. digitalhub/entities/task/_base/builder.py +4 -0
  148. digitalhub/entities/task/_base/entity.py +4 -0
  149. digitalhub/entities/task/_base/models.py +16 -3
  150. digitalhub/entities/task/_base/spec.py +4 -0
  151. digitalhub/entities/task/_base/status.py +4 -0
  152. digitalhub/entities/task/_base/utils.py +4 -0
  153. digitalhub/entities/task/crud.py +4 -0
  154. digitalhub/entities/trigger/__init__.py +3 -0
  155. digitalhub/entities/trigger/_base/__init__.py +3 -0
  156. digitalhub/entities/trigger/_base/builder.py +4 -0
  157. digitalhub/entities/trigger/_base/entity.py +15 -0
  158. digitalhub/entities/trigger/_base/spec.py +4 -0
  159. digitalhub/entities/trigger/_base/status.py +4 -0
  160. digitalhub/entities/trigger/crud.py +4 -0
  161. digitalhub/entities/trigger/lifecycle/__init__.py +3 -0
  162. digitalhub/entities/trigger/lifecycle/builder.py +4 -0
  163. digitalhub/entities/trigger/lifecycle/entity.py +4 -0
  164. digitalhub/entities/trigger/lifecycle/spec.py +4 -0
  165. digitalhub/entities/trigger/lifecycle/status.py +4 -0
  166. digitalhub/entities/trigger/scheduler/__init__.py +3 -0
  167. digitalhub/entities/trigger/scheduler/builder.py +4 -0
  168. digitalhub/entities/trigger/scheduler/entity.py +4 -0
  169. digitalhub/entities/trigger/scheduler/spec.py +4 -0
  170. digitalhub/entities/trigger/scheduler/status.py +4 -0
  171. digitalhub/entities/workflow/__init__.py +3 -0
  172. digitalhub/entities/workflow/_base/__init__.py +3 -0
  173. digitalhub/entities/workflow/_base/builder.py +4 -0
  174. digitalhub/entities/workflow/_base/entity.py +4 -0
  175. digitalhub/entities/workflow/_base/spec.py +4 -0
  176. digitalhub/entities/workflow/_base/status.py +4 -0
  177. digitalhub/entities/workflow/crud.py +4 -0
  178. digitalhub/factory/__init__.py +3 -0
  179. digitalhub/factory/factory.py +29 -3
  180. digitalhub/factory/utils.py +15 -3
  181. digitalhub/runtimes/__init__.py +3 -0
  182. digitalhub/runtimes/_base.py +5 -1
  183. digitalhub/runtimes/builder.py +22 -1
  184. digitalhub/runtimes/enums.py +4 -0
  185. digitalhub/stores/__init__.py +3 -0
  186. digitalhub/stores/client/__init__.py +15 -0
  187. digitalhub/stores/client/_base/__init__.py +3 -0
  188. digitalhub/stores/client/_base/api_builder.py +18 -0
  189. digitalhub/stores/client/_base/client.py +97 -0
  190. digitalhub/stores/client/_base/key_builder.py +32 -0
  191. digitalhub/stores/client/_base/params_builder.py +18 -0
  192. digitalhub/stores/client/api.py +14 -5
  193. digitalhub/stores/client/builder.py +7 -1
  194. digitalhub/stores/client/dhcore/__init__.py +3 -0
  195. digitalhub/stores/client/dhcore/api_builder.py +21 -0
  196. digitalhub/stores/client/dhcore/client.py +329 -70
  197. digitalhub/stores/client/dhcore/configurator.py +489 -193
  198. digitalhub/stores/client/dhcore/enums.py +7 -0
  199. digitalhub/stores/client/dhcore/error_parser.py +39 -1
  200. digitalhub/stores/client/dhcore/key_builder.py +4 -0
  201. digitalhub/stores/client/dhcore/models.py +4 -0
  202. digitalhub/stores/client/dhcore/params_builder.py +117 -17
  203. digitalhub/stores/client/dhcore/utils.py +44 -22
  204. digitalhub/stores/client/local/__init__.py +3 -0
  205. digitalhub/stores/client/local/api_builder.py +21 -0
  206. digitalhub/stores/client/local/client.py +10 -8
  207. digitalhub/stores/client/local/enums.py +4 -0
  208. digitalhub/stores/client/local/key_builder.py +4 -0
  209. digitalhub/stores/client/local/params_builder.py +4 -0
  210. digitalhub/stores/credentials/__init__.py +3 -0
  211. digitalhub/stores/credentials/api.py +35 -0
  212. digitalhub/stores/credentials/configurator.py +210 -0
  213. digitalhub/stores/credentials/enums.py +68 -0
  214. digitalhub/stores/credentials/handler.py +176 -0
  215. digitalhub/stores/credentials/ini_module.py +164 -0
  216. digitalhub/stores/credentials/store.py +81 -0
  217. digitalhub/stores/data/__init__.py +3 -0
  218. digitalhub/stores/data/_base/__init__.py +3 -0
  219. digitalhub/stores/data/_base/store.py +31 -9
  220. digitalhub/stores/data/api.py +53 -9
  221. digitalhub/stores/data/builder.py +94 -41
  222. digitalhub/stores/data/enums.py +4 -0
  223. digitalhub/stores/data/local/__init__.py +3 -0
  224. digitalhub/stores/data/local/store.py +8 -7
  225. digitalhub/stores/data/remote/__init__.py +3 -0
  226. digitalhub/stores/data/remote/store.py +8 -7
  227. digitalhub/stores/data/s3/__init__.py +3 -0
  228. digitalhub/stores/data/s3/configurator.py +69 -80
  229. digitalhub/stores/data/s3/store.py +73 -81
  230. digitalhub/stores/data/s3/utils.py +14 -10
  231. digitalhub/stores/data/sql/__init__.py +3 -0
  232. digitalhub/stores/data/sql/configurator.py +80 -73
  233. digitalhub/stores/data/sql/store.py +195 -102
  234. digitalhub/stores/readers/__init__.py +3 -0
  235. digitalhub/stores/readers/data/__init__.py +3 -0
  236. digitalhub/stores/readers/data/_base/__init__.py +3 -0
  237. digitalhub/stores/readers/data/_base/builder.py +4 -0
  238. digitalhub/stores/readers/data/_base/reader.py +4 -0
  239. digitalhub/stores/readers/data/api.py +4 -0
  240. digitalhub/stores/readers/data/factory.py +4 -0
  241. digitalhub/stores/readers/data/pandas/__init__.py +3 -0
  242. digitalhub/stores/readers/data/pandas/builder.py +4 -0
  243. digitalhub/stores/readers/data/pandas/reader.py +4 -0
  244. digitalhub/stores/readers/query/__init__.py +3 -0
  245. digitalhub/utils/__init__.py +3 -0
  246. digitalhub/utils/enums.py +4 -0
  247. digitalhub/utils/exceptions.py +10 -0
  248. digitalhub/utils/file_utils.py +57 -30
  249. digitalhub/utils/generic_utils.py +45 -33
  250. digitalhub/utils/git_utils.py +28 -14
  251. digitalhub/utils/io_utils.py +23 -18
  252. digitalhub/utils/logger.py +4 -0
  253. digitalhub/utils/types.py +4 -0
  254. digitalhub/utils/uri_utils.py +35 -31
  255. digitalhub-0.13.0.dist-info/METADATA +301 -0
  256. digitalhub-0.13.0.dist-info/RECORD +259 -0
  257. digitalhub-0.13.0.dist-info/licenses/AUTHORS +5 -0
  258. digitalhub-0.13.0.dist-info/licenses/LICENSE +201 -0
  259. digitalhub/entities/_commons/types.py +0 -5
  260. digitalhub/stores/configurator/__init__.py +0 -0
  261. digitalhub/stores/configurator/api.py +0 -31
  262. digitalhub/stores/configurator/configurator.py +0 -198
  263. digitalhub/stores/configurator/credentials_store.py +0 -65
  264. digitalhub/stores/configurator/enums.py +0 -21
  265. digitalhub/stores/configurator/ini_module.py +0 -128
  266. digitalhub/stores/data/s3/enums.py +0 -16
  267. digitalhub/stores/data/sql/enums.py +0 -16
  268. digitalhub/stores/data/utils.py +0 -34
  269. digitalhub-0.11.0b7.dist-info/METADATA +0 -259
  270. digitalhub-0.11.0b7.dist-info/RECORD +0 -261
  271. digitalhub-0.11.0b7.dist-info/licenses/LICENSE.txt +0 -216
  272. {digitalhub-0.11.0b7.dist-info → digitalhub-0.13.0.dist-info}/WHEEL +0 -0
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  import typing
@@ -12,6 +16,7 @@ from digitalhub.utils.types import SourcesOrListOfSources
12
16
  from digitalhub.utils.uri_utils import has_local_scheme
13
17
 
14
18
  if typing.TYPE_CHECKING:
19
+ from digitalhub.stores.credentials.configurator import Configurator
15
20
  from digitalhub.stores.readers.data._base.reader import DataframeReader
16
21
 
17
22
 
@@ -20,6 +25,9 @@ class Store:
20
25
  Store abstract class.
21
26
  """
22
27
 
28
+ def __init__(self, configurator: Configurator | None = None) -> None:
29
+ self._configurator = configurator
30
+
23
31
  ##############################
24
32
  # I/O methods
25
33
  ##############################
@@ -27,19 +35,22 @@ class Store:
27
35
  @abstractmethod
28
36
  def download(
29
37
  self,
30
- root: str,
38
+ src: str,
31
39
  dst: Path,
32
- src: list[str],
33
40
  overwrite: bool = False,
34
41
  ) -> str:
35
42
  """
36
- Method to download artifact from storage.
43
+ Method to download material entity from storage.
37
44
  """
38
45
 
39
46
  @abstractmethod
40
- def upload(self, src: SourcesOrListOfSources, dst: str) -> list[tuple[str, str]]:
47
+ def upload(
48
+ self,
49
+ src: SourcesOrListOfSources,
50
+ dst: str,
51
+ ) -> list[tuple[str, str]]:
41
52
  """
42
- Method to upload artifact to storage.
53
+ Method to upload material entity to storage.
43
54
  """
44
55
 
45
56
  @abstractmethod
@@ -79,7 +90,13 @@ class Store:
79
90
  """
80
91
 
81
92
  @abstractmethod
82
- def write_df(self, df: Any, dst: str, extension: str | None = None, **kwargs) -> str:
93
+ def write_df(
94
+ self,
95
+ df: Any,
96
+ dst: str,
97
+ extension: str | None = None,
98
+ **kwargs,
99
+ ) -> str:
83
100
  """
84
101
  Write DataFrame as parquet or csv.
85
102
  """
@@ -169,9 +186,14 @@ class Store:
169
186
  """
170
187
  if not isinstance(path, Path):
171
188
  path = Path(path)
172
- if path.suffix != "":
173
- path = path.parent
174
- path.mkdir(parents=True, exist_ok=True)
189
+ # If the path does not exist, we need to infer if it's a file or directory
190
+ if path.suffix and not path.name.startswith("."):
191
+ # Looks like a file, use parent
192
+ dir_path = path.parent
193
+ else:
194
+ # Looks like a directory (even if it contains dots)
195
+ dir_path = path
196
+ dir_path.mkdir(parents=True, exist_ok=True)
175
197
 
176
198
  @staticmethod
177
199
  def _build_temp() -> Path:
@@ -1,29 +1,73 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  import typing
4
8
 
9
+ from digitalhub.context.api import get_context
10
+ from digitalhub.stores.credentials.handler import creds_handler
5
11
  from digitalhub.stores.data.builder import store_builder
12
+ from digitalhub.stores.data.enums import StoreEnv
6
13
 
7
14
  if typing.TYPE_CHECKING:
8
15
  from digitalhub.stores.data._base.store import Store
9
16
 
10
17
 
11
- def get_store(project: str, uri: str) -> Store:
18
+ def get_default_store(project: str) -> str:
12
19
  """
13
- Get store instance by URI.
20
+ Returns the default store URI for a given project.
14
21
 
15
22
  Parameters
16
- ---------
23
+ ----------
17
24
  project : str
18
- Project name.
25
+ The name of the project.
26
+
27
+ Returns
28
+ -------
29
+ str
30
+ The default store URI.
31
+
32
+ Raises
33
+ ------
34
+ ValueError
35
+ If no default store is found.
36
+ """
37
+ var = StoreEnv.DEFAULT_FILES_STORE.value
38
+
39
+ context = get_context(project)
40
+ store = context.config.get(var.lower().replace("dhcore_", ""))
41
+ if store is not None:
42
+ return store
43
+
44
+ store = creds_handler.load_from_env([var]).get(var)
45
+ if store is None:
46
+ store = creds_handler.load_from_file([var]).get(var)
47
+
48
+ if store is None or store == "":
49
+ raise ValueError(
50
+ "No default store found. "
51
+ "Please set a default store "
52
+ f"in your environment (e.g. export {var}=) "
53
+ " in the .dhcore.ini file "
54
+ "or set it in project config."
55
+ )
56
+ return store
57
+
58
+
59
+ def get_store(uri: str) -> Store:
60
+ """
61
+ Returns a store instance for the given URI.
62
+
63
+ Parameters
64
+ ----------
19
65
  uri : str
20
- URI to parse.
21
- config : dict
22
- Store configuration.
66
+ The URI to parse.
23
67
 
24
68
  Returns
25
69
  -------
26
70
  Store
27
- Store instance.
71
+ The store instance corresponding to the URI.
28
72
  """
29
- return store_builder.get(project, uri)
73
+ return store_builder.get(uri)
@@ -1,93 +1,146 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  import typing
4
8
 
5
- from digitalhub.stores.configurator.api import get_current_env
6
9
  from digitalhub.stores.data.local.store import LocalStore
7
10
  from digitalhub.stores.data.remote.store import RemoteStore
11
+ from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
8
12
  from digitalhub.stores.data.s3.store import S3Store
13
+ from digitalhub.stores.data.sql.configurator import SqlStoreConfigurator
9
14
  from digitalhub.stores.data.sql.store import SqlStore
10
15
  from digitalhub.utils.uri_utils import SchemeCategory, map_uri_scheme
11
16
 
12
17
  if typing.TYPE_CHECKING:
18
+ from digitalhub.stores.credentials.configurator import Configurator
13
19
  from digitalhub.stores.data._base.store import Store
20
+ from digitalhub.utils.exceptions import StoreError
14
21
 
15
22
 
16
- def _get_class_from_type(type: str) -> Store:
23
+ class StoreInfo:
17
24
  """
18
- Get a store class from its type.
25
+ Container for store class and configurator information.
19
26
 
20
- Parameters
21
- ----------
22
- type : str
23
- Store type.
27
+ Holds store class references and their associated configurators
28
+ for registration and instantiation in the store builder system.
24
29
 
25
- Returns
26
- -------
27
- Store
28
- The store class.
30
+ Attributes
31
+ ----------
32
+ _store : Store
33
+ The store class to be instantiated.
34
+ _configurator : Configurator or None
35
+ The configurator class for store configuration, if required.
29
36
  """
30
- if type == SchemeCategory.LOCAL.value:
31
- return LocalStore
32
- if type == SchemeCategory.S3.value:
33
- return S3Store
34
- if type == SchemeCategory.REMOTE.value:
35
- return RemoteStore
36
- if type == SchemeCategory.SQL.value:
37
- return SqlStore
38
- raise ValueError(f"Unknown store type: {type}")
37
+
38
+ def __init__(self, store: Store, configurator: Configurator | None = None) -> None:
39
+ self._store = store
40
+ self._configurator = configurator
39
41
 
40
42
 
41
43
  class StoreBuilder:
42
44
  """
43
- Store builder class.
45
+ Store factory and registry for managing data store instances.
46
+
47
+ Provides registration, instantiation, and caching of data store
48
+ instances based on URI schemes. Supports various store types
49
+ including S3, SQL, local, and remote stores with their respective
50
+ configurators.
51
+
52
+ Attributes
53
+ ----------
54
+ _builders : dict[str, StoreInfo]
55
+ Registry of store types mapped to their StoreInfo instances.
56
+ _instances : dict[str, Store]
57
+ Cache of instantiated store instances by store type.
44
58
  """
45
59
 
46
60
  def __init__(self) -> None:
61
+ self._builders: dict[str, StoreInfo] = {}
47
62
  self._instances: dict[str, dict[str, Store]] = {}
48
63
 
49
- def build(self, project: str, store_type: str) -> None:
64
+ def register(
65
+ self,
66
+ store_type: str,
67
+ store: Store,
68
+ configurator: Configurator | None = None,
69
+ ) -> None:
50
70
  """
51
- Build a store instance and register it.
71
+ Register a store type with its class and optional configurator.
72
+
73
+ Adds a new store type to the builder registry, associating it
74
+ with a store class and optional configurator for later instantiation.
52
75
 
53
76
  Parameters
54
77
  ----------
55
78
  store_type : str
56
- Store type.
57
- config : dict
79
+ The unique identifier for the store type (e.g., 's3', 'sql').
80
+ store : Store
81
+ The store class to register for this type.
82
+ configurator : Configurator, optional
83
+ The configurator class for store configuration.
84
+ If None, the store will be instantiated without configuration.
58
85
 
59
86
  Returns
60
87
  -------
61
88
  None
89
+
90
+ Raises
91
+ ------
92
+ StoreError
93
+ If the store type is already registered in the builder.
62
94
  """
63
- env = get_current_env()
64
- if env not in self._instances:
65
- self._instances[env] = {}
66
- self._instances[env][store_type] = _get_class_from_type(store_type)()
95
+ if store_type not in self._builders:
96
+ self._builders[store_type] = StoreInfo(store, configurator)
97
+ else:
98
+ raise StoreError(f"Store type {store_type} already registered")
67
99
 
68
- def get(self, project: str, uri: str) -> Store:
100
+ def get(self, uri: str) -> Store:
69
101
  """
70
- Get a store instance by URI.
102
+ Get or create a store instance based on URI scheme.
103
+
104
+ Determines the appropriate store type from the URI scheme,
105
+ instantiates the store if not already cached, and returns
106
+ the store instance. Store instances are cached for reuse.
71
107
 
72
108
  Parameters
73
109
  ----------
74
110
  uri : str
75
- URI to parse.
76
- config : dict
77
- Store configuration.
111
+ The URI to parse for determining the store type.
112
+ The scheme (e.g., 's3://', 'sql://') determines which
113
+ store type to instantiate.
78
114
 
79
115
  Returns
80
116
  -------
81
117
  Store
82
- The store instance.
118
+ The store instance appropriate for handling the given URI.
119
+
120
+ Raises
121
+ ------
122
+ KeyError
123
+ If no store is registered for the URI scheme.
83
124
  """
84
- env = get_current_env()
85
125
  store_type = map_uri_scheme(uri)
86
- try:
87
- return self._instances[env][store_type]
88
- except KeyError:
89
- self.build(project, store_type)
90
- return self._instances[env][store_type]
126
+
127
+ # Build the store instance if not already present
128
+ if store_type not in self._instances:
129
+ store_info = self._builders[store_type]
130
+ store_cls = store_info._store
131
+ cfgrt_cls = store_info._configurator
132
+
133
+ if cfgrt_cls is None:
134
+ store = store_cls()
135
+ else:
136
+ store = store_cls(cfgrt_cls())
137
+ self._instances[store_type] = store
138
+
139
+ return self._instances[store_type]
91
140
 
92
141
 
93
142
  store_builder = StoreBuilder()
143
+ store_builder.register(SchemeCategory.S3.value, S3Store, S3StoreConfigurator)
144
+ store_builder.register(SchemeCategory.SQL.value, SqlStore, SqlStoreConfigurator)
145
+ store_builder.register(SchemeCategory.LOCAL.value, LocalStore)
146
+ store_builder.register(SchemeCategory.REMOTE.value, RemoteStore)
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  from enum import Enum
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  import shutil
@@ -25,7 +29,6 @@ class LocalStore(Store):
25
29
  self,
26
30
  root: str,
27
31
  dst: Path,
28
- src: list[str],
29
32
  overwrite: bool = False,
30
33
  ) -> str:
31
34
  """
@@ -33,19 +36,17 @@ class LocalStore(Store):
33
36
 
34
37
  Parameters
35
38
  ----------
36
- root : str
37
- The root path of the artifact.
39
+ src : str
40
+ Path of the material entity.
38
41
  dst : str
39
- The destination of the artifact on local filesystem.
40
- src : list[str]
41
- List of sources.
42
+ The destination of the material entity on local filesystem.
42
43
  overwrite : bool
43
44
  Specify if overwrite existing file(s).
44
45
 
45
46
  Returns
46
47
  -------
47
48
  str
48
- Destination path of the downloaded artifact.
49
+ Destination path of the downloaded files.
49
50
  """
50
51
  raise StoreError("Local store does not support download.")
51
52
 
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
@@ -1,3 +1,7 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
3
7
  from pathlib import Path
@@ -24,7 +28,6 @@ class RemoteStore(Store):
24
28
  self,
25
29
  root: str,
26
30
  dst: Path,
27
- src: list[str],
28
31
  overwrite: bool = False,
29
32
  ) -> str:
30
33
  """
@@ -32,19 +35,17 @@ class RemoteStore(Store):
32
35
 
33
36
  Parameters
34
37
  ----------
35
- root : str
36
- The root path of the artifact.
38
+ src : str
39
+ Path of the material entity.
37
40
  dst : str
38
- The destination of the artifact on local filesystem.
39
- src : list[str]
40
- List of sources.
41
+ The destination of the material entity on local filesystem.
41
42
  overwrite : bool
42
43
  Specify if overwrite existing file(s).
43
44
 
44
45
  Returns
45
46
  -------
46
47
  str
47
- Destination path of the downloaded artifact.
48
+ Destination path of the downloaded files.
48
49
  """
49
50
  # Handle destination
50
51
  if dst is None:
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
@@ -1,126 +1,115 @@
1
+ # SPDX-FileCopyrightText: © 2025 DSLab - Fondazione Bruno Kessler
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  from __future__ import annotations
2
6
 
7
+ from datetime import datetime, timezone
8
+
3
9
  from botocore.config import Config
4
10
 
5
- from digitalhub.stores.configurator.configurator import configurator
6
- from digitalhub.stores.configurator.enums import CredsOrigin
7
- from digitalhub.stores.data.s3.enums import S3StoreEnv
8
- from digitalhub.utils.exceptions import StoreError
11
+ from digitalhub.stores.client.dhcore.utils import refresh_token
12
+ from digitalhub.stores.credentials.configurator import Configurator
13
+ from digitalhub.stores.credentials.enums import CredsEnvVar
9
14
 
10
15
 
11
- class S3StoreConfigurator:
16
+ class S3StoreConfigurator(Configurator):
12
17
  """
13
18
  Configure the store by getting the credentials from user
14
19
  provided config or from environment.
15
20
  """
16
21
 
17
- required_vars = [
18
- S3StoreEnv.ENDPOINT_URL,
19
- S3StoreEnv.ACCESS_KEY_ID,
20
- S3StoreEnv.SECRET_ACCESS_KEY,
22
+ keys = [
23
+ CredsEnvVar.S3_ENDPOINT_URL.value,
24
+ CredsEnvVar.S3_ACCESS_KEY_ID.value,
25
+ CredsEnvVar.S3_SECRET_ACCESS_KEY.value,
26
+ CredsEnvVar.S3_REGION.value,
27
+ CredsEnvVar.S3_SIGNATURE_VERSION.value,
28
+ CredsEnvVar.S3_SESSION_TOKEN.value,
29
+ CredsEnvVar.S3_PATH_STYLE.value,
30
+ CredsEnvVar.S3_CREDENTIALS_EXPIRATION.value,
21
31
  ]
22
- optional_vars = [
23
- S3StoreEnv.REGION,
24
- S3StoreEnv.SIGNATURE_VERSION,
25
- S3StoreEnv.SESSION_TOKEN,
32
+ required_keys = [
33
+ CredsEnvVar.S3_ENDPOINT_URL.value,
34
+ CredsEnvVar.S3_ACCESS_KEY_ID.value,
35
+ CredsEnvVar.S3_SECRET_ACCESS_KEY.value,
26
36
  ]
27
37
 
38
+ def __init__(self):
39
+ super().__init__()
40
+ self.load_configs()
41
+
28
42
  ##############################
29
43
  # Configuration methods
30
44
  ##############################
31
45
 
32
- def get_boto3_client_config(self, origin: str) -> dict:
46
+ def load_env_vars(self) -> None:
33
47
  """
34
- Get S3 credentials (access key, secret key,
35
- session token and other config).
36
-
37
- Parameters
38
- ----------
39
- origin : str
40
- The origin of the credentials.
48
+ Loads the credentials from the environment variables.
41
49
 
42
50
  Returns
43
51
  -------
44
- dict
45
- The credentials.
52
+ None
46
53
  """
47
- if origin == CredsOrigin.ENV.value:
48
- creds = self._get_env_config()
49
- elif origin == CredsOrigin.FILE.value:
50
- creds = self._get_file_config()
51
- else:
52
- raise StoreError(f"Unknown origin: {origin}")
53
- return {
54
- "endpoint_url": creds[S3StoreEnv.ENDPOINT_URL.value],
55
- "aws_access_key_id": creds[S3StoreEnv.ACCESS_KEY_ID.value],
56
- "aws_secret_access_key": creds[S3StoreEnv.SECRET_ACCESS_KEY.value],
57
- "aws_session_token": creds[S3StoreEnv.SESSION_TOKEN.value],
58
- "config": Config(
59
- region_name=creds[S3StoreEnv.REGION.value],
60
- signature_version=creds[S3StoreEnv.SIGNATURE_VERSION.value],
61
- ),
62
- }
54
+ env_creds = self._creds_handler.load_from_env(self.keys)
55
+ self._creds_handler.set_credentials(self._env, env_creds)
63
56
 
64
- def _get_env_config(self) -> dict:
57
+ def load_file_vars(self) -> None:
65
58
  """
66
- Get the store configuration from environment variables.
59
+ Loads the credentials from a file.
67
60
 
68
61
  Returns
69
62
  -------
70
- dict
71
- The credentials.
63
+ None
72
64
  """
73
- credentials = {
74
- var.value: configurator.load_from_env(var.value) for var in self.required_vars + self.optional_vars
75
- }
76
- self._set_credentials(credentials)
77
- return credentials
65
+ file_creds = self._creds_handler.load_from_file(self.keys)
66
+ self._creds_handler.set_credentials(self._file, file_creds)
78
67
 
79
- def _get_file_config(self) -> dict:
68
+ def get_client_config(self) -> dict:
80
69
  """
81
- Get the store configuration from file.
70
+ Gets S3 credentials (access key, secret key, session token, and other config).
82
71
 
83
72
  Returns
84
73
  -------
85
74
  dict
86
- The credentials.
75
+ Dictionary containing S3 credentials and configuration.
87
76
  """
88
- credentials = {
89
- var.value: configurator.load_from_file(var.value) for var in self.required_vars + self.optional_vars
77
+ creds = self.get_credentials(self._origin)
78
+ expired = creds[CredsEnvVar.S3_CREDENTIALS_EXPIRATION.value]
79
+ if self._origin == self._file and self._is_expired(expired):
80
+ refresh_token()
81
+ self.load_file_vars()
82
+ creds = self.get_credentials(self._origin)
83
+ return {
84
+ "endpoint_url": creds[CredsEnvVar.S3_ENDPOINT_URL.value],
85
+ "aws_access_key_id": creds[CredsEnvVar.S3_ACCESS_KEY_ID.value],
86
+ "aws_secret_access_key": creds[CredsEnvVar.S3_SECRET_ACCESS_KEY.value],
87
+ "aws_session_token": creds[CredsEnvVar.S3_SESSION_TOKEN.value],
88
+ "config": Config(
89
+ region_name=creds[CredsEnvVar.S3_REGION.value],
90
+ signature_version=creds[CredsEnvVar.S3_SIGNATURE_VERSION.value],
91
+ ),
90
92
  }
91
- self._set_credentials(credentials)
92
- return credentials
93
-
94
- def _check_credentials(self, credentials: dict) -> None:
95
- """
96
- Check for missing credentials.
97
-
98
- Parameters
99
- ----------
100
- credentials : dict
101
- The credentials.
102
93
 
103
- Returns
104
- -------
105
- None
94
+ @staticmethod
95
+ def _is_expired(timestamp: str | None) -> bool:
106
96
  """
107
- missing_vars = [key for key, value in credentials.items() if value is None and key in self.required_vars]
108
- if missing_vars:
109
- raise StoreError(f"Missing credentials for S3 store: {', '.join(missing_vars)}")
110
-
111
- def _set_credentials(self, credentials: dict) -> None:
112
- """
113
- Set the store credentials into the configurator.
97
+ Determines whether a given timestamp is after the current UTC time.
114
98
 
115
99
  Parameters
116
100
  ----------
117
- credentials : dict
118
- The credentials.
101
+ timestamp : str or None
102
+ Timestamp string in the format 'YYYY-MM-DDTHH:MM:SSZ'.
119
103
 
120
104
  Returns
121
105
  -------
122
- None
106
+ bool
107
+ True if the given timestamp is later than the current UTC time,
108
+ otherwise False.
123
109
  """
124
- # Set credentials
125
- for key, value in credentials.items():
126
- configurator.set_credential(key, value)
110
+ if timestamp is None:
111
+ return False
112
+ dt = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%SZ")
113
+ dt = dt.replace(tzinfo=timezone.utc)
114
+ now = datetime.now(timezone.utc) + datetime.timedelta(seconds=120)
115
+ return dt < now