digitalhub 0.8.0b0__py3-none-any.whl → 0.8.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 +62 -94
- digitalhub/client/__init__.py +0 -0
- digitalhub/client/builder.py +105 -0
- digitalhub/client/objects/__init__.py +0 -0
- digitalhub/client/objects/base.py +56 -0
- digitalhub/client/objects/dhcore.py +681 -0
- digitalhub/client/objects/local.py +533 -0
- digitalhub/context/__init__.py +0 -0
- digitalhub/context/builder.py +178 -0
- digitalhub/context/context.py +136 -0
- digitalhub/datastores/__init__.py +0 -0
- digitalhub/datastores/builder.py +134 -0
- digitalhub/datastores/objects/__init__.py +0 -0
- digitalhub/datastores/objects/base.py +85 -0
- digitalhub/datastores/objects/local.py +42 -0
- digitalhub/datastores/objects/remote.py +23 -0
- digitalhub/datastores/objects/s3.py +38 -0
- digitalhub/datastores/objects/sql.py +60 -0
- digitalhub/entities/__init__.py +0 -0
- digitalhub/entities/_base/__init__.py +0 -0
- digitalhub/entities/_base/api.py +346 -0
- digitalhub/entities/_base/base.py +82 -0
- digitalhub/entities/_base/crud.py +610 -0
- digitalhub/entities/_base/entity/__init__.py +0 -0
- digitalhub/entities/_base/entity/base.py +132 -0
- digitalhub/entities/_base/entity/context.py +118 -0
- digitalhub/entities/_base/entity/executable.py +380 -0
- digitalhub/entities/_base/entity/material.py +214 -0
- digitalhub/entities/_base/entity/unversioned.py +87 -0
- digitalhub/entities/_base/entity/versioned.py +94 -0
- digitalhub/entities/_base/metadata.py +59 -0
- digitalhub/entities/_base/spec/__init__.py +0 -0
- digitalhub/entities/_base/spec/base.py +58 -0
- digitalhub/entities/_base/spec/material.py +22 -0
- digitalhub/entities/_base/state.py +31 -0
- digitalhub/entities/_base/status/__init__.py +0 -0
- digitalhub/entities/_base/status/base.py +32 -0
- digitalhub/entities/_base/status/material.py +49 -0
- digitalhub/entities/_builders/__init__.py +0 -0
- digitalhub/entities/_builders/metadata.py +60 -0
- digitalhub/entities/_builders/name.py +31 -0
- digitalhub/entities/_builders/spec.py +43 -0
- digitalhub/entities/_builders/status.py +62 -0
- digitalhub/entities/_builders/uuid.py +33 -0
- digitalhub/entities/artifact/__init__.py +0 -0
- digitalhub/entities/artifact/builder.py +133 -0
- digitalhub/entities/artifact/crud.py +358 -0
- digitalhub/entities/artifact/entity/__init__.py +0 -0
- digitalhub/entities/artifact/entity/_base.py +39 -0
- digitalhub/entities/artifact/entity/artifact.py +9 -0
- digitalhub/entities/artifact/spec.py +39 -0
- digitalhub/entities/artifact/status.py +15 -0
- digitalhub/entities/dataitem/__init__.py +0 -0
- digitalhub/entities/dataitem/builder.py +144 -0
- digitalhub/entities/dataitem/crud.py +395 -0
- digitalhub/entities/dataitem/entity/__init__.py +0 -0
- digitalhub/entities/dataitem/entity/_base.py +75 -0
- digitalhub/entities/dataitem/entity/dataitem.py +9 -0
- digitalhub/entities/dataitem/entity/iceberg.py +7 -0
- digitalhub/entities/dataitem/entity/table.py +125 -0
- digitalhub/entities/dataitem/models.py +62 -0
- digitalhub/entities/dataitem/spec.py +61 -0
- digitalhub/entities/dataitem/status.py +38 -0
- digitalhub/entities/entity_types.py +19 -0
- digitalhub/entities/function/__init__.py +0 -0
- digitalhub/entities/function/builder.py +86 -0
- digitalhub/entities/function/crud.py +305 -0
- digitalhub/entities/function/entity.py +101 -0
- digitalhub/entities/function/models.py +118 -0
- digitalhub/entities/function/spec.py +81 -0
- digitalhub/entities/function/status.py +9 -0
- digitalhub/entities/model/__init__.py +0 -0
- digitalhub/entities/model/builder.py +152 -0
- digitalhub/entities/model/crud.py +358 -0
- digitalhub/entities/model/entity/__init__.py +0 -0
- digitalhub/entities/model/entity/_base.py +34 -0
- digitalhub/entities/model/entity/huggingface.py +9 -0
- digitalhub/entities/model/entity/mlflow.py +90 -0
- digitalhub/entities/model/entity/model.py +9 -0
- digitalhub/entities/model/entity/sklearn.py +9 -0
- digitalhub/entities/model/models.py +26 -0
- digitalhub/entities/model/spec.py +146 -0
- digitalhub/entities/model/status.py +33 -0
- digitalhub/entities/project/__init__.py +0 -0
- digitalhub/entities/project/builder.py +82 -0
- digitalhub/entities/project/crud.py +350 -0
- digitalhub/entities/project/entity.py +2060 -0
- digitalhub/entities/project/spec.py +50 -0
- digitalhub/entities/project/status.py +9 -0
- digitalhub/entities/registries.py +48 -0
- digitalhub/entities/run/__init__.py +0 -0
- digitalhub/entities/run/builder.py +77 -0
- digitalhub/entities/run/crud.py +232 -0
- digitalhub/entities/run/entity.py +461 -0
- digitalhub/entities/run/spec.py +153 -0
- digitalhub/entities/run/status.py +114 -0
- digitalhub/entities/secret/__init__.py +0 -0
- digitalhub/entities/secret/builder.py +93 -0
- digitalhub/entities/secret/crud.py +294 -0
- digitalhub/entities/secret/entity.py +73 -0
- digitalhub/entities/secret/spec.py +35 -0
- digitalhub/entities/secret/status.py +9 -0
- digitalhub/entities/task/__init__.py +0 -0
- digitalhub/entities/task/builder.py +74 -0
- digitalhub/entities/task/crud.py +241 -0
- digitalhub/entities/task/entity.py +135 -0
- digitalhub/entities/task/models.py +199 -0
- digitalhub/entities/task/spec.py +51 -0
- digitalhub/entities/task/status.py +9 -0
- digitalhub/entities/utils.py +184 -0
- digitalhub/entities/workflow/__init__.py +0 -0
- digitalhub/entities/workflow/builder.py +91 -0
- digitalhub/entities/workflow/crud.py +304 -0
- digitalhub/entities/workflow/entity.py +77 -0
- digitalhub/entities/workflow/spec.py +15 -0
- digitalhub/entities/workflow/status.py +9 -0
- digitalhub/readers/__init__.py +0 -0
- digitalhub/readers/builder.py +54 -0
- digitalhub/readers/objects/__init__.py +0 -0
- digitalhub/readers/objects/base.py +70 -0
- digitalhub/readers/objects/pandas.py +207 -0
- digitalhub/readers/registry.py +15 -0
- digitalhub/registry/__init__.py +0 -0
- digitalhub/registry/models.py +87 -0
- digitalhub/registry/registry.py +74 -0
- digitalhub/registry/utils.py +150 -0
- digitalhub/runtimes/__init__.py +0 -0
- digitalhub/runtimes/base.py +164 -0
- digitalhub/runtimes/builder.py +53 -0
- digitalhub/runtimes/kind_registry.py +170 -0
- digitalhub/stores/__init__.py +0 -0
- digitalhub/stores/builder.py +257 -0
- digitalhub/stores/objects/__init__.py +0 -0
- digitalhub/stores/objects/base.py +189 -0
- digitalhub/stores/objects/local.py +230 -0
- digitalhub/stores/objects/remote.py +143 -0
- digitalhub/stores/objects/s3.py +563 -0
- digitalhub/stores/objects/sql.py +328 -0
- digitalhub/utils/__init__.py +0 -0
- digitalhub/utils/data_utils.py +127 -0
- digitalhub/utils/env_utils.py +123 -0
- digitalhub/utils/exceptions.py +55 -0
- digitalhub/utils/file_utils.py +204 -0
- digitalhub/utils/generic_utils.py +207 -0
- digitalhub/utils/git_utils.py +148 -0
- digitalhub/utils/io_utils.py +79 -0
- digitalhub/utils/logger.py +17 -0
- digitalhub/utils/uri_utils.py +56 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/METADATA +27 -12
- digitalhub-0.8.0b1.dist-info/RECORD +161 -0
- test/test_crud_artifacts.py +1 -1
- test/test_crud_dataitems.py +1 -1
- test/test_crud_functions.py +1 -1
- test/test_crud_runs.py +1 -1
- test/test_crud_tasks.py +1 -1
- digitalhub-0.8.0b0.dist-info/RECORD +0 -14
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/LICENSE.txt +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/WHEEL +0 -0
- {digitalhub-0.8.0b0.dist-info → digitalhub-0.8.0b1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from abc import abstractmethod
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
from digitalhub.utils.exceptions import EntityError
|
|
8
|
+
from digitalhub.utils.logger import LOGGER
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from digitalhub.runtimes.kind_registry import KindRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Runtime:
|
|
15
|
+
"""
|
|
16
|
+
Base Runtime class.
|
|
17
|
+
|
|
18
|
+
Runtimes are the entities responsible for the actual execution
|
|
19
|
+
of a given run. They are highly specialized components which
|
|
20
|
+
can translate the representation of a given execution as expressed
|
|
21
|
+
in the run into an actual execution operation performed via
|
|
22
|
+
libraries, code, external tools etc.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, kind_registry: KindRegistry, project: str) -> None:
|
|
26
|
+
self.kind_registry = kind_registry
|
|
27
|
+
self.project = project
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def build(self, executable: dict, task: dict, run: dict) -> dict:
|
|
31
|
+
"""
|
|
32
|
+
Build run spec.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def run(self, run: dict) -> dict:
|
|
37
|
+
"""
|
|
38
|
+
Execute run task.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def _get_executable(action: str) -> Callable:
|
|
44
|
+
"""
|
|
45
|
+
Get executable from action.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
##############################
|
|
49
|
+
# Private methods
|
|
50
|
+
##############################
|
|
51
|
+
|
|
52
|
+
def _validate_task(self, run: dict) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Check if task is allowed. This presumes that the
|
|
55
|
+
runtime holds a list of allowed actions in the self.allowed_actions
|
|
56
|
+
attribute.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
run : dict
|
|
61
|
+
Run object dictionary.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
str
|
|
66
|
+
Action to execute.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
task_kind = run["spec"]["task"].split(":")[0]
|
|
70
|
+
except (KeyError, IndexError):
|
|
71
|
+
msg = "Malformed run spec."
|
|
72
|
+
LOGGER.exception(msg)
|
|
73
|
+
raise RuntimeError(msg)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
return self.get_action_from_task_kind(task_kind)
|
|
77
|
+
except EntityError:
|
|
78
|
+
msg = f"Task {task_kind} not allowed."
|
|
79
|
+
LOGGER.exception(msg)
|
|
80
|
+
raise RuntimeError(msg)
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _execute(func: Callable, *args, **kwargs) -> Any:
|
|
84
|
+
"""
|
|
85
|
+
Execute function.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
func : Callable
|
|
90
|
+
Function to execute.
|
|
91
|
+
*args
|
|
92
|
+
Function arguments.
|
|
93
|
+
**kwargs : dict
|
|
94
|
+
Function keyword arguments.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
Any
|
|
99
|
+
Function result.
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
return func(*args, **kwargs)
|
|
103
|
+
except Exception:
|
|
104
|
+
msg = "Something got wrong during function execution."
|
|
105
|
+
LOGGER.exception(msg)
|
|
106
|
+
raise RuntimeError(msg)
|
|
107
|
+
|
|
108
|
+
##############################
|
|
109
|
+
# Wrapper registry methods
|
|
110
|
+
##############################
|
|
111
|
+
|
|
112
|
+
def get_executable_kind(self) -> str:
|
|
113
|
+
"""
|
|
114
|
+
Get executable kind.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
str
|
|
119
|
+
Executable kind.
|
|
120
|
+
"""
|
|
121
|
+
return self.kind_registry.get_executable_kind()
|
|
122
|
+
|
|
123
|
+
def get_task_kind_from_action(self, action: str) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Get task kind from action.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
action : str
|
|
130
|
+
Task action.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
str
|
|
135
|
+
Task kind.
|
|
136
|
+
"""
|
|
137
|
+
return self.kind_registry.get_task_kind_from_action(action)
|
|
138
|
+
|
|
139
|
+
def get_action_from_task_kind(self, kind: str) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Get action from task.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
kind : str
|
|
146
|
+
Task kind.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
str
|
|
151
|
+
Action.
|
|
152
|
+
"""
|
|
153
|
+
return self.kind_registry.get_action_from_task_kind(kind)
|
|
154
|
+
|
|
155
|
+
def get_run_kind(self) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Get run kind.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
str
|
|
162
|
+
Run kind.
|
|
163
|
+
"""
|
|
164
|
+
return self.kind_registry.get_run_kind()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from digitalhub.registry.registry import registry
|
|
6
|
+
from digitalhub.registry.utils import import_class
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from digitalhub.registry.models import RegistryEntry
|
|
10
|
+
from digitalhub.runtimes.base import Runtime
|
|
11
|
+
from digitalhub.runtimes.kind_registry import KindRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_runtime(kind: str, project: str) -> Runtime:
|
|
15
|
+
"""
|
|
16
|
+
Build runtime object. The builder takes in input a kind.
|
|
17
|
+
This kind can derive from functions, tasks, or runs, and
|
|
18
|
+
is inserted in the global kind registry where the runtimes
|
|
19
|
+
pakages are registered.
|
|
20
|
+
The builder requires the module path where the Runtime
|
|
21
|
+
subclass is defined and the class name. It requires also
|
|
22
|
+
the kind registry module, kind registry class and the project
|
|
23
|
+
name.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
kind : str
|
|
28
|
+
The type of runtime to build.
|
|
29
|
+
project : str
|
|
30
|
+
The project name.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
Runtime
|
|
35
|
+
Runtime object.
|
|
36
|
+
"""
|
|
37
|
+
infos: RegistryEntry = getattr(registry, kind)
|
|
38
|
+
cls = import_class(infos.runtime.module, infos.runtime.class_name)
|
|
39
|
+
kind_registry = get_kind_registry(kind)
|
|
40
|
+
return cls(kind_registry, project)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_kind_registry(kind: str) -> KindRegistry:
|
|
44
|
+
"""
|
|
45
|
+
Get kind registry.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
KindRegistry
|
|
50
|
+
Kind registry.
|
|
51
|
+
"""
|
|
52
|
+
infos: RegistryEntry = getattr(registry, kind)
|
|
53
|
+
return import_class(infos.runtime.kind_registry_module, infos.runtime.kind_registry_class_name)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from digitalhub.utils.exceptions import EntityError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TaskModel(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
Task model.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
kind: str
|
|
14
|
+
action: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RunModel(BaseModel):
|
|
18
|
+
"""
|
|
19
|
+
Run model.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
kind: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ExecutableModel(BaseModel):
|
|
26
|
+
"""
|
|
27
|
+
Executable model.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
kind: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DataModel(BaseModel):
|
|
34
|
+
"""
|
|
35
|
+
Data model.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
executable: ExecutableModel
|
|
39
|
+
task: list[TaskModel]
|
|
40
|
+
run: RunModel
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class KindRegistry:
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
Kind registry module for runtimes.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, data: dict[str, dict[str, str]]) -> None:
|
|
50
|
+
"""
|
|
51
|
+
Constructor.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
data : dict[str, dict[str, str]]
|
|
56
|
+
Data to validate.
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
>>> data = {
|
|
61
|
+
... "executable": {
|
|
62
|
+
... "kind": "executable-kind"
|
|
63
|
+
... },
|
|
64
|
+
... "run": {
|
|
65
|
+
... "kind": "run-kind"
|
|
66
|
+
... },
|
|
67
|
+
... "task": [
|
|
68
|
+
... {"kind": "task-kind-0", "action": "action-0"},
|
|
69
|
+
... {"kind": "task-kind-1", "action": "action-1"},
|
|
70
|
+
... {"kind": "task-kind-2", "action": "action-2"},
|
|
71
|
+
... ]
|
|
72
|
+
... }
|
|
73
|
+
"""
|
|
74
|
+
validated_data = self._validate_data(data)
|
|
75
|
+
self.data = validated_data
|
|
76
|
+
|
|
77
|
+
def _validate_data(self, data: dict[str, dict[str, str]]) -> DataModel:
|
|
78
|
+
"""
|
|
79
|
+
Validate data.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
data : dict[str, dict[str, str]]
|
|
84
|
+
Data to validate.
|
|
85
|
+
|
|
86
|
+
Raises
|
|
87
|
+
------
|
|
88
|
+
EntityError
|
|
89
|
+
If data is not valid.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
return DataModel(**data)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise EntityError("Invalid data.") from e
|
|
95
|
+
|
|
96
|
+
def get_task_kind_from_action(self, action: str) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Get task kind from action.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
action : str
|
|
103
|
+
Task action.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
str
|
|
108
|
+
Task kind.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
EntityError
|
|
113
|
+
If action is not allowed.
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
for task in self.data.task:
|
|
117
|
+
if task.action == action:
|
|
118
|
+
return task.kind
|
|
119
|
+
except StopIteration:
|
|
120
|
+
msg = f"Action {action} not allowed."
|
|
121
|
+
raise EntityError(msg)
|
|
122
|
+
|
|
123
|
+
def get_action_from_task_kind(self, kind: str) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Get action from task.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
kind : str
|
|
130
|
+
Task kind.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
str
|
|
135
|
+
Action.
|
|
136
|
+
|
|
137
|
+
Raises
|
|
138
|
+
------
|
|
139
|
+
EntityError
|
|
140
|
+
If task is not allowed.
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
for task in self.data.task:
|
|
144
|
+
if task.kind == kind:
|
|
145
|
+
return task.action
|
|
146
|
+
except StopIteration:
|
|
147
|
+
msg = f"Task {kind} not allowed."
|
|
148
|
+
raise EntityError(msg)
|
|
149
|
+
|
|
150
|
+
def get_run_kind(self) -> str:
|
|
151
|
+
"""
|
|
152
|
+
Get run kind.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
str
|
|
157
|
+
Run kind.
|
|
158
|
+
"""
|
|
159
|
+
return self.data.run.kind
|
|
160
|
+
|
|
161
|
+
def get_executable_kind(self) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Get executable kind.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
str
|
|
168
|
+
Executable kind.
|
|
169
|
+
"""
|
|
170
|
+
return self.data.executable.kind
|
|
File without changes
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
|
|
8
|
+
from digitalhub.stores.objects.base import StoreParameters
|
|
9
|
+
from digitalhub.stores.objects.local import LocalStore, LocalStoreConfig
|
|
10
|
+
from digitalhub.stores.objects.remote import RemoteStore, RemoteStoreConfig
|
|
11
|
+
from digitalhub.stores.objects.s3 import S3Store, S3StoreConfig
|
|
12
|
+
from digitalhub.stores.objects.sql import SqlStore, SQLStoreConfig
|
|
13
|
+
from digitalhub.utils.exceptions import StoreError
|
|
14
|
+
from digitalhub.utils.uri_utils import map_uri_scheme
|
|
15
|
+
|
|
16
|
+
if typing.TYPE_CHECKING:
|
|
17
|
+
from digitalhub.stores.objects.base import Store
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
REGISTRY_STORES = {
|
|
21
|
+
"local": LocalStore,
|
|
22
|
+
"s3": S3Store,
|
|
23
|
+
"remote": RemoteStore,
|
|
24
|
+
"sql": SqlStore,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class StoreBuilder:
|
|
29
|
+
"""
|
|
30
|
+
Store builder class.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
self._instances: dict[str, Store] = {}
|
|
35
|
+
self._default: Store | None = None
|
|
36
|
+
self._def_scheme = "s3"
|
|
37
|
+
|
|
38
|
+
def build(self, store_cfg: StoreParameters) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Build a store instance and register it.
|
|
41
|
+
It overrides any existing instance.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
store_cfg : StoreParameters
|
|
46
|
+
Store configuration.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
None
|
|
51
|
+
"""
|
|
52
|
+
scheme = map_uri_scheme(store_cfg.type)
|
|
53
|
+
self._instances[scheme] = self.build_store(store_cfg)
|
|
54
|
+
|
|
55
|
+
def get(self, uri: str) -> Store:
|
|
56
|
+
"""
|
|
57
|
+
Get a store instance by URI.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
uri : str
|
|
62
|
+
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
|
|
101
|
+
Store configuration.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
Store
|
|
106
|
+
The store instance.
|
|
107
|
+
|
|
108
|
+
Raises
|
|
109
|
+
------
|
|
110
|
+
NotImplementedError
|
|
111
|
+
If the store type is not implemented.
|
|
112
|
+
"""
|
|
113
|
+
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 == "s3":
|
|
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 == "sql":
|
|
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 == "remote":
|
|
195
|
+
return StoreParameters(
|
|
196
|
+
name="remote",
|
|
197
|
+
type="remote",
|
|
198
|
+
config=RemoteStoreConfig(),
|
|
199
|
+
)
|
|
200
|
+
if scheme == "local":
|
|
201
|
+
return StoreParameters(
|
|
202
|
+
name="local",
|
|
203
|
+
type="local",
|
|
204
|
+
config=LocalStoreConfig(
|
|
205
|
+
path="tempsdk",
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
raise ValueError(f"Unsupported scheme {scheme}")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def set_store(store_cfg: StoreParameters) -> None:
|
|
212
|
+
"""
|
|
213
|
+
Set a new store instance with the given configuration.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
store_cfg : StoreParameters
|
|
218
|
+
Store configuration.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
None
|
|
223
|
+
"""
|
|
224
|
+
store_builder.build(store_cfg)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def get_store(uri: str) -> Store:
|
|
228
|
+
"""
|
|
229
|
+
Get store instance by uri.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
---------
|
|
233
|
+
uri : str
|
|
234
|
+
URI to parse.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
Store
|
|
239
|
+
Store instance.
|
|
240
|
+
"""
|
|
241
|
+
return store_builder.get(uri)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_default_store() -> Store:
|
|
245
|
+
"""
|
|
246
|
+
Get the default store instance. The default store is the one that
|
|
247
|
+
can persist artifacts and dataitems.
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
Store
|
|
252
|
+
Default store instance.
|
|
253
|
+
"""
|
|
254
|
+
return store_builder.default()
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
store_builder = StoreBuilder()
|
|
File without changes
|