digitalhub-runtime-python 0.5.0__tar.gz → 0.5.0b4__tar.gz

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.
Files changed (37) hide show
  1. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/PKG-INFO +2 -2
  2. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/__init__.py +0 -2
  3. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/entities/functions/metadata.py +9 -0
  4. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/functions/spec.py +0 -5
  5. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/entities/runs/metadata.py +9 -0
  6. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/entities/runs/spec.py +53 -0
  7. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/entities/tasks/metadata.py +15 -0
  8. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/entities/tasks/spec.py +99 -0
  9. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/runtimes/runtime.py +52 -50
  10. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/utils/configuration.py +18 -47
  11. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/utils/functions.py +24 -0
  12. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python/utils/inputs.py +153 -0
  13. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/utils/outputs.py +19 -34
  14. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python.egg-info/PKG-INFO +2 -2
  15. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python.egg-info/SOURCES.txt +4 -1
  16. digitalhub_runtime_python-0.5.0b4/digitalhub_runtime_python.egg-info/requires.txt +1 -0
  17. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/pyproject.toml +3 -3
  18. digitalhub_runtime_python-0.5.0/digitalhub_runtime_python/entities/runs/spec.py +0 -81
  19. digitalhub_runtime_python-0.5.0/digitalhub_runtime_python/entities/tasks/spec.py +0 -87
  20. digitalhub_runtime_python-0.5.0/digitalhub_runtime_python/runtimes/kind_registry.py +0 -15
  21. digitalhub_runtime_python-0.5.0/digitalhub_runtime_python/utils/inputs.py +0 -149
  22. digitalhub_runtime_python-0.5.0/digitalhub_runtime_python.egg-info/requires.txt +0 -1
  23. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/LICENSE.txt +0 -0
  24. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/README.md +0 -0
  25. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/__init__.py +0 -0
  26. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/functions/__init__.py +0 -0
  27. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/functions/status.py +0 -0
  28. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/runs/__init__.py +0 -0
  29. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/runs/status.py +0 -0
  30. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/tasks/__init__.py +0 -0
  31. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/tasks/models.py +0 -0
  32. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/entities/tasks/status.py +0 -0
  33. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/runtimes/__init__.py +0 -0
  34. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python/utils/utils.py +0 -0
  35. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python.egg-info/dependency_links.txt +0 -0
  36. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/digitalhub_runtime_python.egg-info/top_level.txt +0 -0
  37. {digitalhub_runtime_python-0.5.0 → digitalhub_runtime_python-0.5.0b4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: digitalhub-runtime-python
3
- Version: 0.5.0
3
+ Version: 0.5.0b4
4
4
  Summary: Python runtime for DHCore
5
5
  Author-email: Fondazione Bruno Kessler <dslab@fbk.eu>, Matteo Martini <mmartini@fbk.eu>
6
6
  License: Apache License
@@ -228,6 +228,6 @@ Classifier: Programming Language :: Python :: 3.10
228
228
  Requires-Python: >=3.9
229
229
  Description-Content-Type: text/markdown
230
230
  License-File: LICENSE.txt
231
- Requires-Dist: digitalhub[ml]<0.6,~=0.5.0b
231
+ Requires-Dist: digitalhub[ml]==0.5.0b4
232
232
 
233
233
  # SDK for DHCore
@@ -9,8 +9,6 @@ root = "digitalhub_runtime_python"
9
9
  runtime_info = {
10
10
  "module": f"{root}.runtimes.runtime",
11
11
  "class_name": "RuntimePython",
12
- "kind_registry_module": f"{root}.runtimes.kind_registry",
13
- "kind_registry_class_name": "kind_registry",
14
12
  }
15
13
 
16
14
  root_ent = f"{root}.entities"
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub_core.entities.functions.metadata import FunctionMetadata
4
+
5
+
6
+ class FunctionMetadataPython(FunctionMetadata):
7
+ """
8
+ Function Python metadata.
9
+ """
@@ -21,7 +21,6 @@ class FunctionSpecPython(FunctionSpec):
21
21
  source: dict,
22
22
  image: str | None = None,
23
23
  base_image: str | None = None,
24
- python_version: str | None = None,
25
24
  requirements: list | None = None,
26
25
  ) -> None:
