runnable 0.12.1__tar.gz → 0.12.2__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. {runnable-0.12.1 → runnable-0.12.2}/PKG-INFO +1 -1
  2. {runnable-0.12.1 → runnable-0.12.2}/pyproject.toml +2 -1
  3. {runnable-0.12.1 → runnable-0.12.2}/runnable/__init__.py +2 -0
  4. {runnable-0.12.1 → runnable-0.12.2}/runnable/defaults.py +1 -1
  5. {runnable-0.12.1 → runnable-0.12.2}/runnable/entrypoints.py +7 -6
  6. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/__init__.py +10 -0
  7. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/local_container/implementation.py +54 -1
  8. {runnable-0.12.1 → runnable-0.12.2}/runnable/tasks.py +23 -24
  9. {runnable-0.12.1 → runnable-0.12.2}/LICENSE +0 -0
  10. {runnable-0.12.1 → runnable-0.12.2}/README.md +0 -0
  11. {runnable-0.12.1 → runnable-0.12.2}/runnable/catalog.py +0 -0
  12. {runnable-0.12.1 → runnable-0.12.2}/runnable/cli.py +0 -0
  13. {runnable-0.12.1 → runnable-0.12.2}/runnable/context.py +0 -0
  14. {runnable-0.12.1 → runnable-0.12.2}/runnable/datastore.py +0 -0
  15. {runnable-0.12.1 → runnable-0.12.2}/runnable/exceptions.py +0 -0
  16. {runnable-0.12.1 → runnable-0.12.2}/runnable/executor.py +0 -0
  17. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/__init__.py +0 -0
  18. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/__init__.py +0 -0
  19. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/file_system/__init__.py +0 -0
  20. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/file_system/implementation.py +0 -0
  21. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/k8s_pvc/__init__.py +0 -0
  22. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/k8s_pvc/implementation.py +0 -0
  23. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/catalog/k8s_pvc/integration.py +0 -0
  24. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/argo/__init__.py +0 -0
  25. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/argo/implementation.py +0 -0
  26. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/argo/specification.yaml +0 -0
  27. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/k8s_job/__init__.py +0 -0
  28. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/k8s_job/implementation_FF.py +0 -0
  29. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/k8s_job/integration_FF.py +0 -0
  30. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/local/__init__.py +0 -0
  31. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/local/implementation.py +0 -0
  32. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/local_container/__init__.py +0 -0
  33. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/mocked/__init__.py +0 -0
  34. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/mocked/implementation.py +0 -0
  35. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/retry/__init__.py +0 -0
  36. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/executor/retry/implementation.py +0 -0
  37. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/nodes.py +0 -0
  38. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/__init__.py +0 -0
  39. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/chunked_file_system/__init__.py +0 -0
  40. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/chunked_file_system/implementation.py +0 -0
  41. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/chunked_k8s_pvc/__init__.py +0 -0
  42. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +0 -0
  43. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +0 -0
  44. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/db/implementation_FF.py +0 -0
  45. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/db/integration_FF.py +0 -0
  46. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/file_system/__init__.py +0 -0
  47. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/file_system/implementation.py +0 -0
  48. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/generic_chunked.py +0 -0
  49. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/k8s_pvc/__init__.py +0 -0
  50. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/k8s_pvc/implementation.py +0 -0
  51. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/run_log_store/k8s_pvc/integration.py +0 -0
  52. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/secrets/__init__.py +0 -0
  53. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/secrets/dotenv/__init__.py +0 -0
  54. {runnable-0.12.1 → runnable-0.12.2}/runnable/extensions/secrets/dotenv/implementation.py +0 -0
  55. {runnable-0.12.1 → runnable-0.12.2}/runnable/graph.py +0 -0
  56. {runnable-0.12.1 → runnable-0.12.2}/runnable/integration.py +0 -0
  57. {runnable-0.12.1 → runnable-0.12.2}/runnable/names.py +0 -0
  58. {runnable-0.12.1 → runnable-0.12.2}/runnable/nodes.py +0 -0
  59. {runnable-0.12.1 → runnable-0.12.2}/runnable/parameters.py +0 -0
  60. {runnable-0.12.1 → runnable-0.12.2}/runnable/pickler.py +0 -0
  61. {runnable-0.12.1 → runnable-0.12.2}/runnable/sdk.py +0 -0
  62. {runnable-0.12.1 → runnable-0.12.2}/runnable/secrets.py +0 -0
  63. {runnable-0.12.1 → runnable-0.12.2}/runnable/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runnable
