runnable 0.12.3__py3-none-any.whl → 0.14.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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