27
26
  """
@@ -31,7 +30,6 @@ class FunctionSpecPython(FunctionSpec):
31
30
 
32
31
  self.image = image
33
32
  self.base_image = base_image
34
- self.python_version = python_version
35
33
  self.requirements = requirements
36
34
 
37
35
  source = self._source_check(source)
@@ -137,8 +135,5 @@ class FunctionParamsPython(FunctionParams):
137
135
  base_image: str = None
138
136
  "Base image"
139
137
 
140
- python_version: str = None
141
- "Python version"
142
-
143
138
  requirements: list = None
144
139
  "Requirements list, as used by the runtime"
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub_core.entities.runs.metadata import RunMetadata
4
+
5
+
6
+ class RunMetadataPython(RunMetadata):
7
+ """
8
+ Run Python metadata.
9
+ """
@@ -0,0 +1,53 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub_ml.entities.runs.spec import RunParamsMl, RunSpecMl
4
+
5
+
6
+ class RunSpecPython(RunSpecMl):
7
+ """Run Python specification."""
8
+
9
+ def __init__(
10
+ self,
11
+ task: str,
12
+ inputs: dict | None = None,
13
+ outputs: dict | None = None,
14
+ parameters: dict | None = None,
15
+ values: list | None = None,
16
+ local_execution: bool = False,
17
+ **kwargs,
18
+ ) -> None:
19
+ """
20
+ Constructor.
21
+ """
22
+ super().__init__(task, inputs, outputs, parameters, values, local_execution)
23
+
24
+ # Set function and task parameters
25
+ self._any_setter(**kwargs)
26
+
27
+
28
+ class RunParamsPython(RunParamsMl):
29
+ """Run Python parameters."""
30
+
31
+ # Function parameters
32
+ source: dict = None
33
+ image: str = None
34
+ base_image: str = None
35
+ requirements: list = None
36
+
37
+ # Task parameters
38
+ function: str = None
39
+ node_selector: list[dict] = None
40
+ volumes: list[dict] = None
41
+ resources: list[dict] = None
42
+ affinity: dict = None
43
+ tolerations: list[dict] = None
44
+ env: list[dict] = None
45
+ secrets: list[str] = None
46
+ backoff_limit: int = None
47
+ schedule: str = None
48
+ replicas: int = None
49
+
50
+ # Task build
51
+ context_refs: list[dict] = None
52
+ context_sources: list[dict] = None
53
+ instructions: list[str] = None
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from digitalhub_core.entities.tasks.metadata import TaskMetadata
4
+
5
+
6
+ class TaskMetadataJob(TaskMetadata):
7
+ """
8
+ Task Job metadata.
9
+ """
10
+
11
+
12
+ class TaskMetadataBuild(TaskMetadata):
13
+ """
14
+ Task Build metadata.
15
+ """
@@ -0,0 +1,99 @@
1
+ """
2
+ Task Python specification module.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from digitalhub_core.entities.tasks.models import K8s
7
+ from digitalhub_core.entities.tasks.spec import TaskParams, TaskSpec
8
+ from digitalhub_runtime_python.entities.tasks.models import ContextRef, ContextSource
9
+
10
+
11
+ class TaskSpecPythonBase(TaskSpec):
12
+ """Task Python specification for Kubernetes."""
13
+
14
+ def __init__(
15
+ self,
16
+ function: str,
17
+ k8s: dict | None = None,
18
+ ) -> None:
19
+ """
20
+ Constructor.
21
+ """
22
+ super().__init__(function)
23
+ if k8s is None:
24
+ k8s = {}
25
+ k8s = K8s(**k8s).dict(by_alias=True)
26
+ self.node_selector = k8s.get("node_selector")
27
+ self.volumes = k8s.get("volumes")
28
+ self.resources = k8s.get("resources")
29
+ self.affinity = k8s.get("affinity")
30
+ self.tolerations = k8s.get("tolerations")
31
+ self.envs = k8s.get("envs")
32
+ self.secrets = k8s.get("secrets")
33
+ self.backoff_limit = k8s.get("backoff_limit")
34
+ self.schedule = k8s.get("schedule")
35
+ self.replicas = k8s.get("replicas")
36
+
37
+
38
+ class TaskSpecJob(TaskSpecPythonBase):
39
+ """Task Job specification."""
40
+
41
+ def __init__(
42
+ self,
43
+ function: str,
44
+ k8s: dict | None = None,
45
+ ) -> None:
46
+ """
47
+ Constructor.
48
+ """
49
+ super().__init__(function, k8s)
50
+
51
+ self.backoff_limit = k8s.get("backoff_limit")
52
+ self.schedule = k8s.get("schedule")
53
+
54
+
55
+ class TaskParamsJob(TaskParams):
56
+ """
57
+ TaskParamsJob model.
58
+ """
59
+
60
+ k8s: K8s = None
61
+ """Kubernetes resources."""
62
+
63
+
64
+ class TaskSpecBuild(TaskSpecPythonBase):
65
+ """Task Build specification."""
66
+
67
+ def __init__(
68
+ self,
69
+ function: str,
70
+ k8s: dict | None = None,
71
+ context_refs: list | None = None,
72
+ context_sources: list | None = None,
73
+ instructions: list | None = None,
74
+ ) -> None:
75
+ """
76
+ Constructor.
77
+ """
78
+ super().__init__(function, k8s)
79
+ self.context_refs = context_refs
80
+ self.context_sources = context_sources
81
+ self.instructions = instructions
82
+
83
+
84
+ class TaskParamsBuild(TaskParams):
85
+ """
86
+ TaskParamsBuild model.
87
+ """
88
+
89
+ k8s: K8s = None
90
+ """Kubernetes resources."""
91
+
92
+ context_refs: list[ContextRef] = None
93
+ """Context references."""
94
+
95
+ context_sources: list[ContextSource] = None
96
+ """Context sources."""
97
+
98
+ instructions: list[str] = None
99
+ """Build instructions."""
@@ -3,18 +3,15 @@ Runtime class for running Python functions.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
- import typing
6
+ from pathlib import Path
7
7
  from typing import Callable
8
8
 
9
- from digitalhub_core.context.builder import get_context
10
9
  from digitalhub_core.runtimes.base import Runtime
11
10
  from digitalhub_core.utils.logger import LOGGER
12
11
  from digitalhub_runtime_python.utils.configuration import get_function_from_source
