runnable 0.13.0__py3-none-any.whl → 0.16.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 +1 -12
  2. runnable/catalog.py +29 -5
  3. runnable/cli.py +268 -215
  4. runnable/context.py +10 -3
  5. runnable/datastore.py +212 -53
  6. runnable/defaults.py +13 -55
  7. runnable/entrypoints.py +270 -183
  8. runnable/exceptions.py +28 -2
  9. runnable/executor.py +133 -86
  10. runnable/graph.py +37 -13
  11. runnable/nodes.py +50 -22
  12. runnable/parameters.py +27 -8
  13. runnable/pickler.py +1 -1
  14. runnable/sdk.py +230 -66
  15. runnable/secrets.py +3 -1
  16. runnable/tasks.py +99 -41
  17. runnable/utils.py +59 -39
  18. {runnable-0.13.0.dist-info → runnable-0.16.0.dist-info}/METADATA +28 -31
  19. runnable-0.16.0.dist-info/RECORD +23 -0
  20. {runnable-0.13.0.dist-info → runnable-0.16.0.dist-info}/WHEEL +1 -1
  21. runnable-0.16.0.dist-info/entry_points.txt +45 -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.py +0 -69
  37. runnable/extensions/executor/local_container/__init__.py +0 -0
  38. runnable/extensions/executor/local_container/implementation.py +0 -446
  39. runnable/extensions/executor/mocked/__init__.py +0 -0
  40. runnable/extensions/executor/mocked/implementation.py +0 -154
  41. runnable/extensions/executor/retry/__init__.py +0 -0
  42. runnable/extensions/executor/retry/implementation.py +0 -168
  43. runnable/extensions/nodes.py +0 -870
  44. runnable/extensions/run_log_store/__init__.py +0 -0
  45. runnable/extensions/run_log_store/chunked_file_system/__init__.py +0 -0
  46. runnable/extensions/run_log_store/chunked_file_system/implementation.py +0 -111
  47. runnable/extensions/run_log_store/chunked_k8s_pvc/__init__.py +0 -0
  48. runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +0 -21
  49. runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +0 -61
  50. runnable/extensions/run_log_store/db/implementation_FF.py +0 -157
  51. runnable/extensions/run_log_store/db/integration_FF.py +0 -0
  52. runnable/extensions/run_log_store/file_system/__init__.py +0 -0
  53. runnable/extensions/run_log_store/file_system/implementation.py +0 -140
  54. runnable/extensions/run_log_store/generic_chunked.py +0 -557
  55. runnable/extensions/run_log_store/k8s_pvc/__init__.py +0 -0
  56. runnable/extensions/run_log_store/k8s_pvc/implementation.py +0 -21
  57. runnable/extensions/run_log_store/k8s_pvc/integration.py +0 -56
  58. runnable/extensions/secrets/__init__.py +0 -0
  59. runnable/extensions/secrets/dotenv/__init__.py +0 -0
  60. runnable/extensions/secrets/dotenv/implementation.py +0 -100
  61. runnable/integration.py +0 -192
  62. runnable-0.13.0.dist-info/RECORD +0 -63
  63. runnable-0.13.0.dist-info/entry_points.txt +0 -41
  64. {runnable-0.13.0.dist-info → runnable-0.16.0.dist-info/licenses}/LICENSE +0 -0
