ocean-runner 0.1.2__py3-none-any.whl → 0.1.4__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/__init__.py +4 -0
- ocean_runner/config.py +48 -0
- ocean_runner/runner.py +126 -0
- {ocean_runner-0.1.2.dist-info → ocean_runner-0.1.4.dist-info}/METADATA +3 -3
- ocean_runner-0.1.4.dist-info/RECORD +7 -0
- ocean_runner-0.1.2.dist-info/RECORD +0 -4
- {ocean_runner-0.1.2.dist-info → ocean_runner-0.1.4.dist-info}/WHEEL +0 -0
- {ocean_runner-0.1.2.dist-info → ocean_runner-0.1.4.dist-info}/licenses/LICENSE +0 -0
ocean_runner/__init__.py
ADDED
ocean_runner/config.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from dataclasses import asdict, dataclass, field
|
|
2
|
+
from logging import Logger
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Callable, Iterable, TypeVar
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class Environment:
|
|
12
|
+
"""Environment variables mock"""
|
|
13
|
+
|
|
14
|
+
base_dir: str | None = field(default=os.environ.get("BASE_DIR", None))
|
|
15
|
+
"""Base data directory, defaults to '/data'"""
|
|
16
|
+
|
|
17
|
+
dids: str = field(default=os.environ.get("DIDS"))
|
|
18
|
+
"""Datasets DID's, format: '["XXXX"]'"""
|
|
19
|
+
|
|
20
|
+
transformation_did: str = field(default=os.environ.get("TRANSFORMATION_DID"))
|
|
21
|
+
"""Transformation (algorithm) DID"""
|
|
22
|
+
|
|
23
|
+
secret: str = field(default=os.environ.get("SECRET"))
|
|
24
|
+
"""Super secret secret"""
|
|
25
|
+
|
|
26
|
+
dict = asdict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Config:
|
|
31
|
+
"""Algorithm overall configuration"""
|
|
32
|
+
|
|
33
|
+
custom_input: T | None = None
|
|
34
|
+
"""Algorithm's custom input types, must be a dataclass_json"""
|
|
35
|
+
|
|
36
|
+
error_callback: Callable[[Exception], None] = None
|
|
37
|
+
"""Callback to execute upon exceptions"""
|
|
38
|
+
|
|
39
|
+
logger: Logger | None = None
|
|
40
|
+
"""Logger to use in the algorithm"""
|
|
41
|
+
|
|
42
|
+
source_paths: Iterable[Path] = field(
|
|
43
|
+
default_factory=lambda: [Path("/algorithm/src")]
|
|
44
|
+
)
|
|
45
|
+
"""Paths that should be included so the code executes correctly"""
|
|
46
|
+
|
|
47
|
+
environment: Environment = Environment()
|
|
48
|
+
"""Mock of environment data"""
|
ocean_runner/runner.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from dataclasses import InitVar, asdict, dataclass, field
|
|
2
|
+
from logging import Logger
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Callable, Generic, Self, TypeVar
|
|
5
|
+
|
|
6
|
+
from oceanprotocol_job_details import JobDetails
|
|
7
|
+
|
|
8
|
+
from ocean_runner.config import Config
|
|
9
|
+
|
|
10
|
+
JobDetailsT = TypeVar(
|
|
11
|
+
"JobDetailsT",
|
|
12
|
+
)
|
|
13
|
+
ResultT = TypeVar("ResultT")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def default_error_callback(e: Exception) -> None:
|
|
17
|
+
raise e
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def default_validation(algorithm: "Algorithm") -> None:
|
|
21
|
+
algorithm.logger.info("Validating input using default validation")
|
|
22
|
+
|
|
23
|
+
assert algorithm.job_details.ddos, "DDOs missing"
|
|
24
|
+
assert algorithm.job_details.files, "Files missing"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def default_save(*, result: ResultT, base: Path, algorithm: "Algorithm") -> None:
|
|
28
|
+
algorithm.logger.info("Saving results using default save")
|
|
29
|
+
|
|
30
|
+
with open(base / "result.txt", "w+") as f:
|
|
31
|
+
f.write(str(result))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class Algorithm(Generic[JobDetailsT, ResultT]):
|
|
36
|
+
|
|
37
|
+
config: InitVar[Config | None] = None
|
|
38
|
+
|
|
39
|
+
# Load from config
|
|
40
|
+
logger: Logger = field(init=False)
|
|
41
|
+
error_callback: Callable[[Exception], None] = field(init=False)
|
|
42
|
+
|
|
43
|
+
_job_details: JobDetails[JobDetailsT] = field(init=False)
|
|
44
|
+
_result: ResultT | None = field(default=None, init=False)
|
|
45
|
+
|
|
46
|
+
def __post_init__(self, config: Config | None) -> None:
|
|
47
|
+
config = config or Config()
|
|
48
|
+
|
|
49
|
+
# Use config or use a default implementation
|
|
50
|
+
self.error_callback = config.error_callback or default_error_callback
|
|
51
|
+
|
|
52
|
+
if config.logger:
|
|
53
|
+
self.logger = config.logger
|
|
54
|
+
else:
|
|
55
|
+
import logging
|
|
56
|
+
|
|
57
|
+
logging.basicConfig(
|
|
58
|
+
level=logging.DEBUG,
|
|
59
|
+
format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
|
|
60
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.logger = logging.getLogger("ocean_runner")
|
|
64
|
+
|
|
65
|
+
if isinstance(config.environment.base_dir, str):
|
|
66
|
+
config.environment.base_dir = Path(config.environment.base_dir)
|
|
67
|
+
|
|
68
|
+
if config.source_paths:
|
|
69
|
+
import sys
|
|
70
|
+
|
|
71
|
+
sys.path.extend([str(path.absolute()) for path in config.source_paths])
|
|
72
|
+
self.logger.debug(f"Added [{len(config.source_paths)}] entries to PATH")
|
|
73
|
+
|
|
74
|
+
self._job_details = JobDetails.load(
|
|
75
|
+
config.custom_input,
|
|
76
|
+
**config.environment.dict(),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
self.logger.info("Loaded JobDetails")
|
|
80
|
+
self.logger.debug(asdict(self.job_details))
|
|
81
|
+
|
|
82
|
+
class Error(RuntimeError): ...
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def job_details(self) -> JobDetails:
|
|
86
|
+
if not self._job_details:
|
|
87
|
+
raise Algorithm.Error("JobDetails not initialized or missing")
|
|
88
|
+
return self._job_details
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def result(self) -> ResultT:
|
|
92
|
+
if not self._result:
|
|
93
|
+
raise Algorithm.Error("Result missing, run the algorithm first")
|
|
94
|
+
return self._result
|
|
95
|
+
|
|
96
|
+
def validate(self, callback: Callable[[Self], None] = default_validation) -> Self:
|
|
97
|
+
self.logger.info("Validating instance...")
|
|
98
|
+
try:
|
|
99
|
+
callback(self)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
self.error_callback(e)
|
|
102
|
+
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
def run(self, callable: Callable[[Self], ResultT]) -> Self:
|
|
106
|
+
self.logger.info("Running algorithm ...")
|
|
107
|
+
try:
|
|
108
|
+
self._result = callable(self)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
self.error_callback(e)
|
|
111
|
+
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
def save_results(
|
|
115
|
+
self,
|
|
116
|
+
callable: Callable[[ResultT, Path, "Algorithm"], None] = default_save,
|
|
117
|
+
) -> None:
|
|
118
|
+
self.logger.info("Saving results...")
|
|
119
|
+
try:
|
|
120
|
+
callable(
|
|
121
|
+
results=self.result,
|
|
122
|
+
base_path=self.job_details.paths.outputs,
|
|
123
|
+
algorithm=self,
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
self.error_callback(e)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ocean-runner
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: AgrospAI <agrospai@udl.cat>, Christian López <christian.lopez@udl.cat>
|
|
8
8
|
License: Copyright 2025 spin3l
|
|
9
9
|
|
|
10
10
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
@@ -17,7 +17,7 @@ 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: oceanprotocol-job-details==0.2.
|
|
20
|
+
Requires-Dist: oceanprotocol-job-details==0.2.6
|
|
21
21
|
Requires-Dist: pytest>=8.4.2
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ocean_runner/__init__.py,sha256=awAmE6kZhuwcrD3gT7qFZArdhiuzW-EFTA6tGKhw06k,138
|
|
2
|
+
ocean_runner/config.py,sha256=9XSDHuNY5IspDi5ZZKmt2HX6yrI9gzeogCw-ATMCATs,1323
|
|
3
|
+
ocean_runner/runner.py,sha256=r02O2QHSc_eij1zI8soYzvjPXA8R-Khvg2XyCj58C-M,3770
|
|
4
|
+
ocean_runner-0.1.4.dist-info/METADATA,sha256=EkgvIBXcAeAjRB6FnvZlRRp0vK97CVH--euBDj9n94g,5781
|
|
5
|
+
ocean_runner-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
ocean_runner-0.1.4.dist-info/licenses/LICENSE,sha256=_B25KqK4amoADWkMN150tnZFm_Fy7VvZpvIC8ZydWdI,1053
|
|
7
|
+
ocean_runner-0.1.4.dist-info/RECORD,,
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
ocean_runner-0.1.2.dist-info/METADATA,sha256=MguVYXLegClFt928OhzC-tmT41JgjnAe4aR7zRnDpwc,5780
|
|
2
|
-
ocean_runner-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
3
|
-
ocean_runner-0.1.2.dist-info/licenses/LICENSE,sha256=_B25KqK4amoADWkMN150tnZFm_Fy7VvZpvIC8ZydWdI,1053
|
|
4
|
-
ocean_runner-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|