13
- from digitalhub_runtime_python.utils.inputs import compose_inputs
14
- from digitalhub_runtime_python.utils.outputs import build_status, parse_outputs
15
-
16
- if typing.TYPE_CHECKING:
17
- from digitalhub_core.runtimes.kind_registry import KindRegistry
12
+ from digitalhub_runtime_python.utils.functions import run_python
13
+ from digitalhub_runtime_python.utils.inputs import get_inputs_parameters
14
+ from digitalhub_runtime_python.utils.outputs import build_status
18
15
 
19
16
 
20
17
  class RuntimePython(Runtime):
@@ -22,17 +19,15 @@ class RuntimePython(Runtime):
22
19
  Runtime Python class.
23
20
  """
24
21
 
25
- def __init__(self, kind_registry: KindRegistry, project: str) -> None:
22
+ allowed_actions = ["job", "nuclio"]
23
+
24
+ def __init__(self) -> None:
26
25
  """
27
26
  Constructor.
28
27
  """
29
- super().__init__(kind_registry, project)
30
- ctx = get_context(self.project)
31
- self.root = ctx.runtime_dir
32
- self.tmp_dir = ctx.tmp_dir
33
-
34
- self.root.mkdir(parents=True, exist_ok=True)
35
- self.tmp_dir.mkdir(parents=True, exist_ok=True)
28
+ super().__init__()
29
+ self.root_path = Path("digitalhub_runtime_python")
30
+ self.tmp_path = self.root_path / "temp"
36
31
 
37
32
  def build(self, function: dict, task: dict, run: dict) -> dict:
38
33
  """
@@ -68,27 +63,28 @@ class RuntimePython(Runtime):
68
63
  Status of the executed run.
69
64
  """
70
65
  LOGGER.info("Validating task.")
71
- self._validate_task(run)
66
+ action = self._validate_task(run)
67
+ executable = self._get_executable(action)
72
68
 
73
69
  LOGGER.info("Starting task.")
74
70
  spec = run.get("spec")
75
71
  project = run.get("project")
76
72
 
77
- LOGGER.info("Configuring execution.")
78
- fnc, wrapped = self._configure_execution(spec)
73
+ LOGGER.info("Collecting inputs.")
74
+ fnc_args = self._collect_inputs(spec)
79
75
 
80
- LOGGER.info("Composing function arguments.")
81
- fnc_args = self._compose_args(fnc, spec, project)
76
+ LOGGER.info("Configuring execution.")
77
+ fnc = self._configure_execution(spec)
82
78
 
83
79
  LOGGER.info("Executing run.")
84
- if wrapped:
85
- results: dict = self._execute(fnc, project, **fnc_args)
86
- else:
87
- exec_result = self._execute(fnc, **fnc_args)
88
- LOGGER.info("Collecting outputs.")
89
- results = parse_outputs(exec_result, list(spec.get("outputs", {})), project)
80
+ results = self._execute(executable, fnc, project, **fnc_args)
90
81
 
91
- status = build_status(results, spec.get("outputs"))
82
+ LOGGER.info("Collecting outputs.")
83
+ status = build_status(
84
+ results,
85
+ spec.get("outputs", {}),
86
+ spec.get("values", {}),
87
+ )
92
88
 
93
89
  # Return run status
94
90
  LOGGER.info("Task completed, returning run status.")
@@ -109,51 +105,57 @@ class RuntimePython(Runtime):
109
105
  Callable
110
106
  Function to execute.
111
107
  """
108
+ if action == "job":
109
+ return run_python
112
110
  raise NotImplementedError
113
111
 
114
112
  ####################
115
- # Configuration
113
+ # Inputs
116
114
  ####################
117
115
 
118
- def _configure_execution(self, spec: dict) -> tuple[Callable, bool]:
116
+ def _collect_inputs(self, spec: dict) -> dict:
119
117
  """
120
- Configure execution.
118
+ Collect inputs.
121
119
 
122
120
  Parameters
123
121
  ----------
124
122
  spec : dict
125
- Run spec.
123
+ Run specs.
124
+ project : str
125
+ Project name.
126
126
 
127
127
  Returns
128
128
  -------
129
- Callable
130
- Function to execute.
129
+ dict
130
+ Parameters.
131
131
  """
