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.
Files changed (64) hide show
  1. runnable/__init__.py +0 -11
  2. runnable/catalog.py +27 -5
  3. runnable/cli.py +122 -26
  4. runnable/datastore.py +71 -35
  5. runnable/defaults.py +0 -1
  6. runnable/entrypoints.py +107 -32
  7. runnable/exceptions.py +6 -2
  8. runnable/executor.py +28 -9
  9. runnable/graph.py +37 -12
  10. runnable/integration.py +7 -2
  11. runnable/nodes.py +15 -17
  12. runnable/parameters.py +27 -8
  13. runnable/pickler.py +1 -1
  14. runnable/sdk.py +101 -33
  15. runnable/secrets.py +3 -1
  16. runnable/tasks.py +246 -34
  17. runnable/utils.py +41 -13
  18. {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info}/METADATA +25 -31
  19. runnable-0.14.0.dist-info/RECORD +24 -0
  20. {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info}/WHEEL +1 -1
  21. runnable-0.14.0.dist-info/entry_points.txt +40 -0
  22. runnable/extensions/__init__.py +0 -0
  23. runnable/extensions/catalog/__init__.py +0 -21
  24. runnable/extensions/catalog/file_system/__init__.py +0 -0
  25. runnable/extensions/catalog/file_system/implementation.py +0 -234
  26. runnable/extensions/catalog/k8s_pvc/__init__.py +0 -0
  27. runnable/extensions/catalog/k8s_pvc/implementation.py +0 -16
  28. runnable/extensions/catalog/k8s_pvc/integration.py +0 -59
  29. runnable/extensions/executor/__init__.py +0 -649
  30. runnable/extensions/executor/argo/__init__.py +0 -0
  31. runnable/extensions/executor/argo/implementation.py +0 -1194
  32. runnable/extensions/executor/argo/specification.yaml +0 -51
  33. runnable/extensions/executor/k8s_job/__init__.py +0 -0
  34. runnable/extensions/executor/k8s_job/implementation_FF.py +0 -259
  35. runnable/extensions/executor/k8s_job/integration_FF.py +0 -69
  36. runnable/extensions/executor/local/__init__.py +0 -0
  37. runnable/extensions/executor/local/implementation.py +0 -71
  38. runnable/extensions/executor/local_container/__init__.py +0 -0
  39. runnable/extensions/executor/local_container/implementation.py +0 -446
  40. runnable/extensions/executor/mocked/__init__.py +0 -0
  41. runnable/extensions/executor/mocked/implementation.py +0 -154
  42. runnable/extensions/executor/retry/__init__.py +0 -0
  43. runnable/extensions/executor/retry/implementation.py +0 -168
  44. runnable/extensions/nodes.py +0 -855
  45. runnable/extensions/run_log_store/__init__.py +0 -0
  46. runnable/extensions/run_log_store/chunked_file_system/__init__.py +0 -0
  47. runnable/extensions/run_log_store/chunked_file_system/implementation.py +0 -111
  48. runnable/extensions/run_log_store/chunked_k8s_pvc/__init__.py +0 -0
  49. runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +0 -21
  50. runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +0 -61
  51. runnable/extensions/run_log_store/db/implementation_FF.py +0 -157
  52. runnable/extensions/run_log_store/db/integration_FF.py +0 -0
  53. runnable/extensions/run_log_store/file_system/__init__.py +0 -0
  54. runnable/extensions/run_log_store/file_system/implementation.py +0 -140
  55. runnable/extensions/run_log_store/generic_chunked.py +0 -557
  56. runnable/extensions/run_log_store/k8s_pvc/__init__.py +0 -0
  57. runnable/extensions/run_log_store/k8s_pvc/implementation.py +0 -21
  58. runnable/extensions/run_log_store/k8s_pvc/integration.py +0 -56
  59. runnable/extensions/secrets/__init__.py +0 -0
  60. runnable/extensions/secrets/dotenv/__init__.py +0 -0
  61. runnable/extensions/secrets/dotenv/implementation.py +0 -100
  62. runnable-0.12.3.dist-info/RECORD +0 -64
  63. runnable-0.12.3.dist-info/entry_points.txt +0 -41
  64. {runnable-0.12.3.dist-info → runnable-0.14.0.dist-info/licenses}/LICENSE +0 -0
runnable/entrypoints.py CHANGED
@@ -35,6 +35,7 @@ def prepare_configurations(
35
35
  force_local_executor: bool = False,
36
36
  ) -> context.Context:
