runnable 0.20.0__tar.gz → 0.22.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. {runnable-0.20.0 → runnable-0.22.0}/PKG-INFO +1 -1
  2. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/argo.py +27 -1
  3. {runnable-0.20.0 → runnable-0.22.0}/pyproject.toml +1 -1
  4. {runnable-0.20.0 → runnable-0.22.0}/runnable/__init__.py +3 -1
  5. {runnable-0.20.0 → runnable-0.22.0}/runnable/entrypoints.py +1 -1
  6. {runnable-0.20.0 → runnable-0.22.0}/runnable/sdk.py +69 -8
  7. {runnable-0.20.0 → runnable-0.22.0}/runnable/tasks.py +2 -2
  8. {runnable-0.20.0 → runnable-0.22.0}/runnable/utils.py +11 -3
  9. {runnable-0.20.0 → runnable-0.22.0}/.gitignore +0 -0
  10. {runnable-0.20.0 → runnable-0.22.0}/LICENSE +0 -0
  11. {runnable-0.20.0 → runnable-0.22.0}/README.md +0 -0
  12. {runnable-0.20.0 → runnable-0.22.0}/extensions/README.md +0 -0
  13. {runnable-0.20.0 → runnable-0.22.0}/extensions/__init__.py +0 -0
  14. {runnable-0.20.0 → runnable-0.22.0}/extensions/catalog/README.md +0 -0
  15. {runnable-0.20.0 → runnable-0.22.0}/extensions/catalog/file_system.py +0 -0
  16. {runnable-0.20.0 → runnable-0.22.0}/extensions/catalog/pyproject.toml +0 -0
  17. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/README.md +0 -0
  18. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/__init__.py +0 -0
  19. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/k8s.py +0 -0
  20. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/k8s_job_spec.yaml +0 -0
  21. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/local.py +0 -0
  22. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/local_container.py +0 -0
  23. {runnable-0.20.0 → runnable-0.22.0}/extensions/job_executor/pyproject.toml +0 -0
  24. {runnable-0.20.0 → runnable-0.22.0}/extensions/nodes/README.md +0 -0
  25. {runnable-0.20.0 → runnable-0.22.0}/extensions/nodes/nodes.py +0 -0
  26. {runnable-0.20.0 → runnable-0.22.0}/extensions/nodes/pyproject.toml +0 -0
  27. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/README.md +0 -0
  28. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/__init__.py +0 -0
  29. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/local.py +0 -0
  30. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/local_container.py +0 -0
  31. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/mocked.py +0 -0
  32. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/pyproject.toml +0 -0
  33. {runnable-0.20.0 → runnable-0.22.0}/extensions/pipeline_executor/retry.py +0 -0
  34. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/README.md +0 -0
  35. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/__init__.py +0 -0
  36. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/chunked_fs.py +0 -0
  37. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/db/implementation_FF.py +0 -0
  38. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/db/integration_FF.py +0 -0
  39. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/file_system.py +0 -0
  40. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/generic_chunked.py +0 -0
  41. {runnable-0.20.0 → runnable-0.22.0}/extensions/run_log_store/pyproject.toml +0 -0
  42. {runnable-0.20.0 → runnable-0.22.0}/extensions/secrets/README.md +0 -0
  43. {runnable-0.20.0 → runnable-0.22.0}/extensions/secrets/dotenv.py +0 -0
  44. {runnable-0.20.0 → runnable-0.22.0}/extensions/secrets/pyproject.toml +0 -0
  45. {runnable-0.20.0 → runnable-0.22.0}/runnable/catalog.py +0 -0
  46. {runnable-0.20.0 → runnable-0.22.0}/runnable/cli.py +0 -0
  47. {runnable-0.20.0 → runnable-0.22.0}/runnable/context.py +0 -0
  48. {runnable-0.20.0 → runnable-0.22.0}/runnable/datastore.py +0 -0
  49. {runnable-0.20.0 → runnable-0.22.0}/runnable/defaults.py +0 -0
  50. {runnable-0.20.0 → runnable-0.22.0}/runnable/exceptions.py +0 -0
  51. {runnable-0.20.0 → runnable-0.22.0}/runnable/executor.py +0 -0
  52. {runnable-0.20.0 → runnable-0.22.0}/runnable/graph.py +0 -0
  53. {runnable-0.20.0 → runnable-0.22.0}/runnable/names.py +0 -0
  54. {runnable-0.20.0 → runnable-0.22.0}/runnable/nodes.py +0 -0
  55. {runnable-0.20.0 → runnable-0.22.0}/runnable/parameters.py +0 -0
  56. {runnable-0.20.0 → runnable-0.22.0}/runnable/pickler.py +0 -0
  57. {runnable-0.20.0 → runnable-0.22.0}/runnable/secrets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runnable
