digitalhub 0.9.2__py3-none-any.whl → 0.10.0b0__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 (67) hide show
  1. digitalhub/__init__.py +2 -3
  2. digitalhub/client/_base/client.py +3 -2
  3. digitalhub/client/dhcore/api_builder.py +5 -0
  4. digitalhub/client/dhcore/client.py +27 -399
  5. digitalhub/client/dhcore/configurator.py +339 -0
  6. digitalhub/client/dhcore/error_parser.py +107 -0
  7. digitalhub/client/dhcore/models.py +13 -23
  8. digitalhub/client/dhcore/utils.py +4 -44
  9. digitalhub/client/local/api_builder.py +9 -17
  10. digitalhub/client/local/client.py +12 -2
  11. digitalhub/client/local/enums.py +11 -0
  12. digitalhub/configurator/api.py +31 -0
  13. digitalhub/configurator/configurator.py +194 -0
  14. digitalhub/configurator/credentials_store.py +65 -0
  15. digitalhub/configurator/ini_module.py +74 -0
  16. digitalhub/entities/_base/_base/entity.py +2 -2
  17. digitalhub/entities/_base/material/entity.py +19 -6
  18. digitalhub/entities/_base/material/utils.py +2 -2
  19. digitalhub/entities/_commons/enums.py +1 -0
  20. digitalhub/entities/_commons/models.py +9 -0
  21. digitalhub/entities/_commons/utils.py +25 -0
  22. digitalhub/entities/_operations/processor.py +103 -107
  23. digitalhub/entities/artifact/crud.py +3 -3
  24. digitalhub/entities/dataitem/_base/entity.py +2 -2
  25. digitalhub/entities/dataitem/crud.py +3 -3
  26. digitalhub/entities/dataitem/table/entity.py +2 -2
  27. digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +43 -51
  28. digitalhub/entities/dataitem/utils.py +6 -3
  29. digitalhub/entities/model/_base/entity.py +172 -0
  30. digitalhub/entities/model/_base/spec.py +0 -10
  31. digitalhub/entities/model/_base/status.py +10 -0
  32. digitalhub/entities/model/crud.py +3 -3
  33. digitalhub/entities/model/huggingface/spec.py +6 -3
  34. digitalhub/entities/model/mlflow/models.py +2 -2
  35. digitalhub/entities/model/mlflow/spec.py +1 -3
  36. digitalhub/entities/model/mlflow/utils.py +44 -5
  37. digitalhub/entities/run/_base/entity.py +149 -0
  38. digitalhub/entities/run/_base/status.py +12 -0
  39. digitalhub/entities/task/_base/spec.py +2 -0
  40. digitalhub/entities/task/crud.py +4 -0
  41. digitalhub/readers/{_commons → pandas}/enums.py +4 -0
  42. digitalhub/readers/pandas/reader.py +58 -10
  43. digitalhub/stores/_base/store.py +1 -49
  44. digitalhub/stores/api.py +8 -33
  45. digitalhub/stores/builder.py +44 -161
  46. digitalhub/stores/local/store.py +4 -18
  47. digitalhub/stores/remote/store.py +3 -10
  48. digitalhub/stores/s3/configurator.py +107 -0
  49. digitalhub/stores/s3/enums.py +17 -0
  50. digitalhub/stores/s3/models.py +21 -0
  51. digitalhub/stores/s3/store.py +8 -28
  52. digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
  53. digitalhub/stores/sql/configurator.py +88 -0
  54. digitalhub/stores/sql/enums.py +16 -0
  55. digitalhub/stores/sql/models.py +24 -0
  56. digitalhub/stores/sql/store.py +14 -57
  57. digitalhub/utils/exceptions.py +6 -0
  58. digitalhub/utils/generic_utils.py +9 -8
  59. digitalhub/utils/uri_utils.py +1 -1
  60. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/METADATA +5 -6
  61. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/RECORD +66 -53
  62. test/local/imports/test_imports.py +0 -1
  63. digitalhub/client/dhcore/env.py +0 -23
  64. /digitalhub/{readers/_commons → configurator}/__init__.py +0 -0
  65. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/LICENSE.txt +0 -0
  66. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.dist-info}/WHEEL +0 -0
  67. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0b0.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._commons.enums import Extensions
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.FILE.value:
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) -> Any:
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
- head = head.replace({np.nan: None})
216
- head = head.values.tolist()
217
- preview = get_data_preview(columns, head)
218
- len_df = len(df)
219
- return build_data_preview(preview, len_df)
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))
@@ -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 SchemeCategory, has_local_scheme
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, StoreParameters
8
+ from digitalhub.stores._base.store import Store
9
9
 
10
10
 
11
- def set_store(store_cfg: StoreParameters) -> None:
11
+ def get_store(project: str, uri: str, config: dict | None = None) -> Store:
12
12
  """
13
- Set a new store instance with the given configuration.
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)
@@ -1,28 +1,41 @@
1
1
  from __future__ import annotations
2
2
 
3
- import os
4
3
  import typing
5
4
 
6
- from pydantic import ValidationError
7
-
8
- from digitalhub.stores._base.store import StoreParameters
9
- from digitalhub.stores.local.store import LocalStore, LocalStoreConfig
10
- from digitalhub.stores.remote.store import RemoteStore, RemoteStoreConfig
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
- REGISTRY_STORES = {
21
- "local": LocalStore,
22
- "s3": S3Store,
23
- "remote": RemoteStore,
24
- "sql": SqlStore,
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, store_cfg: StoreParameters) -> None:
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
- store_cfg : StoreParameters
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
- scheme = map_uri_scheme(store_cfg.type)
53
- self._instances[scheme] = self.build_store(store_cfg)
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
- obj = REGISTRY_STORES[cfg.type](cfg.name, cfg.type, cfg.config)
115
- if cfg.is_default and self._default is not None:
116
- raise StoreError("Only one default store!")
117
- return obj
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()
@@ -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, StoreConfig
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, name: str, store_type: str, config: LocalStoreConfig) -> None:
29
- super().__init__(name, store_type)
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.store._check_local_dst(dst)
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, StoreConfig
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, name: str, store_type: str, config: RemoteStoreConfig) -> None:
25
- super().__init__(name, store_type)
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_NAME"
16
+ REGION = "S3_REGION"
17
+ SIGNATURE_VERSION = "S3_SIGNATURE_VERSION"