37
37
  """
38
+ Sets up everything needed
38
39
  Replace the placeholders in the dag/config against the variables file.
39
40
 
40
41
  Attach the secrets_handler, run_log_store, catalog_handler to the executor and return it.
@@ -53,11 +54,17 @@ def prepare_configurations(
53
54
  variables = utils.gather_variables()
54
55
 
55
56
  templated_configuration = {}
56
- configuration_file = os.environ.get("RUNNABLE_CONFIGURATION_FILE", configuration_file)
57
+ configuration_file = os.environ.get(
58
+ "RUNNABLE_CONFIGURATION_FILE", configuration_file
59
+ )
57
60
 
58
61
  if configuration_file:
59
62
  templated_configuration = utils.load_yaml(configuration_file) or {}
60
63
 
64
+ # Since all the services (run_log_store, catalog, secrets, executor) are
65
+ # dynamically loaded via stevedore, we cannot validate the configuration
66
+ # before they are passed to the service.
67
+
61
68
  configuration: RunnableConfig = cast(RunnableConfig, templated_configuration)
62
69
 
63
70
  logger.info(f"Resolved configurations: {configuration}")
@@ -65,23 +72,32 @@ def prepare_configurations(
65
72
  # Run log settings, configuration over-rides everything
66
73
  run_log_config: Optional[ServiceConfig] = configuration.get("run_log_store", None)
67
74
  if not run_log_config:
68
- run_log_config = cast(ServiceConfig, runnable_defaults.get("run_log_store", defaults.DEFAULT_RUN_LOG_STORE))
75
+ run_log_config = cast(
76
+ ServiceConfig,
77
+ runnable_defaults.get("run_log_store", defaults.DEFAULT_RUN_LOG_STORE),
78
+ )
69
79
  run_log_store = utils.get_provider_by_name_and_type("run_log_store", run_log_config)
70
80
 
71
81
  # Catalog handler settings, configuration over-rides everything
72
82
  catalog_config: Optional[ServiceConfig] = configuration.get("catalog", None)
73
83
  if not catalog_config:
74
- catalog_config = cast(ServiceConfig, runnable_defaults.get("catalog", defaults.DEFAULT_CATALOG))
84
+ catalog_config = cast(
85
+ ServiceConfig, runnable_defaults.get("catalog", defaults.DEFAULT_CATALOG)
86
+ )
75
87
  catalog_handler = utils.get_provider_by_name_and_type("catalog", catalog_config)
76
88
 
77
89
  # Secret handler settings, configuration over-rides everything
78
90
  secrets_config: Optional[ServiceConfig] = configuration.get("secrets", None)
79
91
  if not secrets_config:
80
- secrets_config = cast(ServiceConfig, runnable_defaults.get("secrets", defaults.DEFAULT_SECRETS))
92
+ secrets_config = cast(
93
+ ServiceConfig, runnable_defaults.get("secrets", defaults.DEFAULT_SECRETS)
94
+ )
81
95
  secrets_handler = utils.get_provider_by_name_and_type("secrets", secrets_config)
82
96
 
83
97
  # pickler
84
- pickler_config = cast(ServiceConfig, runnable_defaults.get("pickler", defaults.DEFAULT_PICKLER))
98
+ pickler_config = cast(
99
+ ServiceConfig, runnable_defaults.get("pickler", defaults.DEFAULT_PICKLER)
100
+ )
85
101
  pickler_handler = utils.get_provider_by_name_and_type("pickler", pickler_config)
86
102
 
87
103
  # executor configurations, configuration over rides everything
@@ -90,8 +106,12 @@ def prepare_configurations(
90
106
  executor_config = ServiceConfig(type="local", config={})
91
107
 
92
108
  if not executor_config:
93
- executor_config = cast(ServiceConfig, runnable_defaults.get("executor", defaults.DEFAULT_EXECUTOR))
94
- configured_executor = utils.get_provider_by_name_and_type("executor", executor_config)
109
+ executor_config = cast(
110
+ ServiceConfig, runnable_defaults.get("executor", defaults.DEFAULT_EXECUTOR)
111
+ )
112
+ configured_executor = utils.get_provider_by_name_and_type(
113
+ "executor", executor_config
114
+ )
95
115
 
96
116
  # Construct the context
97
117
  run_context = context.Context(
@@ -174,20 +194,26 @@ def execute(
174
194
 
175
195
  run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
176
196
 
177
- utils.set_runnable_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
197
+ utils.set_runnable_environment_variables(
198
+ run_id=run_id, configuration_file=configuration_file, tag=tag
199
+ )
178
200
 
179
201
  # Prepare for graph execution
180
202
  executor.prepare_for_graph_execution()
181
203
 
182
204
  logger.info(f"Executing the graph: {run_context.dag}")
183
205
  with Progress(
184
- TextColumn("[progress.description]{task.description}", table_column=Column(ratio=2)),
206
+ TextColumn(
207
+ "[progress.description]{task.description}", table_column=Column(ratio=2)
208
+ ),
185
209
  BarColumn(table_column=Column(ratio=1), style="dark_orange"),
186
210
  TimeElapsedColumn(table_column=Column(ratio=1)),
187
211
  console=console,
188
212
  expand=True,
189
213
  ) as progress:
190
- pipeline_execution_task = progress.add_task("[dark_orange] Starting execution .. ", total=1)
214
+ pipeline_execution_task = progress.add_task(
215
+ "[dark_orange] Starting execution .. ", total=1
216
+ )
191
217
  try:
192
218
  run_context.progress = progress
193
219
  executor.execute_graph(dag=run_context.dag) # type: ignore
@@ -197,16 +223,30 @@ def execute(
197
223
  executor.send_return_code(stage="traversal")
198
224
  return
199
225
 
200
- run_log = run_context.run_log_store.get_run_log_by_id(run_id=run_context.run_id, full=False)
226
+ run_log = run_context.run_log_store.get_run_log_by_id(
227
+ run_id=run_context.run_id, full=False
228
+ )
201
229
 
202
230
  if run_log.status == defaults.SUCCESS:
203
- progress.update(pipeline_execution_task, description="[green] Success", completed=True)
231
+ progress.update(
232
+ pipeline_execution_task,
233
+ description="[green] Success",
234
+ completed=True,
235
+ )
204
236
  else:
205
- progress.update(pipeline_execution_task, description="[red] Failed", completed=True)
237
+ progress.update(
238
+ pipeline_execution_task, description="[red] Failed", completed=True
239
+ )
206
240
  except Exception as e: # noqa: E722
207
241
  console.print(e, style=defaults.error_style)
208
- progress.update(pipeline_execution_task, description="[red] Errored execution", completed=True)
209
- run_log = run_context.run_log_store.get_run_log_by_id(run_id=run_context.run_id, full=False)
242
+ progress.update(
243
+ pipeline_execution_task,
244
+ description="[red] Errored execution",
245
+ completed=True,
246
+ )
247
+ run_log = run_context.run_log_store.get_run_log_by_id(
248
+ run_id=run_context.run_id, full=False
249
+ )
210
250
  run_log.status = defaults.FAIL
211
251
  run_context.run_log_store.add_branch_log(run_log, run_context.run_id)
212
252
  raise e
@@ -240,9 +280,13 @@ def execute_single_node(
240
280
  """
