digitalhub 0.12.0__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 (79) hide show
  1. digitalhub/__init__.py +1 -1
  2. digitalhub/context/api.py +5 -5
  3. digitalhub/context/builder.py +3 -5
  4. digitalhub/context/context.py +9 -1
  5. digitalhub/entities/_base/executable/entity.py +105 -57
  6. digitalhub/entities/_base/material/entity.py +11 -18
  7. digitalhub/entities/_base/material/utils.py +1 -1
  8. digitalhub/entities/_commons/metrics.py +64 -30
  9. digitalhub/entities/_commons/utils.py +36 -9
  10. digitalhub/entities/_processors/base.py +150 -79
  11. digitalhub/entities/_processors/context.py +366 -215
  12. digitalhub/entities/_processors/utils.py +74 -30
  13. digitalhub/entities/artifact/crud.py +4 -0
  14. digitalhub/entities/artifact/utils.py +28 -13
  15. digitalhub/entities/dataitem/crud.py +14 -2
  16. digitalhub/entities/dataitem/table/entity.py +3 -3
  17. digitalhub/entities/dataitem/utils.py +84 -35
  18. digitalhub/entities/model/crud.py +4 -0
  19. digitalhub/entities/model/utils.py +28 -13
  20. digitalhub/entities/project/_base/entity.py +0 -2
  21. digitalhub/entities/run/_base/entity.py +2 -2
  22. digitalhub/entities/task/_base/models.py +12 -3
  23. digitalhub/entities/trigger/_base/entity.py +11 -0
  24. digitalhub/factory/factory.py +25 -3
  25. digitalhub/factory/utils.py +11 -3
  26. digitalhub/runtimes/_base.py +1 -1
  27. digitalhub/runtimes/builder.py +18 -1
  28. digitalhub/stores/client/__init__.py +12 -0
  29. digitalhub/stores/client/_base/api_builder.py +14 -0
  30. digitalhub/stores/client/_base/client.py +93 -0
  31. digitalhub/stores/client/_base/key_builder.py +28 -0
  32. digitalhub/stores/client/_base/params_builder.py +14 -0
  33. digitalhub/stores/client/api.py +10 -5
  34. digitalhub/stores/client/builder.py +3 -1
  35. digitalhub/stores/client/dhcore/api_builder.py +17 -0
  36. digitalhub/stores/client/dhcore/client.py +325 -70
  37. digitalhub/stores/client/dhcore/configurator.py +485 -193
  38. digitalhub/stores/client/dhcore/enums.py +3 -0
  39. digitalhub/stores/client/dhcore/error_parser.py +35 -1
  40. digitalhub/stores/client/dhcore/params_builder.py +113 -17
  41. digitalhub/stores/client/dhcore/utils.py +40 -22
  42. digitalhub/stores/client/local/api_builder.py +17 -0
  43. digitalhub/stores/client/local/client.py +6 -8
  44. digitalhub/stores/credentials/api.py +35 -0
  45. digitalhub/stores/credentials/configurator.py +210 -0
  46. digitalhub/stores/credentials/enums.py +68 -0
  47. digitalhub/stores/credentials/handler.py +176 -0
  48. digitalhub/stores/{configurator → credentials}/ini_module.py +60 -28
  49. digitalhub/stores/credentials/store.py +81 -0
  50. digitalhub/stores/data/_base/store.py +27 -9
  51. digitalhub/stores/data/api.py +49 -9
  52. digitalhub/stores/data/builder.py +90 -41
  53. digitalhub/stores/data/local/store.py +4 -7
  54. digitalhub/stores/data/remote/store.py +4 -7
  55. digitalhub/stores/data/s3/configurator.py +65 -80
  56. digitalhub/stores/data/s3/store.py +69 -81
  57. digitalhub/stores/data/s3/utils.py +10 -10
  58. digitalhub/stores/data/sql/configurator.py +76 -73
  59. digitalhub/stores/data/sql/store.py +191 -102
  60. digitalhub/utils/exceptions.py +6 -0
  61. digitalhub/utils/file_utils.py +53 -30
  62. digitalhub/utils/generic_utils.py +41 -33
  63. digitalhub/utils/git_utils.py +24 -14
  64. digitalhub/utils/io_utils.py +19 -18
  65. digitalhub/utils/uri_utils.py +31 -31
  66. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/METADATA +1 -1
  67. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/RECORD +71 -74
  68. digitalhub/entities/_commons/types.py +0 -9
  69. digitalhub/stores/configurator/api.py +0 -35
  70. digitalhub/stores/configurator/configurator.py +0 -202
  71. digitalhub/stores/configurator/credentials_store.py +0 -69
  72. digitalhub/stores/configurator/enums.py +0 -25
  73. digitalhub/stores/data/s3/enums.py +0 -20
  74. digitalhub/stores/data/sql/enums.py +0 -20
  75. digitalhub/stores/data/utils.py +0 -38
  76. /digitalhub/stores/{configurator → credentials}/__init__.py +0 -0
  77. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/WHEEL +0 -0
  78. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/AUTHORS +0 -0
  79. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -6,92 +6,141 @@ from __future__ import annotations
