digitalhub 0.9.2__py3-none-any.whl → 0.10.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 (121) hide show
  1. digitalhub/__init__.py +2 -3
  2. digitalhub/client/_base/api_builder.py +1 -1
  3. digitalhub/client/_base/client.py +25 -2
  4. digitalhub/client/_base/params_builder.py +16 -0
  5. digitalhub/client/dhcore/api_builder.py +9 -3
  6. digitalhub/client/dhcore/client.py +30 -398
  7. digitalhub/client/dhcore/configurator.py +361 -0
  8. digitalhub/client/dhcore/error_parser.py +107 -0
  9. digitalhub/client/dhcore/models.py +13 -23
  10. digitalhub/client/dhcore/params_builder.py +178 -0
  11. digitalhub/client/dhcore/utils.py +4 -44
  12. digitalhub/client/local/api_builder.py +13 -18
  13. digitalhub/client/local/client.py +18 -2
  14. digitalhub/client/local/enums.py +11 -0
  15. digitalhub/client/local/params_builder.py +116 -0
  16. digitalhub/configurator/api.py +31 -0
  17. digitalhub/configurator/configurator.py +195 -0
  18. digitalhub/configurator/credentials_store.py +65 -0
  19. digitalhub/configurator/ini_module.py +74 -0
  20. digitalhub/entities/_base/_base/entity.py +2 -2
  21. digitalhub/entities/_base/context/entity.py +4 -4
  22. digitalhub/entities/_base/entity/builder.py +5 -5
  23. digitalhub/entities/_base/executable/entity.py +2 -2
  24. digitalhub/entities/_base/material/entity.py +12 -12
  25. digitalhub/entities/_base/material/status.py +1 -1
  26. digitalhub/entities/_base/material/utils.py +2 -2
  27. digitalhub/entities/_base/unversioned/entity.py +2 -2
  28. digitalhub/entities/_base/versioned/entity.py +2 -2
  29. digitalhub/entities/_commons/enums.py +2 -0
  30. digitalhub/entities/_commons/metrics.py +164 -0
  31. digitalhub/entities/_commons/types.py +5 -0
  32. digitalhub/entities/_commons/utils.py +2 -2
  33. digitalhub/entities/_processors/base.py +527 -0
  34. digitalhub/entities/{_operations/processor.py → _processors/context.py} +212 -837
  35. digitalhub/entities/_processors/utils.py +158 -0
  36. digitalhub/entities/artifact/artifact/spec.py +3 -1
  37. digitalhub/entities/artifact/crud.py +13 -12
  38. digitalhub/entities/artifact/utils.py +1 -1
  39. digitalhub/entities/builders.py +6 -18
  40. digitalhub/entities/dataitem/_base/entity.py +0 -41
  41. digitalhub/entities/dataitem/crud.py +27 -15
  42. digitalhub/entities/dataitem/table/entity.py +49 -35
  43. digitalhub/entities/dataitem/table/models.py +4 -3
  44. digitalhub/{utils/data_utils.py → entities/dataitem/table/utils.py} +46 -54
  45. digitalhub/entities/dataitem/utils.py +58 -10
  46. digitalhub/entities/function/crud.py +9 -9
  47. digitalhub/entities/model/_base/entity.py +120 -0
  48. digitalhub/entities/model/_base/spec.py +6 -17
  49. digitalhub/entities/model/_base/status.py +10 -0
  50. digitalhub/entities/model/crud.py +13 -12
  51. digitalhub/entities/model/huggingface/spec.py +9 -4
  52. digitalhub/entities/model/mlflow/models.py +2 -2
  53. digitalhub/entities/model/mlflow/spec.py +7 -7
  54. digitalhub/entities/model/mlflow/utils.py +44 -5
  55. digitalhub/entities/project/_base/entity.py +317 -9
  56. digitalhub/entities/project/_base/spec.py +8 -6
  57. digitalhub/entities/project/crud.py +12 -11
  58. digitalhub/entities/run/_base/entity.py +103 -6
  59. digitalhub/entities/run/_base/spec.py +4 -2
  60. digitalhub/entities/run/_base/status.py +12 -0
  61. digitalhub/entities/run/crud.py +8 -8
  62. digitalhub/entities/secret/_base/entity.py +3 -3
  63. digitalhub/entities/secret/_base/spec.py +4 -2
  64. digitalhub/entities/secret/crud.py +11 -9
  65. digitalhub/entities/task/_base/entity.py +4 -4
  66. digitalhub/entities/task/_base/models.py +51 -40
  67. digitalhub/entities/task/_base/spec.py +2 -0
  68. digitalhub/entities/task/_base/utils.py +2 -2
  69. digitalhub/entities/task/crud.py +12 -8
  70. digitalhub/entities/workflow/crud.py +9 -9
  71. digitalhub/factory/utils.py +9 -9
  72. digitalhub/readers/{_base → data/_base}/builder.py +1 -1
  73. digitalhub/readers/{_base → data/_base}/reader.py +16 -4
  74. digitalhub/readers/{api.py → data/api.py} +2 -2
  75. digitalhub/readers/{factory.py → data/factory.py} +3 -3
  76. digitalhub/readers/{pandas → data/pandas}/builder.py +2 -2
  77. digitalhub/readers/{pandas → data/pandas}/reader.py +110 -30
  78. digitalhub/readers/query/__init__.py +0 -0
  79. digitalhub/stores/_base/store.py +59 -69
  80. digitalhub/stores/api.py +8 -33
  81. digitalhub/stores/builder.py +44 -161
  82. digitalhub/stores/local/store.py +106 -89
  83. digitalhub/stores/remote/store.py +86 -11
  84. digitalhub/stores/s3/configurator.py +108 -0
  85. digitalhub/stores/s3/enums.py +17 -0
  86. digitalhub/stores/s3/models.py +21 -0
  87. digitalhub/stores/s3/store.py +154 -70
  88. digitalhub/{utils/s3_utils.py → stores/s3/utils.py} +7 -3
  89. digitalhub/stores/sql/configurator.py +88 -0
  90. digitalhub/stores/sql/enums.py +16 -0
  91. digitalhub/stores/sql/models.py +24 -0
  92. digitalhub/stores/sql/store.py +106 -85
  93. digitalhub/{readers/_commons → utils}/enums.py +5 -1
  94. digitalhub/utils/exceptions.py +6 -0
  95. digitalhub/utils/file_utils.py +8 -7
  96. digitalhub/utils/generic_utils.py +28 -15
  97. digitalhub/utils/git_utils.py +16 -9
  98. digitalhub/utils/types.py +5 -0
  99. digitalhub/utils/uri_utils.py +2 -2
  100. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/METADATA +25 -31
  101. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/RECORD +108 -99
  102. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info}/WHEEL +1 -2
  103. digitalhub/client/dhcore/env.py +0 -23
  104. digitalhub/entities/_base/project/entity.py +0 -341
  105. digitalhub-0.9.2.dist-info/top_level.txt +0 -2
  106. test/local/CRUD/test_artifacts.py +0 -96
  107. test/local/CRUD/test_dataitems.py +0 -96
  108. test/local/CRUD/test_models.py +0 -95
  109. test/local/imports/test_imports.py +0 -66
  110. test/local/instances/test_validate.py +0 -55
  111. test/test_crud_functions.py +0 -109
  112. test/test_crud_runs.py +0 -86
  113. test/test_crud_tasks.py +0 -81
  114. test/testkfp.py +0 -37
  115. test/testkfp_pipeline.py +0 -22
  116. /digitalhub/{entities/_base/project → configurator}/__init__.py +0 -0
  117. /digitalhub/entities/{_operations → _processors}/__init__.py +0 -0
  118. /digitalhub/readers/{_base → data}/__init__.py +0 -0
  119. /digitalhub/readers/{_commons → data/_base}/__init__.py +0 -0
  120. /digitalhub/readers/{pandas → data/pandas}/__init__.py +0 -0
  121. {digitalhub-0.9.2.dist-info → digitalhub-0.10.0.dist-info/licenses}/LICENSE.txt +0 -0
