digitalhub-runtime-python 0.5.0b11__tar.gz → 0.6.0__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 (36) hide show
  1. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/PKG-INFO +2 -2
  2. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/__init__.py +1 -1
  3. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/entities/functions/models.py +101 -0
  4. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/entities/functions/spec.py +108 -0
  5. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/entities/runs/spec.py +78 -0
  6. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/entities/tasks/models.py +12 -0
  7. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/tasks/spec.py +20 -21
  8. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/tasks/status.py +6 -0
  9. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/runtimes/runtime.py +24 -6
  10. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/utils/configuration.py +29 -0
  11. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/utils/env.py +3 -0
  12. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/utils/inputs.py +9 -12
  13. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python/utils/nuclio_configuration.py +98 -0
  14. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/utils/outputs.py +32 -7
  15. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python.egg-info/PKG-INFO +2 -2
  16. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python.egg-info/SOURCES.txt +3 -0
  17. digitalhub_runtime_python-0.6.0/digitalhub_runtime_python.egg-info/requires.txt +1 -0
  18. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/pyproject.toml +3 -3
  19. digitalhub_runtime_python-0.5.0b11/digitalhub_runtime_python/entities/functions/spec.py +0 -139
  20. digitalhub_runtime_python-0.5.0b11/digitalhub_runtime_python/entities/runs/spec.py +0 -53
  21. digitalhub_runtime_python-0.5.0b11/digitalhub_runtime_python/entities/tasks/models.py +0 -31
  22. digitalhub_runtime_python-0.5.0b11/digitalhub_runtime_python.egg-info/requires.txt +0 -1
  23. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/LICENSE.txt +0 -0
  24. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/README.md +0 -0
  25. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/__init__.py +0 -0
  26. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/functions/__init__.py +0 -0
  27. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/functions/status.py +0 -0
  28. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/runs/__init__.py +0 -0
  29. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/runs/status.py +0 -0
  30. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/entities/tasks/__init__.py +0 -0
  31. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/runtimes/__init__.py +0 -0
  32. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/runtimes/kind_registry.py +0 -0
  33. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python/utils/utils.py +0 -0
  34. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python.egg-info/dependency_links.txt +0 -0
  35. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/digitalhub_runtime_python.egg-info/top_level.txt +0 -0
  36. {digitalhub_runtime_python-0.5.0b11 → digitalhub_runtime_python-0.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: digitalhub-runtime-python
3
- Version: 0.5.0b11
3
+ Version: 0.6.0
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.6.0b
232
232
 
233
233
  # SDK for DHCore
@@ -27,7 +27,7 @@ registry.register(func_kind, func_info)
27
27
 
28
28
  # Tasks
29
29
  entity_type = EntityTypes.TASKS.value
30
- for i in ["job", "build"]:
30
+ for i in ["job", "build", "serve"]:
31
31
  task_kind = f"{func_kind}+{i}"
32
32
  prefix = entity_type.removesuffix("s").capitalize()
33
33
  suffix = i.capitalize()
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from digitalhub_core.entities.functions.models import SourceCodeParams, SourceCodeStruct
6
+ from digitalhub_core.utils.exceptions import EntityError
7
+ from digitalhub_core.utils.generic_utils import encode_source, encode_string
8
+ from digitalhub_core.utils.uri_utils import map_uri_scheme
9
+
10
+
11
+ class SourceCodeStructPython(SourceCodeStruct):
12
+ """
13
+ Source code struct for python.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ source: str | None = None,
19
+ handler: str | None = None,
20
+ code: str | None = None,
21
+ base64: str | None = None,
22
+ lang: str | None = None,
23
+ init_function: str | None = None,
24
+ ) -> None:
25
+ super().__init__(
26
+ source=source,
27
+ handler=handler,
28
+ code=code,
29
+ base64=base64,
30
+ lang=lang,
31
+ )
32
+ self.init_function = init_function
33
+
34
+ @staticmethod
35
+ def source_check(source: dict) -> dict:
36
+ """
37
+ Check source. Overrides SourceCodeStruct.source_check.
38
+
39
+ Parameters
40
+ ----------
41
+ source : dict
42
+ Source.
43
+
44
+ Returns
45
+ -------
46
+ dict
47
+ Checked source.
48
+ """
49
+ # Source check
50
+ source_path = source.get("source")
51
+ code = source.get("code")
52
+ base64 = source.get("base64")
53
+ handler = source.get("handler")
54
+ source["lang"] = "python"
55
+
56
+ if handler is None:
57
+ raise EntityError("Handler must be provided.")
58
+
59
+ if source_path is None and code is None and base64 is None:
60
+ raise EntityError("Source must be provided.")
61
+
62
+ if base64 is not None:
63
+ return source
64
+
65
+ if code is not None:
66
+ source["base64"] = encode_string(code)
67
+ return source
68
+
69
+ if source_path is not None:
70
+ if map_uri_scheme(source_path) == "local":
71
+ if not (Path(source_path).suffix == ".py" and Path(source_path).is_file()):
72
+ raise EntityError("Source is not a valid python file.")
73
+ source["base64"] = encode_source(source_path)
74
+ else:
75
+ if handler is None:
76
+ raise EntityError("Handler must be provided if source is not local.")
77
+
78
+ return source
79
+
80
+ def to_dict(self) -> dict:
81
+ """
82
+ Convert to dictionary.
83
+
84
+ Returns
85
+ -------
86
+ dict
87
+ Dictionary representation of the object.
88
+ """
89
+ dict_ = super().to_dict()
90
+ if self.init_function is not None:
91
+ dict_["init_function"] = self.init_function
92
+ return dict_
93
+
94
+
95
+ class SourceCodeParamsPython(SourceCodeParams):
96
+ """
97
+ Source code params for python.
98
+ """
99
+
100
+ init_function: str = None
101
+ """Handler for init function."""
@@ -0,0 +1,108 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal
4
+
5
+ from digitalhub_core.entities.functions.spec import FunctionParams, FunctionSpec
6
+ from digitalhub_runtime_python.entities.functions.models import SourceCodeParamsPython, SourceCodeStructPython
7
+
8
+
9
+ class FunctionSpecPython(FunctionSpec):
10
+ """
11
+ Specification for a Function job.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ source: dict | None = None,
17
+ code_src: str | None = None,
18
+ handler: str | None = None,
19
+ code: str | None = None,
20
+ base64: str | None = None,
21
+ init_function: str | None = None,
22
+ lang: str | None = None,
23
+ image: str | None = None,
24
+ base_image: str | None = None,
25
+ python_version: str | None = None,
26
+ requirements: list | None = None,
27
+ ) -> None:
28
+ super().__init__()
29
+
30
+ self.image = image
31
+ self.base_image = base_image
32
+ self.python_version = python_version
33
+ self.requirements = requirements
34
+
35
+ # Give source precedence
36
+ if source is not None:
37
+ source_dict = source
38
+ else:
39
+ source_dict = {
40
+ "source": code_src,
41
+ "handler": handler,
42
+ "code": code,
43
+ "base64": base64,
44
+ "lang": lang,
45
+ "init_function": init_function,
46
+ }
47
+
48
+ source_checked = self.source_check(source_dict)
49
+ self.source = SourceCodeStructPython(**source_checked)
50
+
51
+ @staticmethod
52
+ def source_check(source: dict) -> dict:
53
+ """
54
+ Check source.
55
+
56
+ Parameters
57
+ ----------
58
+ source : dict
59
+ Source.
60
+
61
+ Returns
62
+ -------
63
+ dict
64
+ Checked source.
65
+ """
66
+ return SourceCodeStructPython.source_check(source)
67
+
68
+ def show_source_code(self) -> str:
69
+ """
70
+ Show source code.
71
+
72
+ Returns
73
+ -------
74
+ str
75
+ Source code.
76
+ """
77
+ return self.source.show_source_code()
78
+
79
+ def to_dict(self) -> dict:
80
+ """
81
+ Override to_dict to exclude code from source.
82
+
83
+ Returns
84
+ -------
85
+ dict
86
+ Dictionary representation of the object.
87
+ """
88
+ dict_ = super().to_dict()
89
+ dict_["source"] = self.source.to_dict()
90
+ return dict_
91
+
92
+
93
+ class FunctionParamsPython(FunctionParams, SourceCodeParamsPython):
94
+ """
95
+ Function python parameters model.
96
+ """
97
+
98
+ python_version: Literal["PYTHON3_9", "PYTHON3_10", "PYTHON3_11"]
99
+ "Python version"
100
+
101
+ image: str = None
102
+ "Image where the function will be executed"
103
+
104
+ base_image: str = None
105
+ "Base image used to build the image where the function will be executed"
106
+
107
+ requirements: list = None
108
+ "Requirements list to be installed in the image where the function will be executed"
@@ -0,0 +1,78 @@
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
+ super().__init__(task, local_execution)
16
+
17
+ self.source = kwargs.get("source")
18
+ self.image = kwargs.get("image")
19
+ self.base_image = kwargs.get("base_image")
20
+ self.python_version = kwargs.get("python_version")
21
+ self.requirements = kwargs.get("requirements")
22
+
23
+ self.function = kwargs.get("function")
24
+ self.node_selector = kwargs.get("node_selector")
25
+ self.volumes = kwargs.get("volumes")
26
+ self.resources = kwargs.get("resources")
27
+ self.affinity = kwargs.get("affinity")
28
+ self.tolerations = kwargs.get("tolerations")
29
+ self.env = kwargs.get("env")
30
+ self.secrets = kwargs.get("secrets")
31
+ self.backoff_limit = kwargs.get("backoff_limit")
32
+ self.schedule = kwargs.get("schedule")
33
+ self.replicas = kwargs.get("replicas")
34
+
35
+ # Task job
36
+
37
+ # Task build
38
+ self.instructions = kwargs.get("instructions")
39
+
40
+ self.inputs = kwargs.get("inputs")
41
+ self.outputs = kwargs.get("outputs")
42
+ self.parameters = kwargs.get("parameters")
43
+
44
+
45
+ class RunParamsPython(RunParamsMl):
46
+ """Run Python parameters."""
47
+
48
+ # Function parameters
49
+ source: dict = None
50
+ image: str = None
51
+ base_image: str = None
52
+ python_version: str = None
53
+ requirements: list = None
54
+
55
+ # Task parameters
56
+ function: str = None
57
+ node_selector: list[dict] = None
58
+ volumes: list[dict] = None
59
+ resources: list[dict] = None
60
+ affinity: dict = None
61
+ tolerations: list[dict] = None
62
+ env: list[dict] = None
63
+ secrets: list[str] = None
64
+
65
+ # Task job
66
+ backoff_limit: int = None
67
+
68
+ # Task serve
69
+ service_type: str = None
70
+ replicas: int = None
71
+
72
+ # Task build
73
+ instructions: list[str] = None
74
+
75
+ # Run parameters
76
+ inputs: dict = None
77
+ outputs: dict = None
78
+ parameters: dict = None
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class CorePort(BaseModel):
7
+ """
8
+ Port mapper model.
9
+ """
10
+
11
+ port: int
12
+ target_port: int
@@ -1,10 +1,8 @@
1
- """
2
- Task Python specification module.
3
- """
4
1
  from __future__ import annotations