6
6
 
7
7
  import typing
8
8
 
9
- from digitalhub.stores.configurator.api import get_current_env
10
9
  from digitalhub.stores.data.local.store import LocalStore
11
10
  from digitalhub.stores.data.remote.store import RemoteStore
11
+ from digitalhub.stores.data.s3.configurator import S3StoreConfigurator
12
12
  from digitalhub.stores.data.s3.store import S3Store
13
+ from digitalhub.stores.data.sql.configurator import SqlStoreConfigurator
13
14
  from digitalhub.stores.data.sql.store import SqlStore
14
15
  from digitalhub.utils.uri_utils import SchemeCategory, map_uri_scheme
15
16
 
16
17
  if typing.TYPE_CHECKING:
18
+ from digitalhub.stores.credentials.configurator import Configurator
17
19
  from digitalhub.stores.data._base.store import Store
20
+ from digitalhub.utils.exceptions import StoreError
18
21
 
19
22
 
20
- def _get_class_from_type(type: str) -> Store:
23
+ class StoreInfo:
21
24
  """
22
- Get a store class from its type.
25
+ Container for store class and configurator information.
23
26
 
24
- Parameters
25
- ----------
26
- type : str
27
- Store type.
27
+ Holds store class references and their associated configurators
28
+ for registration and instantiation in the store builder system.
28
29
 
29
- Returns
30
- -------
31
- Store
32
- 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.
33
36
  """
34
- if type == SchemeCategory.LOCAL.value:
35
- return LocalStore
36
- if type == SchemeCategory.S3.value:
37
- return S3Store
38
- if type == SchemeCategory.REMOTE.value:
39
- return RemoteStore
40
- if type == SchemeCategory.SQL.value:
41
- return SqlStore
42
- 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
43
41
 
44
42
 
45
43
  class StoreBuilder:
46
44
  """
47
- 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.
48
58
  """
49
59
 
50
60
  def __init__(self) -> None:
61
+ self._builders: dict[str, StoreInfo] = {}
51
62
  self._instances: dict[str, dict[str, Store]] = {}
52
63
 
53
- 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:
54
70
  """
55
- 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.
56
75
 
57
76
  Parameters
58
77
  ----------
59
78
  store_type : str
60
- Store type.
61
- 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.
62
85
 
63
86
  Returns
64
87
  -------
65
88
  None
89
+
90
+ Raises
91
+ ------
92
+ StoreError
93
+ If the store type is already registered in the builder.
66
94
  """
67
- env = get_current_env()
68
- if env not in self._instances:
69
- self._instances[env] = {}
70
- 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")
71
99
 