3
- Version: 0.20.0
3
+ Version: 0.22.0
4
4
  Summary: Add your description here
5
5
  Author-email: "Vammi, Vijay" <vijay.vammi@astrazeneca.com>
6
6
  License-File: LICENSE
@@ -244,6 +244,7 @@ class TemplateDefaults(BaseModelWIthConfig):
244
244
  image: str
245
245
  image_pull_policy: Optional[ImagePullPolicy] = Field(default=ImagePullPolicy.Always)
246
246
  resources: Resources = Field(default_factory=Resources)
247
+ env: list[EnvVar | SecretEnvVar] = Field(default_factory=list, exclude=True)
247
248
 
248
249
 
249
250
  # User provides this as part of the argoSpec
@@ -365,6 +366,7 @@ class ArgoExecutor(GenericPipelineExecutor):
365
366
  custom_volumes: Optional[list[CustomVolume]] = Field(
366
367
  default_factory=list[CustomVolume]
367
368
  )
369
+ env: list[EnvVar] = Field(default_factory=list[EnvVar])
368
370
 
369
371
  expose_parameters_as_inputs: bool = True
370
372
  secret_from_k8s: Optional[str] = Field(default=None)
@@ -508,6 +510,10 @@ class ArgoExecutor(GenericPipelineExecutor):
508
510
  )
509
511
 
510
512
  self._set_up_initial_container(container_template=core_container_template)
513
+ self._expose_secrets_to_task(
514
+ working_on=node, container_template=core_container_template
515
+ )
516
+ self._set_env_vars_to_task(node, core_container_template)
511
517
 
