digitalhub 0.9.2__py3-none-any.whl → 0.10.0b1__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 +2 -3
- digitalhub/client/_base/client.py +3 -2
- digitalhub/client/dhcore/api_builder.py +5 -0
- digitalhub/client/dhcore/client.py +27 -399
- digitalhub/client/dhcore/configurator.py +339 -0
- digitalhub/client/dhcore/error_parser.py +107 -0
- digitalhub/client/dhcore/models.py +13 -23
- digitalhub/client/dhcore/utils.py +4 -44
- digitalhub/client/local/api_builder.py +9 -17
- digitalhub/client/local/client.py +12 -2
- digitalhub/client/local/enums.py +11 -0
- digitalhub/configurator/api.py +31 -0
- digitalhub/configurator/configurator.py +194 -0
- digitalhub/configurator/credentials_store.py +65 -0
- digitalhub/configurator/ini_module.py +74 -0
- digitalhub/entities/_base/_base/entity.py +2 -2
- digitalhub/entities/_base/material/entity.py +19 -6
- digitalhub/entities/_base/material/utils.py +2 -2
- digitalhub/entities/_commons/enums.py +1 -0
- digitalhub/entities/_commons/models.py +9 -0
- digitalhub/entities/_commons/utils.py +25 -0
- digitalhub/entities/_operations/processor.py +103 -107
- digitalhub/entities/artifact/crud.py +3 -3
- digitalhub/entities/artifact/utils.py +1 -1
- digitalhub/entities/dataitem/_base/entity.py +2 -2
- digitalhub/entities/dataitem/crud.py +3 -3
- digitalhub/entities/dataitem/table/entity.py +2 -2
- digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +43 -51
- digitalhub/entities/dataitem/utils.py +6 -3
- digitalhub/entities/model/_base/entity.py +172 -0
- digitalhub/entities/model/_base/spec.py +0 -10
- digitalhub/entities/model/_base/status.py +10 -0
- digitalhub/entities/model/crud.py +3 -3
- digitalhub/entities/model/huggingface/spec.py +6 -3
- digitalhub/entities/model/mlflow/models.py +2 -2
- digitalhub/entities/model/mlflow/spec.py +1 -3
- digitalhub/entities/model/mlflow/utils.py +44 -5
- digitalhub/entities/run/_base/entity.py +149 -0
- digitalhub/entities/run/_base/status.py +12 -0
- digitalhub/entities/task/_base/spec.py +2 -0
- digitalhub/entities/task/crud.py +4 -0
- digitalhub/readers/{_commons → pandas}/enums.py +4 -0
- digitalhub/readers/pandas/reader.py +58 -10
- digitalhub/stores/_base/store.py +1 -49
- digitalhub/stores/api.py +8 -33
- digitalhub/stores/builder.py +44 -161
- digitalhub/stores/local/store.py +4 -18
- digitalhub/stores/remote/store.py +3 -10
- digitalhub/stores/s3/configurator.py +107 -0
- digitalhub/stores/s3/enums.py +17 -0
- digitalhub/stores/s3/models.py +21 -0
- digitalhub/stores/s3/store.py +8 -28
- digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
- digitalhub/stores/sql/configurator.py +88 -0
- digitalhub/stores/sql/enums.py +16 -0
- digitalhub/stores/sql/models.py +24 -0
- digitalhub/stores/sql/store.py +14 -57
- digitalhub/utils/exceptions.py +6 -0
- digitalhub/utils/generic_utils.py +9 -8
- digitalhub/utils/uri_utils.py +1 -1
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/METADATA +5 -6
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/RECORD +67 -54
- test/local/imports/test_imports.py +0 -1
- digitalhub/client/dhcore/env.py +0 -23
- /digitalhub/{readers/_commons → configurator}/__init__.py +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/WHEEL +0 -0
- {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b1.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
from io import BytesIO
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
@@ -7,10 +8,11 @@ import numpy as np
|
|
|
7
8
|
import pandas as pd
|
|
8
9
|
from pandas.errors import ParserError
|
|
9
10
|
|
|
11
|
+
from digitalhub.entities.dataitem.table.utils import check_preview_size, finalize_preview, prepare_data, prepare_preview
|
|
10
12
|
from digitalhub.readers._base.reader import DataframeReader
|
|
11
|
-
from digitalhub.readers.
|
|
12
|
-
from digitalhub.utils.data_utils import build_data_preview, get_data_preview
|
|
13
|
+
from digitalhub.readers.pandas.enums import Extensions
|
|
13
14
|
from digitalhub.utils.exceptions import ReaderError
|
|
15
|
+
from digitalhub.utils.generic_utils import CustomJsonEncoder
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class DataframeReaderPandas(DataframeReader):
|
|
@@ -44,7 +46,11 @@ class DataframeReaderPandas(DataframeReader):
|
|
|
44
46
|
method = pd.read_csv
|
|
45
47
|
elif extension == Extensions.PARQUET.value:
|
|
46
48
|
method = pd.read_parquet
|
|
47
|
-
elif extension == Extensions.
|
|
49
|
+
elif extension == Extensions.JSON.value:
|
|
50
|
+
method = pd.read_json
|
|
51
|
+
elif extension in (Extensions.EXCEL.value, Extensions.EXCEL_OLD.value):
|
|
52
|
+
method = pd.read_excel
|
|
53
|
+
elif extension in (Extensions.TXT.value, Extensions.FILE.value):
|
|
48
54
|
try:
|
|
49
55
|
return self.read_df(path, Extensions.CSV.value, **kwargs)
|
|
50
56
|
except ParserError:
|
|
@@ -196,7 +202,7 @@ class DataframeReaderPandas(DataframeReader):
|
|
|
196
202
|
return schema
|
|
197
203
|
|
|
198
204
|
@staticmethod
|
|
199
|
-
def get_preview(df: pd.DataFrame) ->
|
|
205
|
+
def get_preview(df: pd.DataFrame) -> dict:
|
|
200
206
|
"""
|
|
201
207
|
Get preview.
|
|
202
208
|
|
|
@@ -211,9 +217,51 @@ class DataframeReaderPandas(DataframeReader):
|
|
|
211
217
|
The preview.
|
|
212
218
|
"""
|
|
213
219
|
columns = [str(col) for col, _ in df.dtypes.items()]
|
|
214
|
-
head = df.head(10)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
preview =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
+
head = df.head(10).replace({np.nan: None})
|
|
221
|
+
data = head.values.tolist()
|
|
222
|
+
prepared_data = prepare_data(data)
|
|
223
|
+
preview = prepare_preview(columns, prepared_data)
|
|
224
|
+
finalizes = finalize_preview(preview, df.shape[0])
|
|
225
|
+
serialized = _serialize_deserialize_preview(finalizes)
|
|
226
|
+
return check_preview_size(serialized)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class PandasJsonEncoder(CustomJsonEncoder):
|
|
230
|
+
"""
|
|
231
|
+
JSON pd.Timestamp to ISO format serializer.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def default(self, obj: Any) -> Any:
|
|
235
|
+
"""
|
|
236
|
+
Pandas datetime to ISO format serializer.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
obj : Any
|
|
241
|
+
The object to serialize.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
Any
|
|
246
|
+
The serialized object.
|
|
247
|
+
"""
|
|
248
|
+
if isinstance(obj, pd.Timestamp):
|
|
249
|
+
return obj.isoformat()
|
|
250
|
+
return super().default(obj)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _serialize_deserialize_preview(preview: dict) -> dict:
|
|
254
|
+
"""
|
|
255
|
+
Serialize and deserialize preview.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
preview : dict
|
|
260
|
+
The preview.
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
dict
|
|
265
|
+
The serialized preview.
|
|
266
|
+
"""
|
|
267
|
+
return json.loads(json.dumps(preview, cls=PandasJsonEncoder))
|
digitalhub/stores/_base/store.py
CHANGED
|
@@ -5,11 +5,9 @@ from pathlib import Path
|
|
|
5
5
|
from tempfile import mkdtemp
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from pydantic import BaseModel, ConfigDict
|
|
9
|
-
|
|
10
8
|
from digitalhub.readers.api import get_reader_by_engine
|
|
11
9
|
from digitalhub.utils.exceptions import StoreError
|
|
12
|
-
from digitalhub.utils.uri_utils import
|
|
10
|
+
from digitalhub.utils.uri_utils import has_local_scheme
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class Store:
|
|
@@ -17,24 +15,6 @@ class Store:
|
|
|
17
15
|
Store abstract class.
|
|
18
16
|
"""
|
|
19
17
|
|
|
20
|
-
def __init__(self, name: str, store_type: str) -> None:
|
|
21
|
-
"""
|
|
22
|
-
Constructor.
|
|
23
|
-
|
|
24
|
-
Parameters
|
|
25
|
-
----------
|
|
26
|
-
name : str
|
|
27
|
-
Store name.
|
|
28
|
-
store_type : str
|
|
29
|
-
Store type. Used to choose the right store implementation.
|
|
30
|
-
|
|
31
|
-
Returns
|
|
32
|
-
-------
|
|
33
|
-
None
|
|
34
|
-
"""
|
|
35
|
-
self.name = name
|
|
36
|
-
self.type = store_type
|
|
37
|
-
|
|
38
18
|
##############################
|
|
39
19
|
# I/O methods
|
|
40
20
|
##############################
|
|
@@ -207,31 +187,3 @@ class Store:
|
|
|
207
187
|
"""
|
|
208
188
|
tmpdir = mkdtemp()
|
|
209
189
|
return Path(tmpdir)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class StoreConfig(BaseModel):
|
|
213
|
-
"""
|
|
214
|
-
Store configuration base class.
|
|
215
|
-
"""
|
|
216
|
-
|
|
217
|
-
model_config = ConfigDict(use_enum_values=True)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
class StoreParameters(BaseModel):
|
|
221
|
-
"""
|
|
222
|
-
Store configuration class.
|
|
223
|
-
"""
|
|
224
|
-
|
|
225
|
-
model_config = ConfigDict(use_enum_values=True)
|
|
226
|
-
|
|
227
|
-
name: str
|
|
228
|
-
"""Store id."""
|
|
229
|
-
|
|
230
|
-
type: SchemeCategory
|
|
231
|
-
"""Store type to instantiate."""
|
|
232
|
-
|
|
233
|
-
config: StoreConfig = None
|
|
234
|
-
"""Configuration for the store."""
|
|
235
|
-
|
|
236
|
-
is_default: bool = False
|
|
237
|
-
"""Flag to determine if the store is the default one."""
|
digitalhub/stores/api.py
CHANGED
|
@@ -5,50 +5,25 @@ import typing
|
|
|
5
5
|
from digitalhub.stores.builder import store_builder
|
|
6
6
|
|
|
7
7
|
if typing.TYPE_CHECKING:
|
|
8
|
-
from digitalhub.stores._base.store import Store
|
|
8
|
+
from digitalhub.stores._base.store import Store
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def
|
|
11
|
+
def get_store(project: str, uri: str, config: dict | None = None) -> Store:
|
|
12
12
|
"""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Parameters
|
|
16
|
-
----------
|
|
17
|
-
store_cfg : StoreParameters
|
|
18
|
-
Store configuration.
|
|
19
|
-
|
|
20
|
-
Returns
|
|
21
|
-
-------
|
|
22
|
-
None
|
|
23
|
-
"""
|
|
24
|
-
store_builder.build(store_cfg)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_store(uri: str) -> Store:
|
|
28
|
-
"""
|
|
29
|
-
Get store instance by uri.
|
|
13
|
+
Get store instance by URI.
|
|
30
14
|
|
|
31
15
|
Parameters
|
|
32
16
|
---------
|
|
17
|
+
project : str
|
|
18
|
+
Project name.
|
|
33
19
|
uri : str
|
|
34
20
|
URI to parse.
|
|
21
|
+
config : dict
|
|
22
|
+
Store configuration.
|
|
35
23
|
|
|
36
24
|
Returns
|
|
37
25
|
-------
|
|
38
26
|
Store
|
|
39
27
|
Store instance.
|
|
40
28
|
"""
|
|
41
|
-
return store_builder.get(uri)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def get_default_store() -> Store:
|
|
45
|
-
"""
|
|
46
|
-
Get the default store instance. The default store is the one that
|
|
47
|
-
can persist artifacts and dataitems.
|
|
48
|
-
|
|
49
|
-
Returns
|
|
50
|
-
-------
|
|
51
|
-
Store
|
|
52
|
-
Default store instance.
|
|
53
|
-
"""
|
|
54
|
-
return store_builder.default()
|
|
29
|
+
return store_builder.get(uri, config)
|
digitalhub/stores/builder.py
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import typing
|
|
5
4
|
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from digitalhub.stores.
|
|
9
|
-
from digitalhub.stores.
|
|
10
|
-
from digitalhub.stores.
|
|
11
|
-
from digitalhub.stores.s3.store import S3Store, S3StoreConfig
|
|
12
|
-
from digitalhub.stores.sql.store import SqlStore, SQLStoreConfig
|
|
13
|
-
from digitalhub.utils.exceptions import StoreError
|
|
5
|
+
from digitalhub.configurator.api import get_current_env
|
|
6
|
+
from digitalhub.stores.local.store import LocalStore
|
|
7
|
+
from digitalhub.stores.remote.store import RemoteStore
|
|
8
|
+
from digitalhub.stores.s3.store import S3Store
|
|
9
|
+
from digitalhub.stores.sql.store import SqlStore
|
|
14
10
|
from digitalhub.utils.uri_utils import SchemeCategory, map_uri_scheme
|
|
15
11
|
|
|
16
12
|
if typing.TYPE_CHECKING:
|
|
17
13
|
from digitalhub.stores._base.store import Store
|
|
18
14
|
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
def _get_class_from_type(type: str) -> Store:
|
|
17
|
+
"""
|
|
18
|
+
Get a store class from its type.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
type : str
|
|
23
|
+
Store type.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
Store
|
|
28
|
+
The store class.
|
|
29
|
+
"""
|
|
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}")
|
|
26
39
|
|
|
27
40
|
|
|
28
41
|
class StoreBuilder:
|
|
@@ -32,27 +45,28 @@ class StoreBuilder:
|
|
|
32
45
|
|
|
33
46
|
def __init__(self) -> None:
|
|
34
47
|
self._instances: dict[str, Store] = {}
|
|
35
|
-
self._default: Store | None = None
|
|
36
|
-
self._def_scheme = "s3"
|
|
37
48
|
|
|
38
|
-
def build(self,
|
|
49
|
+
def build(self, store_type: str, config: dict | None = None) -> None:
|
|
39
50
|
"""
|
|
40
51
|
Build a store instance and register it.
|
|
41
|
-
It overrides any existing instance.
|
|
42
52
|
|
|
43
53
|
Parameters
|
|
44
54
|
----------
|
|
45
|
-
|
|
55
|
+
store_type : str
|
|
56
|
+
Store type.
|
|
57
|
+
config : dict
|
|
46
58
|
Store configuration.
|
|
47
59
|
|
|
48
60
|
Returns
|
|
49
61
|
-------
|
|
50
62
|
None
|
|
51
63
|
"""
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
env = get_current_env()
|
|
65
|
+
if env not in self._instances:
|
|
66
|
+
self._instances[env] = {}
|
|
67
|
+
self._instances[env][store_type] = _get_class_from_type(store_type)(config)
|
|
54
68
|
|
|
55
|
-
def get(self, uri: str) -> Store:
|
|
69
|
+
def get(self, uri: str, config: dict | None = None) -> Store:
|
|
56
70
|
"""
|
|
57
71
|
Get a store instance by URI.
|
|
58
72
|
|
|
@@ -60,152 +74,21 @@ class StoreBuilder:
|
|
|
60
74
|
----------
|
|
61
75
|
uri : str
|
|
62
76
|
URI to parse.
|
|
63
|
-
|
|
64
|
-
Returns
|
|
65
|
-
-------
|
|
66
|
-
Store
|
|
67
|
-
The store instance.
|
|
68
|
-
"""
|
|
69
|
-
scheme = map_uri_scheme(uri)
|
|
70
|
-
if scheme not in self._instances:
|
|
71
|
-
store_cfg = get_env_store_config(scheme)
|
|
72
|
-
self._instances[scheme] = self.build_store(store_cfg)
|
|
73
|
-
return self._instances[scheme]
|
|
74
|
-
|
|
75
|
-
def default(self) -> Store:
|
|
76
|
-
"""
|
|
77
|
-
Get the default store instance.
|
|
78
|
-
|
|
79
|
-
Returns
|
|
80
|
-
-------
|
|
81
|
-
Store
|
|
82
|
-
The default store instance.
|
|
83
|
-
|
|
84
|
-
Raises
|
|
85
|
-
------
|
|
86
|
-
StoreError
|
|
87
|
-
If no default store is set.
|
|
88
|
-
"""
|
|
89
|
-
if self._default is None:
|
|
90
|
-
store_cfg = get_env_store_config(self._def_scheme)
|
|
91
|
-
self._default = self.build_store(store_cfg)
|
|
92
|
-
return self._default
|
|
93
|
-
|
|
94
|
-
def build_store(self, cfg: StoreParameters) -> Store:
|
|
95
|
-
"""
|
|
96
|
-
Build a store instance.
|
|
97
|
-
|
|
98
|
-
Parameters
|
|
99
|
-
----------
|
|
100
|
-
cfg : StoreParameters
|
|
77
|
+
config : dict
|
|
101
78
|
Store configuration.
|
|
102
79
|
|
|
103
80
|
Returns
|
|
104
81
|
-------
|
|
105
82
|
Store
|
|
106
83
|
The store instance.
|
|
107
|
-
|
|
108
|
-
Raises
|
|
109
|
-
------
|
|
110
|
-
NotImplementedError
|
|
111
|
-
If the store type is not implemented.
|
|
112
84
|
"""
|
|
85
|
+
env = get_current_env()
|
|
86
|
+
store_type = map_uri_scheme(uri)
|
|
113
87
|
try:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return
|
|
118
|
-
except KeyError as e:
|
|
119
|
-
raise NotImplementedError from e
|
|
120
|
-
|
|
121
|
-
@staticmethod
|
|
122
|
-
def _check_config(config: StoreParameters | dict) -> StoreParameters:
|
|
123
|
-
"""
|
|
124
|
-
Check the store configuration validity.
|
|
125
|
-
|
|
126
|
-
Parameters
|
|
127
|
-
----------
|
|
128
|
-
config : StoreParameters | dict
|
|
129
|
-
The store configuration.
|
|
130
|
-
|
|
131
|
-
Returns
|
|
132
|
-
-------
|
|
133
|
-
StoreParameters
|
|
134
|
-
The store configuration.
|
|
135
|
-
|
|
136
|
-
Raises
|
|
137
|
-
------
|
|
138
|
-
TypeError
|
|
139
|
-
If the config parameter is not a StoreParameters instance or a well-formed dictionary.
|
|
140
|
-
"""
|
|
141
|
-
if not isinstance(config, StoreParameters):
|
|
142
|
-
try:
|
|
143
|
-
return StoreParameters(**config)
|
|
144
|
-
except TypeError as e:
|
|
145
|
-
raise StoreError("Invalid store configuration type.") from e
|
|
146
|
-
except ValidationError as e:
|
|
147
|
-
raise StoreError("Malformed store configuration parameters.") from e
|
|
148
|
-
return config
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def get_env_store_config(scheme: str) -> StoreParameters:
|
|
152
|
-
"""
|
|
153
|
-
Get a store configuration from the environment.
|
|
154
|
-
|
|
155
|
-
Parameters
|
|
156
|
-
----------
|
|
157
|
-
scheme : str
|
|
158
|
-
URI scheme.
|
|
159
|
-
|
|
160
|
-
Returns
|
|
161
|
-
-------
|
|
162
|
-
StoreParameters
|
|
163
|
-
The store configuration based on the scheme.
|
|
164
|
-
|
|
165
|
-
Raises
|
|
166
|
-
------
|
|
167
|
-
ValueError
|
|
168
|
-
If the scheme is not supported.
|
|
169
|
-
"""
|
|
170
|
-
if scheme == SchemeCategory.S3.value:
|
|
171
|
-
return StoreParameters(
|
|
172
|
-
name="s3",
|
|
173
|
-
type="s3",
|
|
174
|
-
config=S3StoreConfig(
|
|
175
|
-
endpoint_url=os.getenv("S3_ENDPOINT_URL"), # type: ignore
|
|
176
|
-
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), # type: ignore
|
|
177
|
-
aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), # type: ignore
|
|
178
|
-
bucket_name=os.getenv("S3_BUCKET_NAME"), # type: ignore
|
|
179
|
-
),
|
|
180
|
-
)
|
|
181
|
-
if scheme == SchemeCategory.SQL.value:
|
|
182
|
-
return StoreParameters(
|
|
183
|
-
name="sql",
|
|
184
|
-
type="sql",
|
|
185
|
-
config=SQLStoreConfig(
|
|
186
|
-
host=os.getenv("POSTGRES_HOST"), # type: ignore
|
|
187
|
-
port=os.getenv("POSTGRES_PORT"), # type: ignore
|
|
188
|
-
user=os.getenv("POSTGRES_USER"), # type: ignore
|
|
189
|
-
password=os.getenv("POSTGRES_PASSWORD"), # type: ignore
|
|
190
|
-
database=os.getenv("POSTGRES_DATABASE"), # type: ignore
|
|
191
|
-
pg_schema=os.getenv("POSTGRES_SCHEMA"), # type: ignore
|
|
192
|
-
),
|
|
193
|
-
)
|
|
194
|
-
if scheme == SchemeCategory.REMOTE.value:
|
|
195
|
-
return StoreParameters(
|
|
196
|
-
name="remote",
|
|
197
|
-
type="remote",
|
|
198
|
-
config=RemoteStoreConfig(),
|
|
199
|
-
)
|
|
200
|
-
if scheme == SchemeCategory.LOCAL.value:
|
|
201
|
-
return StoreParameters(
|
|
202
|
-
name="local",
|
|
203
|
-
type="local",
|
|
204
|
-
config=LocalStoreConfig(
|
|
205
|
-
path="tempsdk",
|
|
206
|
-
),
|
|
207
|
-
)
|
|
208
|
-
raise ValueError(f"Unsupported scheme {scheme}")
|
|
88
|
+
return self._instances[env][store_type]
|
|
89
|
+
except KeyError:
|
|
90
|
+
self.build(store_type, config)
|
|
91
|
+
return self._instances[env][store_type]
|
|
209
92
|
|
|
210
93
|
|
|
211
94
|
store_builder = StoreBuilder()
|
digitalhub/stores/local/store.py
CHANGED
|
@@ -5,29 +5,19 @@ from pathlib import Path
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from digitalhub.readers.api import get_reader_by_object
|
|
8
|
-
from digitalhub.stores._base.store import Store
|
|
8
|
+
from digitalhub.stores._base.store import Store
|
|
9
9
|
from digitalhub.utils.exceptions import StoreError
|
|
10
10
|
from digitalhub.utils.file_utils import get_file_info_from_local
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class LocalStoreConfig(StoreConfig):
|
|
14
|
-
"""
|
|
15
|
-
Local store configuration class.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
path: str
|
|
19
|
-
"""Local path."""
|
|
20
|
-
|
|
21
|
-
|
|
22
13
|
class LocalStore(Store):
|
|
23
14
|
"""
|
|
24
15
|
Local store class. It implements the Store interface and provides methods to fetch and persist
|
|
25
16
|
artifacts on local filesystem based storage.
|
|
26
17
|
"""
|
|
27
18
|
|
|
28
|
-
def __init__(self,
|
|
29
|
-
super().__init__(
|
|
30
|
-
self.config = config
|
|
19
|
+
def __init__(self, config: dict | None = None) -> None:
|
|
20
|
+
super().__init__()
|
|
31
21
|
|
|
32
22
|
##############################
|
|
33
23
|
# I/O methods
|
|
@@ -218,13 +208,9 @@ class LocalStore(Store):
|
|
|
218
208
|
str
|
|
219
209
|
Path of written dataframe.
|
|
220
210
|
"""
|
|
221
|
-
self.
|
|
222
|
-
self._validate_extension(Path(dst).suffix.removeprefix("."))
|
|
223
|
-
|
|
224
|
-
# Write dataframe
|
|
211
|
+
self._check_local_dst(dst)
|
|
225
212
|
reader = get_reader_by_object(df)
|
|
226
213
|
reader.write_df(df, dst, extension=extension, **kwargs)
|
|
227
|
-
|
|
228
214
|
return dst
|
|
229
215
|
|
|
230
216
|
##############################
|
|
@@ -5,25 +5,18 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
import requests
|
|
7
7
|
|
|
8
|
-
from digitalhub.stores._base.store import Store
|
|
8
|
+
from digitalhub.stores._base.store import Store
|
|
9
9
|
from digitalhub.utils.exceptions import StoreError
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class RemoteStoreConfig(StoreConfig):
|
|
13
|
-
"""
|
|
14
|
-
Remote store configuration class.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
|
|
18
12
|
class RemoteStore(Store):
|
|
19
13
|
"""
|
|
20
14
|
HTTP store class. It implements the Store interface and provides methods to fetch
|
|
21
15
|
artifacts from remote HTTP based storage.
|
|
22
16
|
"""
|
|
23
17
|
|
|
24
|
-
def __init__(self,
|
|
25
|
-
super().__init__(
|
|
26
|
-
self.config = config
|
|
18
|
+
def __init__(self, config: dict | None = None) -> None:
|
|
19
|
+
super().__init__()
|
|
27
20
|
|
|
28
21
|
##############################
|
|
29
22
|
# I/O methods
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from botocore.config import Config
|
|
4
|
+
|
|
5
|
+
from digitalhub.configurator.configurator import configurator
|
|
6
|
+
from digitalhub.stores.s3.enums import S3StoreEnv
|
|
7
|
+
from digitalhub.stores.s3.models import S3StoreConfig
|
|
8
|
+
from digitalhub.utils.exceptions import StoreError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class S3StoreConfigurator:
|
|
12
|
+
"""
|
|
13
|
+
Configure the store by getting the credentials from user
|
|
14
|
+
provided config or from environment.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: dict | None = None) -> None:
|
|
18
|
+
self.configure(config)
|
|
19
|
+
|
|
20
|
+
##############################
|
|
21
|
+
# Configuration methods
|
|
22
|
+
##############################
|
|
23
|
+
|
|
24
|
+
def configure(self, config: dict | None = None) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Configure the store by getting the credentials from user
|
|
27
|
+
provided config or from environment.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
config : dict
|
|
32
|
+
Configuration dictionary.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
None
|
|
37
|
+
"""
|
|
38
|
+
# Validate config
|
|
39
|
+
if config is None:
|
|
40
|
+
self._get_config()
|
|
41
|
+
else:
|
|
42
|
+
config = S3StoreConfig(**config)
|
|
43
|
+
for pair in [
|
|
44
|
+
(S3StoreEnv.ENDPOINT_URL.value, config.endpoint),
|
|
45
|
+
(S3StoreEnv.ACCESS_KEY_ID.value, config.access_key),
|
|
46
|
+
(S3StoreEnv.SECRET_ACCESS_KEY.value, config.secret_key),
|
|
47
|
+
(S3StoreEnv.SESSION_TOKEN.value, config.session_token),
|
|
48
|
+
(S3StoreEnv.BUCKET_NAME.value, config.bucket_name),
|
|
49
|
+
(S3StoreEnv.REGION.value, config.region),
|
|
50
|
+
(S3StoreEnv.SIGNATURE_VERSION.value, config.signature_version),
|
|
51
|
+
]:
|
|
52
|
+
configurator.set_credential(*pair)
|
|
53
|
+
|
|
54
|
+
def get_s3_creds(self) -> dict:
|
|
55
|
+
"""
|
|
56
|
+
Get endpoint, access key and secret key.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
dict
|
|
61
|
+
The credentials.
|
|
62
|
+
"""
|
|
63
|
+
creds = configurator.get_all_cred()
|
|
64
|
+
try:
|
|
65
|
+
return {
|
|
66
|
+
"endpoint_url": creds[S3StoreEnv.ENDPOINT_URL.value],
|
|
67
|
+
"aws_access_key_id": creds[S3StoreEnv.ACCESS_KEY_ID.value],
|
|
68
|
+
"aws_secret_access_key": creds[S3StoreEnv.SECRET_ACCESS_KEY.value],
|
|
69
|
+
"aws_session_token": creds[S3StoreEnv.SESSION_TOKEN.value],
|
|
70
|
+
"config": Config(
|
|
71
|
+
region_name=creds[S3StoreEnv.REGION.value],
|
|
72
|
+
signature_version=creds[S3StoreEnv.SIGNATURE_VERSION.value],
|
|
73
|
+
),
|
|
74
|
+
}
|
|
75
|
+
except KeyError as e:
|
|
76
|
+
raise StoreError(f"Missing credentials for S3 store. {str(e)}")
|
|
77
|
+
|
|
78
|
+
def _get_config(self) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Get the store configuration.
|
|
81
|
+
|
|
82
|
+
Returns
|
|
83
|
+
-------
|
|
84
|
+
None
|
|
85
|
+
"""
|
|
86
|
+
required_vars = [
|
|
87
|
+
S3StoreEnv.ENDPOINT_URL,
|
|
88
|
+
S3StoreEnv.ACCESS_KEY_ID,
|
|
89
|
+
S3StoreEnv.SECRET_ACCESS_KEY,
|
|
90
|
+
S3StoreEnv.BUCKET_NAME,
|
|
91
|
+
]
|
|
92
|
+
optional_vars = [S3StoreEnv.REGION, S3StoreEnv.SIGNATURE_VERSION, S3StoreEnv.SESSION_TOKEN]
|
|
93
|
+
|
|
94
|
+
# Load required environment variables
|
|
95
|
+
credentials = {var.value: configurator.load_var(var.value) for var in required_vars}
|
|
96
|
+
|
|
97
|
+
# Check for missing required credentials
|
|
98
|
+
missing_vars = [key for key, value in credentials.items() if value is None]
|
|
99
|
+
if missing_vars:
|
|
100
|
+
raise StoreError(f"Missing credentials for S3 store: {', '.join(missing_vars)}")
|
|
101
|
+
|
|
102
|
+
# Load optional environment variables
|
|
103
|
+
credentials.update({var.value: configurator.load_var(var.value) for var in optional_vars})
|
|
104
|
+
|
|
105
|
+
# Set credentials
|
|
106
|
+
for key, value in credentials.items():
|
|
107
|
+
configurator.set_credential(key, value)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class S3StoreEnv(Enum):
|
|
7
|
+
"""
|
|
8
|
+
S3Store environment
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
ENDPOINT_URL = "S3_ENDPOINT_URL"
|
|
12
|
+
ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"
|
|
13
|
+
SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"
|
|
14
|
+
SESSION_TOKEN = "AWS_SESSION_TOKEN"
|
|
15
|
+
BUCKET_NAME = "S3_BUCKET"
|
|
16
|
+
REGION = "S3_REGION"
|
|
17
|
+
SIGNATURE_VERSION = "S3_SIGNATURE_VERSION"
|