5
2
 
3
+ from typing import Literal
4
+
6
5
  from digitalhub_core.entities.tasks.spec import TaskParamsK8s, TaskSpecK8s
7
- from digitalhub_runtime_python.entities.tasks.models import ContextRef, ContextSource
8
6
 
9
7
 
10
8
  class TaskSpecJob(TaskSpecK8s):
@@ -15,13 +13,9 @@ class TaskSpecJob(TaskSpecK8s):
15
13
  function: str,
16
14
  **kwargs,
17
15
  ) -> None:
18
- """
19
- Constructor.
20
- """
21
16
  super().__init__(function, **kwargs)
22
17
 
23
18
  self.backoff_limit = kwargs.get("backoff_limit")
24
- self.schedule = kwargs.get("schedule")
25
19
 
26
20
 
27
21
  class TaskParamsJob(TaskParamsK8s):
@@ -29,6 +23,9 @@ class TaskParamsJob(TaskParamsK8s):
29
23
  TaskParamsJob model.
30
24
  """
31
25
 
26
+ backoff_limit: int = None
27
+ """Backoff limit."""
28
+
32
29
 
33
30
  class TaskSpecBuild(TaskSpecK8s):
34
31
  """Task Build specification."""
@@ -36,18 +33,11 @@ class TaskSpecBuild(TaskSpecK8s):
36
33
  def __init__(
37
34
  self,
38
35
  function: str,
39
- context_refs: list | None = None,
40
- context_sources: list | None = None,
41
36
  instructions: list | None = None,
42
37
  **kwargs,
43
38
  ) -> None:
44
- """
45
- Constructor.
46
- """
47
39
  super().__init__(function, **kwargs)
48
40
 
49
- self.context_refs = context_refs
50
- self.context_sources = context_sources
51
41
  self.instructions = instructions
52
42
 
53
43
 
@@ -56,12 +46,6 @@ class TaskParamsBuild(TaskParamsK8s):
56
46
  TaskParamsBuild model.
57
47
  """
