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 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 Callable, Iterable, TypeVar
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(_: Algorithm, e: Exception) -> None:
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
- def __post_init__(self, config: Config | None) -> None:
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
- self._setup_logger(config)
48
- self._load_job_details(config)
49
- self._load_user_defined_functions()
50
- self._maybe_autorun(config)
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
- # Setup helpers
54
- # ---------------------
54
+ def __post_init__(self, config: Config | None) -> None:
55
+ config: Config = config or Config()
55
56
 
56
- def _setup_logger(self, config: Config) -> None:
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
- def _load_job_details(self, config: Config) -> None:
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
- # Auto-detect functions
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
- def validate(self, callback: Callable[[Self], None] | None = None) -> Self:
142
- callback = callback or getattr(self, "_user_validation", default_validation)
143
- self.logger.info("Validating instance...")
144
- try:
145
- callback(self)
146
- except Exception as e:
147
- self.error_callback(e)
148
- return self
149
-
150
- def run(self, callback: Callable[[Self], ResultT] | None = None) -> Self:
151
- callback = callback or getattr(self, "_user_run", None)
152
- if callback is None:
153
- raise Algorithm.Error("No 'run' function found")
154
- self.logger.info("Running algorithm...")
155
- try:
156
- self._result = callback(self)
157
- except Exception as e:
158
- self.error_callback(e)
159
- return self
160
-
161
- def save_results(
162
- self,
163
- callback: Callable[[ResultT, Path, Algorithm], None] | None = None,
164
- *,
165
- override_path: Path | None = None,
166
- ) -> None:
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
- callback(
171
- result=self.result,
172
- base=override_path or self.job_details.paths.outputs,
173
- algorithm=self,
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.error_callback(e)
169
+ self.logger.exception("Error during algorithm execution")
170
+ self.error_callback(self, e)
177
171
 
178
- class Error(RuntimeError): ...
172
+ return self._result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocean-runner
3
- Version: 0.2.13
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,,