@@ -4,7 +4,7 @@ import os
4
4
  import typing
5
5
 
6
6
  from digitalhub.client.api import get_client
7
- from digitalhub.client.dhcore.enums import AuthType, DhcoreEnvVar
7
+ from digitalhub.client.dhcore.enums import DhcoreEnvVar
8
8
 
9
9
  if typing.TYPE_CHECKING:
10
10
  from digitalhub.client.dhcore.client import ClientDHCore
@@ -21,8 +21,7 @@ def set_dhcore_env(
21
21
  """
22
22
  Function to set environment variables for DHCore config.
23
23
  Note that if the environment variable is already set, it
24
- will be overwritten. It also ovverides the remote client
25
- configuration.
24
+ will be overwritten.
26
25
 
27
26
  Parameters
28
27
  ----------
@@ -56,47 +55,8 @@ def set_dhcore_env(
56
55
  if client_id is not None:
57
56
  os.environ[DhcoreEnvVar.CLIENT_ID.value] = client_id
58
57
 
59
- update_client_from_env()
60
-
61
-
62
- def update_client_from_env() -> None:
63
- """
64
- Function to update client from environment variables.
65
-
66
- Returns
67
- -------
68
- None
69
- """
70
58
  client: ClientDHCore = get_client(local=False)
71
-
72
- # Update endpoint
73
- endpoint = os.getenv(DhcoreEnvVar.ENDPOINT.value)
74
- if endpoint is not None:
75
- client._endpoint_core = endpoint
76
-
77
- # Update auth
78
-
79
- # If token is set, it will override the other auth options
80
- access_token = os.getenv(DhcoreEnvVar.ACCESS_TOKEN.value)
81
- refresh_token = os.getenv(DhcoreEnvVar.REFRESH_TOKEN.value)
82
- client_id = os.getenv(DhcoreEnvVar.CLIENT_ID.value)
83
-
84
- if access_token is not None:
85
- if refresh_token is not None:
86
- client._refresh_token = refresh_token
87
- if client_id is not None:
88
- client._client_id = client_id
89
- client._access_token = access_token
90
- client._auth_type = AuthType.OAUTH2.value
91
- return
92
-
93
- # Otherwise, if user and password are set, basic auth will be used
94
- username = os.getenv(DhcoreEnvVar.USER.value)
95
- password = os.getenv(DhcoreEnvVar.PASSWORD.value)
96
- if username is not None and password is not None:
97
- client._user = username
98
- client._password = password
99
- client._auth_type = AuthType.BASIC.value
59
+ client._configurator.configure()
100
60
 
101
61
 
102
62
  def refresh_token() -> None:
@@ -108,4 +68,4 @@ def refresh_token() -> None:
108
68
  None
109
69
  """
110
70
  client: ClientDHCore = get_client(local=False)
111
- client._get_new_access_token()
71
+ client._configurator.get_new_access_token()
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from digitalhub.client._base.api_builder import ClientApiBuilder
4
+ from digitalhub.client.local.enums import LocalClientVar
4
5
  from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
5
6
  from digitalhub.utils.exceptions import BackendError
6
7
 
@@ -63,15 +64,16 @@ class ClientLocalApiBuilder(ClientApiBuilder):
63
64
  BackendOperations.DELETE.value,
64
65
  ):