58
48
 
59
- context_refs: list[ContextRef] = None
60
- """Context references."""
61
-
62
- context_sources: list[ContextSource] = None
63
- """Context sources."""
64
-
65
49
  instructions: list[str] = None
66
50
  """Build instructions."""
67
51
 
@@ -69,8 +53,23 @@ class TaskParamsBuild(TaskParamsK8s):
69
53
  class TaskSpecServe(TaskSpecK8s):
70
54
  """Task Serve specification."""
71
55
 
56
+ def __init__(
57
+ self,
58
+ function: str,
59
+ replicas: int | None = None,
60
+ service_type: str | None = None,
61
+ **kwargs,
62
+ ) -> None:
63
+ super().__init__(function, **kwargs)
64
+
65
+ self.replicas = replicas
66
+ self.service_type = service_type
67
+
72
68
 
73
69
  class TaskParamsServe(TaskParamsK8s):
74
70
  """
75
71
  TaskParamsServe model.
76
72
  """
73
+
74
+ replicas: int = None
75
+ service_type: Literal["ClusterIP", "NodePort", "LoadBalancer"] = "NodePort"
@@ -13,3 +13,9 @@ class TaskStatusBuild(TaskStatus):
13
13
  """
14
14
  Task Build status.
15
15
  """
16
+
17
+
18
+ class TaskStatusServe(TaskStatus):
19
+ """
20
+ Task Serve status.
21
+ """
@@ -1,6 +1,3 @@
1
- """
2
- Runtime class for running Python functions.
3
- """
4
1
  from __future__ import annotations