241
281
  from runnable import nodes
242
282
 
243
- task_console.print(f"Executing the single node: {step_name} with map variable: {map_variable}")
283
+ task_console.print(
284
+ f"Executing the single node: {step_name} with map variable: {map_variable}"
285
+ )
244
286
 
245
- configuration_file = os.environ.get("RUNNABLE_CONFIGURATION_FILE", configuration_file)
287
+ configuration_file = os.environ.get(
288
+ "RUNNABLE_CONFIGURATION_FILE", configuration_file
289
+ )
246
290
 
247
291
  run_context = prepare_configurations(
248
292
  configuration_file=configuration_file,
@@ -257,7 +301,9 @@ def execute_single_node(
257
301
 
258
302
  executor = run_context.executor
259
303
  run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
260
- utils.set_runnable_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
304
+ utils.set_runnable_environment_variables(
305
+ run_id=run_id, configuration_file=configuration_file, tag=tag
306
+ )
261
307
 
262
308
  executor.prepare_for_node_execution()
263
309
 
@@ -271,7 +317,9 @@ def execute_single_node(
271
317
  map_variable_dict = utils.json_to_ordered_dict(map_variable)
272
318
 
273
319
  step_internal_name = nodes.BaseNode._get_internal_name_from_command_name(step_name)
274
- node_to_execute, _ = graph.search_node_by_internal_name(run_context.dag, step_internal_name)
320
+ node_to_execute, _ = graph.search_node_by_internal_name(
321
+ run_context.dag, step_internal_name
322
+ )
275
323
 
276
324
  logger.info("Executing the single node of : %s", node_to_execute)
277
325
  ## This step is where we save the log file
@@ -316,7 +364,9 @@ def execute_notebook(
316
364
 
317
365
  executor = run_context.executor
318
366
  run_context.execution_plan = defaults.EXECUTION_PLAN.UNCHAINED.value
319
- utils.set_runnable_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
367
+ utils.set_runnable_environment_variables(
368
+ run_id=run_id, configuration_file=configuration_file, tag=tag
369
+ )
320
370
 
321
371
  console.print("Working with context:")
322
372
  console.print(run_context)
@@ -336,17 +386,25 @@ def execute_notebook(
336
386
  # Prepare for graph execution
337
387
  executor.prepare_for_graph_execution()
338
388
 
339
- logger.info("Executing the job from the user. We are still in the caller's compute environment")
389
+ logger.info(
390
+ "Executing the job from the user. We are still in the caller's compute environment"
391
+ )
340
392
  executor.execute_job(node=node)
341
393
 
342
394
  elif entrypoint == defaults.ENTRYPOINT.SYSTEM.value:
343
395
  executor.prepare_for_node_execution()
344
- logger.info("Executing the job from the system. We are in the config's compute environment")
396
+ logger.info(
397
+ "Executing the job from the system. We are in the config's compute environment"
398
+ )
345
399
  executor.execute_node(node=node)
346
400
 
347
401
  # Update the status of the run log
348
- step_log = run_context.run_log_store.get_step_log(node._get_step_log_name(), run_id)
349
- run_context.run_log_store.update_run_log_status(run_id=run_id, status=step_log.status)
402
+ step_log = run_context.run_log_store.get_step_log(
403
+ node._get_step_log_name(), run_id
404
+ )
405
+ run_context.run_log_store.update_run_log_status(
406
+ run_id=run_id, status=step_log.status
407
+ )
350
408
 
351
409
  else:
352
410
  raise ValueError(f"Invalid entrypoint {entrypoint}")
@@ -379,7 +437,9 @@ def execute_function(
379
437
  executor = run_context.executor
380
438
 
381
439
  run_context.execution_plan = defaults.EXECUTION_PLAN.UNCHAINED.value
382
- utils.set_runnable_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
440
+ utils.set_runnable_environment_variables(
441
+ run_id=run_id, configuration_file=configuration_file, tag=tag
442
+ )
383
443
 
384
444
  console.print("Working with context:")
385
445
  console.print(run_context)
@@ -399,17 +459,25 @@ def execute_function(
399
459
  # Prepare for graph execution
400
460
  executor.prepare_for_graph_execution()
401
461
 
402
- logger.info("Executing the job from the user. We are still in the caller's compute environment")
462
+ logger.info(
463
+ "Executing the job from the user. We are still in the caller's compute environment"
464
+ )
403
465
  executor.execute_job(node=node)
404
466
 
405
467
  elif entrypoint == defaults.ENTRYPOINT.SYSTEM.value:
406
468
  executor.prepare_for_node_execution()
407
- logger.info("Executing the job from the system. We are in the config's compute environment")
469
+ logger.info(
470
+ "Executing the job from the system. We are in the config's compute environment"
471
+ )
408
472
  executor.execute_node(node=node)
409
473
 
410
474
  # Update the status of the run log
411
- step_log = run_context.run_log_store.get_step_log(node._get_step_log_name(), run_id)
412
- run_context.run_log_store.update_run_log_status(run_id=run_id, status=step_log.status)
475
+ step_log = run_context.run_log_store.get_step_log(
476
+ node._get_step_log_name(), run_id
477
+ )
478
+ run_context.run_log_store.update_run_log_status(
479
+ run_id=run_id, status=step_log.status
480
+ )
413
481
 
414
482
  else:
415
483
  raise ValueError(f"Invalid entrypoint {entrypoint}")
@@ -444,7 +512,9 @@ def fan(
444
512
  """
445
513
  from runnable import nodes
446
514
 
447
- configuration_file = os.environ.get("RUNNABLE_CONFIGURATION_FILE", configuration_file)
515
+ configuration_file = os.environ.get(
516
+ "RUNNABLE_CONFIGURATION_FILE", configuration_file
517
+ )
448
518
 
449
519
  run_context = prepare_configurations(
450
520
  configuration_file=configuration_file,
@@ -459,12 +529,17 @@ def fan(
459
529
 
460
530
  executor = run_context.executor
461
531
  run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
462
- utils.set_runnable_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
532
+ utils.set_runnable_environment_variables(
533
+ run_id=run_id, configuration_file=configuration_file, tag=tag
534
+ )
463
535
 
464
536
  executor.prepare_for_node_execution()
465
537
 
466
538
  step_internal_name = nodes.BaseNode._get_internal_name_from_command_name(step_name)
467
- node_to_execute, _ = graph.search_node_by_internal_name(run_context.dag, step_internal_name) # type: ignore
539
+ node_to_execute, _ = graph.search_node_by_internal_name(
540
+ run_context.dag, # type: ignore
541
+ step_internal_name,
542
+ )
468
543
 
469
544
  map_variable_dict = utils.json_to_ordered_dict(map_variable)
470
545
 
runnable/exceptions.py CHANGED
@@ -31,7 +31,9 @@ class StepLogNotFoundError(Exception): # pragma: no cover
31
31
 
32
32
  def __init__(self, run_id, name):
33
33
  super().__init__()
34
- self.message = f"Step log for {name} is not found in the datastore for Run id: {run_id}"
34
+ self.message = (
35
+ f"Step log for {name} is not found in the datastore for Run id: {run_id}"
36
+ )
35
37
 
36
38
 
37
39
  class BranchLogNotFoundError(Exception): # pragma: no cover
@@ -43,7 +45,9 @@ class BranchLogNotFoundError(Exception): # pragma: no cover
43
45
 
44
46
  def __init__(self, run_id, name):
45
47
  super().__init__()
46
- self.message = f"Branch log for {name} is not found in the datastore for Run id: {run_id}"
48
+ self.message = (
49
+ f"Branch log for {name} is not found in the datastore for Run id: {run_id}"
50
+ )
47
51
 
48
52
 
49
53
  class NodeNotFoundError(Exception): # pragma: no cover
runnable/executor.py CHANGED
@@ -14,7 +14,7 @@ from runnable.defaults import TypeMapVariable
14
14
  from runnable.graph import Graph
15
15
 
16
16
  if TYPE_CHECKING: # pragma: no cover
17
- from runnable.extensions.nodes import TaskNode
17
+ from extensions.nodes.nodes import TaskNode
18
18
  from runnable.nodes import BaseNode
19
19
 
20
20
  logger = logging.getLogger(defaults.LOGGER_NAME)
@@ -36,9 +36,12 @@ class BaseExecutor(ABC, BaseModel):
36
36
 
37
37
  overrides: dict = {}
38
38
 
39
- _local: bool = False # This is a flag to indicate whether the executor is local or not.
39
+ _local: bool = (
40
+ False # This is a flag to indicate whether the executor is local or not.
41
+ )
40
42
 
41
- _context_node = None # type: BaseNode
43
+ # TODO: Change this to _is_local
44
+ _context_node: Optional[BaseNode] = None
42
45
  model_config = ConfigDict(extra="forbid")
43
46
 
44
47
  @property
@@ -90,7 +93,9 @@ class BaseExecutor(ABC, BaseModel):
90
93
  ...
91
94
 
92
95
  @abstractmethod
93
- def _sync_catalog(self, stage: str, synced_catalogs=None) -> Optional[List[DataCatalog]]:
96
+ def _sync_catalog(
97
+ self, stage: str, synced_catalogs=None
98
+ ) -> Optional[List[DataCatalog]]:
94
99
  """
95
100
  1). Identify the catalog settings by over-riding node settings with the global settings.
96
101
  2). For stage = get:
@@ -141,7 +146,13 @@ class BaseExecutor(ABC, BaseModel):
141
146
  return int(os.environ.get(defaults.ATTEMPT_NUMBER, 1))
142
147
 
143
148
  @abstractmethod
144
- def _execute_node(self, node: BaseNode, map_variable: TypeMapVariable = None, mock: bool = False, **kwargs):
149
+ def _execute_node(
150
+ self,
151
+ node: BaseNode,
152
+ map_variable: TypeMapVariable = None,
153
+ mock: bool = False,
154
+ **kwargs,
155
+ ):
145
156
  """
146
157
  This is the entry point when we do the actual execution of the function.
147
158
 
@@ -163,7 +174,9 @@ class BaseExecutor(ABC, BaseModel):
163
174
  ...
164
175
 
165
176
  @abstractmethod
166
- def execute_node(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
177
+ def execute_node(
178
+ self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs
179
+ ):
167
180
  """
168
181
  The entry point for all executors apart from local.
169
182
  We have already prepared for node execution.
@@ -191,7 +204,9 @@ class BaseExecutor(ABC, BaseModel):
191
204
  ...
192
205
 
193
206
  @abstractmethod
194
- def execute_from_graph(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
207
+ def execute_from_graph(
208
+ self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs
209
+ ):
195
210
  """
196
211
  This is the entry point to from the graph execution.
197
212
 
@@ -219,7 +234,9 @@ class BaseExecutor(ABC, BaseModel):
219
234
  ...
220
235
 
221
236
  @abstractmethod
222
- def trigger_job(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
237
+ def trigger_job(
238
+ self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs
239
+ ):
223
240
  """
224
241
  Executor specific way of triggering jobs when runnable does both traversal and execution
225
242
 
@@ -236,7 +253,9 @@ class BaseExecutor(ABC, BaseModel):
236
253
  ...
237
254
 
238
255
  @abstractmethod
239
- def _get_status_and_next_node_name(self, current_node: BaseNode, dag: Graph, map_variable: TypeMapVariable = None):
256
+ def _get_status_and_next_node_name(
257
+ self, current_node: BaseNode, dag: Graph, map_variable: TypeMapVariable = None
258
+ ):
240
259
  """
241
260
  Given the current node and the graph, returns the name of the next node to execute.
242
261
 
runnable/graph.py CHANGED
@@ -24,7 +24,9 @@ class Graph(BaseModel):
24
24
  name: str = ""
25
25
  description: Optional[str] = ""
26
26
  internal_branch_name: str = Field(default="", exclude=True)
27
- nodes: SerializeAsAny[Dict[str, "BaseNode"]] = Field(default_factory=dict, serialization_alias="steps")
27
+ nodes: SerializeAsAny[Dict[str, "BaseNode"]] = Field(
28
+ default_factory=dict, serialization_alias="steps"
29
+ )
28
30
 
29
31
  def get_summary(self) -> Dict[str, Any]:
30
32
  """
@@ -229,7 +231,9 @@ class Graph(BaseModel):
229
231
  return False
230
232
  return True
231
233
 
232
- def is_cyclic_util(self, node: "BaseNode", visited: Dict[str, bool], recstack: Dict[str, bool]) -> bool:
234
+ def is_cyclic_util(
235
+ self, node: "BaseNode", visited: Dict[str, bool], recstack: Dict[str, bool]
236
+ ) -> bool:
233
237
  """
234
238
  Recursive utility that determines if a node and neighbors has a cycle. Is used in is_dag method.
235
239
 
@@ -327,7 +331,9 @@ def create_graph(dag_config: Dict[str, Any], internal_branch_name: str = "") ->
327
331
  Graph: The created graph object
328
332
  """
329
333
  description: str = dag_config.get("description", None)
330
- start_at: str = cast(str, dag_config.get("start_at")) # Let the start_at be relative to the graph
334
+ start_at: str = cast(
335
+ str, dag_config.get("start_at")
336
+ ) # Let the start_at be relative to the graph
331
337
 
332
338
  graph = Graph(
333
339
  start_at=start_at,
@@ -339,7 +345,9 @@ def create_graph(dag_config: Dict[str, Any], internal_branch_name: str = "") ->
339
345
  for name, step_config in dag_config.get("steps", {}).items():
340
346
  logger.info(f"Adding node {name} with :{step_config}")
341
347
 
342
- node = create_node(name, step_config=step_config, internal_branch_name=internal_branch_name)
348
+ node = create_node(
349
+ name, step_config=step_config, internal_branch_name=internal_branch_name
350
+ )
343
351
  graph.add_node(node)
344
352
 
345
353
  graph.add_terminal_nodes(internal_branch_name=internal_branch_name)
@@ -369,8 +377,12 @@ def create_node(name: str, step_config: dict, internal_branch_name: Optional[str
369
377
  internal_name = internal_branch_name + "." + name
370
378
 
371
379
  try:
372
- node_type = step_config.pop("type") # Remove the type as it is not used in node creation.
373
- node_mgr: BaseNode = driver.DriverManager(namespace="nodes", name=node_type).driver
380
+ node_type = step_config.pop(
381
+ "type"
382
+ ) # Remove the type as it is not used in node creation.
383
+ node_mgr: BaseNode = driver.DriverManager(
384
+ namespace="nodes", name=node_type
385
+ ).driver
374
386
 
375
387
  next_node = step_config.pop("next", None)
376
388
 
@@ -424,11 +436,18 @@ def search_node_by_internal_name(dag: Graph, internal_name: str):
424
436
  for i in range(len(dot_path)):
425
437
  if i % 2:
426
438
  # Its odd, so we are in brach name
427
- current_branch = current_node._get_branch_by_name(".".join(dot_path[: i + 1])) # type: ignore
428
- logger.debug(f"Finding step for {internal_name} in branch: {current_branch}")
439
+
440
+ current_branch = current_node._get_branch_by_name( # type: ignore
441
+ ".".join(dot_path[: i + 1])
442
+ )
443
+ logger.debug(
444
+ f"Finding step for {internal_name} in branch: {current_branch}"
445
+ )
429
446
  else:
430
447
  # Its even, so we are in Step, we start here!
431
- current_node = current_branch.get_node_by_internal_name(".".join(dot_path[: i + 1]))
448
+ current_node = current_branch.get_node_by_internal_name(
449
+ ".".join(dot_path[: i + 1])
450
+ )
432
451
  logger.debug(f"Finding {internal_name} in node: {current_node}")
433
452
 
434
453
  logger.debug(f"current branch : {current_branch}, current step {current_node}")
@@ -463,12 +482,18 @@ def search_branch_by_internal_name(dag: Graph, internal_name: str):
463
482
  for i in range(len(dot_path)):
464
483
  if i % 2:
465
484
  # Its odd, so we are in brach name
466
- current_branch = current_node._get_branch_by_name(".".join(dot_path[: i + 1])) # type: ignore
467
- logger.debug(f"Finding step for {internal_name} in branch: {current_branch}")
485
+ current_branch = current_node._get_branch_by_name( # type: ignore
486
+ ".".join(dot_path[: i + 1])
487
+ )
488
+ logger.debug(
489
+ f"Finding step for {internal_name} in branch: {current_branch}"
490
+ )
468
491
 
469
492
  else:
470
493
  # Its even, so we are in Step, we start here!
471
- current_node = current_branch.get_node_by_internal_name(".".join(dot_path[: i + 1]))
494
+ current_node = current_branch.get_node_by_internal_name(
495
+ ".".join(dot_path[: i + 1])
496
+ )
472
497
  logger.debug(f"Finding {internal_name} in node: {current_node}")
473
498
 
474
499
  logger.debug(f"current branch : {current_branch}, current step {current_node}")
runnable/integration.py CHANGED
@@ -49,7 +49,9 @@ class BaseIntegration:
49
49
  # --8<-- [end:docs]
50
50
 
51
51
 
52
- def get_integration_handler(executor: "BaseExecutor", service: object) -> BaseIntegration:
52
+ def get_integration_handler(
53
+ executor: "BaseExecutor", service: object
54
+ ) -> BaseIntegration:
53
55
  """
54
56
  Return the integration handler between executor and the service.
55
57
 
@@ -147,6 +149,7 @@ def configure_for_execution(executor: "BaseExecutor", service: object, **kwargs)
147
149
  integration_handler.configure_for_execution(**kwargs)
148
150
 
149
151
 
152
+ # TODO: Move all of these to the proper locations
150
153
  class BufferedRunLogStore(BaseIntegration):
151
154
  """
152
155
  Integration between any executor and buffered run log store
@@ -157,7 +160,9 @@ class BufferedRunLogStore(BaseIntegration):
157
160
 
158
161
  def validate(self, **kwargs):
159
162
  if not self.executor.service_name == "local":
160
- raise Exception("Buffered run log store is only supported for local executor")
163
+ raise Exception(
164
+ "Buffered run log store is only supported for local executor"
165
+ )
161
166
 
162
167
  msg = (
163
168
  "Run log generated by buffered run log store are not persisted. "
runnable/nodes.py CHANGED
@@ -51,7 +51,9 @@ class BaseNode(ABC, BaseModel):
51
51
  raise ValueError("Node names cannot have . or '%' in them")
52
52
  return name
53
53
 
54
- def _command_friendly_name(self, replace_with=defaults.COMMAND_FRIENDLY_CHARACTER) -> str:
54
+ def _command_friendly_name(
55
+ self, replace_with=defaults.COMMAND_FRIENDLY_CHARACTER
56
+ ) -> str:
55
57
  """
56
58
  Replace spaces with special character for spaces.
57
59
  Spaces in the naming of the node is convenient for the user but causes issues when used programmatically.
@@ -76,7 +78,9 @@ class BaseNode(ABC, BaseModel):
76
78
  return command_name.replace(defaults.COMMAND_FRIENDLY_CHARACTER, " ")
77
79
 
78
80
  @classmethod
79
- def _resolve_map_placeholders(cls, name: str, map_variable: TypeMapVariable = None) -> str:
81
+ def _resolve_map_placeholders(
82
+ cls, name: str, map_variable: TypeMapVariable = None
83
+ ) -> str:
80
84
  """
81
85
  If there is no map step used, then we just return the name as we find it.
82
86
 
@@ -141,7 +145,9 @@ class BaseNode(ABC, BaseModel):
141
145
  Returns:
142
146
  str: The dot path name of the step log name
143
147
  """
144
- return self._resolve_map_placeholders(self.internal_name, map_variable=map_variable)
148
+ return self._resolve_map_placeholders(
149
+ self.internal_name, map_variable=map_variable
150
+ )
145
151
 
146
152
  def _get_branch_log_name(self, map_variable: TypeMapVariable = None) -> str:
147
153
  """
@@ -158,7 +164,9 @@ class BaseNode(ABC, BaseModel):
158
164
  Returns:
159
165
  str: The dot path name of the branch log
160
166
  """
161
- return self._resolve_map_placeholders(self.internal_branch_name, map_variable=map_variable)
167
+ return self._resolve_map_placeholders(
168
+ self.internal_branch_name, map_variable=map_variable
169
+ )
162
170
 
163
171
  def __str__(self) -> str: # pragma: no cover
164
172
  """
@@ -180,7 +188,6 @@ class BaseNode(ABC, BaseModel):
180
188
  str: The on_failure node defined by the dag or ''
181
189
  This is a base implementation which the BaseNode does not satisfy
182
190
  """
183
- ...
184
191
 
185
192
  @abstractmethod
186
193
  def _get_next_node(self) -> str:
@@ -190,7 +197,6 @@ class BaseNode(ABC, BaseModel):
190
197
  Returns:
191
198
  str: The node name, relative to the dag, as defined by the config
192
199
  """
193
- ...
194
200
 
195
201
  @abstractmethod
196
202
  def _is_terminal_node(self) -> bool:
@@ -200,7 +206,6 @@ class BaseNode(ABC, BaseModel):
200
206
  Returns:
201
207
  bool: True or False of whether there is next node.
202
208
  """
203
- ...
204
209
 
205
210
  @abstractmethod
206
211
  def _get_catalog_settings(self) -> Dict[str, Any]:
@@ -210,7 +215,6 @@ class BaseNode(ABC, BaseModel):
210
215
  Returns:
211
216
  dict: catalog settings defined as per the node or None
212
217
  """
213
- ...
214
218
 
215
219
  @abstractmethod
216
220
  def _get_branch_by_name(self, branch_name: str):
@@ -225,7 +229,6 @@ class BaseNode(ABC, BaseModel):
225
229
  Raises:
226
230
  Exception: [description]
227
231
  """
228
- ...
229
232
 
230
233
  def _get_neighbors(self) -> List[str]:
231
234
  """
@@ -261,7 +264,6 @@ class BaseNode(ABC, BaseModel):
261
264
  Returns:
262
265
  dict: The executor config, if defined or an empty dict
263
266
  """
264
- ...
265
267
 
266
268
  @abstractmethod
267
269
  def _get_max_attempts(self) -> int:
@@ -271,7 +273,6 @@ class BaseNode(ABC, BaseModel):
271
273
  Returns:
272
274
  int: The number of maximum retries as defined by the config or 1.
273
275
  """
274
- ...
275
276
 
276
277
  @abstractmethod
277
278
  def execute(
@@ -296,7 +297,6 @@ class BaseNode(ABC, BaseModel):
296
297
  Raises:
297
298
  NotImplementedError: Base class, hence not implemented.
298
299
  """
299
- ...
300
300
 
301
301
  @abstractmethod
302
302
  def execute_as_graph(self, map_variable: TypeMapVariable = None, **kwargs):
@@ -312,7 +312,6 @@ class BaseNode(ABC, BaseModel):
312
312
  Raises:
313
313
  NotImplementedError: Base class, hence not implemented.
314
314
  """
315
- ...
316
315
 
317
316
  @abstractmethod
318
317
  def fan_out(self, map_variable: TypeMapVariable = None, **kwargs):
@@ -329,7 +328,6 @@ class BaseNode(ABC, BaseModel):
329
328
  Raises:
330
329
  Exception: If the node is not a composite node.
331
330
  """
332
- ...
333
331
 
334
332
  @abstractmethod
335
333
  def fan_in(self, map_variable: TypeMapVariable = None, **kwargs):
@@ -346,7 +344,6 @@ class BaseNode(ABC, BaseModel):
346
344
  Raises:
347
345
  Exception: If the node is not a composite node.
348
346
  """
349
- ...
350
347
 
351
348
  @classmethod
352
349
  @abstractmethod
@@ -360,7 +357,6 @@ class BaseNode(ABC, BaseModel):
360
357
  Returns:
361
358
  BaseNode: The corresponding node.
362
359
  """
363
- ...
364
360
 
365
361
  @abstractmethod
366
362
  def get_summary(self) -> Dict[str, Any]:
@@ -471,7 +467,9 @@ class CompositeNode(TraversalNode):
471
467
  attempt_number: int = 1,
472
468
  **kwargs,
473
469
  ) -> StepLog:
474
- raise Exception("This is a composite node and does not have an execute function")
470
+ raise Exception(
471
+ "This is a composite node and does not have an execute function"
472
+ )
475
473
 
476
474
 
477
475
  class TerminalNode(BaseNode):