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.
- digitalhub/__init__.py +4 -1
- digitalhub/context/api.py +9 -5
- digitalhub/context/builder.py +7 -5
- digitalhub/context/context.py +13 -1
- digitalhub/entities/__init__.py +3 -0
- digitalhub/entities/_base/__init__.py +3 -0
- digitalhub/entities/_base/_base/__init__.py +3 -0
- digitalhub/entities/_base/_base/entity.py +4 -0
- digitalhub/entities/_base/context/__init__.py +3 -0
- digitalhub/entities/_base/context/entity.py +4 -0
- digitalhub/entities/_base/entity/__init__.py +3 -0
- digitalhub/entities/_base/entity/_constructors/__init__.py +3 -0
- digitalhub/entities/_base/entity/_constructors/metadata.py +4 -0
- digitalhub/entities/_base/entity/_constructors/name.py +4 -0
- digitalhub/entities/_base/entity/_constructors/spec.py +4 -0
- digitalhub/entities/_base/entity/_constructors/status.py +4 -0
- digitalhub/entities/_base/entity/_constructors/uuid.py +4 -0
- digitalhub/entities/_base/entity/builder.py +4 -0
- digitalhub/entities/_base/entity/entity.py +4 -0
- digitalhub/entities/_base/entity/metadata.py +4 -0
- digitalhub/entities/_base/entity/spec.py +4 -0
- digitalhub/entities/_base/entity/status.py +4 -0
- digitalhub/entities/_base/executable/__init__.py +3 -0
- digitalhub/entities/_base/executable/entity.py +109 -57
- digitalhub/entities/_base/material/__init__.py +3 -0
- digitalhub/entities/_base/material/entity.py +15 -18
- digitalhub/entities/_base/material/spec.py +4 -0
- digitalhub/entities/_base/material/status.py +4 -0
- digitalhub/entities/_base/material/utils.py +5 -1
- digitalhub/entities/_base/runtime_entity/__init__.py +3 -0
- digitalhub/entities/_base/runtime_entity/builder.py +4 -0
- digitalhub/entities/_base/unversioned/__init__.py +3 -0
- digitalhub/entities/_base/unversioned/builder.py +4 -0
- digitalhub/entities/_base/unversioned/entity.py +4 -0
- digitalhub/entities/_base/versioned/__init__.py +3 -0
- digitalhub/entities/_base/versioned/builder.py +4 -0
- digitalhub/entities/_base/versioned/entity.py +4 -0
- digitalhub/entities/_commons/__init__.py +3 -0
- digitalhub/entities/_commons/enums.py +4 -0
- digitalhub/entities/_commons/metrics.py +68 -30
- digitalhub/entities/_commons/utils.py +40 -9
- digitalhub/entities/_processors/__init__.py +3 -0
- digitalhub/entities/_processors/base.py +154 -79
- digitalhub/entities/_processors/context.py +370 -215
- digitalhub/entities/_processors/utils.py +78 -30
- digitalhub/entities/artifact/__init__.py +3 -0
- digitalhub/entities/artifact/_base/__init__.py +3 -0
- digitalhub/entities/artifact/_base/builder.py +4 -0
- digitalhub/entities/artifact/_base/entity.py +4 -0
- digitalhub/entities/artifact/_base/spec.py +4 -0
- digitalhub/entities/artifact/_base/status.py +4 -0
- digitalhub/entities/artifact/artifact/__init__.py +3 -0
- digitalhub/entities/artifact/artifact/builder.py +4 -0
- digitalhub/entities/artifact/artifact/entity.py +4 -0
- digitalhub/entities/artifact/artifact/spec.py +4 -0
- digitalhub/entities/artifact/artifact/status.py +4 -0
- digitalhub/entities/artifact/crud.py +8 -0
- digitalhub/entities/artifact/utils.py +32 -13
- digitalhub/entities/builders.py +4 -0
- digitalhub/entities/dataitem/__init__.py +3 -0
- digitalhub/entities/dataitem/_base/__init__.py +3 -0
- digitalhub/entities/dataitem/_base/builder.py +4 -0
- digitalhub/entities/dataitem/_base/entity.py +4 -0
- digitalhub/entities/dataitem/_base/spec.py +4 -0
- digitalhub/entities/dataitem/_base/status.py +4 -0
- digitalhub/entities/dataitem/crud.py +18 -2
- digitalhub/entities/dataitem/dataitem/__init__.py +3 -0
- digitalhub/entities/dataitem/dataitem/builder.py +4 -0
- digitalhub/entities/dataitem/dataitem/entity.py +4 -0
- digitalhub/entities/dataitem/dataitem/spec.py +4 -0
- digitalhub/entities/dataitem/dataitem/status.py +4 -0
- digitalhub/entities/dataitem/iceberg/__init__.py +3 -0
- digitalhub/entities/dataitem/iceberg/builder.py +4 -0
- digitalhub/entities/dataitem/iceberg/entity.py +4 -0
- digitalhub/entities/dataitem/iceberg/spec.py +4 -0
- digitalhub/entities/dataitem/iceberg/status.py +4 -0
- digitalhub/entities/dataitem/table/__init__.py +3 -0
- digitalhub/entities/dataitem/table/builder.py +4 -0
- digitalhub/entities/dataitem/table/entity.py +7 -3
- digitalhub/entities/dataitem/table/models.py +4 -0
- digitalhub/entities/dataitem/table/spec.py +4 -0
- digitalhub/entities/dataitem/table/status.py +4 -0
- digitalhub/entities/dataitem/table/utils.py +4 -0
- digitalhub/entities/dataitem/utils.py +88 -35
- digitalhub/entities/function/__init__.py +3 -0
- digitalhub/entities/function/_base/__init__.py +3 -0
- digitalhub/entities/function/_base/builder.py +4 -0
- digitalhub/entities/function/_base/entity.py +4 -0
- digitalhub/entities/function/_base/spec.py +4 -0
- digitalhub/entities/function/_base/status.py +4 -0
- digitalhub/entities/function/crud.py +4 -0
- digitalhub/entities/model/__init__.py +3 -0
- digitalhub/entities/model/_base/__init__.py +3 -0
- digitalhub/entities/model/_base/builder.py +4 -0
- digitalhub/entities/model/_base/entity.py +4 -0
- digitalhub/entities/model/_base/spec.py +4 -0
- digitalhub/entities/model/_base/status.py +4 -0
- digitalhub/entities/model/crud.py +8 -0
- digitalhub/entities/model/huggingface/__init__.py +3 -0
- digitalhub/entities/model/huggingface/builder.py +4 -0
- digitalhub/entities/model/huggingface/entity.py +4 -0
- digitalhub/entities/model/huggingface/spec.py +4 -0
- digitalhub/entities/model/huggingface/status.py +4 -0
- digitalhub/entities/model/mlflow/__init__.py +3 -0
- digitalhub/entities/model/mlflow/builder.py +4 -0
- digitalhub/entities/model/mlflow/entity.py +4 -0
- digitalhub/entities/model/mlflow/models.py +4 -0
- digitalhub/entities/model/mlflow/spec.py +4 -0
- digitalhub/entities/model/mlflow/status.py +4 -0
- digitalhub/entities/model/mlflow/utils.py +4 -0
- digitalhub/entities/model/model/__init__.py +3 -0
- digitalhub/entities/model/model/builder.py +4 -0
- digitalhub/entities/model/model/entity.py +4 -0
- digitalhub/entities/model/model/spec.py +4 -0
- digitalhub/entities/model/model/status.py +4 -0
- digitalhub/entities/model/sklearn/__init__.py +3 -0
- digitalhub/entities/model/sklearn/builder.py +4 -0
- digitalhub/entities/model/sklearn/entity.py +4 -0
- digitalhub/entities/model/sklearn/spec.py +4 -0
- digitalhub/entities/model/sklearn/status.py +4 -0
- digitalhub/entities/model/utils.py +32 -13
- digitalhub/entities/project/__init__.py +3 -0
- digitalhub/entities/project/_base/__init__.py +3 -0
- digitalhub/entities/project/_base/builder.py +4 -0
- digitalhub/entities/project/_base/entity.py +4 -2
- digitalhub/entities/project/_base/models.py +4 -0
- digitalhub/entities/project/_base/spec.py +4 -0
- digitalhub/entities/project/_base/status.py +4 -0
- digitalhub/entities/project/crud.py +4 -0
- digitalhub/entities/project/utils.py +4 -0
- digitalhub/entities/run/__init__.py +3 -0
- digitalhub/entities/run/_base/__init__.py +3 -0
- digitalhub/entities/run/_base/builder.py +4 -0
- digitalhub/entities/run/_base/entity.py +6 -2
- digitalhub/entities/run/_base/spec.py +4 -0
- digitalhub/entities/run/_base/status.py +4 -0
- digitalhub/entities/run/crud.py +4 -0
- digitalhub/entities/secret/__init__.py +3 -0
- digitalhub/entities/secret/_base/__init__.py +3 -0
- digitalhub/entities/secret/_base/builder.py +4 -0
- digitalhub/entities/secret/_base/entity.py +4 -0
- digitalhub/entities/secret/_base/spec.py +4 -0
- digitalhub/entities/secret/_base/status.py +4 -0
- digitalhub/entities/secret/crud.py +4 -0
- digitalhub/entities/task/__init__.py +3 -0
- digitalhub/entities/task/_base/__init__.py +3 -0
- digitalhub/entities/task/_base/builder.py +4 -0
- digitalhub/entities/task/_base/entity.py +4 -0
- digitalhub/entities/task/_base/models.py +16 -3
- digitalhub/entities/task/_base/spec.py +4 -0
- digitalhub/entities/task/_base/status.py +4 -0
- digitalhub/entities/task/_base/utils.py +4 -0
- digitalhub/entities/task/crud.py +4 -0
- digitalhub/entities/trigger/__init__.py +3 -0
- digitalhub/entities/trigger/_base/__init__.py +3 -0
- digitalhub/entities/trigger/_base/builder.py +4 -0
- digitalhub/entities/trigger/_base/entity.py +15 -0
- digitalhub/entities/trigger/_base/spec.py +4 -0
- digitalhub/entities/trigger/_base/status.py +4 -0
- digitalhub/entities/trigger/crud.py +4 -0
- digitalhub/entities/trigger/lifecycle/__init__.py +3 -0
- digitalhub/entities/trigger/lifecycle/builder.py +4 -0
- digitalhub/entities/trigger/lifecycle/entity.py +4 -0
- digitalhub/entities/trigger/lifecycle/spec.py +4 -0
- digitalhub/entities/trigger/lifecycle/status.py +4 -0
- digitalhub/entities/trigger/scheduler/__init__.py +3 -0
- digitalhub/entities/trigger/scheduler/builder.py +4 -0
- digitalhub/entities/trigger/scheduler/entity.py +4 -0
- digitalhub/entities/trigger/scheduler/spec.py +4 -0
- digitalhub/entities/trigger/scheduler/status.py +4 -0
- digitalhub/entities/workflow/__init__.py +3 -0
- digitalhub/entities/workflow/_base/__init__.py +3 -0
- digitalhub/entities/workflow/_base/builder.py +4 -0
- digitalhub/entities/workflow/_base/entity.py +4 -0
- digitalhub/entities/workflow/_base/spec.py +4 -0
- digitalhub/entities/workflow/_base/status.py +4 -0
- digitalhub/entities/workflow/crud.py +4 -0
- digitalhub/factory/__init__.py +3 -0
- digitalhub/factory/factory.py +29 -3
- digitalhub/factory/utils.py +15 -3
- digitalhub/runtimes/__init__.py +3 -0
- digitalhub/runtimes/_base.py +5 -1
- digitalhub/runtimes/builder.py +22 -1
- digitalhub/runtimes/enums.py +4 -0
- digitalhub/stores/__init__.py +3 -0
- digitalhub/stores/client/__init__.py +15 -0
- digitalhub/stores/client/_base/__init__.py +3 -0
- digitalhub/stores/client/_base/api_builder.py +18 -0
- digitalhub/stores/client/_base/client.py +97 -0
- digitalhub/stores/client/_base/key_builder.py +32 -0
- digitalhub/stores/client/_base/params_builder.py +18 -0
- digitalhub/stores/client/api.py +14 -5
- digitalhub/stores/client/builder.py +7 -1
- digitalhub/stores/client/dhcore/__init__.py +3 -0
- digitalhub/stores/client/dhcore/api_builder.py +21 -0
- digitalhub/stores/client/dhcore/client.py +329 -70
- digitalhub/stores/client/dhcore/configurator.py +489 -193
- digitalhub/stores/client/dhcore/enums.py +7 -0
- digitalhub/stores/client/dhcore/error_parser.py +39 -1
- digitalhub/stores/client/dhcore/key_builder.py +4 -0
- digitalhub/stores/client/dhcore/models.py +4 -0
- digitalhub/stores/client/dhcore/params_builder.py +117 -17
- digitalhub/stores/client/dhcore/utils.py +44 -22
- digitalhub/stores/client/local/__init__.py +3 -0
- digitalhub/stores/client/local/api_builder.py +21 -0
- digitalhub/stores/client/local/client.py +10 -8
- digitalhub/stores/client/local/enums.py +4 -0
- digitalhub/stores/client/local/key_builder.py +4 -0
- digitalhub/stores/client/local/params_builder.py +4 -0
- digitalhub/stores/credentials/__init__.py +3 -0
- digitalhub/stores/credentials/api.py +35 -0
- digitalhub/stores/credentials/configurator.py +210 -0
- digitalhub/stores/credentials/enums.py +68 -0
- digitalhub/stores/credentials/handler.py +176 -0
- digitalhub/stores/credentials/ini_module.py +164 -0
- digitalhub/stores/credentials/store.py +81 -0
- digitalhub/stores/data/__init__.py +3 -0
- digitalhub/stores/data/_base/__init__.py +3 -0
- digitalhub/stores/data/_base/store.py +31 -9
- digitalhub/stores/data/api.py +53 -9
- digitalhub/stores/data/builder.py +94 -41
- digitalhub/stores/data/enums.py +4 -0
- digitalhub/stores/data/local/__init__.py +3 -0
- digitalhub/stores/data/local/store.py +8 -7
- digitalhub/stores/data/remote/__init__.py +3 -0
- digitalhub/stores/data/remote/store.py +8 -7
- digitalhub/stores/data/s3/__init__.py +3 -0
- digitalhub/stores/data/s3/configurator.py +69 -80
- digitalhub/stores/data/s3/store.py +73 -81
- digitalhub/stores/data/s3/utils.py +14 -10
- digitalhub/stores/data/sql/__init__.py +3 -0
- digitalhub/stores/data/sql/configurator.py +80 -73
- digitalhub/stores/data/sql/store.py +195 -102
- digitalhub/stores/readers/__init__.py +3 -0
- digitalhub/stores/readers/data/__init__.py +3 -0
- digitalhub/stores/readers/data/_base/__init__.py +3 -0
- digitalhub/stores/readers/data/_base/builder.py +4 -0
- digitalhub/stores/readers/data/_base/reader.py +4 -0
- digitalhub/stores/readers/data/api.py +4 -0
- digitalhub/stores/readers/data/factory.py +4 -0
- digitalhub/stores/readers/data/pandas/__init__.py +3 -0
- digitalhub/stores/readers/data/pandas/builder.py +4 -0
- digitalhub/stores/readers/data/pandas/reader.py +4 -0
- digitalhub/stores/readers/query/__init__.py +3 -0
- digitalhub/utils/__init__.py +3 -0
- digitalhub/utils/enums.py +4 -0
- digitalhub/utils/exceptions.py +10 -0
- digitalhub/utils/file_utils.py +57 -30
- digitalhub/utils/generic_utils.py +45 -33
- digitalhub/utils/git_utils.py +28 -14
- digitalhub/utils/io_utils.py +23 -18
- digitalhub/utils/logger.py +4 -0
- digitalhub/utils/types.py +4 -0
- digitalhub/utils/uri_utils.py +35 -31
- digitalhub-0.13.0.dist-info/METADATA +301 -0
- digitalhub-0.13.0.dist-info/RECORD +259 -0
- digitalhub-0.13.0.dist-info/licenses/AUTHORS +5 -0
- digitalhub-0.13.0.dist-info/licenses/LICENSE +201 -0
- digitalhub/entities/_commons/types.py +0 -5
- digitalhub/stores/configurator/__init__.py +0 -0
- digitalhub/stores/configurator/api.py +0 -31
- digitalhub/stores/configurator/configurator.py +0 -198
- digitalhub/stores/configurator/credentials_store.py +0 -65
- digitalhub/stores/configurator/enums.py +0 -21
- digitalhub/stores/configurator/ini_module.py +0 -128
- digitalhub/stores/data/s3/enums.py +0 -16
- digitalhub/stores/data/sql/enums.py +0 -16
- digitalhub/stores/data/utils.py +0 -34
- digitalhub-0.11.0b7.dist-info/METADATA +0 -259
- digitalhub-0.11.0b7.dist-info/RECORD +0 -261
- digitalhub-0.11.0b7.dist-info/licenses/LICENSE.txt +0 -216
- {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
|
-
|
|
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
|
|
43
|
+
Method to download material entity from storage.
|
|
37
44
|
"""
|
|
38
45
|
|
|
39
46
|
@abstractmethod
|
|
40
|
-
def upload(
|
|
47
|
+
def upload(
|
|
48
|
+
self,
|
|
49
|
+
src: SourcesOrListOfSources,
|
|
50
|
+
dst: str,
|
|
51
|
+
) -> list[tuple[str, str]]:
|
|
41
52
|
"""
|
|
42
|
-
Method to upload
|
|
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(
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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:
|
digitalhub/stores/data/api.py
CHANGED
|
@@ -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
|
|
18
|
+
def get_default_store(project: str) -> str:
|
|
12
19
|
"""
|
|
13
|
-
|
|
20
|
+
Returns the default store URI for a given project.
|
|
14
21
|
|
|
15
22
|
Parameters
|
|
16
|
-
|
|
23
|
+
----------
|
|
17
24
|
project : str
|
|
18
|
-
|
|
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
|
-
|
|
71
|
+
The store instance corresponding to the URI.
|
|
28
72
|
"""
|
|
29
|
-
return store_builder.get(
|
|
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
|
-
|
|
23
|
+
class StoreInfo:
|
|
17
24
|
"""
|
|
18
|
-
|
|
25
|
+
Container for store class and configurator information.
|
|
19
26
|
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
|
64
|
+
def register(
|
|
65
|
+
self,
|
|
66
|
+
store_type: str,
|
|
67
|
+
store: Store,
|
|
68
|
+
configurator: Configurator | None = None,
|
|
69
|
+
) -> None:
|
|
50
70
|
"""
|
|
51
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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,
|
|
100
|
+
def get(self, uri: str) -> Store:
|
|
69
101
|
"""
|
|
70
|
-
Get a store instance
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
self.
|
|
90
|
-
|
|
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)
|
digitalhub/stores/data/enums.py
CHANGED
|
@@ -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
|
-
|
|
37
|
-
|
|
39
|
+
src : str
|
|
40
|
+
Path of the material entity.
|
|
38
41
|
dst : str
|
|
39
|
-
The destination of the
|
|
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
|
|
49
|
+
Destination path of the downloaded files.
|
|
49
50
|
"""
|
|
50
51
|
raise StoreError("Local store does not support download.")
|
|
51
52
|
|
|
@@ -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
|
-
|
|
36
|
-
|
|
38
|
+
src : str
|
|
39
|
+
Path of the material entity.
|
|
37
40
|
dst : str
|
|
38
|
-
The destination of the
|
|
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
|
|
48
|
+
Destination path of the downloaded files.
|
|
48
49
|
"""
|
|
49
50
|
# Handle destination
|
|
50
51
|
if dst is None:
|
|
@@ -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.
|
|
6
|
-
from digitalhub.stores.configurator
|
|
7
|
-
from digitalhub.stores.
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
46
|
+
def load_env_vars(self) -> None:
|
|
33
47
|
"""
|
|
34
|
-
|
|
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
|
-
|
|
45
|
-
The credentials.
|
|
52
|
+
None
|
|
46
53
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
57
|
+
def load_file_vars(self) -> None:
|
|
65
58
|
"""
|
|
66
|
-
|
|
59
|
+
Loads the credentials from a file.
|
|
67
60
|
|
|
68
61
|
Returns
|
|
69
62
|
-------
|
|
70
|
-
|
|
71
|
-
The credentials.
|
|
63
|
+
None
|
|
72
64
|
"""
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
68
|
+
def get_client_config(self) -> dict:
|
|
80
69
|
"""
|
|
81
|
-
|
|
70
|
+
Gets S3 credentials (access key, secret key, session token, and other config).
|
|
82
71
|
|
|
83
72
|
Returns
|
|
84
73
|
-------
|
|
85
74
|
dict
|
|
86
|
-
|
|
75
|
+
Dictionary containing S3 credentials and configuration.
|
|
87
76
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
None
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _is_expired(timestamp: str | None) -> bool:
|
|
106
96
|
"""
|
|
107
|
-
|
|
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
|
-
|
|
118
|
-
|
|
101
|
+
timestamp : str or None
|
|
102
|
+
Timestamp string in the format 'YYYY-MM-DDTHH:MM:SSZ'.
|
|
119
103
|
|
|
120
104
|
Returns
|
|
121
105
|
-------
|
|
122
|
-
|
|
106
|
+
bool
|
|
107
|
+
True if the given timestamp is later than the current UTC time,
|
|
108
|
+
otherwise False.
|
|
123
109
|
"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|