5
2
 
6
3
  import typing
@@ -23,9 +20,6 @@ class RuntimePython(Runtime):
23
20
  """
24
21
 
25
22
  def __init__(self, kind_registry: KindRegistry, project: str) -> None:
26
- """
27
- Constructor.
28
- """
29
23
  super().__init__(kind_registry, project)
30
24
  ctx = get_context(self.project)
31
25
  self.root = ctx.runtime_dir
@@ -70,6 +64,9 @@ class RuntimePython(Runtime):
70
64
  LOGGER.info("Validating task.")
71
65
  self._validate_task(run)
72
66
 
67
+ LOGGER.info("Validating run.")
68
+ self._validate_run(run)
69
+
73
70
  LOGGER.info("Starting task.")
74
71
  spec = run.get("spec")
75
72
  project = run.get("project")
@@ -111,6 +108,27 @@ class RuntimePython(Runtime):
111
108
  """
112
109
  raise NotImplementedError
113
110
 
111
+ @staticmethod
112
+ def _validate_run(run: dict) -> None:
113
+ """
114
+ Check if run is locally allowed.
115
+
116
+ Parameters
117
+ ----------
118
+ run : dict
119
+ Run object dictionary.
120
+
121
+ Returns
122
+ -------
123
+ None
124
+ """
125
+ task_kind = run["spec"]["task"].split(":")[0]
126
+ local_execution = run["spec"]["local_execution"]
127
+ if task_kind != "python+job" and local_execution:
128
+ msg = f"Local execution not allowed for task kind {task_kind}."
129
+ LOGGER.exception(msg)
130
+ raise RuntimeError(msg)
131
+
114
132
  ####################