72
- def get(self, project: str, uri: str) -> Store:
100
+ def get(self, uri: str) -> Store:
73
101
  """
74
- 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.
75
107
 
76
108
  Parameters
77
109
  ----------
78
110
  uri : str
79
- URI to parse.
80
- config : dict
81
- 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.
82
114
 
83
115
  Returns
84
116
  -------
85
117
  Store
86
- 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.
87
124
  """
88
- env = get_current_env()
89
125
  store_type = map_uri_scheme(uri)
90
- try:
91
- return self._instances[env][store_type]
92
- except KeyError:
93
- self.build(project, store_type)
94
- 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]
95
140
 
96
141
 
97
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)
@@ -29,7 +29,6 @@ class LocalStore(Store):
29
29
  self,
30
30
  root: str,
31
31
  dst: Path,
32
- src: list[str],
33
32
  overwrite: bool = False,
34
33
  ) -> str:
35
34
  """
@@ -37,19 +36,17 @@ class LocalStore(Store):
37
36
 
38
37
  Parameters
39
38
  ----------
40
- root : str
41
- The root path of the artifact.
39
+ src : str
40
+ Path of the material entity.
42
41
  dst : str
43
- The destination of the artifact on local filesystem.
44
- src : list[str]
45
- List of sources.
42
+ The destination of the material entity on local filesystem.
46
43
  overwrite : bool
47
44
  Specify if overwrite existing file(s).
48
45
 
49
46
  Returns
50
47
  -------
51
48
  str
52
- Destination path of the downloaded artifact.
49
+ Destination path of the downloaded files.
53
50
  """
54
51
  raise StoreError("Local store does not support download.")
55
52
 
@@ -28,7 +28,6 @@ class RemoteStore(Store):
28
28
  self,
29
29
  root: str,
30
30
  dst: Path,
31
- src: list[str],
32
31
  overwrite: bool = False,
33
32
  ) -> str:
34
33
  """
@@ -36,19 +35,17 @@ class RemoteStore(Store):
36
35
 
37
36
  Parameters
38
37
  ----------
39
- root : str
40
- The root path of the artifact.
38
+ src : str
39
+ Path of the material entity.
41
40
  dst : str
42
- The destination of the artifact on local filesystem.
43
- src : list[str]
44
- List of sources.
41
+ The destination of the material entity on local filesystem.
45
42
  overwrite : bool
46
43
  Specify if overwrite existing file(s).
47
44
 
48
45
  Returns
49
46
  -------
50
47
  str
51
- Destination path of the downloaded artifact.
48
+ Destination path of the downloaded files.
52
49
  """
53
50
  # Handle destination
54
51
  if dst is None:
@@ -4,127 +4,112 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ from datetime import datetime, timezone
8
+
7
9
  from botocore.config import Config
8
10
 
9
- from digitalhub.stores.configurator.configurator import configurator
10
- from digitalhub.stores.configurator.enums import CredsOrigin
11
- from digitalhub.stores.data.s3.enums import S3StoreEnv
12
- 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
13
14
 
14
15
 
15
- class S3StoreConfigurator:
16
+ class S3StoreConfigurator(Configurator):
16
17
  """
17
18
  Configure the store by getting the credentials from user
18
19
  provided config or from environment.
19
20
  """
20
21
 
21
- required_vars = [
22
- S3StoreEnv.ENDPOINT_URL,
23
- S3StoreEnv.ACCESS_KEY_ID,
24
- 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,
25
31
  ]
26
- optional_vars = [
27
- S3StoreEnv.REGION,
28
- S3StoreEnv.SIGNATURE_VERSION,
29
- 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,
30
36
  ]
31
37
 
38
+ def __init__(self):
39
+ super().__init__()
40
+ self.load_configs()
41
+
32
42
  ##############################
33
43
  # Configuration methods
34
44
  ##############################
35
45
 
