runnable 0.13.0__py3-none-any.whl → 0.16.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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