115
133
  # Configuration
116
134
  ####################
@@ -43,6 +43,35 @@ def get_function_from_source(path: Path, source_spec: dict) -> Callable:
43
43
  raise RuntimeError(msg) from e
44
44
 
45
45
 
46
+ def get_init_function(path: Path, source_spec: dict) -> Callable:
47
+ """
48
+ Get function from source.
49
+
50
+ Parameters
51
+ ----------
52
+ path : Path
53
+ Path where to save the function source.
54
+ source_spec : dict
55
+ Funcrion source spec.
56
+
57
+ Returns
58
+ -------
59
+ Callable
60
+ Function.
61
+ """
62
+ try:
63
+ if "init_function" not in source_spec:
64
+ return
65
+ function_code = save_function_source(path, source_spec)
66
+ handler_path, _ = parse_handler(source_spec["handler"])
67
+ function_path = (function_code / handler_path).with_suffix(".py")
68
+ return import_function(function_path, source_spec["init_function"])
69
+ except Exception as e:
70
+ msg = f"Some error occurred while getting init function. Exception: {e.__class__}. Error: {e.args}"
71
+ LOGGER.exception(msg)
72
+ raise RuntimeError(msg) from e
73
+
74
+
46
75
  def parse_handler(handler: str) -> tuple:
