ocean-runner 0.2.14__py3-none-any.whl → 0.2.16__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.
- ocean_runner/runner.py +12 -12
- {ocean_runner-0.2.14.dist-info → ocean_runner-0.2.16.dist-info}/METADATA +81 -50
- ocean_runner-0.2.16.dist-info/RECORD +7 -0
- ocean_runner-0.2.14.dist-info/RECORD +0 -7
- {ocean_runner-0.2.14.dist-info → ocean_runner-0.2.16.dist-info}/WHEEL +0 -0
- {ocean_runner-0.2.14.dist-info → ocean_runner-0.2.16.dist-info}/licenses/LICENSE +0 -0
ocean_runner/runner.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import InitVar, asdict, dataclass, field
|
|
4
4
|
from logging import Logger
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Callable, Generic,
|
|
6
|
+
from typing import Callable, Generic, TypeVar
|
|
7
7
|
|
|
8
8
|
from oceanprotocol_job_details import JobDetails
|
|
9
9
|
|
|
@@ -42,14 +42,15 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
42
42
|
_job_details: JobDetails[JobDetailsT] = field(init=False)
|
|
43
43
|
_result: ResultT | None = field(default=None, init=False)
|
|
44
44
|
|
|
45
|
-
error_callback: Callable[[Algorithm, Exception], None] = default_error_callback
|
|
46
|
-
|
|
47
45
|
# Decorator-registered callbacks
|
|
48
46
|
_validate_fn: Callable[[Algorithm], None] | None = field(default=None, init=False)
|
|
49
47
|
_run_fn: Callable[[Algorithm], ResultT] | None = field(default=None, init=False)
|
|
50
48
|
_save_fn: Callable[[ResultT, Path, Algorithm], None] | None = field(
|
|
51
49
|
default=None, init=False
|
|
52
50
|
)
|
|
51
|
+
_error_callback: Callable[[Algorithm, Exception], None] = field(
|
|
52
|
+
default=default_error_callback, init=False
|
|
53
|
+
)
|
|
53
54
|
|
|
54
55
|
def __post_init__(self, config: Config | None) -> None:
|
|
55
56
|
config: Config = config or Config()
|
|
@@ -110,20 +111,20 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
110
111
|
# Decorators (FastAPI-style)
|
|
111
112
|
# ---------------------------
|
|
112
113
|
|
|
113
|
-
def validate(self, fn: Callable[[
|
|
114
|
+
def validate(self, fn: Callable[[], None]) -> Callable[[], None]:
|
|
114
115
|
self._validate_fn = fn
|
|
115
116
|
return fn
|
|
116
117
|
|
|
117
|
-
def run(self, fn: Callable[[
|
|
118
|
+
def run(self, fn: Callable[[], ResultT]) -> Callable[[], ResultT]:
|
|
118
119
|
self._run_fn = fn
|
|
119
120
|
return fn
|
|
120
121
|
|
|
121
|
-
def save_results(self, fn: Callable[[ResultT, Path
|
|
122
|
+
def save_results(self, fn: Callable[[ResultT, Path], None]) -> Callable:
|
|
122
123
|
self._save_fn = fn
|
|
123
124
|
return fn
|
|
124
125
|
|
|
125
|
-
def on_error(self, fn: Callable[[
|
|
126
|
-
self.
|
|
126
|
+
def on_error(self, fn: Callable[[Exception], None]) -> Callable:
|
|
127
|
+
self._error_callback = fn
|
|
127
128
|
return fn
|
|
128
129
|
|
|
129
130
|
# ---------------------------
|
|
@@ -136,7 +137,7 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
136
137
|
# Validation step
|
|
137
138
|
if self._validate_fn:
|
|
138
139
|
self.logger.info("Running custom validation...")
|
|
139
|
-
self._validate_fn(
|
|
140
|
+
self._validate_fn()
|
|
140
141
|
else:
|
|
141
142
|
self.logger.info("Running default validation...")
|
|
142
143
|
default_validation(self)
|
|
@@ -144,7 +145,7 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
144
145
|
# Run step
|
|
145
146
|
if self._run_fn:
|
|
146
147
|
self.logger.info("Running algorithm...")
|
|
147
|
-
self._result = self._run_fn(
|
|
148
|
+
self._result = self._run_fn()
|
|
148
149
|
else:
|
|
149
150
|
self.logger.warning("No run() function defined. Skipping execution.")
|
|
150
151
|
self._result = None
|
|
@@ -155,7 +156,6 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
155
156
|
self._save_fn(
|
|
156
157
|
self._result,
|
|
157
158
|
self.job_details.paths.outputs,
|
|
158
|
-
self,
|
|
159
159
|
)
|
|
160
160
|
else:
|
|
161
161
|
self.logger.info("No save_results() defined. Using default.")
|
|
@@ -167,6 +167,6 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
167
167
|
|
|
168
168
|
except Exception as e:
|
|
169
169
|
self.logger.exception("Error during algorithm execution")
|
|
170
|
-
self.
|
|
170
|
+
self._error_callback(e)
|
|
171
171
|
|
|
172
172
|
return self._result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ocean-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.16
|
|
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
|
|
@@ -23,7 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
# ocean-runner
|
|
25
25
|
|
|
26
|
-
Ocean Runner is a package that
|
|
26
|
+
Ocean Runner is a package that eases algorithm creation in the scope of OceanProtocol.
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
@@ -40,33 +40,40 @@ uv add ocean-runner
|
|
|
40
40
|
|
|
41
41
|
```python
|
|
42
42
|
import random
|
|
43
|
-
from ocean_runner import Algorithm
|
|
43
|
+
from ocean_runner import Algorithm
|
|
44
|
+
|
|
45
|
+
algorithm = Algorithm()
|
|
46
|
+
|
|
44
47
|
|
|
48
|
+
@algorithm.run
|
|
49
|
+
def run():
|
|
50
|
+
return random.randint()
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
algorithm()
|
|
47
55
|
```
|
|
48
56
|
|
|
49
|
-
|
|
57
|
+
This code snippet will:
|
|
50
58
|
|
|
51
|
-
- Read the OceanProtocol JobDetails from the environment variables and use default file paths.
|
|
52
|
-
-
|
|
53
|
-
-
|
|
59
|
+
- Read the OceanProtocol JobDetails from the environment variables and use default configuration file paths.
|
|
60
|
+
- Execute the run function.
|
|
61
|
+
- Execute the default saving function, storing the result in a "result.txt" file within the default outputs path.
|
|
54
62
|
|
|
55
63
|
### Tuning
|
|
56
64
|
|
|
57
65
|
#### Application Config
|
|
58
66
|
|
|
59
|
-
The application configuration can be tweaked by passing a Config instance to its
|
|
67
|
+
The application configuration can be tweaked by passing a Config instance to its constructor.
|
|
60
68
|
|
|
61
69
|
```python
|
|
62
|
-
Algorithm
|
|
70
|
+
from ocean_runner import Algorithm, Config
|
|
71
|
+
|
|
72
|
+
algorithm = Algorithm(
|
|
63
73
|
Config(
|
|
64
74
|
custom_input: ... # dataclass
|
|
65
75
|
# Custom algorithm parameters dataclass.
|
|
66
76
|
|
|
67
|
-
error_callback: ... # Callable[[Exception], None]
|
|
68
|
-
# Callback to run on exceptions.
|
|
69
|
-
|
|
70
77
|
logger: ... # type: logging.Logger
|
|
71
78
|
# Custom logger to use.
|
|
72
79
|
|
|
@@ -82,6 +89,8 @@ Algorithm(
|
|
|
82
89
|
```python
|
|
83
90
|
import logging
|
|
84
91
|
|
|
92
|
+
from ocean_runner import Algorithm, Config
|
|
93
|
+
|
|
85
94
|
|
|
86
95
|
@dataclass
|
|
87
96
|
class CustomInput:
|
|
@@ -91,19 +100,13 @@ class CustomInput:
|
|
|
91
100
|
logger = logging.getLogger(__name__)
|
|
92
101
|
|
|
93
102
|
|
|
94
|
-
Algorithm(
|
|
103
|
+
algorithm = Algorithm(
|
|
95
104
|
Config(
|
|
96
105
|
custom_input: CustomInput,
|
|
97
106
|
"""
|
|
98
107
|
Load the Algorithm's Custom Input into a CustomInput dataclass instance.
|
|
99
108
|
"""
|
|
100
109
|
|
|
101
|
-
error_callback: lambda ex: logger.exception(ex),
|
|
102
|
-
"""
|
|
103
|
-
Run this callback when an exception is caught
|
|
104
|
-
NOTE: it's not recommended to catch exceptions this way. Should re-raise and halt the execution.
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
110
|
source_paths: [Path("/algorithm/src")],
|
|
108
111
|
"""
|
|
109
112
|
Source paths to include in the PATH. '/algorithm/src' is the default since our templates place the algorithm source files there.
|
|
@@ -143,44 +146,72 @@ Algorithm(
|
|
|
143
146
|
|
|
144
147
|
```
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
#### Behaviour Config
|
|
147
150
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
As seen in the minimal example, all methods implemented in `Algorithm` have a default implementation which will be commented here.
|
|
151
|
+
To fully configure the behaviour of the algorithm as in the [Minimal Example](#minimal-example), you can do it decorating your defined function as in the following example, which features all the possible algorithm customization.
|
|
151
152
|
|
|
152
153
|
```python
|
|
154
|
+
from pathlib import Path
|
|
153
155
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"""
|
|
158
|
-
Default constructor, will use default values of Config.
|
|
159
|
-
"""
|
|
160
|
-
|
|
161
|
-
.validate()
|
|
162
|
-
|
|
163
|
-
"""
|
|
164
|
-
Will validate the algorithm's job detail instance, checking for the existence of:
|
|
165
|
-
- `job_details.ddos`
|
|
166
|
-
- `job_details.files`
|
|
167
|
-
"""
|
|
156
|
+
import pandas as pd
|
|
157
|
+
from ocean_runner import Algorithm
|
|
168
158
|
|
|
169
|
-
|
|
159
|
+
algorithm = Algorithm()
|
|
170
160
|
|
|
171
|
-
"""
|
|
172
|
-
Has NO default implementation, must pass a callback that returns a result of any type.
|
|
173
|
-
"""
|
|
174
161
|
|
|
175
|
-
|
|
162
|
+
@algorithm.on_error
|
|
163
|
+
def error_callback(ex: Exception):
|
|
164
|
+
algorithm.logger.exception(ex)
|
|
165
|
+
raise algorithm.Error() from ex
|
|
176
166
|
|
|
177
|
-
"""
|
|
178
|
-
Stores the result of running the algorithm in "outputs/results.txt"
|
|
179
|
-
"""
|
|
180
167
|
|
|
181
|
-
|
|
168
|
+
@algorithm.validate
|
|
169
|
+
def val():
|
|
170
|
+
assert algorithm.job_details.files, "Empty input dir"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@algorithm.run
|
|
174
|
+
def run() -> pd.DataFrame:
|
|
175
|
+
_, filename = next(algorithm.job_details.next_path())
|
|
176
|
+
return pd.read_csv(filename).describe(include="all")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@algorithm.save_results
|
|
180
|
+
def save(results: pd.DataFrame, path: Path):
|
|
181
|
+
algorithm.logger.info(f"Descriptive statistics: {results}")
|
|
182
|
+
results.to_csv(path / "results.csv")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
if __name__ == "__main__":
|
|
186
|
+
algorithm()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
### Default implementations
|
|
192
|
+
|
|
193
|
+
As seen in the minimal example, all methods implemented in `Algorithm` have a default implementation which will be commented here.
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
.validate()
|
|
197
|
+
|
|
198
|
+
"""
|
|
199
|
+
Will validate the algorithm's job detail instance, checking for the existence of:
|
|
200
|
+
- `job_details.ddos`
|
|
201
|
+
- `job_details.files`
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
.run()
|
|
205
|
+
|
|
206
|
+
"""
|
|
207
|
+
Has NO default implementation, must pass a callback that returns a result of any type.
|
|
208
|
+
"""
|
|
182
209
|
|
|
210
|
+
.save_results()
|
|
183
211
|
|
|
212
|
+
"""
|
|
213
|
+
Stores the result of running the algorithm in "outputs/results.txt"
|
|
214
|
+
"""
|
|
184
215
|
```
|
|
185
216
|
|
|
186
217
|
### Job Details
|
|
@@ -188,7 +219,7 @@ As seen in the minimal example, all methods implemented in `Algorithm` have a de
|
|
|
188
219
|
To load the OceanProtocol JobDetails instance, the program will read some environment variables, they can be mocked passing an instance of `Environment` through the configuration of the algorithm.
|
|
189
220
|
|
|
190
221
|
Environment variables:
|
|
191
|
-
- `DIDS` Input dataset(s) DID's, must have format: `["abc..90"]`
|
|
192
|
-
- `TRANSFORMATION_DID` Algorithm DID, must have format: `abc..90
|
|
193
|
-
- `SECRET` Algorithm secret.
|
|
222
|
+
- `DIDS` (optional) Input dataset(s) DID's, must have format: `["abc..90"]`. Defaults to reading them automatically from the `DDO` data directory.
|
|
223
|
+
- `TRANSFORMATION_DID` (optional, default="DEFAULT"): Algorithm DID, must have format: `abc..90`.
|
|
224
|
+
- `SECRET` (optional, default="DEFAULT"): Algorithm secret.
|
|
194
225
|
- `BASE_DIR` (optional, default="/data"): Base path to the OceanProtocol data directories.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ocean_runner/__init__.py,sha256=awAmE6kZhuwcrD3gT7qFZArdhiuzW-EFTA6tGKhw06k,138
|
|
2
|
+
ocean_runner/config.py,sha256=gyyUotPJ7n8wPPdsJZIBUT4zBlkoNbhV876JDTdPNsY,1398
|
|
3
|
+
ocean_runner/runner.py,sha256=mPxnJCP-XYl0akRAI4HdAku0KlsTCrXTVPCwlQj5JoY,5721
|
|
4
|
+
ocean_runner-0.2.16.dist-info/METADATA,sha256=s4MrShZfoQzbWIXZRV06Rt0S4lhqYuRjK_OG9QKJoKg,6562
|
|
5
|
+
ocean_runner-0.2.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
ocean_runner-0.2.16.dist-info/licenses/LICENSE,sha256=_B25KqK4amoADWkMN150tnZFm_Fy7VvZpvIC8ZydWdI,1053
|
|
7
|
+
ocean_runner-0.2.16.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
ocean_runner/__init__.py,sha256=awAmE6kZhuwcrD3gT7qFZArdhiuzW-EFTA6tGKhw06k,138
|
|
2
|
-
ocean_runner/config.py,sha256=gyyUotPJ7n8wPPdsJZIBUT4zBlkoNbhV876JDTdPNsY,1398
|
|
3
|
-
ocean_runner/runner.py,sha256=AM4GlgpGGOSLa_TfO8rAdWmTGTPTZoTT84LNtUswTXw,5762
|
|
4
|
-
ocean_runner-0.2.14.dist-info/METADATA,sha256=bIdyxazZ4mWCOUW7JULJaykDgDCd03yZ91CWG4BbsT8,5919
|
|
5
|
-
ocean_runner-0.2.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
ocean_runner-0.2.14.dist-info/licenses/LICENSE,sha256=_B25KqK4amoADWkMN150tnZFm_Fy7VvZpvIC8ZydWdI,1053
|
|
7
|
-
ocean_runner-0.2.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|