65
66
  return f"{API_BASE}/{entity_type}/{kwargs['entity_name']}"
66
- elif operation == BackendOperations.SHARE.value:
67
- raise BackendError("Share API not implemented for Local.")
68
- raise BackendError(f"Invalid operation '{operation}' for entity type '{entity_type}' in Local.")
67
+ raise BackendError(f"API for operation '{operation}' for entity type '{entity_type}' not implemented in Local.")
69
68
 
70
69
  def build_api_context(self, operation: str, **kwargs) -> str:
71
70
  """
72
71
  Build the context API for the client.
73
72
  """
74
- entity_type = kwargs["entity_type"] + "s"
73
+ try:
74
+ entity_type = kwargs["entity_type"] + "s"
75
+ except KeyError:
76
+ pass
75
77
  project = kwargs["project"]
76
78
  if operation in (
77
79
  BackendOperations.CREATE.value,
@@ -84,17 +86,10 @@ class ClientLocalApiBuilder(ClientApiBuilder):
84
86
  BackendOperations.DELETE.value,
85
87
  ):
86
88
  return f"{API_CONTEXT}/{project}/{entity_type}/{kwargs['entity_id']}"
87
- elif operation == BackendOperations.LOGS.value:
88
- raise BackendError("Logs run API not implemented for Local.")
89
- elif operation == BackendOperations.STOP.value:
90
- raise BackendError("Stop run API not implemented for Local.")
91
- elif operation == BackendOperations.RESUME.value:
92
- raise BackendError("Resume run API not implemented for Local.")
93
- elif operation == BackendOperations.DATA.value:
94
- raise BackendError("Secret API (read/set value) not implemented for Local.")
95
- elif operation == BackendOperations.FILES.value:
96
- raise BackendError("Files API not implemented for Local.")
97
- elif operation == BackendOperations.SEARCH.value:
98
- raise BackendError("Search API not implemented for Local.")
99
-
100
- raise BackendError(f"Invalid operation '{operation}' for entity type '{entity_type}' in Local.")
89
+ elif operation in (
90
+ BackendOperations.LOGS.value,
91
+ BackendOperations.FILES.value,
92
+ BackendOperations.METRICS.value,
93
+ ):
94
+ return LocalClientVar.EMPTY.value
95
+ raise BackendError(f"API for operation '{operation}' for entity type '{entity_type}' not implemented in Local.")
@@ -2,10 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from copy import deepcopy
4
4
  from datetime import datetime, timezone