47
76
  """
48
77
  Parse handler.
@@ -0,0 +1,3 @@
1
+ import os
2
+
3
+ S3_BUCKET = os.getenv("S3_BUCKET_NAME")
@@ -6,7 +6,7 @@ from typing import Any, Callable
6
6
 
7
7
  from digitalhub_core.context.builder import get_context
8
8
  from digitalhub_core.entities.artifacts.crud import artifact_from_dict
9
- from digitalhub_core.utils.generic_utils import parse_entity_key
9
+ from digitalhub_core.entities.utils import parse_entity_key
10
10
  from digitalhub_core.utils.logger import LOGGER
11
11
  from digitalhub_data.entities.dataitems.crud import dataitem_from_dict
12
12
  from digitalhub_ml.entities.entity_types import EntityTypes
@@ -115,31 +115,28 @@ def compose_inputs(
115
115
  fnc_args = {**parameters, **entity_inputs}
116
116
 
117
117
  fnc_parameters = inspect.signature(func).parameters
118
- LOGGER.info(f"Function parameters: {'project' in fnc_parameters}")
119
118
 
120
119
  _has_project = "project" in fnc_parameters
121
120
  _has_context = "context" in fnc_parameters
122
121
  _has_event = "event" in fnc_parameters
123
122
 
123
+ # Project is reserved keyword argument
124
+ # both in local and remote executions
124
125
  if _has_project:
125
- if _has_context:
126
+ if _has_context and not local_execution:
126
127
  fnc_args["project"] = context.project
127
128
  elif isinstance(project, str):
128
129
  fnc_args["project"] = get_project_(project)
129
130
  else:
130
131
  fnc_args["project"] = project
131
132
 
132
- if _has_context:
133
- if context is not None and not local_execution:
133
+ # Context and event are reserved keyword arguments
134
+ # only in remote executions
135
+ if not local_execution:
136
+ if _has_context:
134
137
  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:
138
+ if _has_event:
140
139
  fnc_args["event"] = event
141
- else:
142
- raise RuntimeError("Event is not available on local execution.")
143
140
 
144
141
  return fnc_args
145
142
 
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Callable, Union
5
+
6
+ from digitalhub_core.utils.uri_utils import map_uri_scheme
7
+ from digitalhub_runtime_python.utils.configuration import import_function
8
+
9
+
10
+ def parse_handler(handler: str) -> tuple[Path, str]:
11
+ """
12
+ Parse handler.
13
+
14
+ Parameters
15
+ ----------
16
+ handler : str
17
+ Function handler
18
+
19
+ Returns
20
+ -------
21
+ tuple[Path, str]
22
+ Function handler.
23
+ """
24
+ parsed = handler.split(":")
25
+ if len(parsed) == 1:
26
+ return None, parsed[0]
27
+ return Path(*parsed[0].split(".")), parsed[1]
28
+
29
+
30
+ def get_function_source(source_spec: dict) -> Path:
31
+ """
32
+ Get function source.
33
+
34
+ Parameters
35
+ ----------
36
+ source : dict
37
+ Function source.
38
+
39
+ Returns
40
+ -------
41
+ Path
42
+ Path to function source.
43
+ """
44
+ path = Path("/shared")
45
+
46
+ # Get relevant information
47
+ base64 = source_spec.get("base64")
48
+ source = source_spec.get("source", "main.py")
49
+ handler = source_spec.get("handler")
50
+
51
+ handler_path, _ = parse_handler(handler)
52
+
53
+ # Get scheme in source
54
+ scheme = map_uri_scheme(source)
55
+
56
+ # Check base64. If it is set, it means
57
+ # that the source comes from a local file
58
+ if base64 is not None:
59
+ if scheme == "local":
60
+ if handler_path is not None:
61
+ return path / handler_path / Path(source)
62
+ return path / Path(source)
63
+ raise RuntimeError("Source is not a local file.")
64
+
65
+ if handler_path is not None:
66
+ return path / handler_path.with_suffix(".py")
67
+ raise RuntimeError("Must provide handler path in handler in form <root>.<dir>.<module>:<function_name>.")
68
+
69
+
70
+ def import_function_and_init(source: dict) -> tuple[Callable, Union[Callable, None]]:
71
+ """
72
+ Import function from source.
73
+
74
+ Parameters
75
+ ----------
76
+ source : dict
77
+ Function source.
78
+
79
+ Returns
80
+ -------
81
+ tuple
82
+ Function and init function.
83
+ """
84
+
85
+ # Get function source
86
+ function_path = get_function_source(source)
87
+ _, handler_name = parse_handler(source.get("handler"))
88
+
89
+ # Import function
90
+ fnc = import_function(function_path, handler_name)
91
+
92
+ # Get init function
93
+ init_handler = source.get("init_function")
94
+ init_fnc = None
95
+ if init_handler is not None:
96
+ init_fnc = import_function(function_path, init_handler)
97
+
98
+ return fnc, init_fnc
@@ -4,12 +4,16 @@ import pickle
4
4
  from typing import Any
5
5
 
6
6
  from digitalhub_core.entities._base.status import State
7
+ from digitalhub_core.entities._builders.uuid import build_uuid
7
8
  from digitalhub_core.entities.artifacts.crud import new_artifact
8
9
  from digitalhub_core.entities.artifacts.entity import Artifact
9
10
  from digitalhub_core.utils.logger import LOGGER
10
- from digitalhub_data.entities.dataitems.crud import new_dataitem
11
+ from digitalhub_data.entities.dataitems.crud import create_dataitem
11
12
  from digitalhub_data.entities.dataitems.entity.table import DataitemTable
13
+ from digitalhub_data.readers.builder import get_reader_by_object
12
14
  from digitalhub_data.readers.registry import DATAFRAME_TYPES
15
+ from digitalhub_ml.entities.entity_types import EntityTypes
16
+ from digitalhub_runtime_python.utils.env import S3_BUCKET
13
17
 
14
18
 
15
19
  def collect_outputs(results: Any, outputs: list[str], project_name: str) -> dict:
@@ -117,8 +121,22 @@ def build_and_load_dataitem(name: str, project_name: str, data: Any) -> Dataitem
117
121
  Dataitem key.
118
122
  """
