ocean-runner 0.2.19__tar.gz → 0.2.21__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.
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/PKG-INFO +3 -1
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/ocean_runner/runner.py +62 -49
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/pyproject.toml +3 -1
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/.gitignore +0 -0
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/LICENSE +0 -0
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/README.md +0 -0
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/ocean_runner/__init__.py +0 -0
- {ocean_runner-0.2.19 → ocean_runner-0.2.21}/ocean_runner/config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ocean-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.21
|
|
4
4
|
Summary: A fluent API for OceanProtocol algorithms
|
|
5
5
|
Project-URL: Homepage, https://github.com/AgrospAI/ocean-runner
|
|
6
6
|
Project-URL: Issues, https://github.com/AgrospAI/ocean-runner/issues
|
|
@@ -17,10 +17,12 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
20
21
|
Requires-Dist: oceanprotocol-job-details>=0.2.8
|
|
21
22
|
Requires-Dist: pydantic-settings>=2.12.0
|
|
22
23
|
Requires-Dist: pydantic>=2.12.5
|
|
23
24
|
Requires-Dist: pytest>=8.4.2
|
|
25
|
+
Requires-Dist: types-aiofiles>=25.1.0.20251011
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
25
27
|
|
|
26
28
|
# ocean-runner
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import inspect
|
|
3
5
|
from dataclasses import InitVar, asdict, dataclass, field
|
|
4
6
|
from logging import Logger
|
|
5
7
|
from pathlib import Path
|
|
6
|
-
from typing import Callable, Generic, TypeVar
|
|
8
|
+
from typing import Awaitable, Callable, Generic, TypeAlias, TypeVar
|
|
7
9
|
|
|
8
10
|
from oceanprotocol_job_details import JobDetails # type: ignore
|
|
9
11
|
|
|
@@ -11,16 +13,19 @@ from ocean_runner.config import Config
|
|
|
11
13
|
|
|
12
14
|
InputT = TypeVar("InputT")
|
|
13
15
|
ResultT = TypeVar("ResultT")
|
|
16
|
+
T = TypeVar("T")
|
|
14
17
|
|
|
15
|
-
ValidateFuncT = Callable[["Algorithm"], None]
|
|
16
|
-
RunFuncT = Callable[["Algorithm"], ResultT] | None
|
|
17
|
-
SaveFuncT = Callable[["Algorithm", ResultT, Path], None]
|
|
18
|
-
ErrorFuncT = Callable[["Algorithm", Exception], None]
|
|
19
18
|
|
|
19
|
+
Algo: TypeAlias = "Algorithm[InputT, ResultT]"
|
|
20
|
+
ValidateFuncT: TypeAlias = Callable[[Algo], None | Awaitable[None] | None]
|
|
21
|
+
RunFuncT: TypeAlias = Callable[[Algo], ResultT | Awaitable[ResultT]]
|
|
22
|
+
SaveFuncT: TypeAlias = Callable[[Algo, ResultT, Path], Awaitable[None] | None]
|
|
23
|
+
ErrorFuncT: TypeAlias = Callable[[Algo, Exception], Awaitable[None] | None]
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
|
|
26
|
+
def default_error_callback(algorithm: Algorithm, error: Exception) -> None:
|
|
22
27
|
algorithm.logger.exception("Error during algorithm execution")
|
|
23
|
-
raise
|
|
28
|
+
raise error
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
def default_validation(algorithm: Algorithm) -> None:
|
|
@@ -29,10 +34,33 @@ def default_validation(algorithm: Algorithm) -> None:
|
|
|
29
34
|
assert algorithm.job_details.files, "Files missing"
|
|
30
35
|
|
|
31
36
|
|
|
32
|
-
def default_save(algorithm: Algorithm, result: ResultT, base: Path) -> None:
|
|
37
|
+
async def default_save(algorithm: Algorithm, result: ResultT, base: Path) -> None:
|
|
38
|
+
import aiofiles
|
|
39
|
+
|
|
33
40
|
algorithm.logger.info("Saving results using default save")
|
|
34
|
-
with open(base / "result.txt", "w+") as f:
|
|
35
|
-
f.write(str(result))
|
|
41
|
+
async with aiofiles.open(base / "result.txt", "w+") as f:
|
|
42
|
+
await f.write(str(result))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def execute(
|
|
46
|
+
function: Callable[..., T | Awaitable[T]],
|
|
47
|
+
*args,
|
|
48
|
+
**kwargs,
|
|
49
|
+
) -> T:
|
|
50
|
+
result = function(*args, **kwargs)
|
|
51
|
+
|
|
52
|
+
if inspect.isawaitable(result):
|
|
53
|
+
return await result
|
|
54
|
+
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(slots=True)
|
|
59
|
+
class Functions(Generic[InputT, ResultT]):
|
|
60
|
+
validate: ValidateFuncT = field(default=default_validation, init=False)
|
|
61
|
+
run: RunFuncT | None = field(default=None, init=False)
|
|
62
|
+
save: SaveFuncT = field(default=default_save, init=False)
|
|
63
|
+
error: ErrorFuncT = field(default=default_error_callback, init=False)
|
|
36
64
|
|
|
37
65
|
|
|
38
66
|
@dataclass
|
|
@@ -44,33 +72,13 @@ class Algorithm(Generic[InputT, ResultT]):
|
|
|
44
72
|
"""
|
|
45
73
|
|
|
46
74
|
config: InitVar[Config[InputT] | None] = field(default=None)
|
|
47
|
-
logger: Logger = field(init=False)
|
|
48
|
-
_job_details: JobDetails[InputT] = field(init=False)
|
|
49
|
-
_result: ResultT | None = field(default=None, init=False)
|
|
50
75
|
|
|
51
|
-
|
|
52
|
-
_validate_fn: ValidateFuncT = field(
|
|
53
|
-
default=default_validation,
|
|
54
|
-
init=False,
|
|
55
|
-
repr=False,
|
|
56
|
-
)
|
|
76
|
+
logger: Logger = field(init=False, repr=False)
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
repr=False
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
_save_fn: SaveFuncT = field(
|
|
65
|
-
default=default_save,
|
|
66
|
-
init=False,
|
|
67
|
-
repr=False,
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
_error_callback: ErrorFuncT = field(
|
|
71
|
-
default=default_error_callback,
|
|
72
|
-
init=False,
|
|
73
|
-
repr=False,
|
|
78
|
+
_job_details: JobDetails[InputT] = field(init=False)
|
|
79
|
+
_result: ResultT | None = field(default=None, init=False)
|
|
80
|
+
_functions: Functions[InputT, ResultT] = field(
|
|
81
|
+
default_factory=Functions, init=False, repr=False
|
|
74
82
|
)
|
|
75
83
|
|
|
76
84
|
def __post_init__(self, config: Config[InputT] | None) -> None:
|
|
@@ -127,27 +135,26 @@ class Algorithm(Generic[InputT, ResultT]):
|
|
|
127
135
|
# ---------------------------
|
|
128
136
|
|
|
129
137
|
def validate(self, fn: ValidateFuncT) -> ValidateFuncT:
|
|
130
|
-
self.
|
|
138
|
+
self._functions.validate = fn
|
|
131
139
|
return fn
|
|
132
140
|
|
|
133
141
|
def run(self, fn: RunFuncT) -> RunFuncT:
|
|
134
|
-
self.
|
|
142
|
+
self._functions.run = fn
|
|
135
143
|
return fn
|
|
136
144
|
|
|
137
145
|
def save_results(self, fn: SaveFuncT) -> SaveFuncT:
|
|
138
|
-
self.
|
|
146
|
+
self._functions.save = fn
|
|
139
147
|
return fn
|
|
140
148
|
|
|
141
149
|
def on_error(self, fn: ErrorFuncT) -> ErrorFuncT:
|
|
142
|
-
self.
|
|
150
|
+
self._functions.error = fn
|
|
143
151
|
return fn
|
|
144
152
|
|
|
145
153
|
# ---------------------------
|
|
146
154
|
# Execution Pipeline
|
|
147
155
|
# ---------------------------
|
|
148
156
|
|
|
149
|
-
def
|
|
150
|
-
"""Executes the algorithm pipeline: validate → run → save_results."""
|
|
157
|
+
async def execute(self) -> ResultT | None:
|
|
151
158
|
# Load job details
|
|
152
159
|
self._job_details = JobDetails.load(
|
|
153
160
|
_type=self.configuration.custom_input,
|
|
@@ -161,21 +168,27 @@ class Algorithm(Generic[InputT, ResultT]):
|
|
|
161
168
|
self.logger.debug(asdict(self.job_details))
|
|
162
169
|
|
|
163
170
|
try:
|
|
164
|
-
|
|
165
|
-
self._validate_fn(self)
|
|
171
|
+
await execute(self._functions.validate, self)
|
|
166
172
|
|
|
167
|
-
|
|
168
|
-
if self._run_fn:
|
|
173
|
+
if self._functions.run:
|
|
169
174
|
self.logger.info("Running algorithm...")
|
|
170
|
-
self._result = self.
|
|
175
|
+
self._result = await execute(self._functions.run, self)
|
|
171
176
|
else:
|
|
172
177
|
self.logger.error("No run() function defined. Skipping execution.")
|
|
173
178
|
self._result = None
|
|
174
179
|
|
|
175
|
-
|
|
176
|
-
|
|
180
|
+
await execute(
|
|
181
|
+
self._functions.save,
|
|
182
|
+
algorithm=self,
|
|
183
|
+
result=self._result,
|
|
184
|
+
base=self.job_details.paths.outputs,
|
|
185
|
+
)
|
|
177
186
|
|
|
178
187
|
except Exception as e:
|
|
179
|
-
self.
|
|
188
|
+
await execute(self._functions.error, self, e)
|
|
180
189
|
|
|
181
190
|
return self._result
|
|
191
|
+
|
|
192
|
+
def __call__(self) -> ResultT | None:
|
|
193
|
+
"""Executes the algorithm pipeline: validate → run → save_results."""
|
|
194
|
+
return asyncio.run(self.execute())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ocean-runner"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.21"
|
|
4
4
|
description = "A fluent API for OceanProtocol algorithms"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "AgrospAI", email = "agrospai@udl.cat" },
|
|
@@ -15,10 +15,12 @@ classifiers = [
|
|
|
15
15
|
"License :: OSI Approved :: MIT License",
|
|
16
16
|
]
|
|
17
17
|
dependencies = [
|
|
18
|
+
"aiofiles>=25.1.0",
|
|
18
19
|
"oceanprotocol-job-details>=0.2.8",
|
|
19
20
|
"pydantic>=2.12.5",
|
|
20
21
|
"pydantic-settings>=2.12.0",
|
|
21
22
|
"pytest>=8.4.2",
|
|
23
|
+
"types-aiofiles>=25.1.0.20251011",
|
|
22
24
|
]
|
|
23
25
|
|
|
24
26
|
[project.urls]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|