ocean-runner 0.2.13__py3-none-any.whl → 0.2.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ocean-runner might be problematic. Click here for more details.
- ocean_runner/config.py +2 -7
- ocean_runner/runner.py +85 -91
- {ocean_runner-0.2.13.dist-info → ocean_runner-0.2.14.dist-info}/METADATA +1 -1
- ocean_runner-0.2.14.dist-info/RECORD +7 -0
- ocean_runner-0.2.13.dist-info/RECORD +0 -7
- {ocean_runner-0.2.13.dist-info → ocean_runner-0.2.14.dist-info}/WHEEL +0 -0
- {ocean_runner-0.2.13.dist-info → ocean_runner-0.2.14.dist-info}/licenses/LICENSE +0 -0
ocean_runner/config.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
from dataclasses import asdict, dataclass, field
|
|
3
3
|
from logging import Logger
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Iterable, TypeVar
|
|
6
6
|
|
|
7
7
|
T = TypeVar("T")
|
|
8
8
|
|
|
@@ -43,9 +43,6 @@ class Config:
|
|
|
43
43
|
custom_input: T | None = None
|
|
44
44
|
"""Algorithm's custom input types, must be a dataclass_json"""
|
|
45
45
|
|
|
46
|
-
error_callback: Callable[[Exception], None] = None
|
|
47
|
-
"""Callback to execute upon exceptions"""
|
|
48
|
-
|
|
49
46
|
logger: Logger | None = None
|
|
50
47
|
"""Logger to use in the algorithm"""
|
|
51
48
|
|
|
@@ -54,7 +51,5 @@ class Config:
|
|
|
54
51
|
)
|
|
55
52
|
"""Paths that should be included so the code executes correctly"""
|
|
56
53
|
|
|
57
|
-
environment: Environment = field(
|
|
58
|
-
default_factory=lambda: Environment(),
|
|
59
|
-
)
|
|
54
|
+
environment: Environment = field(default_factory=lambda: Environment())
|
|
60
55
|
"""Mock of environment data"""
|
ocean_runner/runner.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import inspect
|
|
4
3
|
from dataclasses import InitVar, asdict, dataclass, field
|
|
5
4
|
from logging import Logger
|
|
6
5
|
from pathlib import Path
|
|
@@ -14,7 +13,7 @@ JobDetailsT = TypeVar("JobDetailsT")
|
|
|
14
13
|
ResultT = TypeVar("ResultT")
|
|
15
14
|
|
|
16
15
|
|
|
17
|
-
def default_error_callback(_
|
|
16
|
+
def default_error_callback(_, e: Exception) -> None:
|
|
18
17
|
raise e
|
|
19
18
|
|
|
20
19
|
|
|
@@ -32,28 +31,30 @@ def default_save(*, result: ResultT, base: Path, algorithm: Algorithm) -> None:
|
|
|
32
31
|
|
|
33
32
|
@dataclass
|
|
34
33
|
class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
34
|
+
"""
|
|
35
|
+
A configurable algorithm runner that behaves like a FastAPI app:
|
|
36
|
+
- You register `validate`, `run`, and `save_results` via decorators.
|
|
37
|
+
- You execute the full pipeline by calling `app()`.
|
|
38
|
+
"""
|
|
35
39
|
|
|
36
40
|
config: InitVar[Config | None] = None
|
|
37
41
|
logger: Logger = field(init=False)
|
|
38
42
|
_job_details: JobDetails[JobDetailsT] = field(init=False)
|
|
39
43
|
_result: ResultT | None = field(default=None, init=False)
|
|
40
|
-
error_callback = default_error_callback
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
config: Config = config or Config()
|
|
44
|
-
if config.error_callback:
|
|
45
|
-
self.error_callback = config.error_callback
|
|
45
|
+
error_callback: Callable[[Algorithm, Exception], None] = default_error_callback
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
# Decorator-registered callbacks
|
|
48
|
+
_validate_fn: Callable[[Algorithm], None] | None = field(default=None, init=False)
|
|
49
|
+
_run_fn: Callable[[Algorithm], ResultT] | None = field(default=None, init=False)
|
|
50
|
+
_save_fn: Callable[[ResultT, Path, Algorithm], None] | None = field(
|
|
51
|
+
default=None, init=False
|
|
52
|
+
)
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# ---------------------
|
|
54
|
+
def __post_init__(self, config: Config | None) -> None:
|
|
55
|
+
config: Config = config or Config()
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
# Configure logger
|
|
57
58
|
if config.logger:
|
|
58
59
|
self.logger = config.logger
|
|
59
60
|
else:
|
|
@@ -66,15 +67,18 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
66
67
|
)
|
|
67
68
|
self.logger = logging.getLogger("ocean_runner")
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
# Normalize base_dir
|
|
70
71
|
if isinstance(config.environment.base_dir, str):
|
|
71
72
|
config.environment.base_dir = Path(config.environment.base_dir)
|
|
73
|
+
|
|
74
|
+
# Extend sys.path for custom imports
|
|
72
75
|
if config.source_paths:
|
|
73
76
|
import sys
|
|
74
77
|
|
|
75
78
|
sys.path.extend([str(path.absolute()) for path in config.source_paths])
|
|
76
79
|
self.logger.debug(f"Added [{len(config.source_paths)}] entries to PATH")
|
|
77
80
|
|
|
81
|
+
# Load job details
|
|
78
82
|
self._job_details = JobDetails.load(
|
|
79
83
|
_type=config.custom_input,
|
|
80
84
|
base_dir=config.environment.base_dir,
|
|
@@ -82,49 +86,13 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
82
86
|
transformation_did=config.environment.transformation_did,
|
|
83
87
|
secret=config.environment.secret,
|
|
84
88
|
)
|
|
89
|
+
|
|
85
90
|
self.logger.info("Loaded JobDetails")
|
|
86
91
|
self.logger.debug(asdict(self.job_details))
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _load_user_defined_functions(self) -> None:
|
|
93
|
-
caller = inspect.getmodule(inspect.stack()[2][0])
|
|
94
|
-
if not caller:
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
for name, default in {
|
|
98
|
-
"validation": default_validation,
|
|
99
|
-
"run": None,
|
|
100
|
-
"save": default_save,
|
|
101
|
-
}.items():
|
|
102
|
-
fn = getattr(caller, name, None)
|
|
103
|
-
if callable(fn):
|
|
104
|
-
self.logger.debug(f"Found user-defined '{name}' function")
|
|
105
|
-
setattr(self, f"_user_{name}", fn)
|
|
106
|
-
elif default:
|
|
107
|
-
setattr(self, f"_user_{name}", default)
|
|
108
|
-
|
|
109
|
-
self._caller_module = caller
|
|
110
|
-
|
|
111
|
-
# ---------------------
|
|
112
|
-
# Auto-run logic
|
|
113
|
-
# ---------------------
|
|
114
|
-
|
|
115
|
-
def _maybe_autorun(self, config) -> None:
|
|
116
|
-
"""Automatically runs if caller has __ocean_runner_autorun__ = True"""
|
|
117
|
-
if (
|
|
118
|
-
getattr(self, "_caller_module", None)
|
|
119
|
-
and getattr(self._caller_module, "__ocean_runner_autorun__", False)
|
|
120
|
-
and not getattr(config, "_from_test", False)
|
|
121
|
-
):
|
|
122
|
-
self.logger.info("Auto-running algorithm...")
|
|
123
|
-
self.validate().run().save_results()
|
|
124
|
-
|
|
125
|
-
# ---------------------
|
|
126
|
-
# Main API
|
|
127
|
-
# ---------------------
|
|
93
|
+
self.config = config
|
|
94
|
+
|
|
95
|
+
class Error(RuntimeError): ...
|
|
128
96
|
|
|
129
97
|
@property
|
|
130
98
|
def job_details(self) -> JobDetails:
|
|
@@ -138,41 +106,67 @@ class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
|
138
106
|
raise Algorithm.Error("Result missing, run the algorithm first")
|
|
139
107
|
return self._result
|
|
140
108
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
self.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
callback = callback or getattr(self, "_user_save", default_save)
|
|
168
|
-
self.logger.info("Saving results...")
|
|
109
|
+
# ---------------------------
|
|
110
|
+
# Decorators (FastAPI-style)
|
|
111
|
+
# ---------------------------
|
|
112
|
+
|
|
113
|
+
def validate(self, fn: Callable[[Self], None]) -> Callable[[Self], None]:
|
|
114
|
+
self._validate_fn = fn
|
|
115
|
+
return fn
|
|
116
|
+
|
|
117
|
+
def run(self, fn: Callable[[Self], ResultT]) -> Callable[[Self], ResultT]:
|
|
118
|
+
self._run_fn = fn
|
|
119
|
+
return fn
|
|
120
|
+
|
|
121
|
+
def save_results(self, fn: Callable[[ResultT, Path, Algorithm], None]) -> Callable:
|
|
122
|
+
self._save_fn = fn
|
|
123
|
+
return fn
|
|
124
|
+
|
|
125
|
+
def on_error(self, fn: Callable[[Algorithm, Exception], None]) -> Callable:
|
|
126
|
+
self.error_callback = fn
|
|
127
|
+
return fn
|
|
128
|
+
|
|
129
|
+
# ---------------------------
|
|
130
|
+
# Execution Pipeline
|
|
131
|
+
# ---------------------------
|
|
132
|
+
|
|
133
|
+
def __call__(self) -> ResultT | None:
|
|
134
|
+
"""Executes the algorithm pipeline: validate → run → save_results."""
|
|
169
135
|
try:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
136
|
+
# Validation step
|
|
137
|
+
if self._validate_fn:
|
|
138
|
+
self.logger.info("Running custom validation...")
|
|
139
|
+
self._validate_fn(self)
|
|
140
|
+
else:
|
|
141
|
+
self.logger.info("Running default validation...")
|
|
142
|
+
default_validation(self)
|
|
143
|
+
|
|
144
|
+
# Run step
|
|
145
|
+
if self._run_fn:
|
|
146
|
+
self.logger.info("Running algorithm...")
|
|
147
|
+
self._result = self._run_fn(self)
|
|
148
|
+
else:
|
|
149
|
+
self.logger.warning("No run() function defined. Skipping execution.")
|
|
150
|
+
self._result = None
|
|
151
|
+
|
|
152
|
+
# Save step
|
|
153
|
+
if self._save_fn:
|
|
154
|
+
self.logger.info("Saving results...")
|
|
155
|
+
self._save_fn(
|
|
156
|
+
self._result,
|
|
157
|
+
self.job_details.paths.outputs,
|
|
158
|
+
self,
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
self.logger.info("No save_results() defined. Using default.")
|
|
162
|
+
default_save(
|
|
163
|
+
result=self._result,
|
|
164
|
+
base=self.job_details.paths.outputs,
|
|
165
|
+
algorithm=self,
|
|
166
|
+
)
|
|
167
|
+
|
|
175
168
|
except Exception as e:
|
|
176
|
-
self.
|
|
169
|
+
self.logger.exception("Error during algorithm execution")
|
|
170
|
+
self.error_callback(self, e)
|
|
177
171
|
|
|
178
|
-
|
|
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.14
|
|
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
|
|
@@ -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=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,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
ocean_runner/__init__.py,sha256=awAmE6kZhuwcrD3gT7qFZArdhiuzW-EFTA6tGKhw06k,138
|
|
2
|
-
ocean_runner/config.py,sha256=EgloiUTLBvca4B_pr2eNa8kHYfsyVE73ZxszLCbiDYY,1525
|
|
3
|
-
ocean_runner/runner.py,sha256=-nYH6_TtAEJ9RWv_PWmeDMyfJOhQ1tlrJrNHi7ZmYKo,5941
|
|
4
|
-
ocean_runner-0.2.13.dist-info/METADATA,sha256=orDe6w9hnST2HI33Ta3rU2-kUg9ulFSq9mQhLOhuVxI,5919
|
|
5
|
-
ocean_runner-0.2.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
ocean_runner-0.2.13.dist-info/licenses/LICENSE,sha256=_B25KqK4amoADWkMN150tnZFm_Fy7VvZpvIC8ZydWdI,1053
|
|
7
|
-
ocean_runner-0.2.13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|