3
- Version: 0.12.1
3
+ Version: 0.12.2
4
4
  Summary: A Compute agnostic pipelining software
5
5
  Home-page: https://github.com/vijayvammi/runnable
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "runnable"
3
- version = "0.12.1"
3
+ version = "0.12.2"
4
4
  description = "A Compute agnostic pipelining software"
5
5
  authors = ["Vijay Vammi <mesanthu@gmail.com>"]
6
6
  license = "Apache-2.0"
@@ -97,6 +97,7 @@ runnable = 'runnable.cli:cli'
97
97
  [tool.poetry.plugins."secrets"]
98
98
  "do-nothing" = "runnable.secrets:DoNothingSecretManager"
99
99
  "dotenv" = "runnable.extensions.secrets.dotenv.implementation:DotEnvSecrets"
100
+ "env-secrets" = "runnable.secrets:EnvSecretsManager"
100
101
 
101
102
  # Plugins for Run Log store
102
103
  [tool.poetry.plugins."run_log_store"]
@@ -15,6 +15,8 @@ logger = logging.getLogger(defaults.LOGGER_NAME)
15
15
  console = Console(record=True)
16
16
  console.print(":runner: Lets go!!")
17
17
 
18
+ task_console = Console(record=True)
19
+
18
20
  from runnable.sdk import ( # noqa
19
21
  Catalog,
20
22
  Fail,
@@ -77,7 +77,7 @@ DEFAULT_CONTAINER_OUTPUT_PARAMETERS = "parameters.json"
77
77
  DEFAULT_EXECUTOR = ServiceConfig(type="local", config={})
78
78
  DEFAULT_RUN_LOG_STORE = ServiceConfig(type="file-system", config={})
79
79
  DEFAULT_CATALOG = ServiceConfig(type="file-system", config={})
80
- DEFAULT_SECRETS = ServiceConfig(type="do-nothing", config={})
80
+ DEFAULT_SECRETS = ServiceConfig(type="env-secrets", config={})
81
81
  DEFAULT_EXPERIMENT_TRACKER = ServiceConfig(type="do-nothing", config={})
82
82
  DEFAULT_PICKLER = ServiceConfig(type="pickle", config={})
83
83
 
@@ -9,7 +9,7 @@ from rich.progress import BarColumn, Progress, TextColumn, TimeElapsedColumn
9
9
  from rich.table import Column
10
10
 
11
11
  import runnable.context as context
12
- from runnable import console, defaults, graph, utils
12
+ from runnable import console, defaults, graph, task_console, utils
13
13
  from runnable.defaults import RunnableConfig, ServiceConfig
14
14
 
15
15
  logger = logging.getLogger(defaults.LOGGER_NAME)
@@ -165,6 +165,7 @@ def execute(
165
165
  tag=tag,
166
166
  parameters_file=parameters_file,
167
167
  )
168
+
168
169
  console.print("Working with context:")
169
170
  console.print(run_context)
170
171
  console.rule(style="[dark orange]")
@@ -239,7 +240,7 @@ def execute_single_node(
239
240
  """
240
241
  from runnable import nodes
241
242
 
242
- console.print(f"Executing the single node: {step_name} with map variable: {map_variable}")
243
+ task_console.print(f"Executing the single node: {step_name} with map variable: {map_variable}")
243
244
 
244
245
  configuration_file = os.environ.get("RUNNABLE_CONFIGURATION_FILE", configuration_file)
245
246
 
@@ -250,9 +251,9 @@ def execute_single_node(
250
251
  tag=tag,
251
252
  parameters_file=parameters_file,
252
253
  )
253
- console.print("Working with context:")
254
- console.print(run_context)
255
- console.rule(style="[dark orange]")
254
+ task_console.print("Working with context:")
255
+ task_console.print(run_context)
256
+ task_console.rule(style="[dark orange]")
256
257
 
257
258
  executor = run_context.executor
258
259
  run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
@@ -281,7 +282,7 @@ def execute_single_node(
281
282
  node=node_to_execute,
282
283
  map_variable=map_variable_dict,
283
284
  )
284
- console.save_text(log_file_name)
285
+ task_console.save_text(log_file_name)
285
286
 
286
287
  # Put the log file in the catalog
287
288
  run_context.catalog_handler.put(name=log_file_name, run_id=run_context.run_id)
@@ -11,6 +11,7 @@ from runnable import (
11
11
  exceptions,
12
12
  integration,
13
13
  parameters,
14
+ task_console,
14
15
  utils,
15
16
  )
16
17
  from runnable.datastore import DataCatalog, JsonParameter, RunLog, StepLog
@@ -340,10 +341,18 @@ class GenericExecutor(BaseExecutor):
340
341
  node.execute_as_graph(map_variable=map_variable, **kwargs)
341
342
  return
342
343
 
344
+ task_console.export_text(clear=True)
345
+
343
346
  task_name = node._resolve_map_placeholders(node.internal_name, map_variable)
344
347
  console.print(f":runner: Executing the node {task_name} ... ", style="bold color(208)")
345
348
  self.trigger_job(node=node, map_variable=map_variable, **kwargs)
346
349
 
350
+ log_file_name = utils.make_log_file_name(node=node, map_variable=map_variable)
351
+ task_console.save_text(log_file_name, clear=True)
352
+
353
+ self._context.catalog_handler.put(name=log_file_name, run_id=self._context.run_id)
354
+ os.remove(log_file_name)
355
+
347
356
  def trigger_job(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
348
357
  """
349
358
  Call this method only if we are responsible for traversing the graph via
@@ -493,6 +502,7 @@ class GenericExecutor(BaseExecutor):
493
502
 
494
503
  logger.info(f"Finished execution of the {branch} with status {run_log.status}")
495
504
 
505
+ # We are in the root dag
496
506
  if dag == self._context.dag:
497
507
  run_log = cast(RunLog, run_log)
498
508
  console.print("Completed Execution, Summary:", style="bold color(208)")
@@ -5,7 +5,7 @@ from typing import Dict, cast
5
5
  from pydantic import Field
6
6
  from rich import print
7
7
 
8
- from runnable import defaults, utils
8
+ from runnable import console, defaults, task_console, utils
9
9
  from runnable.datastore import StepLog
10
10
  from runnable.defaults import TypeMapVariable
11
11
  from runnable.extensions.executor import GenericExecutor
@@ -96,6 +96,59 @@ class LocalContainerExecutor(GenericExecutor):
96
96
  """
97
97
  return self._execute_node(node, map_variable, **kwargs)
98
98
 
99
+ def execute_from_graph(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
100
+ """
101
+ This is the entry point to from the graph execution.
102
+
103
+ While the self.execute_graph is responsible for traversing the graph, this function is responsible for
104
+ actual execution of the node.
105
+
106
+ If the node type is:
107
+ * task : We can delegate to _execute_node after checking the eligibility for re-run in cases of a re-run
108
+ * success: We can delegate to _execute_node
109
+ * fail: We can delegate to _execute_node
110
+
111
+ For nodes that are internally graphs:
112
+ * parallel: Delegate the responsibility of execution to the node.execute_as_graph()
113
+ * dag: Delegate the responsibility of execution to the node.execute_as_graph()
114
+ * map: Delegate the responsibility of execution to the node.execute_as_graph()
115
+
116
+ Transpilers will NEVER use this method and will NEVER call ths method.
117
+ This method should only be used by interactive executors.
118
+
119
+ Args:
120
+ node (Node): The node to execute
121
+ map_variable (dict, optional): If the node if of a map state, this corresponds to the value of iterable.
122
+ Defaults to None.
123
+ """
124
+ step_log = self._context.run_log_store.create_step_log(node.name, node._get_step_log_name(map_variable))
125
+
126
+ self.add_code_identities(node=node, step_log=step_log)
127
+
128
+ step_log.step_type = node.node_type
129
+ step_log.status = defaults.PROCESSING
130
+
131
+ self._context.run_log_store.add_step_log(step_log, self._context.run_id)
132
+
133
+ logger.info(f"Executing node: {node.get_summary()}")
134
+
135
+ # Add the step log to the database as per the situation.
136
+ # If its a terminal node, complete it now
137
+ if node.node_type in ["success", "fail"]:
138
+ self._execute_node(node, map_variable=map_variable, **kwargs)
139
+ return
140
+
141
+ # We call an internal function to iterate the sub graphs and execute them
142
+ if node.is_composite:
143
+ node.execute_as_graph(map_variable=map_variable, **kwargs)
144
+ return
145
+
146
+ task_console.export_text(clear=True)
147
+
148
+ task_name = node._resolve_map_placeholders(node.internal_name, map_variable)
149
+ console.print(f":runner: Executing the node {task_name} ... ", style="bold color(208)")
150
+ self.trigger_job(node=node, map_variable=map_variable, **kwargs)
151
+
99
152
  def execute_job(self, node: TaskNode):
100
153
  """
101
154
  Set up the step log and call the execute node
@@ -17,7 +17,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
17
17
  from stevedore import driver
18
18
 
19
19
  import runnable.context as context
20
- from runnable import console, defaults, exceptions, parameters, utils
20
+ from runnable import console, defaults, exceptions, parameters, task_console, utils
21
21
  from runnable.datastore import (
22
22
  JsonParameter,
23
23
  MetricParameter,
@@ -144,8 +144,8 @@ class BaseTaskType(BaseModel):
144
144
  if context_param in params:
145
145
  params[param_name].value = params[context_param].value
146
146
 
147
- console.log("Parameters available for the execution:")
148
- console.log(params)
147
+ task_console.log("Parameters available for the execution:")
148
+ task_console.log(params)
149
149
 
150
150
  logger.debug(f"Resolved parameters: {params}")
151
151
 
@@ -153,18 +153,12 @@ class BaseTaskType(BaseModel):
153
153
  params = {key: value for key, value in params.items() if isinstance(value, JsonParameter)}
154
154
 
155
155
  parameters_in = copy.deepcopy(params)
156
- f = io.StringIO()
157
156
  try:
158
- with contextlib.redirect_stdout(f):
159
- # with contextlib.nullcontext():
160
- yield params
157
+ yield params
161
158
  except Exception as e: # pylint: disable=broad-except
162
159
  console.log(e, style=defaults.error_style)
163
160
  logger.exception(e)
164
161
  finally:
165
- print(f.getvalue()) # print to console
166
- f.close()
167
-
168
162
  # Update parameters
169
163
  # This should only update the parameters that are changed at the root level.
170
164
  diff_parameters = self._diff_parameters(parameters_in=parameters_in, context_params=params)
@@ -226,9 +220,11 @@ class PythonTaskType(BaseTaskType): # pylint: disable=too-few-public-methods
226
220
  filtered_parameters = parameters.filter_arguments_for_func(f, params.copy(), map_variable)
227
221
  logger.info(f"Calling {func} from {module} with {filtered_parameters}")
228
222
 
229
- user_set_parameters = f(**filtered_parameters) # This is a tuple or single value
223
+ out_file = io.StringIO()
224
+ with contextlib.redirect_stdout(out_file):
225
+ user_set_parameters = f(**filtered_parameters) # This is a tuple or single value
226
+ task_console.print(out_file.getvalue())
230
227
  except Exception as e:
231
- console.log(e, style=defaults.error_style, markup=False)
232
228
  raise exceptions.CommandCallError(f"Function call: {self.command} did not succeed.\n") from e
233
229
 
234
230
  attempt_log.input_parameters = params.copy()
@@ -272,8 +268,8 @@ class PythonTaskType(BaseTaskType): # pylint: disable=too-few-public-methods
272
268
  except Exception as _e:
273
269
  msg = f"Call to the function {self.command} did not succeed.\n"
274
270
  attempt_log.message = msg
275
- console.print_exception(show_locals=False)
276
- console.log(_e, style=defaults.error_style)
271
+ task_console.print_exception(show_locals=False)
272
+ task_console.log(_e, style=defaults.error_style)
277
273
 
278
274
  attempt_log.end_time = str(datetime.now())
279
275
 
@@ -359,7 +355,11 @@ class NotebookTaskType(BaseTaskType):
359
355
  }
360
356
  kwds.update(ploomber_optional_args)
361
357
 
362
- pm.execute_notebook(**kwds)
358
+ out_file = io.StringIO()
359
+ with contextlib.redirect_stdout(out_file):
360
+ pm.execute_notebook(**kwds)
361
+ task_console.print(out_file.getvalue())
362
+
363
363
  context.run_context.catalog_handler.put(name=notebook_output_path, run_id=context.run_context.run_id)
364
364
 
365
365
  client = PloomberClient.from_path(path=notebook_output_path)
@@ -380,8 +380,8 @@ class NotebookTaskType(BaseTaskType):
380
380
  )
381
381
  except PicklingError as e:
382
382
  logger.exception("Notebooks cannot return objects")
383
- console.log("Notebooks cannot return objects", style=defaults.error_style)
384
- console.log(e, style=defaults.error_style)
383
+ # task_console.log("Notebooks cannot return objects", style=defaults.error_style)
384
+ # task_console.log(e, style=defaults.error_style)
385
385
 
386
386
  logger.exception(e)
387
387
  raise
@@ -400,8 +400,7 @@ class NotebookTaskType(BaseTaskType):
400
400
  logger.exception(msg)
401
401
  logger.exception(e)
402
402
 
403
- console.log(msg, style=defaults.error_style)
404
-
403
+ # task_console.log(msg, style=defaults.error_style)
405
404
  attempt_log.status = defaults.FAIL
406
405
 
407
406
  attempt_log.end_time = str(datetime.now())
@@ -488,14 +487,14 @@ class ShellTaskType(BaseTaskType):
488
487
 
489
488
  if proc.returncode != 0:
490
489
  msg = ",".join(result[1].split("\n"))
491
- console.print(msg, style=defaults.error_style)
490
+ task_console.print(msg, style=defaults.error_style)
492
491
  raise exceptions.CommandCallError(msg)
493
492
 
494
493
  # for stderr
495
494
  for line in result[1].split("\n"):
496
495
  if line.strip() == "":
497
496
  continue
498
- console.print(line, style=defaults.warning_style)
497
+ task_console.print(line, style=defaults.warning_style)
499
498
 
500
499
  output_parameters: Dict[str, Parameter] = {}
501
500
  metrics: Dict[str, Parameter] = {}
@@ -506,7 +505,7 @@ class ShellTaskType(BaseTaskType):
506
505
  continue
507
506
 
508
507
  logger.info(line)
509
- console.print(line)
508
+ task_console.print(line)
510
509
 
511
510
  if line.strip() == collect_delimiter:
512
511
  # The lines from now on should be captured
@@ -548,8 +547,8 @@ class ShellTaskType(BaseTaskType):
548
547
  logger.exception(msg)
549
548
  logger.exception(e)
550
549
 
551
- console.log(msg, style=defaults.error_style)
552
- console.log(e, style=defaults.error_style)
550
+ task_console.log(msg, style=defaults.error_style)
551
+ task_console.log(e, style=defaults.error_style)
553
552
 
554
553
  attempt_log.status = defaults.FAIL
555
554
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes