runnable 0.12.3__py3-none-any.whl → 0.14.0__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.
- runnable/__init__.py +0 -11
- runnable/catalog.py +27 -5
- runnable/cli.py +122 -26
- runnable/datastore.py +71 -35
- runnable/defaults.py +0 -1
- runnable/entrypoints.py +107 -32
- runnable/exceptions.py +6 -2
- runnable/executor.py +28 -9
- runnable/graph.py +37 -12
- runnable/integration.py +7 -2
- runnable/nodes.py +15 -17
- runnable/parameters.py +27 -8
- runnable/pickler.py +1 -1
- runnable/sdk.py +101 -33
- runnable/secrets.py +3 -1
- runnable/tasks.py +246 -34
- runnable/utils.py +41 -13
- {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info}/METADATA +25 -31
- runnable-0.14.0.dist-info/RECORD +24 -0
- {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info}/WHEEL +1 -1
- runnable-0.14.0.dist-info/entry_points.txt +40 -0
- runnable/extensions/__init__.py +0 -0
- runnable/extensions/catalog/__init__.py +0 -21
- runnable/extensions/catalog/file_system/__init__.py +0 -0
- runnable/extensions/catalog/file_system/implementation.py +0 -234
- runnable/extensions/catalog/k8s_pvc/__init__.py +0 -0
- runnable/extensions/catalog/k8s_pvc/implementation.py +0 -16
- runnable/extensions/catalog/k8s_pvc/integration.py +0 -59
- runnable/extensions/executor/__init__.py +0 -649
- runnable/extensions/executor/argo/__init__.py +0 -0
- runnable/extensions/executor/argo/implementation.py +0 -1194
- runnable/extensions/executor/argo/specification.yaml +0 -51
- runnable/extensions/executor/k8s_job/__init__.py +0 -0
- runnable/extensions/executor/k8s_job/implementation_FF.py +0 -259
- runnable/extensions/executor/k8s_job/integration_FF.py +0 -69
- runnable/extensions/executor/local/__init__.py +0 -0
- runnable/extensions/executor/local/implementation.py +0 -71
- runnable/extensions/executor/local_container/__init__.py +0 -0
- runnable/extensions/executor/local_container/implementation.py +0 -446
- runnable/extensions/executor/mocked/__init__.py +0 -0
- runnable/extensions/executor/mocked/implementation.py +0 -154
- runnable/extensions/executor/retry/__init__.py +0 -0
- runnable/extensions/executor/retry/implementation.py +0 -168
- runnable/extensions/nodes.py +0 -855
- runnable/extensions/run_log_store/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_file_system/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_file_system/implementation.py +0 -111
- runnable/extensions/run_log_store/chunked_k8s_pvc/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +0 -21
- runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +0 -61
- runnable/extensions/run_log_store/db/implementation_FF.py +0 -157
- runnable/extensions/run_log_store/db/integration_FF.py +0 -0
- runnable/extensions/run_log_store/file_system/__init__.py +0 -0
- runnable/extensions/run_log_store/file_system/implementation.py +0 -140
- runnable/extensions/run_log_store/generic_chunked.py +0 -557
- runnable/extensions/run_log_store/k8s_pvc/__init__.py +0 -0
- runnable/extensions/run_log_store/k8s_pvc/implementation.py +0 -21
- runnable/extensions/run_log_store/k8s_pvc/integration.py +0 -56
- runnable/extensions/secrets/__init__.py +0 -0
- runnable/extensions/secrets/dotenv/__init__.py +0 -0
- runnable/extensions/secrets/dotenv/implementation.py +0 -100
- runnable-0.12.3.dist-info/RECORD +0 -64
- runnable-0.12.3.dist-info/entry_points.txt +0 -41
- {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info/licenses}/LICENSE +0 -0
runnable/__init__.py
CHANGED
@@ -34,14 +34,3 @@ from runnable.sdk import ( # noqa
|
|
34
34
|
|
35
35
|
# Needed to disable ploomber telemetry
|
36
36
|
os.environ["PLOOMBER_STATS_ENABLED"] = "false"
|
37
|
-
|
38
|
-
## TODO: Summary should be a bit better for catalog.
|
39
|
-
## If the execution fails, hint them about the retry executor.
|
40
|
-
# Make the retry executor loose!
|
41
|
-
|
42
|
-
# TODO: Think of model registry as a central place to store models.
|
43
|
-
# TODO: Implement Sagemaker pipelines as a executor.
|
44
|
-
|
45
|
-
|
46
|
-
# TODO: Think of way of generating dag hash without executor configuration
|
47
|
-
# Try to get a release
|
runnable/catalog.py
CHANGED
@@ -11,6 +11,26 @@ from runnable.datastore import DataCatalog
|
|
11
11
|
logger = logging.getLogger(defaults.LOGGER_NAME)
|
12
12
|
|
13
13
|
|
14
|
+
def is_catalog_out_of_sync(
|
15
|
+
catalog, synced_catalogs=Optional[List[DataCatalog]]
|
16
|
+
) -> bool:
|
17
|
+
"""
|
18
|
+
Check if the catalog items are out of sync from already cataloged objects.
|
19
|
+
If they are, return False.
|
20
|
+
If the object does not exist or synced catalog does not exist, return True
|
21
|
+
"""
|
22
|
+
if not synced_catalogs:
|
23
|
+
return True # If nothing has been synced in the past
|
24
|
+
|
25
|
+
for synced_catalog in synced_catalogs:
|
26
|
+
if synced_catalog.catalog_relative_path == catalog.catalog_relative_path:
|
27
|
+
if synced_catalog.data_hash == catalog.data_hash:
|
28
|
+
return False
|
29
|
+
return True
|
30
|
+
|
31
|
+
return True # The object does not exist, sync it
|
32
|
+
|
33
|
+
|
14
34
|
# --8<-- [start:docs]
|
15
35
|
|
16
36
|
|
@@ -26,8 +46,7 @@ class BaseCatalog(ABC, BaseModel):
|
|
26
46
|
model_config = ConfigDict(extra="forbid")
|
27
47
|
|
28
48
|
@abstractmethod
|
29
|
-
def get_summary(self) -> Dict[str, Any]:
|
30
|
-
...
|
49
|
+
def get_summary(self) -> Dict[str, Any]: ...
|
31
50
|
|
32
51
|
@property
|
33
52
|
def _context(self):
|
@@ -38,7 +57,9 @@ class BaseCatalog(ABC, BaseModel):
|
|
38
57
|
return defaults.COMPUTE_DATA_FOLDER
|
39
58
|
|
40
59
|
@abstractmethod
|
41
|
-
def get(
|
60
|
+
def get(
|
61
|
+
self, name: str, run_id: str, compute_data_folder: str = "", **kwargs
|
62
|
+
) -> List[DataCatalog]:
|
42
63
|
"""
|
43
64
|
Get the catalog item by 'name' for the 'run id' and store it in compute data folder.
|
44
65
|
|
@@ -119,7 +140,9 @@ class DoNothingCatalog(BaseCatalog):
|
|
119
140
|
def get_summary(self) -> Dict[str, Any]:
|
120
141
|
return {}
|
121
142
|
|
122
|
-
def get(
|
143
|
+
def get(
|
144
|
+
self, name: str, run_id: str, compute_data_folder: str = "", **kwargs
|
145
|
+
) -> List[DataCatalog]:
|
123
146
|
"""
|
124
147
|
Does nothing
|
125
148
|
"""
|
@@ -145,4 +168,3 @@ class DoNothingCatalog(BaseCatalog):
|
|
145
168
|
Does nothing
|
146
169
|
"""
|
147
170
|
logger.info("Using a do-nothing catalog, doing nothing while sync between runs")
|
148
|
-
...
|
runnable/cli.py
CHANGED
@@ -22,9 +22,13 @@ def cli():
|
|
22
22
|
|
23
23
|
|
24
24
|
@cli.command("execute", short_help="Execute/translate a pipeline")
|
25
|
-
@click.
|
25
|
+
@click.argument("filename")
|
26
26
|
@click.option(
|
27
|
-
"-c",
|
27
|
+
"-c",
|
28
|
+
"--config-file",
|
29
|
+
default=None,
|
30
|
+
help="config file, in yaml, to be used for the run",
|
31
|
+
show_default=True,
|
28
32
|
)
|
29
33
|
@click.option(
|
30
34
|
"-p",
|
@@ -41,8 +45,12 @@ def cli():
|
|
41
45
|
type=click.Choice(["INFO", "DEBUG", "WARNING", "ERROR", "FATAL"]),
|
42
46
|
)
|
43
47
|
@click.option("--tag", default="", help="A tag attached to the run")
|
44
|
-
@click.option(
|
45
|
-
|
48
|
+
@click.option(
|
49
|
+
"--run-id", help="An optional run_id, one would be generated if not provided"
|
50
|
+
)
|
51
|
+
def execute(
|
52
|
+
filename, config_file, parameters_file, log_level, tag, run_id
|
53
|
+
): # pragma: no cover
|
46
54
|
"""
|
47
55
|
Execute a pipeline
|
48
56
|
|
@@ -64,20 +72,35 @@ def execute(file, config_file, parameters_file, log_level, tag, run_id): # prag
|
|
64
72
|
|
65
73
|
entrypoints.execute(
|
66
74
|
configuration_file=config_file,
|
67
|
-
pipeline_file=
|
75
|
+
pipeline_file=filename,
|
68
76
|
tag=tag,
|
69
77
|
run_id=run_id,
|
70
78
|
parameters_file=parameters_file,
|
71
79
|
)
|
72
80
|
|
73
81
|
|
74
|
-
@cli.command(
|
82
|
+
@cli.command(
|
83
|
+
"execute_single_node",
|
84
|
+
short_help="Internal entry point to execute a single node",
|
85
|
+
hidden=True,
|
86
|
+
)
|
75
87
|
@click.argument("run_id")
|
76
88
|
@click.argument("step_name")
|
77
|
-
@click.option("--map-variable", default="", help="The map variable dictionary in str", show_default=True)
|
78
|
-
@click.option("-f", "--file", default="", help="The pipeline definition file", show_default=True)
|
79
89
|
@click.option(
|
80
|
-
"
|
90
|
+
"--map-variable",
|
91
|
+
default="",
|
92
|
+
help="The map variable dictionary in str",
|
93
|
+
show_default=True,
|
94
|
+
)
|
95
|
+
@click.option(
|
96
|
+
"-f", "--file", default="", help="The pipeline definition file", show_default=True
|
97
|
+
)
|
98
|
+
@click.option(
|
99
|
+
"-c",
|
100
|
+
"--config-file",
|
101
|
+
default=None,
|
102
|
+
help="config file, in yaml, to be used for the run",
|
103
|
+
show_default=True,
|
81
104
|
)
|
82
105
|
@click.option(
|
83
106
|
"-p",
|
@@ -94,7 +117,9 @@ def execute(file, config_file, parameters_file, log_level, tag, run_id): # prag
|
|
94
117
|
type=click.Choice(["INFO", "DEBUG", "WARNING", "ERROR", "FATAL"]),
|
95
118
|
)
|
96
119
|
@click.option("--tag", default="", help="A tag attached to the run")
|
97
|
-
def execute_single_node(
|
120
|
+
def execute_single_node(
|
121
|
+
run_id, step_name, map_variable, file, config_file, parameters_file, log_level, tag
|
122
|
+
):
|
98
123
|
"""
|
99
124
|
Internal entrypoint for runnable to execute a single node.
|
100
125
|
|
@@ -119,7 +144,11 @@ def execute_single_node(run_id, step_name, map_variable, file, config_file, para
|
|
119
144
|
@click.argument("filename")
|
120
145
|
@click.option("--entrypoint", default=defaults.ENTRYPOINT.USER.value, hidden=True)
|
121
146
|
@click.option(
|
122
|
-
"-c",
|
147
|
+
"-c",
|
148
|
+
"--config-file",
|
149
|
+
default=None,
|
150
|
+
help="config file, in yaml, to be used for the run",
|
151
|
+
show_default=True,
|
123
152
|
)
|
124
153
|
@click.option(
|
125
154
|
"-p",
|
@@ -136,10 +165,20 @@ def execute_single_node(run_id, step_name, map_variable, file, config_file, para
|
|
136
165
|
type=click.Choice(["INFO", "DEBUG", "WARNING", "ERROR", "FATAL"]),
|
137
166
|
)
|
138
167
|
@click.option("--data-folder", "-d", default="data/", help="The catalog data folder")
|
139
|
-
@click.option(
|
140
|
-
|
168
|
+
@click.option(
|
169
|
+
"--put-in-catalog",
|
170
|
+
"-put",
|
171
|
+
default=None,
|
172
|
+
multiple=True,
|
173
|
+
help="The data to put from the catalog",
|
174
|
+
)
|
175
|
+
@click.option(
|
176
|
+
"--notebook-output-path", default="", help="The output path for the notebook"
|
177
|
+
)
|
141
178
|
@click.option("--tag", help="A tag attached to the run")
|
142
|
-
@click.option(
|
179
|
+
@click.option(
|
180
|
+
"--run-id", help="An optional run_id, one would be generated if not provided"
|
181
|
+
)
|
143
182
|
def execute_notebook(
|
144
183
|
filename,
|
145
184
|
entrypoint,
|
@@ -159,7 +198,10 @@ def execute_notebook(
|
|
159
198
|
The execution plan is unchained.
|
160
199
|
"""
|
161
200
|
logger.setLevel(log_level)
|
162
|
-
catalog_config = {
|
201
|
+
catalog_config = {
|
202
|
+
"compute_data_folder": data_folder,
|
203
|
+
"put": list(put_in_catalog) if put_in_catalog else None,
|
204
|
+
}
|
163
205
|
if not filename.endswith(".ipynb"):
|
164
206
|
raise Exception("A notebook should always have ipynb as the extension")
|
165
207
|
|
@@ -179,7 +221,11 @@ def execute_notebook(
|
|
179
221
|
@click.argument("command")
|
180
222
|
@click.option("--entrypoint", default=defaults.ENTRYPOINT.USER.value, hidden=True)
|
181
223
|
@click.option(
|
182
|
-
"-c",
|
224
|
+
"-c",
|
225
|
+
"--config-file",
|
226
|
+
default=None,
|
227
|
+
help="config file, in yaml, to be used for the run",
|
228
|
+
show_default=True,
|
183
229
|
)
|
184
230
|
@click.option(
|
185
231
|
"-p",
|
@@ -196,11 +242,27 @@ def execute_notebook(
|
|
196
242
|
type=click.Choice(["INFO", "DEBUG", "WARNING", "ERROR", "FATAL"]),
|
197
243
|
)
|
198
244
|
@click.option("--data-folder", "-d", default="data/", help="The catalog data folder")
|
199
|
-
@click.option(
|
245
|
+
@click.option(
|
246
|
+
"--put-in-catalog",
|
247
|
+
"-put",
|
248
|
+
default=None,
|
249
|
+
multiple=True,
|
250
|
+
help="The data to put from the catalog",
|
251
|
+
)
|
200
252
|
@click.option("--tag", help="A tag attached to the run")
|
201
|
-
@click.option(
|
253
|
+
@click.option(
|
254
|
+
"--run-id", help="An optional run_id, one would be generated if not provided"
|
255
|
+
)
|
202
256
|
def execute_function(
|
203
|
-
command,
|
257
|
+
command,
|
258
|
+
entrypoint,
|
259
|
+
config_file,
|
260
|
+
parameters_file,
|
261
|
+
log_level,
|
262
|
+
data_folder,
|
263
|
+
put_in_catalog,
|
264
|
+
tag,
|
265
|
+
run_id,
|
204
266
|
):
|
205
267
|
"""
|
206
268
|
External entry point to execute a python function in isolation.
|
@@ -209,7 +271,10 @@ def execute_function(
|
|
209
271
|
The execution plan is unchained.
|
210
272
|
"""
|
211
273
|
logger.setLevel(log_level)
|
212
|
-
catalog_config = {
|
274
|
+
catalog_config = {
|
275
|
+
"compute_data_folder": data_folder,
|
276
|
+
"put": list(put_in_catalog) if put_in_catalog else None,
|
277
|
+
}
|
213
278
|
entrypoints.execute_function(
|
214
279
|
entrypoint=entrypoint,
|
215
280
|
command=command,
|
@@ -221,14 +286,35 @@ def execute_function(
|
|
221
286
|
)
|
222
287
|
|
223
288
|
|
224
|
-
@cli.command(
|
289
|
+
@cli.command(
|
290
|
+
"fan",
|
291
|
+
short_help="Internal entry point to fan in or out a composite node",
|
292
|
+
hidden=True,
|
293
|
+
)
|
225
294
|
@click.argument("run_id")
|
226
295
|
@click.argument("step_name")
|
227
|
-
@click.option("-m", "--mode", help="fan in or fan out", required=True, type=click.Choice(["in", "out"]))
|
228
|
-
@click.option("--map-variable", default="", help="The map variable dictionary in str", show_default=True)
|
229
|
-
@click.option("-f", "--file", default="", help="The pipeline definition file", show_default=True)
|
230
296
|
@click.option(
|
231
|
-
"-
|
297
|
+
"-m",
|
298
|
+
"--mode",
|
299
|
+
help="fan in or fan out",
|
300
|
+
required=True,
|
301
|
+
type=click.Choice(["in", "out"]),
|
302
|
+
)
|
303
|
+
@click.option(
|
304
|
+
"--map-variable",
|
305
|
+
default="",
|
306
|
+
help="The map variable dictionary in str",
|
307
|
+
show_default=True,
|
308
|
+
)
|
309
|
+
@click.option(
|
310
|
+
"-f", "--file", default="", help="The pipeline definition file", show_default=True
|
311
|
+
)
|
312
|
+
@click.option(
|
313
|
+
"-c",
|
314
|
+
"--config-file",
|
315
|
+
default=None,
|
316
|
+
help="config file, in yaml, to be used for the run",
|
317
|
+
show_default=True,
|
232
318
|
)
|
233
319
|
@click.option(
|
234
320
|
"-p",
|
@@ -245,7 +331,17 @@ def execute_function(
|
|
245
331
|
type=click.Choice(["INFO", "DEBUG", "WARNING", "ERROR", "FATAL"]),
|
246
332
|
)
|
247
333
|
@click.option("--tag", default="", help="A tag attached to the run")
|
248
|
-
def fan(
|
334
|
+
def fan(
|
335
|
+
run_id,
|
336
|
+
step_name,
|
337
|
+
mode,
|
338
|
+
map_variable,
|
339
|
+
file,
|
340
|
+
config_file,
|
341
|
+
parameters_file,
|
342
|
+
log_level,
|
343
|
+
tag,
|
344
|
+
):
|
249
345
|
"""
|
250
346
|
Internal entrypoint for runnable to fan in or out a composite node.
|
251
347
|
|
runnable/datastore.py
CHANGED
@@ -18,7 +18,6 @@ from typing import (
|
|
18
18
|
|
19
19
|
from pydantic import BaseModel, Field, computed_field
|
20
20
|
|
21
|
-
import runnable.context as context
|
22
21
|
from runnable import defaults, exceptions
|
23
22
|
|
24
23
|
logger = logging.getLogger(defaults.LOGGER_NAME)
|
@@ -56,14 +55,11 @@ class DataCatalog(BaseModel, extra="allow"):
|
|
56
55
|
return other.name == self.name
|
57
56
|
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
Once the map state is complete, we can set the reduce to true and have the value as
|
65
|
-
the reduced value. Its either a list or a custom function return.
|
66
|
-
"""
|
58
|
+
# The theory behind reduced:
|
59
|
+
# parameters returned by steps in map node are only reduced by the end of the map step, fan-in.
|
60
|
+
# If they are accessed within the map step, the value should be the value returned by the step in the map step.
|
61
|
+
# Once the map state is complete, we can set the reduce to true and have the value as
|
62
|
+
# the reduced value. Its either a list or a custom function return.
|
67
63
|
|
68
64
|
|
69
65
|
class JsonParameter(BaseModel):
|
@@ -125,7 +121,9 @@ class ObjectParameter(BaseModel):
|
|
125
121
|
os.remove(self.file_name) # Remove after loading
|
126
122
|
|
127
123
|
|
128
|
-
Parameter = Annotated[
|
124
|
+
Parameter = Annotated[
|
125
|
+
Union[JsonParameter, ObjectParameter, MetricParameter], Field(discriminator="kind")
|
126
|
+
]
|
129
127
|
|
130
128
|
|
131
129
|
class StepAttempt(BaseModel):
|
@@ -157,8 +155,12 @@ class CodeIdentity(BaseModel, extra="allow"):
|
|
157
155
|
|
158
156
|
code_identifier: Optional[str] = "" # GIT sha code or docker image id
|
159
157
|
code_identifier_type: Optional[str] = "" # git or docker
|
160
|
-
code_identifier_dependable: Optional[bool] =
|
161
|
-
|
158
|
+
code_identifier_dependable: Optional[bool] = (
|
159
|
+
False # If git, checks if the tree is clean.
|
160
|
+
)
|
161
|
+
code_identifier_url: Optional[str] = (
|
162
|
+
"" # The git remote url or docker repository url
|
163
|
+
)
|
162
164
|
code_identifier_message: Optional[str] = "" # Any optional message
|
163
165
|
|
164
166
|
|
@@ -185,18 +187,28 @@ class StepLog(BaseModel):
|
|
185
187
|
summary: Dict[str, Any] = {}
|
186
188
|
|
187
189
|
summary["Name"] = self.internal_name
|
188
|
-
summary["Input catalog content"] = [
|
190
|
+
summary["Input catalog content"] = [
|
191
|
+
dc.name for dc in self.data_catalog if dc.stage == "get"
|
192
|
+
]
|
189
193
|
summary["Available parameters"] = [
|
190
|
-
(p, v.description)
|
194
|
+
(p, v.description)
|
195
|
+
for attempt in self.attempts
|
196
|
+
for p, v in attempt.input_parameters.items()
|
191
197
|
]
|
192
198
|
|
193
|
-
summary["Output catalog content"] = [
|
199
|
+
summary["Output catalog content"] = [
|
200
|
+
dc.name for dc in self.data_catalog if dc.stage == "put"
|
201
|
+
]
|
194
202
|
summary["Output parameters"] = [
|
195
|
-
(p, v.description)
|
203
|
+
(p, v.description)
|
204
|
+
for attempt in self.attempts
|
205
|
+
for p, v in attempt.output_parameters.items()
|
196
206
|
]
|
197
207
|
|
198
208
|
summary["Metrics"] = [
|
199
|
-
(p, v.description)
|
209
|
+
(p, v.description)
|
210
|
+
for attempt in self.attempts
|
211
|
+
for p, v in attempt.user_defined_metrics.items()
|
200
212
|
]
|
201
213
|
|
202
214
|
cis = []
|
@@ -312,10 +324,18 @@ class RunLog(BaseModel):
|
|
312
324
|
summary["Catalog Location"] = _context.catalog_handler.get_summary()
|
313
325
|
summary["Full Run log present at: "] = _context.run_log_store.get_summary()
|
314
326
|
|
315
|
-
run_log = _context.run_log_store.get_run_log_by_id(
|
327
|
+
run_log = _context.run_log_store.get_run_log_by_id(
|
328
|
+
run_id=_context.run_id, full=True
|
329
|
+
)
|
316
330
|
|
317
|
-
summary["Final Parameters"] = {
|
318
|
-
|
331
|
+
summary["Final Parameters"] = {
|
332
|
+
p: v.description for p, v in run_log.parameters.items()
|
333
|
+
}
|
334
|
+
summary["Collected metrics"] = {
|
335
|
+
p: v.description
|
336
|
+
for p, v in run_log.parameters.items()
|
337
|
+
if v.kind == "metric"
|
338
|
+
}
|
319
339
|
|
320
340
|
return summary
|
321
341
|
|
@@ -338,7 +358,9 @@ class RunLog(BaseModel):
|
|
338
358
|
|
339
359
|
return list(set(data_catalogs))
|
340
360
|
|
341
|
-
def search_branch_by_internal_name(
|
361
|
+
def search_branch_by_internal_name(
|
362
|
+
self, i_name: str
|
363
|
+
) -> Tuple[Union[BranchLog, RunLog], Union[StepLog, None]]:
|
342
364
|
"""
|
343
365
|
Given a branch internal name, search for it in the run log.
|
344
366
|
|
@@ -385,7 +407,9 @@ class RunLog(BaseModel):
|
|
385
407
|
|
386
408
|
raise exceptions.BranchLogNotFoundError(self.run_id, i_name)
|
387
409
|
|
388
|
-
def search_step_by_internal_name(
|
410
|
+
def search_step_by_internal_name(
|
411
|
+
self, i_name: str
|
412
|
+
) -> Tuple[StepLog, Union[BranchLog, None]]:
|
389
413
|
"""
|
390
414
|
Given a steps internal name, search for the step name.
|
391
415
|
|
@@ -415,7 +439,9 @@ class RunLog(BaseModel):
|
|
415
439
|
# Its odd, so we are in brach name
|
416
440
|
current_branch = current_step.branches[".".join(dot_path[: i + 1])] # type: ignore
|
417
441
|
current_steps = current_branch.steps
|
418
|
-
logger.debug(
|
442
|
+
logger.debug(
|
443
|
+
f"Finding step log for {i_name} in branch: {current_branch}"
|
444
|
+
)
|
419
445
|
else:
|
420
446
|
# Its even, so we are in step, we start here!
|
421
447
|
current_step = current_steps[".".join(dot_path[: i + 1])]
|
@@ -428,10 +454,6 @@ class RunLog(BaseModel):
|
|
428
454
|
raise exceptions.StepLogNotFoundError(self.run_id, i_name)
|
429
455
|
|
430
456
|
|
431
|
-
# All outside modules should interact with dataclasses using the RunLogStore to promote extensibility
|
432
|
-
# If you want to customize dataclass, extend BaseRunLogStore and implement the methods as per the specification
|
433
|
-
|
434
|
-
|
435
457
|
class BaseRunLogStore(ABC, BaseModel):
|
436
458
|
"""
|
437
459
|
The base class of a Run Log Store with many common methods implemented.
|
@@ -441,8 +463,7 @@ class BaseRunLogStore(ABC, BaseModel):
|
|
441
463
|
service_type: str = "run_log_store"
|
442
464
|
|
443
465
|
@abstractmethod
|
444
|
-
def get_summary(self) -> Dict[str, Any]:
|
445
|
-
...
|
466
|
+
def get_summary(self) -> Dict[str, Any]: ...
|
446
467
|
|
447
468
|
@property
|
448
469
|
def _context(self):
|
@@ -629,7 +650,9 @@ class BaseRunLogStore(ABC, BaseModel):
|
|
629
650
|
RunLogNotFoundError: If the run log for run_id is not found in the datastore
|
630
651
|
StepLogNotFoundError: If the step log for internal_name is not found in the datastore for run_id
|
631
652
|
"""
|
632
|
-
logger.info(
|
653
|
+
logger.info(
|
654
|
+
f"{self.service_name} Getting the step log: {internal_name} of {run_id}"
|
655
|
+
)
|
633
656
|
run_log = self.get_run_log_by_id(run_id=run_id)
|
634
657
|
step_log, _ = run_log.search_step_by_internal_name(internal_name)
|
635
658
|
return step_log
|
@@ -675,10 +698,14 @@ class BaseRunLogStore(ABC, BaseModel):
|
|
675
698
|
BranchLog: Uncommitted and initialized with defaults BranchLog object
|
676
699
|
"""
|
677
700
|
# Create a new BranchLog
|
678
|
-
logger.info(
|
701
|
+
logger.info(
|
702
|
+
f"{self.service_name} Creating a Branch Log : {internal_branch_name}"
|
703
|
+
)
|
679
704
|
return BranchLog(internal_name=internal_branch_name, status=defaults.CREATED)
|
680
705
|
|
681
|
-
def get_branch_log(
|
706
|
+
def get_branch_log(
|
707
|
+
self, internal_branch_name: str, run_id: str, **kwargs
|
708
|
+
) -> Union[BranchLog, RunLog]:
|
682
709
|
"""
|
683
710
|
Returns the branch log by the internal branch name for the run id
|
684
711
|
|
@@ -697,7 +724,9 @@ class BaseRunLogStore(ABC, BaseModel):
|
|
697
724
|
branch, _ = run_log.search_branch_by_internal_name(internal_branch_name)
|
698
725
|
return branch
|
699
726
|
|
700
|
-
def add_branch_log(
|
727
|
+
def add_branch_log(
|
728
|
+
self, branch_log: Union[BranchLog, RunLog], run_id: str, **kwargs
|
729
|
+
):
|
701
730
|
"""
|
702
731
|
The method should:
|
703
732
|
# Get the run log
|
@@ -775,7 +804,9 @@ class BufferRunLogstore(BaseRunLogStore):
|
|
775
804
|
"""
|
776
805
|
|
777
806
|
service_name: str = "buffered"
|
778
|
-
run_log: Optional[RunLog] = Field(
|
807
|
+
run_log: Optional[RunLog] = Field(
|
808
|
+
default=None, exclude=True
|
809
|
+
) # For a buffered Run Log, this is the database
|
779
810
|
|
780
811
|
def get_summary(self) -> Dict[str, Any]:
|
781
812
|
summary = {"Type": self.service_name, "Location": "Not persisted"}
|
@@ -826,5 +857,10 @@ class BufferRunLogstore(BaseRunLogStore):
|
|
826
857
|
# Puts the run log in the db
|
827
858
|
# Raises Exception if not found
|
828
859
|
"""
|
829
|
-
logger.info(
|
860
|
+
logger.info(
|
861
|
+
f"{self.service_name} Putting the run log in the DB: {run_log.run_id}"
|
862
|
+
)
|
830
863
|
self.run_log = run_log
|
864
|
+
|
865
|
+
|
866
|
+
import runnable.context as context # noqa: F401, E402
|
runnable/defaults.py
CHANGED