512
518
  container_template = ContainerTemplate(
513
519
  container=core_container_template,
@@ -523,12 +529,32 @@ class ArgoExecutor(GenericPipelineExecutor):
523
529
 
524
530
  return container_template
525
531
 
532
+ def _set_env_vars_to_task(
533
+ self, working_on: BaseNode, container_template: CoreContainerTemplate
534
+ ):
535
+ if not isinstance(working_on, TaskNode):
536
+ return
537
+ global_envs: dict[str, str] = {}
538
+
539
+ for env_var in self.env:
540
+ global_envs[env_var.name] = env_var.value
541
+
542
+ node_overide = {}
543
+ if hasattr(working_on, "overides"):
544
+ node_overide = working_on.overides
545
+
546
+ global_envs.update(node_overide.get("env", {}))
547
+ for key, value in global_envs.items():
548
+ env_var_to_add = EnvVar(name=key, value=value)
549
+ container_template.env.append(env_var_to_add)
550
+
526
551
  def _expose_secrets_to_task(
527
552
  self,
528
553
  working_on: BaseNode,
529
554
  container_template: CoreContainerTemplate,
530
555
  ):
531
- assert isinstance(working_on, TaskNode)
556
+ if not isinstance(working_on, TaskNode):
557
+ return
532
558
  secrets = working_on.executable.secrets
533
559
  for secret in secrets:
534
560
  assert self.secret_from_k8s is not None
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "runnable"
3
- version = "0.20.0"
3
+ version = "0.22.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -20,12 +20,14 @@ task_console = Console(record=True)
20
20
  from runnable.sdk import ( # noqa
21
21
  Catalog,
22
22
  Fail,
23
- Job,
24
23
  Map,
24
+ NotebookJob,
25
25
  NotebookTask,
26
26
  Parallel,
27
27
  Pipeline,
28
+ PythonJob,
28
29
  PythonTask,
30
+ ShellJob,
29
31
  ShellTask,
30
32
  Stub,
31
33
  Success,
@@ -438,7 +438,7 @@ def set_job_spec_from_python(run_context: context.Context, python_module: str):
438
438
  imported_module = importlib.import_module(module)
439
439
 
440
440
  run_context.from_sdk = True
441
- task = getattr(imported_module, func)().return_task()
441
+ task = getattr(imported_module, func)().get_task()
442
442
  catalog_settings = getattr(imported_module, func)().return_catalog_settings()
443
443
 
444
444
  run_context.job_definition_file = python_module
@@ -822,17 +822,21 @@ class Pipeline(BaseModel):
822
822
  )
823
823
 
824
824
 
825
- class Job(BaseModel):
826
- name: str
827
- task: BaseTask
825
+ class BaseJob(BaseModel):
826
+ catalog: Optional[Catalog] = Field(default=None, alias="catalog")
827
+ overrides: Dict[str, Any] = Field(default_factory=dict, alias="overrides")
828
+ returns: List[Union[str, TaskReturns]] = Field(
829
+ default_factory=list, alias="returns"
830
+ )
831
+ secrets: List[str] = Field(default_factory=list)
828
832
 
829
- def return_task(self) -> RunnableTask:
830
- return self.task.create_job()
833
+ def get_task(self) -> RunnableTask:
834
+ raise NotImplementedError
831
835
 
832
836
  def return_catalog_settings(self) -> Optional[List[str]]:
833
- if self.task.catalog is None:
837
+ if self.catalog is None:
834
838
  return []
835
- return self.task.catalog.put
839
+ return self.catalog.put
836
840
 
837
841
  def _is_called_for_definition(self) -> bool:
838
842
  """
@@ -892,7 +896,7 @@ class Job(BaseModel):
892
896
 
893
897
  run_context.job_definition_file = f"{module_to_call}.py"
894
898
 
895
- job = self.task.create_job()
899
+ job = self.get_task()
896
900
  catalog_settings = self.return_catalog_settings()
897
901
 
898
902
  run_context.executor.submit_job(job, catalog_settings=catalog_settings)
@@ -905,3 +909,60 @@ class Job(BaseModel):
905
909
  return run_context.run_log_store.get_run_log_by_id(
906
910
  run_id=run_context.run_id
907
911
  )
912
+
913
+
914
+ class PythonJob(BaseJob):
915
+ function: Callable = Field(exclude=True)
916
+
917
+ @property
918
+ @computed_field
919
+ def command(self) -> str:
920
+ module = self.function.__module__
921
+ name = self.function.__name__
922
+
923
+ return f"{module}.{name}"
924
+
925
+ def get_task(self) -> RunnableTask:
926
+ # Piggy bank on existing tasks as a hack
927
+ task = PythonTask(
928
+ name="dummy",
929
+ terminate_with_success=True,
930
+ returns=self.returns,
931
+ secrets=self.secrets,
932
+ function=self.function,
933
+ )
934
+ return task.create_node().executable
935
+
936
+
937
+ class NotebookJob(BaseJob):
938
+ notebook: str = Field(serialization_alias="command")
939
+ optional_ploomber_args: Optional[Dict[str, Any]] = Field(
940
+ default=None, alias="optional_ploomber_args"
941
+ )
942
+
943
+ def get_task(self) -> RunnableTask:
944
+ # Piggy bank on existing tasks as a hack
945
+ task = NotebookTask(
946
+ name="dummy",
947
+ terminate_with_success=True,
948
+ returns=self.returns,
949
+ secrets=self.secrets,
950
+ notebook=self.notebook,
951
+ optional_ploomber_args=self.optional_ploomber_args,
952
+ )
953
+ return task.create_node().executable
954
+
955
+
956
+ class ShellJob(BaseJob):
957
+ command: str = Field(alias="command")
958
+
959
+ def get_task(self) -> RunnableTask:
960
+ # Piggy bank on existing tasks as a hack
961
+ task = ShellTask(
962
+ name="dummy",
963
+ terminate_with_success=True,
964
+ returns=self.returns,
965
+ secrets=self.secrets,
966
+ command=self.command,
967
+ )
968
+ return task.create_node().executable
@@ -409,10 +409,10 @@ class NotebookTaskType(BaseTaskType):
409
409
  for key, value in map_variable.items():
410
410
  tag += f"{key}_{value}_"
411
411
 
412
- if self._context.executor._context_node:
412
+ if hasattr(self._context.executor, "_context_node"):
413
413
  tag += self._context.executor._context_node.name
414
414
 
415
- tag = "".join(x for x in tag if x.isalnum())
415
+ tag = "".join(x for x in tag if x.isalnum()).strip("-")
416
416
 
417
417
  output_path = Path(".", self.command)
418
418
  file_name = output_path.parent / (output_path.stem + f"-{tag}_out.ipynb")
@@ -109,8 +109,16 @@ def apply_variables(
109
109
  raise Exception("Argument Variables should be dict")
110
110
 
111
111
  json_d = json.dumps(apply_to)
112
- transformed = str_template(json_d).substitute(**variables)
113
- return json.loads(transformed)
112
+ string_template = str_template(json_d)
113
+
114
+ template = string_template.safe_substitute(variables)
115
+
116
+ if "$" in template:
117
+ logger.warning(
118
+ "Not all variables found in the config are found in the variables"
119
+ )
120
+
121
+ return json.loads(template)
114
122
 
115
123
 
116
124
  def get_module_and_attr_names(command: str) -> Tuple[str, str]:
@@ -490,7 +498,7 @@ def get_job_execution_command(over_write_run_id: str = "") -> str:
490
498
  log_level = logging.getLevelName(logger.getEffectiveLevel())
491
499
 
492
500
  action = (
493
- f"runnable execute-job /app/{context.run_context.job_definition_file} {run_id} "
501
+ f"runnable execute-job {context.run_context.job_definition_file} {run_id} "
494
502
  f" --log-level {log_level}"
495
503
  )
496
504
 
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