@@ -1,51 +0,0 @@
1
- apiVersion: argoproj.io/v1alpha1
2
- kind: Workflow
3
- metadata:
4
- generateName: runnable-dag
5
- spec:
6
- activeDeadlineSeconds: int # max run time of the workflow
7
- entrypoint: str
8
- nodeSelector: Dict[str, str] # global node selector
9
- parallelism: # global level
10
- podGC: OnPodCompletion
11
- resources: # Should be converted to podSpecPath
12
- limits:
13
- requests:
14
- podSpecPatch: json str representation of resources for defaults
15
- retryStrategy: # global level for all templates
16
- limit: int
17
- retryPolicy: # global level for all templates
18
- backoff:
19
- duration: str
20
- factor: int
21
- maxDuration: str
22
- serviceAccountName: str # Optionally required
23
- templateDefaults:
24
- activeDeadlineSeconds: int, for a template
25
- timeout: str # max time including the wait time
26
- failFast: true
27
- volumes:
28
- templates:
29
- activeDeadlineSeconds: # override
30
- nodeSelector: # override
31
- retryStrategy: # override
32
- tolerations: # override
33
- container:
34
- command:
35
- env:
36
- image:
37
- imagePullPolicy:
38
- volumeMounts:
39
- resources:
40
- limits:
41
- requests:
42
- dag:
43
- tasks:
44
- depends:
45
- continueOn:
46
- tolerations: # global level for all templates
47
- effect: str
48
- key: str
49
- operator: str
50
- value: str
51
- volumes:
File without changes
@@ -1,259 +0,0 @@
1
- # import logging
2
- # import shlex
3
- # from typing import Dict, List, Optional
4
-
5
- # from pydantic import BaseModel
6
-
7
- # from runnable import defaults, integration, utils
8
- # from runnable.executor import BaseExecutor
9
- # from runnable.graph import Graph
10
- # from runnable.nodes import BaseNode
11
-
12
- # logger = logging.getLogger(defaults.NAME)
13
-
14
- # try:
15
- # from kubernetes import client
16
- # from kubernetes.client import V1EnvVar, V1EnvVarSource, V1PersistentVolumeClaimVolumeSource, V1SecretKeySelector
17
- # except ImportError as _e:
18
- # msg = "Kubernetes Dependencies have not been installed!!"
19
- # # raise Exception(msg) from _e
20
-
21
-
22
- # class Toleration(BaseModel):
23
- # effect: str
24
- # key: str
25
- # operator: str
26
- # value: str
27
-
28
-
29
- # class K8sJobExecutor(BaseExecutor):
30
- # service_name = "k8s-job"
31
-
32
- # # TODO: move this to K8's style config.
33
- # class ContextConfig(BaseModel):
34
- # docker_image: str
35
- # config_path: str = "" # Let the client decide on the path to the config file.
36
- # namespace: str = "default"
37
- # cpu_limit: str = "250m"
38
- # memory_limit: str = "1G"
39
- # gpu_limit: int = 0
40
- # gpu_vendor: str = "nvidia.com/gpu"
41
- # cpu_request: str = ""
42
- # memory_request: str = ""
43
- # active_deadline_seconds: int = 60 * 60 * 2 # 2 hours
44
- # ttl_seconds_after_finished: int = 60 #  1 minute
45
- # image_pull_policy: str = "Always"
46
- # secrets_from_k8s: dict = {} # EnvVar=SecretName:Key
47
- # persistent_volumes: dict = {} # volume-name:mount_path
48
- # node_selector: Dict[str, str] = {}
49
- # tolerations: List[Toleration] = []
50
- # labels: Dict[str, str] = {}
51
-
52
- # def __init__(self, config: Optional[dict] = None):
53
- # self.config = self.ContextConfig(**(config or {}))
54
- # self.persistent_volumes = {}
55
-
56
- # for i, (claim, mount_path) in enumerate(self.config.persistent_volumes.items()):
57
- # self.persistent_volumes[f"executor-{i}"] = (claim, mount_path)
58
-
59
- # def prepare_for_graph_execution(self):
60
- # """
61
- # This method would be called prior to calling execute_graph.
62
- # Perform any steps required before doing the graph execution.
63
-
64
- # The most common implementation is to prepare a run log for the run if the run uses local interactive compute.
65
-
66
- # But in cases of actual rendering the job specs (eg: AWS step functions, K8's) we need not do anything.
67
- # """
68
-
69
- # integration.validate(self, self.run_log_store)
70
- # integration.configure_for_traversal(self, self.run_log_store)
71
-
72
- # integration.validate(self, self.catalog_handler)
73
- # integration.configure_for_traversal(self, self.catalog_handler)
74
-
75
- # integration.validate(self, self.secrets_handler)
76
- # integration.configure_for_traversal(self, self.secrets_handler)
77
-
78
- # integration.validate(self, self.experiment_tracker)
79
- # integration.configure_for_traversal(self, self.experiment_tracker)
80
-
81
- # def prepare_for_node_execution(self):
82
- # """
83
- # Perform any modifications to the services prior to execution of the node.
84
-
85
- # Args:
86
- # node (Node): [description]
87
- # map_variable (dict, optional): [description]. Defaults to None.
88
- # """
89
-
90
- # integration.validate(self, self.run_log_store)
91
- # integration.configure_for_execution(self, self.run_log_store)
92
-
93
- # integration.validate(self, self.catalog_handler)
94
- # integration.configure_for_execution(self, self.catalog_handler)
95
-
96
- # integration.validate(self, self.secrets_handler)
97
- # integration.configure_for_execution(self, self.secrets_handler)
98
-
99
- # integration.validate(self, self.experiment_tracker)
100
- # integration.configure_for_execution(self, self.experiment_tracker)
101
-
102
- # self._set_up_run_log(exists_ok=True)
103
-
104
- # @property
105
- # def _client(self):
106
- # from kubernetes import config as k8s_config
107
-
108
- # if self.config.config_path:
109
- # k8s_config.load_kube_config(kube_config_path=self.config.config_path)
110
- # else:
111
- # # https://github.com/kubernetes-client/python/blob/master/kubernetes/base/config/__init__.py
112
- # k8s_config.load_config()
113
- # return client
114
-
115
- # @property
116
- # def tolerations(self):
117
- # return [toleration.dict() for toleration in self.config.tolerations]
118
-
119
- # def execute_job(self, node: BaseNode):
120
- # command = utils.get_job_execution_command(self, node)
121
- # logger.info(f"Triggering a kubernetes job with : {command}")
122
-
123
- # self.config.labels["job_name"] = self.run_id
124
-
125
- # k8s_batch = self._client.BatchV1Api()
126
-
127
- # cpu_limit = self.config.cpu_limit
128
- # memory_limit = self.config.memory_limit
129
-
130
- # cpu_request = self.config.cpu_request or cpu_limit
131
- # memory_request = self.config.memory_request or memory_limit
132
-
133
- # gpu_limit = str(self.config.gpu_limit) # Should be something like nvidia -etc
134
-
135
- # limits = {
136
- # "cpu": cpu_limit,
137
- # "memory": memory_limit,
138
- # self.config.gpu_vendor: gpu_limit,
139
- # }
140
- # requests = {"cpu": cpu_request, "memory": memory_request}
141
- # resources = {"limits": limits, "requests": requests}
142
-
143
- # environment_variables = []
144
- # for secret_env, k8_secret in self.config.secrets_from_k8s.items():
145
- # try:
146
- # secret_name, key = k8_secret.split(":")
147
- # except Exception as _e:
148
- # msg = "K8's secret should be of format EnvVar=SecretName:Key"
149
- # raise Exception(msg) from _e
150
- # secret_as_env = V1EnvVar(
151
- # name=secret_env,
152
- # value_from=V1EnvVarSource(secret_key_ref=V1SecretKeySelector(name=secret_name, key=key)),
153
- # )
154
- # environment_variables.append(secret_as_env)
155
-
156
- # overridden_params = utils.get_user_set_parameters()
157
- # # The parameters present in the environment override the parameters present in the parameters file
158
- # # The values are coerced to be strings, hopefully they will be fine on the other side.
159
- # for k, v in overridden_params.items():
160
- # environment_variables.append(V1EnvVar(name=defaults.PARAMETER_PREFIX + k, value=str(v)))
161
-
162
- # pod_volumes = []
163
- # volume_mounts = []
164
- # for claim_name, (claim, mount_path) in self.persistent_volumes.items():
165
- # pod_volumes.append(
166
- # self._client.V1Volume(
167
- # name=claim_name,
168
- # persistent_volume_claim=V1PersistentVolumeClaimVolumeSource(claim_name=claim),
169
- # )
170
- # )
171
- # volume_mounts.append(self._client.V1VolumeMount(name=claim_name, mount_path=mount_path))
172
-
173
- # base_container = self._client.V1Container(
174
- # name=self.run_id,
175
- # image=self.config.docker_image,
176
- # command=shlex.split(command),
177
- # resources=resources,
178
- # env=environment_variables,
179
- # image_pull_policy="Always",
180
- # volume_mounts=volume_mounts or None,
181
- # )
182
-
183
- # pod_spec = self._client.V1PodSpec(
184
- # volumes=pod_volumes or None,
185
- # restart_policy="Never",
186
- # containers=[base_container],
187
- # node_selector=self.config.node_selector,
188
- # tolerations=self.tolerations,
189
- # )
190
-
191
- # pod_template = self._client.V1PodTemplateSpec(
192
- # metadata=client.V1ObjectMeta(
193
- # labels=self.config.labels,
194
- # annotations={"sidecar.istio.io/inject": "false"},
195
- # ),
196
- # spec=pod_spec,
197
- # )
198
-
199
- # job_spec = client.V1JobSpec(
200
- # template=pod_template,
201
- # backoff_limit=2,
202
- # ttl_seconds_after_finished=self.config.ttl_seconds_after_finished,
203
- # )
204
- # job_spec.active_deadline_seconds = self.config.active_deadline_seconds
205
-
206
- # job = client.V1Job(
207
- # api_version="batch/v1",
208
- # kind="Job",
209
- # metadata=client.V1ObjectMeta(name=self.run_id),
210
- # spec=job_spec,
211
- # )
212
-
213
- # logger.debug(f"Submitting kubernetes job: {job}")
214
-
215
- # try:
216
- # response = k8s_batch.create_namespaced_job(
217
- # body=job,
218
- # namespace=self.config.namespace,
219
- # _preload_content=False,
220
- # pretty=True,
221
- # )
222
- # print(f"Kubernetes job {self.run_id} created")
223
- # logger.debug(f"Kubernetes job response: {response}")
224
- # except Exception as e:
225
- # logger.exception(e)
226
- # raise
227
-
228
- # def execute_node(self, node: BaseNode, map_variable: Optional[dict] = None, **kwargs):
229
- # step_log = self.run_log_store.create_step_log(node.name, node._get_step_log_name(map_variable))
230
-
231
- # self.add_code_identities(node=node, step_log=step_log)
232
-
233
- # step_log.step_type = node.node_type
234
- # step_log.status = defaults.PROCESSING
235
- # self.run_log_store.add_step_log(step_log, self.run_id)
236
-
237
- # super()._execute_node(node, map_variable=map_variable, **kwargs)
238
-
239
- # step_log = self.run_log_store.get_step_log(node._get_step_log_name(map_variable), self.run_id)
240
- # if step_log.status == defaults.FAIL:
241
- # raise Exception(f"Step {node.name} failed")
242
-
243
- # def execute_graph(self, dag: Graph, map_variable: Optional[dict] = None, **kwargs):
244
- # msg = "This executor is not supported to execute any graphs but only jobs (functions or notebooks)"
245
- # raise NotImplementedError(msg)
246
-
247
- # def send_return_code(self, stage="traversal"):
248
- # """
249
- # Convenience function used by pipeline to send return code to the caller of the cli
250
-
251
- # Raises:
252
- # Exception: If the pipeline execution failed
253
- # """
254
- # if stage != "traversal": # traversal does no actual execution, so return code is pointless
255
- # run_id = self.run_id
256
-
257
- # run_log = self.run_log_store.get_run_log_by_id(run_id=run_id, full=False)
258
- # if run_log.status == defaults.FAIL:
259
- # raise Exception("Pipeline execution failed")
@@ -1,69 +0,0 @@
1
- import logging
2
-
3
- from runnable import defaults
4
- from runnable.integration import BaseIntegration
5
-
6
- logger = logging.getLogger(defaults.NAME)
7
-
8
-
9
- class BufferedRunLogStore(BaseIntegration):
10
- """
11
- Only local execution mode is possible for Buffered Run Log store
12
- """
13
-
14
- executor_type = "k8s-job"
15
- service_type = "run_log_store" # One of secret, catalog, datastore
16
- service_provider = "buffered" # The actual implementation of the service
17
-
18
- def validate(self, **kwargs):
19
- raise Exception("K8s job cannot run work with buffered run log store")
20
-
21
-
22
- class FileSystemRunLogStore(BaseIntegration):
23
- """
24
- Only local execution mode is possible for Buffered Run Log store
25
- """
26
-
27
- executor_type = "k8s-job"
28
- service_type = "run_log_store" # One of secret, catalog, datastore
29
- service_provider = "file-system" # The actual implementation of the service
30
-
31
- def validate(self, **kwargs):
32
- msg = (
33
- "K8s job cannot run work with file-system run log store."
34
- "Unless you have made a mechanism to use volume mounts"
35
- )
36
- logger.warning(msg)
37
-
38
-
39
- class ChunkedFSRunLogStore(BaseIntegration):
40
- """
41
- Only local execution mode is possible for Buffered Run Log store
42
- """
43
-
44
- executor_type = "k8s-job"
45
- service_type = "run_log_store" # One of secret, catalog, datastore
46
- service_provider = "chunked-fs" # The actual implementation of the service
47
-
48
- def validate(self, **kwargs):
49
- msg = (
50
- "K8s job cannot run work with chunked-fs run log store."
51
- "Unless you have made a mechanism to use volume mounts"
52
- )
53
- logger.warning(msg)
54
-
55
-
56
- class FileSystemCatalog(BaseIntegration):
57
- """
58
- Only local execution mode is possible for Buffered Run Log store
59
- """
60
-
61
- executor_type = "k8s-job"
62
- service_type = "catalog" # One of secret, catalog, datastore
63
- service_provider = "file-system" # The actual implementation of the service
64
-
65
- def validate(self, **kwargs):
66
- msg = (
67
- "K8s Job cannot run work with file-system catalog." "Unless you have made a mechanism to use volume mounts"
68
- )
69
- logger.warning(msg)
@@ -1,69 +0,0 @@
1
- import logging
2
-
3
- from runnable import defaults
4
- from runnable.defaults import TypeMapVariable
5
- from runnable.extensions.executor import GenericExecutor
6
- from runnable.extensions.nodes import TaskNode
7
- from runnable.nodes import BaseNode
8
-
9
- logger = logging.getLogger(defaults.LOGGER_NAME)
10
-
11
-
12
- class LocalExecutor(GenericExecutor):
13
- """
14
- In the mode of local execution, we run everything on the local computer.
15
-
16
- This has some serious implications on the amount of time it would take to complete the run.
17
- Also ensure that the local compute is good enough for the compute to happen of all the steps.
18
-
19
- Example config:
20
- execution:
21
- type: local
22
-
23
- """
24
-
25
- service_name: str = "local"
26
- _local: bool = True
27
-
28
- def trigger_job(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
29
- """
30
- In this mode of execution, we prepare for the node execution and execute the node
31
-
32
- Args:
33
- node (BaseNode): [description]
34
- map_variable (str, optional): [description]. Defaults to ''.
35
- """
36
-
37
- self.prepare_for_node_execution()
38
- self.execute_node(node=node, map_variable=map_variable, **kwargs)
39
-
40
- def execute_node(self, node: BaseNode, map_variable: TypeMapVariable = None, **kwargs):
41
- """
42
- For local execution, we just execute the node.
43
-
44
- Args:
45
- node (BaseNode): _description_
46
- map_variable (dict[str, str], optional): _description_. Defaults to None.
47
- """
48
- self._execute_node(node=node, map_variable=map_variable, **kwargs)
49
-
50
- def execute_job(self, node: TaskNode):
51
- """
52
- Set up the step log and call the execute node
53
-
54
- Args:
55
- node (BaseNode): _description_
56
- """
57
-
58
- step_log = self._context.run_log_store.create_step_log(node.name, node._get_step_log_name(map_variable=None))
59
-
60
- self.add_code_identities(node=node, step_log=step_log)
61
-
62
- step_log.step_type = node.node_type
63
- step_log.status = defaults.PROCESSING
64
- self._context.run_log_store.add_step_log(step_log, self._context.run_id)
65
- self.execute_node(node=node)
66
-
67
- # Update the run log status
68
- step_log = self._context.run_log_store.get_step_log(node._get_step_log_name(), self._context.run_id)
69
- self._context.run_log_store.update_run_log_status(run_id=self._context.run_id, status=step_log.status)
File without changes