5
+ from typing import Any
5
6
 
6
7
  from digitalhub.client._base.client import Client
7
8
  from digitalhub.client.local.api_builder import ClientLocalApiBuilder
9
+ from digitalhub.client.local.enums import LocalClientVar
8
10
  from digitalhub.client.local.key_builder import ClientLocalKeyBuilder
11
+ from digitalhub.client.local.params_builder import ClientLocalParametersBuilder
9
12
  from digitalhub.utils.exceptions import BackendError
10
13
 
11
14
 
@@ -26,13 +29,14 @@ class ClientLocal(Client):
26
29
  super().__init__()
27
30
  self._api_builder = ClientLocalApiBuilder()
28
31
  self._key_builder = ClientLocalKeyBuilder()
32
+ self._params_builder = ClientLocalParametersBuilder()
29
33
  self._db: dict[str, dict[str, dict]] = {}
30
34
 
31
35
  ##############################
32
36
  # CRUD
33
37
  ##############################
34
38
 
35
- def create_object(self, api: str, obj: dict, **kwargs) -> dict:
39
+ def create_object(self, api: str, obj: Any, **kwargs) -> dict:
36
40
  """
37
41
  Create an object in local.
38
42
 
@@ -48,6 +52,11 @@ class ClientLocal(Client):
48
52
  dict
49
53
  The created object.
50
54
  """
55
+ if api == LocalClientVar.EMPTY.value:
56
+ return {}
57
+ if not isinstance(obj, dict):
58
+ raise TypeError("Object must be a dictionary")
59
+
51
60
  entity_type, _, context_api = self._parse_api(api)
52
61
  try:
53
62
  # Check if entity_type is valid
@@ -114,6 +123,8 @@ class ClientLocal(Client):
114
123
  dict
115
124
  The read object.
116
125
  """
126
+ if api == LocalClientVar.EMPTY.value:
127
+ return {}
117
128
  entity_type, entity_id, context_api = self._parse_api(api)
118
129
  if entity_id is None:
119
130
  msg = self._format_msg(4)
@@ -155,7 +166,7 @@ class ClientLocal(Client):
155
166
  msg = self._format_msg(3, entity_type=entity_type, entity_id=entity_id)
156
167
  raise BackendError(msg)
157
168
 
158
- def update_object(self, api: str, obj: dict, **kwargs) -> dict:
169
+ def update_object(self, api: str, obj: Any, **kwargs) -> dict:
159
170
  """
160
171
  Update an object in local.
161
172
 
@@ -171,6 +182,11 @@ class ClientLocal(Client):
171
182
  dict
172
183
  The updated object.