36
- def get_boto3_client_config(self, origin: str) -> dict:
46
+ def load_env_vars(self) -> None:
37
47
  """
38
- Get S3 credentials (access key, secret key,
39
- session token and other config).
40
-
41
- Parameters
42
- ----------
43
- origin : str
44
- The origin of the credentials.
48
+ Loads the credentials from the environment variables.
45
49
 
46
50
  Returns
47
51
  -------
48
- dict
49
- The credentials.
52
+ None
50
53
  """
51
- if origin == CredsOrigin.ENV.value:
52
- creds = self._get_env_config()
53
- elif origin == CredsOrigin.FILE.value:
54
- creds = self._get_file_config()
55
- else:
56
- raise StoreError(f"Unknown origin: {origin}")
57
- return {
58
- "endpoint_url": creds[S3StoreEnv.ENDPOINT_URL.value],
59
- "aws_access_key_id": creds[S3StoreEnv.ACCESS_KEY_ID.value],
60
- "aws_secret_access_key": creds[S3StoreEnv.SECRET_ACCESS_KEY.value],
61
- "aws_session_token": creds[S3StoreEnv.SESSION_TOKEN.value],
62
- "config": Config(
63
- region_name=creds[S3StoreEnv.REGION.value],
64
- signature_version=creds[S3StoreEnv.SIGNATURE_VERSION.value],
65
- ),
66
- }
54
+ env_creds = self._creds_handler.load_from_env(self.keys)
55
+ self._creds_handler.set_credentials(self._env, env_creds)
67
56
 
68
- def _get_env_config(self) -> dict:
57
+ def load_file_vars(self) -> None:
69
58
  """
70
- Get the store configuration from environment variables.
59
+ Loads the credentials from a file.
71
60
 
72
61
  Returns
73
62
  -------
74
- dict
75
- The credentials.
63
+ None
76
64
  """
77
- credentials = {
78
- var.value: configurator.load_from_env(var.value) for var in self.required_vars + self.optional_vars
79
- }
80
- self._set_credentials(credentials)
81
- return credentials
65
+ file_creds = self._creds_handler.load_from_file(self.keys)
66
+ self._creds_handler.set_credentials(self._file, file_creds)
82
67
 
83
- def _get_file_config(self) -> dict:
68
+ def get_client_config(self) -> dict:
84
69
  """
85
- Get the store configuration from file.
70
+ Gets S3 credentials (access key, secret key, session token, and other config).
86
71
 
87
72
  Returns
88
73
  -------
89
74
  dict
90
- The credentials.
75
+ Dictionary containing S3 credentials and configuration.
91
76
  """
92
- credentials = {
93
- 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
+ ),
94
92
  }
95
- self._set_credentials(credentials)
96
- return credentials
97
-
98
- def _check_credentials(self, credentials: dict) -> None:
99
- """
100
- Check for missing credentials.
101
-
102
- Parameters
103
- ----------
104
- credentials : dict
105
- The credentials.
106
93
 
107
- Returns
108
- -------
109
- None
94
+ @staticmethod
95
+ def _is_expired(timestamp: str | None) -> bool:
110
96
  """
111
- missing_vars = [key for key, value in credentials.items() if value is None and key in self.required_vars]
112
- if missing_vars:
113
- raise StoreError(f"Missing credentials for S3 store: {', '.join(missing_vars)}")
114
-
115
- def _set_credentials(self, credentials: dict) -> None:
116
- """
117
- Set the store credentials into the configurator.
97
+ Determines whether a given timestamp is after the current UTC time.
118
98
 
119
99
  Parameters
120
100
  ----------
121
- credentials : dict
122
- The credentials.
101
+ timestamp : str or None
102
+ Timestamp string in the format 'YYYY-MM-DDTHH:MM:SSZ'.
123
103
 
124
104
  Returns
125
105
  -------
126
- None
106
+ bool
107
+ True if the given timestamp is later than the current UTC time,
108
+ otherwise False.
127
109
  """
128
- # Set credentials
129
- for key, value in credentials.items():
130
- 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