132
- fnc = get_function_from_source(
133
- self.root,
134
- spec.get("source", {}),
132
+ LOGGER.info("Getting inputs.")
133
+ self.tmp_path.mkdir(parents=True, exist_ok=True)
134
+ return get_inputs_parameters(
135
+ spec.get("inputs", {}),
136
+ spec.get("parameters", {}),
137
+ self.tmp_path,
135
138
  )
136
- return fnc, hasattr(fnc, "__wrapped__")
137
139
 
138
- def _compose_args(self, func: Callable, spec: dict, project: str) -> dict:
140
+ ####################
141
+ # Configuration
142
+ ####################
143
+
144
+ def _configure_execution(self, spec: dict) -> Callable:
139
145
  """
140
- Collect inputs.
146
+ Configure execution.
141
147
 
142
148
  Parameters
143
149
  ----------
144
- func : Callable
145
- Function to execute.
146
150
  spec : dict
147
- Run specs.
148
- project : str
149
- Project name.
151
+ Run spec.
150
152
 
151
153
  Returns
152
154
  -------
153
- dict
154
- Parameters.
155
+ Callable
156
+ Function to execute.
155
157
  """
156
- inputs = spec.get("inputs", {})
157
- parameters = spec.get("parameters", {})
158
- local_execution = spec.get("local_execution")
159
- return compose_inputs(inputs, parameters, local_execution, func, project)
158
+ return get_function_from_source(
159
+ self.root_path,
160
+ spec.get("source", {}),
161
+ )
@@ -13,7 +13,6 @@ from digitalhub_core.utils.generic_utils import (
13
13
  )
14
14
  from digitalhub_core.utils.git_utils import clone_repository
15
15
  from digitalhub_core.utils.logger import LOGGER
16
- from digitalhub_core.utils.uri_utils import map_uri_scheme
17
16
 
18
17
 
19
18
  def get_function_from_source(path: Path, source_spec: dict) -> Callable:
@@ -34,35 +33,14 @@ def get_function_from_source(path: Path, source_spec: dict) -> Callable:
34
33
  """
35
34
  try:
36
35
  function_code = save_function_source(path, source_spec)
37
- handler_path, function_name = parse_handler(source_spec["handler"])
38
- function_path = (function_code / handler_path).with_suffix(".py")
39
- return import_function(function_path, function_name)
36
+ handler = source_spec["handler"]
37
+ return import_function(function_code, handler)
40
38
  except Exception as e:
41
39
  msg = f"Some error occurred while getting function. Exception: {e.__class__}. Error: {e.args}"
42
40
  LOGGER.exception(msg)
43
41
  raise RuntimeError(msg) from e
44
42
 
45
43
 
46
- def parse_handler(handler: str) -> tuple:
47
- """
48
- Parse handler.
49
-
50
- Parameters
51
- ----------
52
- handler : str
53
- Function handler
54
-
55
- Returns
56
- -------
57
- str
58
- Function handler.
59
- """
60
- parsed = handler.split(":")
61
- if len(parsed) == 1:
62
- return Path(""), parsed[0]
63
- return Path(*parsed[0].split(".")), parsed[1]
64
-
65
-
66
44
  def save_function_source(path: Path, source_spec: dict) -> Path:
67
45
  """
68
46
  Save function source.
@@ -85,45 +63,38 @@ def save_function_source(path: Path, source_spec: dict) -> Path:
85
63
  # Get relevant information
86
64
  base64 = source_spec.get("base64")
87
65
  source = source_spec.get("source")
66
+ handler = source_spec.get("handler")
88
67
 
89
- scheme = None
90
- if source is not None:
91
- scheme = map_uri_scheme(source)
92
-
93
- # Base64
94
68
  if base64 is not None:
95
- filename = "main.py"
96
- if scheme == "local":
97
- filename = Path(source).name
98
-
99
- base64_path = path / filename
100
- base64_path.write_text(decode_base64(base64))
69
+ path = path / "main.py"
70
+ path.write_text(decode_base64(base64))
71
+ return path
101
72
 
102
- if scheme is None or scheme == "local":
103
- return base64_path
104
-
105
- # Git repo
106
- if scheme == "git":
107
- get_repository(path, source)
73
+ scheme = source.split("://")[0]
108
74
 
109
75
  # Http(s) or s3 presigned urls
110
- elif scheme == "remote":
76
+ if scheme in ["http", "https"]:
111
77
  filename = path / "archive.zip"
112
78
  get_remote_source(source, filename)
113
79
  unzip(path, filename)
80
+ return path / handler
81
+
82
+ # Git repo
83
+ if scheme == "git+https":
84
+ path = path / "repository"
85
+ get_repository(path, source)
86
+ return path / handler
114
87
 
115
88
  # S3 path
116
- elif scheme == "s3":
89
+ if scheme == "zip+s3":
117
90
  filename = path / "archive.zip"
118
91
  bucket, key = get_bucket_and_key(source)
119
92
  get_s3_source(bucket, key, filename)
120
93
  unzip(path, filename)
94
+ return path / handler
121
95
 
122
96
  # Unsupported scheme
123
- else:
124
- raise RuntimeError(f"Unsupported scheme: {scheme}")
125
-
126
- return path
97
+ raise RuntimeError(f"Unsupported scheme: {scheme}")
127
98
 
128
99
 
129
100
  def get_remote_source(source: str, filename: Path) -> None:
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable
4
+
5
+
6
+ def run_python(func: Callable, *args, **kwargs) -> dict:
7
+ """
8
+ Execute function.
9
+
10
+ Parameters
11
+ ----------
12
+ func : Callable
13
+ Function.
14
+ args : tuple
15
+ Function arguments.
16
+ kwargs : dict
17
+ Function keyword arguments.
18
+
19
+ Returns
20
+ -------
21
+ dict
22
+ Outputs from the function.
23
+ """
24
+ return func(*args, **kwargs)
@@ -0,0 +1,153 @@
1
+ from __future__ import annotations
2
+
3
+ import typing
4
+ from pathlib import Path
5
+
6
+ from digitalhub_core.entities.artifacts.crud import artifact_from_dict
7
+ from digitalhub_core.utils.exceptions import EntityError
8
+ from digitalhub_core.utils.generic_utils import parse_entity_key
9
+ from digitalhub_core.utils.logger import LOGGER
10
+ from digitalhub_data.entities.dataitems.crud import dataitem_from_dict
11
+ from digitalhub_ml.entities.models.crud import model_from_dict
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from digitalhub_core.entities._base.entity import Entity
15
+ from digitalhub_core.entities.artifacts.entity import Artifact
16
+ from digitalhub_data.entities.dataitems.entity._base import Dataitem
17
+ from digitalhub_ml.entities.models.entity import Model
18
+ from pandas import DataFrame
19
+
20
+
21
+ def persist_dataitem(dataitem: Dataitem, tmp_dir: Path) -> str:
22
+ """
23
+ Persist dataitem locally.
24
+
25
+ Parameters
26
+ ----------
27
+ dataitem : Dataitem
28
+ The dataitem to persist.
29
+ tmp_dir : Path
30
+ Temporary download directory.
31
+
32
+ Returns
33
+ -------
34
+ str
35
+ Temporary dataitem path.
36
+
37
+ Raises
38
+ ------
39
+ EntityError
40
+ If the dataitem cannot be persisted.
41
+ """
42
+ name = dataitem.name
43
+ try:
44
+ LOGGER.info(f"Persisting dataitem '{name}' locally.")
45
+ tmp_path = tmp_dir / f"{name}.csv"
46
+ dataframe: DataFrame = dataitem.as_df()
47
+ dataframe.to_csv(tmp_path, sep=",", index=False)
48
+ return str(tmp_path)
49
+ except Exception as e:
50
+ msg = f"Error during dataitem '{name}' collection. Exception: {e.__class__}. Error: {e.args}"
51
+ LOGGER.exception(msg)
52
+ raise EntityError(msg) from e
53
+
54
+
55
+ def persist_artifact(artifact: Artifact, tmp_dir: Path) -> str:
56
+ """
57
+ Persist artifact locally.
58
+
59
+ Parameters
60
+ ----------
61
+ artifact : Artifact
62
+ The artifact object.
63
+ tmp_dir : Path
64
+ Temporary directory.
65
+
66
+ Returns
67
+ -------
68
+ str
69
+ Temporary artifact path.
70
+
71
+ Raises
72
+ ------
73
+ EntityError
74
+ If the artifact cannot be persisted.
75
+ """
76
+ name = artifact.name
77
+ try:
78
+ LOGGER.info(f"Persisting artifact '{name}' locally.")
79
+ filename = Path(artifact.spec.path).name
80
+ dst = tmp_dir / filename
81
+ tmp_path = artifact.download(dst=dst)
82
+ return str(tmp_path)
83
+ except Exception as e:
84
+ msg = f"Error during artifact '{name}' collection. Exception: {e.__class__}. Error: {e.args}"
85
+ LOGGER.exception(msg)
86
+ raise EntityError(msg) from e
87
+
88
+
89
+ def persist_model(model: Model, tmp_dir: Path) -> str:
90
+ """
91
+ Persist model locally.
92
+
93
+ Parameters
94
+ ----------
95
+ model : Model
96
+ The model object.
97
+ tmp_dir : Path
98
+ Temporary directory.
99
+
100
+ Returns
101
+ -------
102
+ str
103
+ Temporary model path.
104
+
105
+ Raises
106
+ ------
107
+ EntityError
108
+ If the model cannot be persisted.
109
+ """
110
+ name = model.name
111
+ try:
112
+ LOGGER.info(f"Persisting model '{name}' locally.")
113
+ filename = Path(model.spec.path).name
114
+ dst = tmp_dir / filename
115
+ tmp_path = model.download(dst=dst)
116
+ return str(tmp_path)
117
+ except Exception as e:
118
+ msg = f"Error during model '{name}' collection. Exception: {e.__class__}. Error: {e.args}"
119
+ LOGGER.exception(msg)
120
+ raise EntityError(msg) from e
121
+
122
+
123
+ def get_inputs_parameters(inputs: dict[str, Entity], parameters: dict, tmp_dir: Path) -> dict:
124
+ """
125
+ Set inputs.
126
+
127
+ Parameters
128
+ ----------
129
+ inputs : dict[str, Entity]
130
+ Run inputs.
131
+ parameters : dict
132
+ Run parameters.
133
+ tmp_dir : Path
134
+ Temporary directory for storing dataitms and artifacts.
135
+
136
+ Returns
137
+ -------
138
+ dict
139
+ Mlrun inputs.
140
+ """
141
+ inputs_objects = {}
142
+ for k, v in inputs.items():
143
+ _, entity_type, _, _, _ = parse_entity_key(v.get("key"))
144
+ if entity_type == "dataitems":
145
+ v = dataitem_from_dict(v)
146
+ inputs_objects[k] = persist_dataitem(v, tmp_dir)
147
+ elif entity_type == "artifacts":
148
+ v = artifact_from_dict(v)
149
+ inputs_objects[k] = persist_artifact(v, tmp_dir)
150
+ elif entity_type == "models":
151
+ v = model_from_dict(v)
152
+ inputs_objects[k] = persist_model(v, tmp_dir)
153
+ return {**inputs_objects, **parameters}
@@ -1,16 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import pickle
4
+ import typing
4
5
  from typing import Any
5
6
 
6
7
  from digitalhub_core.entities._base.status import State
7
8
  from digitalhub_core.entities.artifacts.crud import new_artifact
8
- from digitalhub_core.entities.artifacts.entity import Artifact
9
9
  from digitalhub_core.utils.logger import LOGGER
10
10
  from digitalhub_data.entities.dataitems.crud import new_dataitem
11
- from digitalhub_data.entities.dataitems.entity.table import DataitemTable
12
11
  from digitalhub_data.readers.registry import DATAFRAME_TYPES
13
12
 
13
+ if typing.TYPE_CHECKING:
14
+ from digitalhub_core.entities.artifacts.entity import Artifact
15
+ from digitalhub_data.entities.dataitems.entity.table import DataitemTable
16
+
14
17
 
15
18
  def collect_outputs(results: Any, outputs: list[str], project_name: str) -> dict:
16
19
  """
@@ -49,32 +52,6 @@ def collect_outputs(results: Any, outputs: list[str], project_name: str) -> dict
49
52
  return objects
50
53
 
51
54
 
52
- def parse_outputs(results: Any, run_outputs: list, project_name: str) -> dict:
53
- """
54
- Parse outputs.
55
-
56
- Parameters
57
- ----------
58
- results : Any
59
- Function outputs.
60
- project : Project
61
- Project object.
62
-
63
- Returns
64
- -------
65
- dict
66
- Function outputs.
67
- """
68
- results_list = listify_results(results)
69
- out_list = []
70
- for idx, _ in enumerate(results_list):
71
- try:
72
- out_list.append(run_outputs.pop(0))
73
- except IndexError:
74
- out_list.append(f"output_{idx}")
75
- return collect_outputs(results, out_list, project_name)
76
-
77
-
78
55
  def listify_results(results: Any) -> list:
79
56
  """
80
57
  Listify results.
@@ -165,6 +142,7 @@ def build_and_load_artifact(name: str, project_name: str, data: Any) -> Artifact
165
142
  def build_status(
166
143
  parsed_execution: dict,
167
144
  mapped_outputs: dict | None = None,
145
+ values_list: list | None = None,
168
146
  ) -> dict:
169
147
  """
170
148
  Collect outputs.
@@ -175,24 +153,31 @@ def build_status(
175
153
  Parsed execution dict.
176
154
  mapped_outputs : dict
177
155
  Mapped outputs.
156
+ values_list : list
157
+ Values list.
178
158
 
179
159
  Returns
180
160
  -------
181
161
  dict
182
162
  Status dict.
183
163
  """
184
- results = {}
185
164
  outputs = {}
186
165
  if mapped_outputs is None:
187
166
  mapped_outputs = {}
188
167
 
168
+ results = {}
169
+ if values_list is None:
170
+ values_list = []
171
+
189
172
  try:
190
- for key, _ in mapped_outputs.items():
173
+ for key, value in mapped_outputs.items():
191
174
  if key in parsed_execution:
192
- if isinstance(parsed_execution[key], (DataitemTable, Artifact)):
193
- outputs[key] = parsed_execution[key].key
194
- else:
195
- results[key] = parsed_execution[key]
175
+ outputs[key] = parsed_execution[key].key
176
+
177
+ for value in values_list:
178
+ if value in parsed_execution:
179
+ results[value] = parsed_execution[value]
180
+
196
181
  return {
197
182
  "state": State.COMPLETED.value,
198
183
  "outputs": outputs,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: digitalhub-runtime-python
3
- Version: 0.5.0
3
+ Version: 0.5.0b4
4
4
  Summary: Python runtime for DHCore
5
5
  Author-email: Fondazione Bruno Kessler <dslab@fbk.eu>, Matteo Martini <mmartini@fbk.eu>
6
6
  License: Apache License
@@ -228,6 +228,6 @@ Classifier: Programming Language :: Python :: 3.10
228
228
  Requires-Python: >=3.9
229
229
  Description-Content-Type: text/markdown
230
230
  License-File: LICENSE.txt
231
- Requires-Dist: digitalhub[ml]<0.6,~=0.5.0b
231
+ Requires-Dist: digitalhub[ml]==0.5.0b4
232
232
 
233
233
  # SDK for DHCore
@@ -9,19 +9,22 @@ digitalhub_runtime_python.egg-info/requires.txt
9
9
  digitalhub_runtime_python.egg-info/top_level.txt
10
10
  digitalhub_runtime_python/entities/__init__.py
11
11
  digitalhub_runtime_python/entities/functions/__init__.py
12
+ digitalhub_runtime_python/entities/functions/metadata.py
12
13
  digitalhub_runtime_python/entities/functions/spec.py
13
14
  digitalhub_runtime_python/entities/functions/status.py
14
15
  digitalhub_runtime_python/entities/runs/__init__.py
16
+ digitalhub_runtime_python/entities/runs/metadata.py
15
17
  digitalhub_runtime_python/entities/runs/spec.py
16
18
  digitalhub_runtime_python/entities/runs/status.py
17
19
  digitalhub_runtime_python/entities/tasks/__init__.py
20
+ digitalhub_runtime_python/entities/tasks/metadata.py
18
21
  digitalhub_runtime_python/entities/tasks/models.py
19
22
  digitalhub_runtime_python/entities/tasks/spec.py
20
23
  digitalhub_runtime_python/entities/tasks/status.py
21
24
  digitalhub_runtime_python/runtimes/__init__.py
22
- digitalhub_runtime_python/runtimes/kind_registry.py
23
25
  digitalhub_runtime_python/runtimes/runtime.py
24
26
  digitalhub_runtime_python/utils/configuration.py
27
+ digitalhub_runtime_python/utils/functions.py
25
28
  digitalhub_runtime_python/utils/inputs.py
26
29
  digitalhub_runtime_python/utils/outputs.py
27
30
  digitalhub_runtime_python/utils/utils.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "digitalhub-runtime-python"
7
- version = "0.5.0"
7
+ version = "0.5.0b4"
8
8
  description = "Python runtime for DHCore"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -20,7 +20,7 @@ classifiers = [
20
20
  keywords = ["data", "dataops", "kubernetes"]
21
21
  requires-python = ">=3.9"
22
22
  dependencies = [
23
- "digitalhub[ml]~=0.5.0b, <0.6",
23
+ "digitalhub[ml]==0.5.0b4",
24
24
  ]
25
25
 
26
26
  [project.urls]
@@ -39,7 +39,7 @@ line-length = 120
39
39
  convention = "numpy"
40
40
 
41
41
  [tool.bumpver]
42
- current_version = "0.5.0"
42
+ current_version = "0.5.0b4"
43
43
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
44
44
  commit_message = "Bump version {old_version} -> {new_version}"
45
45
  commit = false
@@ -1,81 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from digitalhub_ml.entities.runs.spec import RunParamsMl, RunSpecMl
4
-
5
-
6
- class RunSpecPython(RunSpecMl):
7
- """Run Python specification."""
8
-
9
- def __init__(
10
- self,
11
- task: str,
12
- local_execution: bool = False,
13
- **kwargs,
14
- ) -> None:
15
- """
16
- Constructor.
17
- """
18
- super().__init__(task, local_execution)
19
-
20
- self.source = kwargs.get("source")
21
- self.image = kwargs.get("image")
22
- self.base_image = kwargs.get("base_image")
23
- self.python_version = kwargs.get("python_version")
24
- self.requirements = kwargs.get("requirements")
25
-
26
- self.function = kwargs.get("function")
27
- self.node_selector = kwargs.get("node_selector")
28
- self.volumes = kwargs.get("volumes")
29
- self.resources = kwargs.get("resources")
30
- self.affinity = kwargs.get("affinity")
31
- self.tolerations = kwargs.get("tolerations")
32
- self.env = kwargs.get("env")
33
- self.secrets = kwargs.get("secrets")
34
- self.backoff_limit = kwargs.get("backoff_limit")
35
- self.schedule = kwargs.get("schedule")
36
- self.replicas = kwargs.get("replicas")
37
-
38
- # Task job
39
-
40
- # Task build
41
- self.instructions = kwargs.get("instructions")
42
-
43
- self.inputs = kwargs.get("inputs")
44
- self.outputs = kwargs.get("outputs")
45
- self.parameters = kwargs.get("parameters")
46
-
47
-
48
- class RunParamsPython(RunParamsMl):
49
- """Run Python parameters."""
50
-
51
- # Function parameters
52
- source: dict = None
53
- image: str = None
54
- base_image: str = None
55
- python_version: str = None
56
- requirements: list = None
57
-
58
- # Task parameters
59
- function: str = None
60
- node_selector: list[dict] = None
61
- volumes: list[dict] = None
62
- resources: list[dict] = None
63
- affinity: dict = None
64
- tolerations: list[dict] = None
65
- env: list[dict] = None
66
- secrets: list[str] = None
67
-
68
- # Task job
69
- backoff_limit: int = None
70
-
71
- # Task serve
72
- service_type: str = None
73
- replicas: int = None
74
-
75
- # Task build
76
- instructions: list[str] = None
77
-
78
- # Run parameters
79
- inputs: dict = None
80
- outputs: dict = None
81
- parameters: dict = None
@@ -1,87 +0,0 @@
1
- """
2
- Task Python specification module.
3
- """
4
- from __future__ import annotations
5
-
6
- from typing import Literal
7
-
8
- from digitalhub_core.entities.tasks.spec import TaskParamsK8s, TaskSpecK8s
9
-
10
-
11
- class TaskSpecJob(TaskSpecK8s):
12
- """Task Job specification."""
13
-
14
- def __init__(
15
- self,
16
- function: str,
17
- **kwargs,
18
- ) -> None:
19
- """
20
- Constructor.
21
- """
22
- super().__init__(function, **kwargs)
23
-
24
- self.backoff_limit = kwargs.get("backoff_limit")
25
-
26
-
27
- class TaskParamsJob(TaskParamsK8s):
28
- """
29
- TaskParamsJob model.
30
- """
31
-
32
- backoff_limit: int = None
33
- """Backoff limit."""
34
-
35
-
36
- class TaskSpecBuild(TaskSpecK8s):
37
- """Task Build specification."""
38
-
39
- def __init__(
40
- self,
41
- function: str,
42
- instructions: list | None = None,
43
- **kwargs,
44
- ) -> None:
45
- """
46
- Constructor.
47
- """
48
- super().__init__(function, **kwargs)
49
-
50
- self.instructions = instructions
51
-
52
-
53
- class TaskParamsBuild(TaskParamsK8s):
54
- """
55
- TaskParamsBuild model.
56
- """
57
-
58
- instructions: list[str] = None
59
- """Build instructions."""
60
-
61
-
62
- class TaskSpecServe(TaskSpecK8s):
63
- """Task Serve specification."""
64
-
65
- def __init__(
66
- self,
67
- function: str,
68
- replicas: int | None = None,
69
- service_type: str | None = None,
70
- **kwargs,
71
- ) -> None:
72
- """
73
- Constructor.
74
- """
75
- super().__init__(function, **kwargs)
76
-
77
- self.replicas = replicas
78
- self.service_type = service_type
79
-
80
-
81
- class TaskParamsServe(TaskParamsK8s):
82
- """
83
- TaskParamsServe model.
84
- """
85
-
86
- replicas: int = None
87
- service_type: Literal["ClusterIP", "NodePort", "LoadBalancer"] = "NodePort"
@@ -1,15 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from digitalhub_core.runtimes.kind_registry import KindRegistry
4
-
5
- kind_registry = KindRegistry(
6
- {
7
- "executable": {"kind": "python"},
8
- "task": [
9
- {"kind": "python+job", "action": "job"},
10
- {"kind": "python+serve", "action": "serve"},
11
- {"kind": "python+build", "action": "build"},
12
- ],
13
- "run": {"kind": "python+run"},
14
- }
15
- )
@@ -1,149 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import inspect
4
- import typing
5
- from typing import Any, Callable
6
-
7
- from digitalhub_core.context.builder import get_context
8
- from digitalhub_core.entities.artifacts.crud import artifact_from_dict
9
- from digitalhub_core.utils.generic_utils import parse_entity_key
10
- from digitalhub_core.utils.logger import LOGGER
11
- from digitalhub_data.entities.dataitems.crud import dataitem_from_dict
12
- from digitalhub_ml.entities.entity_types import EntityTypes
13
- from digitalhub_ml.entities.models.crud import model_from_dict
14
- from digitalhub_ml.entities.projects.crud import get_project
15
-
16
- if typing.TYPE_CHECKING:
17
- from digitalhub_core.entities._base.entity import Entity
18
- from digitalhub_core.entities.projects.entity import Project
19
-
20
-
21
- def get_project_(project_name: str) -> Project:
22
- """
23
- Get project.
24
-
25
- Parameters
26
- ----------
27
- project_name : str
28
- Project name.
29
-
30
- Returns
31
- -------
32
- Project
33
- Project.
34
- """
35
- try:
36
- ctx = get_context(project_name)
37
- return get_project(project_name, local=ctx.local)
38
- except Exception as e:
39
- msg = f"Error during project collection. Exception: {e.__class__}. Error: {e.args}"
40
- LOGGER.exception(msg)
41
- raise RuntimeError(msg)
42
-
43
-
44
- def get_entity_inputs(inputs: dict) -> dict[str, Entity]:
45
- """
46
- Set inputs.
47
-
48
- Parameters
49
- ----------
50
- inputs : dict
51
- Run inputs.
52
- parameters : dict
53
- Run parameters.
54
- tmp_dir : Path
55
- Temporary directory for storing dataitms and artifacts.
56
-
57
- Returns
58
- -------
59
- dict
60
- Mlrun inputs.
61
- """
62
- try:
63
- inputs_objects = {}
64
- for k, v in inputs.items():
65
- _, entity_type, _, _, _ = parse_entity_key(v.get("key"))
66
- if entity_type == EntityTypes.DATAITEMS.value:
67
- inputs_objects[k] = dataitem_from_dict(v)
68
- elif entity_type == EntityTypes.ARTIFACTS.value:
69
- inputs_objects[k] = artifact_from_dict(v)
70
- elif entity_type == EntityTypes.MODELS.value:
71
- inputs_objects[k] = model_from_dict(v)
72
- return inputs_objects
73
- except Exception as e:
74
- msg = f"Error during inputs collection. Exception: {e.__class__}. Error: {e.args}"
75
- LOGGER.exception(msg)
76
- raise RuntimeError(msg) from e
77
-
78
-
79
- def compose_inputs(
80
- inputs: dict,
81
- parameters: dict,
82
- local_execution: bool,
83
- func: Callable,
84
- project: str | Project,
85
- context: Any | None = None,
86
- event: Any | None = None,
87
- ) -> dict:
88
- """
89
- Compose inputs.
90
-
91
- Parameters
92
- ----------
93
- inputs : dict
94
- Run inputs.
95
- parameters : dict
96
- Run parameters.
97
- local_execution : bool
98
- Local execution.
99
- func : Callable
100
- Function to execute.
101
- project : str
102
- Project name.
103
- context : nuclio_sdk.Context
104
- Nuclio context.
105
- event : nuclio_sdk.Event
106
- Nuclio event.
107
-
108
- Returns
109
- -------
110
- dict
111
- Function inputs.
112
- """
113
- try:
114
- entity_inputs = get_entity_inputs(inputs)
115
- fnc_args = {**parameters, **entity_inputs}
116
-
117
- fnc_parameters = inspect.signature(func).parameters
118
- LOGGER.info(f"Function parameters: {'project' in fnc_parameters}")
119
-
120
- _has_project = "project" in fnc_parameters
121
- _has_context = "context" in fnc_parameters
122
- _has_event = "event" in fnc_parameters
123
-
124
- if _has_project:
125
- if _has_context:
126
- fnc_args["project"] = context.project
127
- elif isinstance(project, str):
128
- fnc_args["project"] = get_project_(project)
129
- else:
130
- fnc_args["project"] = project
131
-
132
- if _has_context:
133
- if context is not None and not local_execution:
134
- fnc_args["context"] = context
135
- else:
136
- raise RuntimeError("Context is not available on local execution.")
137
-
138
- if _has_event:
139
- if event is not None and not local_execution:
140
- fnc_args["event"] = event
141
- else:
142
- raise RuntimeError("Event is not available on local execution.")
143
-
144
- return fnc_args
145
-
146
- except Exception as e:
147
- msg = f"Error during function arguments compostion. Exception: {e.__class__}. Error: {e.args}"
148
- LOGGER.exception(msg)
149
- raise RuntimeError(msg) from e
@@ -1 +0,0 @@
1
- digitalhub[ml]<0.6,~=0.5.0b