173
184
  """
185
+ if api == LocalClientVar.EMPTY.value:
186
+ return {}
187
+ if not isinstance(obj, dict):
188
+ raise TypeError("Object must be a dictionary")
189
+
174
190
  entity_type, entity_id, context_api = self._parse_api(api)
175
191
  try:
176
192
  # API example
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class LocalClientVar(Enum):
7
+ """
8
+ Variables for Local.
9
+ """
10
+
11
+ EMPTY = "EMPTY"
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub.client._base.params_builder import ClientParametersBuilder
4
+ from digitalhub.entities._commons.enums import ApiCategories, BackendOperations
5
+
6
+
7
+ class ClientLocalParametersBuilder(ClientParametersBuilder):
8
+ """
9
+ This class is used to build the parameters for the Local client calls.
10
+ """
11
+
12
+ def build_parameters(self, category: str, operation: str, **kwargs) -> dict:
13
+ """
14
+ Build the parameters for the client call.
15
+
16
+ Parameters
17
+ ----------
18
+ category : str
19
+ API category.
20
+ operation : str
21
+ API operation.
22
+ **kwargs : dict
23
+ Parameters to build.
24
+
25
+ Returns
26
+ -------
27
+ dict
28
+ Parameters formatted.
29
+ """
30
+ if category == ApiCategories.BASE.value:
31
+ return self.build_parameters_base(operation, **kwargs)
32
+ return self.build_parameters_context(operation, **kwargs)
33
+
34
+ def build_parameters_base(self, operation: str, **kwargs) -> dict:
35
+ """
36
+ Build the base parameters for the client call.
37
+
38
+ Parameters
39
+ ----------
40
+ operation : str
41
+ API operation.
42
+ **kwargs : dict
43
+ Parameters to build.
44
+
45
+ Returns
46
+ -------
47
+ dict
48
+ Parameters formatted.
49
+ """
50
+ kwargs = self._set_params(**kwargs)
51
+ if operation == BackendOperations.DELETE.value:
52
+ if (cascade := kwargs.pop("cascade", None)) is not None:
53
+ kwargs["params"]["cascade"] = str(cascade).lower()
54
+ return kwargs
55
+
56
+ def build_parameters_context(self, operation: str, **kwargs) -> dict:
57
+ """
58
+ Build the context parameters for the client call.
59
+
60
+ Parameters
61
+ ----------
62
+ operation : str
63
+ API operation.
64
+ **kwargs : dict
65
+ Parameters to build.
66
+
67
+ Returns
68
+ -------
69
+ dict
70
+ Parameters formatted.
71
+ """
72
+ kwargs = self._set_params(**kwargs)
73
+
74
+ # Handle read
75
+ if operation == BackendOperations.READ_ALL_VERSIONS.value:
76
+ kwargs["params"]["versions"] = "all"
77
+ kwargs["params"]["name"] = kwargs.pop("entity_name")
78
+ # Handle delete
79
+ elif operation == BackendOperations.DELETE.value:
80
+ # Handle cascade
81
+ if cascade := kwargs.pop("cascade", None) is not None:
82
+ kwargs["params"]["cascade"] = str(cascade).lower()
83
+
84
+ # Handle delete all versions
85
+ entity_id = kwargs.pop("entity_id")
86
+ entity_name = kwargs.pop("entity_name")
87
+ if not kwargs.pop("delete_all_versions", False):
88
+ if entity_id is None:
89
+ raise ValueError(
90
+ "If `delete_all_versions` is False, `entity_id` must be provided,"
91
+ " either as an argument or in key `identifier`.",
92
+ )
93
+ else:
94
+ kwargs["params"]["name"] = entity_name
95
+ return kwargs
96
+
97
+ @staticmethod
98
+ def _set_params(**kwargs) -> dict:
99
+ """
100
+ Format params parameter.
101
+
102
+ Parameters
103
+ ----------
104
+ **kwargs : dict
105
+ Keyword arguments.
106
+
107
+ Returns
108
+ -------
109
+ dict
110
+ Parameters with initialized params.
111
+ """
112
+ if not kwargs:
113
+ kwargs = {}
114
+ if "params" not in kwargs:
115
+ kwargs["params"] = {}
116
+ return kwargs
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub.configurator.configurator import configurator
4
+
5
+
6
+ def set_current_env(environment: str) -> None:
7
+ """
8
+ Set the current credentials set.
9
+
10
+ Parameters
11
+ ----------
12
+ environment : str
13
+ Credentials set name.
14
+
15
+ Returns
16
+ -------
17
+ None
18
+ """
19
+ configurator.set_current_env(environment)
20
+
21
+
22
+ def get_current_env() -> str:
23
+ """
24
+ Get the current credentials set.
25
+
26
+ Returns
27
+ -------
28
+ str
29
+ Credentials set name.
30
+ """
31
+ return configurator.get_current_env()
@@ -0,0 +1,195 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ from digitalhub.configurator.credentials_store import CredentialsStore
6
+ from digitalhub.configurator.ini_module import load_from_config, write_config
7
+
8
+
9
+ class EnvConfigurator:
10
+ """
11
+ Configurator object used to configure clients and store credentials.
12
+ """
13
+
14
+ def __init__(self) -> None:
15
+ # Store
16
+ self._creds_store = CredentialsStore()
17
+
18
+ # Current credentials set (__default by default)
19
+ self._environment = "__default"
20
+
21
+ ##############################
22
+ # Public methods
23
+ ##############################
24
+
25
+ def set_current_env(self, creds_set: str) -> None:
26
+ """
27
+ Set the current credentials set.
28
+
29
+ Parameters
30
+ ----------
31
+ creds_set : str
32
+ Credentials set name.
33
+
34
+ Returns
35
+ -------
36
+ None
37
+ """
38
+ self._environment = creds_set
39
+
40
+ def get_current_env(self) -> str:
41
+ """
42
+ Get the current credentials set.
43
+
44
+ Returns
45
+ -------
46
+ str
47
+ Credentials set name.
48
+ """
49
+ return self._environment
50
+
51
+ ##############################
52
+ # Private methods
53
+ ##############################
54
+
55
+ def load_var(self, var_name: str) -> str | None:
56
+ """
57
+ Get env variable from credentials store, env or file (in order).
58
+
59
+ Parameters
60
+ ----------
61
+ var_name : str
62
+ Environment variable name.
63
+
64
+ Returns
65
+ -------
66
+ str | None
67
+ Environment variable value.
68
+ """
69
+ var = self.get_credential(var_name)
70
+ if var is None:
71
+ var = self.load_from_env(var_name)
72
+ if var is None:
73
+ var = self.load_from_config(var_name)
74
+ return var
75
+
76
+ def load_from_env(self, var: str) -> str | None:
77
+ """
78
+ Load variable from env.
79
+
80
+ Parameters
81
+ ----------
82
+ var : str
83
+ Environment variable name.
84
+
85
+ Returns
86
+ -------
87
+ str | None
88
+ Environment variable value.
89
+ """
90
+ env_var = os.getenv(var)
91
+ if env_var != "":
92
+ return env_var
93
+
94
+ def load_from_config(self, var: str) -> str | None:
95
+ """
96
+ Load variable from config file.
97
+
98
+ Parameters
99
+ ----------
100
+ var : str
101
+ Environment variable name.
102
+
103
+ Returns
104
+ -------
105
+ str | None
106
+ Environment variable value.
107
+ """
108
+ return load_from_config(var)
109
+
110
+ def write_env(self, key_to_include: list[str] | None = None) -> None:
111
+ """
112
+ Write the env variables to the .dhcore file.
113
+ It will overwrite any existing env variables.
114
+
115
+ Parameters
116
+ ----------
117
+ key_to_include : list[str]
118
+ List of keys to include.
119
+
120
+ Returns
121
+ -------
122
+ None
123
+ """
124
+ if key_to_include is None:
125
+ key_to_include = []
126
+ creds = self.get_credential_list(key_to_include)
127
+ write_config(creds, self._environment)
128
+
129
+ ##############################
130
+ # Credentials store methods
131
+ ##############################
132
+
133
+ def set_credential(self, key: str, value: str) -> None:
134
+ """
135
+ Register a credential value.
136
+
137
+ Parameters
138
+ ----------
139
+ key : str
140
+ The key.
141
+ value : str
142
+ The value.
143
+
144
+ Returns
145
+ -------
146
+ None
147
+ """
148
+ self._creds_store.set(self._environment, key, value)
149
+
150
+ def get_credential(self, key: str) -> dict:
151
+ """
152
+ Get single credential value from key.
153
+
154
+ Parameters
155
+ ----------
156
+ key : str
157
+ The key.
158
+
159
+ Returns
160
+ -------
161
+ dict
162
+ The credential.
163
+ """
164
+ return self._creds_store.get(self._environment, key)
165
+
166
+ def get_all_credentials(self) -> dict:
167
+ """
168
+ Get all the credentials from the current credentials set.
169
+
170
+ Returns
171
+ -------
172
+ dict
173
+ The credentials.
174
+ """
175
+ return self._creds_store.get_all(self._environment)
176
+
177
+ def get_credential_list(self, keys: list[str]) -> list[str]:
178
+ """
179
+ Get the list of credentials.
180
+
181
+ Parameters
182
+ ----------
183
+ keys : list[str]
184
+ The list of keys.
185
+
186
+ Returns
187
+ -------
188
+ list[str]
189
+ The list of credentials.
190
+ """
191
+ return {k: v for k, v in self.get_all_credentials().items() if k in keys}
192
+
193
+
194
+ # Define global configurator
195
+ configurator = EnvConfigurator()
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class CredentialsStore:
5
+ """
6
+ Credentials store to store and retrieve credentials.
7
+ """
8
+
9
+ def __init__(self) -> None:
10
+ self._credentials: dict[str, dict] = {}
11
+
12
+ def set(self, profile: str, key: str, value: str) -> None:
13
+ """
14
+ Set credentials.
15
+
16
+ Parameters
17
+ ----------
18
+ profile : str
19
+ The profile of the credentials.
20
+ key : str
21
+ The key of the credentials.
22
+ value : str
23
+ The value of the credentials.
24
+
25
+ Returns
26
+ -------
27
+ None
28
+ """
29
+ if profile not in self._credentials:
30
+ self._credentials[profile] = {}
31
+ self._credentials[profile][key] = value
32
+
33
+ def get(self, profile: str, key: str) -> str | None:
34
+ """
35
+ Get credentials.
36
+
37
+ Parameters
38
+ ----------
39
+ profile : str
40
+ The profile of the credentials.
41
+ key : str
42
+ The key of the credentials.
43
+
44
+ Returns
45
+ -------
46
+ str | None
47
+ The value of the credentials.
48
+ """
49
+ return self._credentials.get(profile, {}).get(key)
50
+
51
+ def get_all(self, profile: str) -> dict[str, str]:
52
+ """
53
+ Get all credentials.
54
+
55
+ Parameters
56
+ ----------
57
+ profile : str
58
+ The profile of the credentials.
59
+
60
+ Returns
61
+ -------
62
+ dict[str, str]
63
+ The credentials.
64
+ """
65
+ return self._credentials.get(profile, {})
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ from configparser import ConfigParser
4
+ from pathlib import Path
5
+
6
+ from digitalhub.utils.exceptions import ClientError
7
+
8
+ # File where to write DHCORE_ACCESS_TOKEN and DHCORE_REFRESH_TOKEN
9
+ # It's used because we inject the variables in jupyter env,
10
+ # but refresh token is only available once. Is it's used, we cannot
11
+ # overwrite it with coder, so we need to store the new one in a file,
12
+ # preserved for jupyter restart
13
+ ENV_FILE = Path.home() / ".dhcore.ini"
14
+
15
+
16
+ def load_from_config(var: str) -> str | None:
17
+ """
18
+ Load variable from config file.
19
+
20
+ Parameters
21
+ ----------
22
+ profile : str
23
+ Credentials set name.
24
+ var : str
25
+ Environment variable name.
26
+
27
+ Returns
28
+ -------
29
+ str | None
30
+ Environment variable value.
31
+ """
32
+ cfg = ConfigParser()
33
+ cfg.read(ENV_FILE)
34
+ try:
35
+ profile = cfg["DEFAULT"]["current_environment"]
36
+ return cfg[profile].get(var)
37
+ except KeyError:
38
+ return
39
+
40
+
41
+ def write_config(creds: dict, environment: str) -> None:
42
+ """
43
+ Write the env variables to the .dhcore.ini file.
44
+ It will overwrite any existing env variables.
45
+
46
+ Parameters
47
+ ----------
48
+ creds : dict
49
+ Credentials.
50
+ environment : str
51
+ Credentials set name.
52
+
53
+ Returns
54
+ -------
55
+ None
56
+ """
57
+ try:
58
+ cfg = ConfigParser()
59
+ cfg.read(ENV_FILE)
60
+
61
+ sections = cfg.sections()
62
+ if environment not in sections:
63
+ cfg.add_section(environment)
64
+
65
+ cfg["DEFAULT"]["current_environment"] = environment
66
+ for k, v in creds.items():
67
+ cfg[environment][k] = v
68
+
69
+ ENV_FILE.touch(exist_ok=True)
70
+ with open(ENV_FILE, "w") as inifile:
71
+ cfg.write(inifile)
72
+
73
+ except Exception as e:
74
+ raise ClientError(f"Failed to write env file: {e}")