119
123
  try:
120
- path = f"s3://datalake/{project_name}/dataitems/table/{name}.parquet"
121
- di: DataitemTable = new_dataitem(project=project_name, name=name, kind="table", path=path)
124
+ kwargs = {}
125
+ kwargs["project"] = project_name
126
+ kwargs["name"] = name
127
+ kwargs["kind"] = "table"
128
+ new_id = build_uuid()
129
+ kwargs["uuid"] = new_id
130
+ kwargs["path"] = f"s3://{S3_BUCKET}/{project_name}/{EntityTypes.DATAITEMS.value}/{new_id}/data.parquet"
131
+
132
+ di: DataitemTable = create_dataitem(**kwargs)
133
+
134
+ reader = get_reader_by_object(data)
135
+ di.spec.schema = reader.get_schema(data)
136
+ di.status.preview = reader.get_preview(data)
137
+
138
+ di.save()
139
+
122
140
  di.write_df(df=data)
123
141
  return di
124
142
  except Exception as e:
@@ -146,14 +164,21 @@ def build_and_load_artifact(name: str, project_name: str, data: Any) -> Artifact
146
164
  Artifact key.
147
165
  """
148
166
  try:
149
- path = f"s3://datalake/{project_name}/artifacts/artifact/{name}.pickle"
167
+ kwargs = {}
168
+ kwargs["project"] = project_name
169
+ kwargs["name"] = name
170
+ kwargs["kind"] = "artifact"
171
+ new_id = build_uuid()
172
+ kwargs["uuid"] = new_id
173
+ pickle_file = f"{name}.pickle"
174
+ kwargs["path"] = f"s3://{S3_BUCKET}/{project_name}/{EntityTypes.ARTIFACTS.value}/{new_id}/{pickle_file}"
150
175
 
151
176
  # Dump item to pickle
152
- with open(f"{name}.pickle", "wb") as f:
177
+ with open(pickle_file, "wb") as f:
153
178
  f.write(pickle.dumps(data))
154
179
 
155
- art = new_artifact(project=project_name, name=name, kind="artifact", path=path)
156
- art.upload(source=f"{name}.pickle")
180
+ art = new_artifact(**kwargs)
181
+ art.upload(source=pickle_file)
157
182
  return art
158
183
 
159
184
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: digitalhub-runtime-python
3
- Version: 0.5.0b11
3
+ Version: 0.6.0
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.6.0b
232
232
 
233
233
  # SDK for DHCore
@@ -9,6 +9,7 @@ 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/models.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
@@ -22,6 +23,8 @@ digitalhub_runtime_python/runtimes/__init__.py
22
23
  digitalhub_runtime_python/runtimes/kind_registry.py
23
24
  digitalhub_runtime_python/runtimes/runtime.py
24
25
  digitalhub_runtime_python/utils/configuration.py
26
+ digitalhub_runtime_python/utils/env.py
25
27
  digitalhub_runtime_python/utils/inputs.py
28
+ digitalhub_runtime_python/utils/nuclio_configuration.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.0b11"
7
+ version = "0.6.0"
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.6.0b",
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.0b11"
42
+ current_version = "0.6.0"
43
43
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
44
44
  commit_message = "Bump version {old_version} -> {new_version}"
45
45
  commit = false
@@ -1,139 +0,0 @@
1
- """
2
- Function Conatiner specification module.
3
- """
4
- from __future__ import annotations
5
-
6
- from pathlib import Path
7
-
8
- from digitalhub_core.entities.functions.spec import FunctionParams, FunctionSpec, SourceCodeStruct
9
- from digitalhub_core.utils.exceptions import EntityError
10
- from digitalhub_core.utils.generic_utils import decode_string, encode_source, encode_string
11
- from digitalhub_core.utils.uri_utils import map_uri_scheme
12
-
13
-
14
- class FunctionSpecPython(FunctionSpec):
15
- """
16
- Specification for a Function job.
17
- """
18
-
19
- def __init__(
20
- self,
21
- source: dict,
22
- image: str | None = None,
23
- base_image: str | None = None,
24
- requirements: list | None = None,
25
- ) -> None:
26
- """
27
- Constructor.
28
- """
29
- super().__init__()
30
-
31
- self.image = image
32
- self.base_image = base_image
33
- self.requirements = requirements
34
-
35
- source = self._source_check(source)
36
- self.source = SourceCodeStruct(**source)
37
-
38
- @staticmethod
39
- def _source_check(source: dict) -> dict:
40
- """
41
- Check source.
42
-
43
- Parameters
44
- ----------
45
- source : dict
46
- Source.
47
-
48
- Returns
49
- -------
50
- dict
51
- Checked source.
52
- """
53
- if source is None:
54
- raise EntityError("Source must be provided.")
55
-
56
- # Source check
57
- source_path = source.get("source")
58
- code = source.get("code")
59
- base64 = source.get("base64")
60
- handler = source.get("handler")
61
- source["lang"] = "python"
62
-
63
- if handler is None:
64
- raise EntityError("Handler must be provided.")
65
-
66
- if source_path is None and code is None and base64 is None:
67
- raise EntityError("Source must be provided.")
68
-
69
- if base64 is not None:
70
- return source
71
-
72
- if code is not None:
73
- source["base64"] = encode_string(code)
74
- return source
75
-
76
- if source_path is not None:
77
- if map_uri_scheme(source_path) == "local":
78
- if not (Path(source_path).suffix == ".py" and Path(source_path).is_file()):
79
- raise EntityError("Source is not a valid python file.")
80
- source["base64"] = encode_source(source_path)
81
- else:
82
- if handler is None:
83
- raise EntityError("Handler must be provided if source is not local.")
84
-
85
- return source
86
-
87
- def show_source_code(self) -> str:
88
- """
89
- Show source code.
90
-
91
- Returns
92
- -------
93
- str
94
- Source code.
95
- """
96
- if self.source.code is not None:
97
- return str(self.source.code)
98
- if self.source.base64 is not None:
99
- try:
100
- return decode_string(self.source.base64)
101
- except Exception:
102
- raise EntityError("Something got wrong during source code decoding.")
103
- if (self.source.source is not None) and (map_uri_scheme(self.source.source) == "local"):
104
- try:
105
- return Path(self.source.source).read_text()
106
- except Exception:
107
- raise EntityError("Cannot access source code.")
108
- return ""
109
-
110
- def to_dict(self) -> dict:
111
- """
112
- Override to_dict to exclude code from source.
113
-
114
- Returns
115
- -------
116
- dict
117
- Dictionary representation of the object.
118
- """
119
- dict_ = super().to_dict()
120
- dict_["source"] = self.source.to_dict()
121
- return dict_
122
-
123
-
124
- class FunctionParamsPython(FunctionParams):
125
- """
126
- Function python parameters model.
127
- """
128
-
129
- source: dict
130
- "Source code"
131
-
132
- image: str = None
133
- "Image"
134
-
135
- base_image: str = None
136
- "Base image"
137
-
138
- requirements: list = None
139
- "Requirements list, as used by the runtime"
@@ -1,53 +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
- 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
@@ -1,31 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pydantic import BaseModel
4
-
5
-
6
- class CorePort(BaseModel):
7
- """
8
- Port mapper model.
9
- """
10
-
11
- port: int
12
- target_port: int
13
-
14
-
15
- class ContextRef(BaseModel):
16
- """
17
- ContextRef model.
18
- """
19
-
20
- destination: str = None
21
- protocol: str = None
22
- source: str = None
23
-
24
-
25
- class ContextSource(BaseModel):
26
- """
27
- ContextSource model.
28
- """
29
-
30
- base64: str = None
31
- name: str = None
@@ -1 +0,0 @@
1
- digitalhub[ml]<0.6,~=0.5.0b