runnable 0.12.2__tar.gz → 0.13.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. runnable-0.13.0/PKG-INFO +270 -0
  2. runnable-0.13.0/README.md +234 -0
  3. {runnable-0.12.2 → runnable-0.13.0}/pyproject.toml +35 -35
  4. runnable-0.12.2/runnable/extensions/executor/local/implementation.py → runnable-0.13.0/runnable/extensions/executor/local.py +0 -2
  5. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/nodes.py +16 -1
  6. {runnable-0.12.2 → runnable-0.13.0}/runnable/sdk.py +150 -101
  7. {runnable-0.12.2 → runnable-0.13.0}/runnable/tasks.py +148 -3
  8. runnable-0.12.2/PKG-INFO +0 -453
  9. runnable-0.12.2/README.md +0 -417
  10. runnable-0.12.2/runnable/extensions/secrets/dotenv/__init__.py +0 -0
  11. {runnable-0.12.2 → runnable-0.13.0}/LICENSE +0 -0
  12. {runnable-0.12.2 → runnable-0.13.0}/runnable/__init__.py +0 -0
  13. {runnable-0.12.2 → runnable-0.13.0}/runnable/catalog.py +0 -0
  14. {runnable-0.12.2 → runnable-0.13.0}/runnable/cli.py +0 -0
  15. {runnable-0.12.2 → runnable-0.13.0}/runnable/context.py +0 -0
  16. {runnable-0.12.2 → runnable-0.13.0}/runnable/datastore.py +0 -0
  17. {runnable-0.12.2 → runnable-0.13.0}/runnable/defaults.py +0 -0
  18. {runnable-0.12.2 → runnable-0.13.0}/runnable/entrypoints.py +0 -0
  19. {runnable-0.12.2 → runnable-0.13.0}/runnable/exceptions.py +0 -0
  20. {runnable-0.12.2 → runnable-0.13.0}/runnable/executor.py +0 -0
  21. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/__init__.py +0 -0
  22. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/__init__.py +0 -0
  23. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/file_system/__init__.py +0 -0
  24. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/file_system/implementation.py +0 -0
  25. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/k8s_pvc/__init__.py +0 -0
  26. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/k8s_pvc/implementation.py +0 -0
  27. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/catalog/k8s_pvc/integration.py +0 -0
  28. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/__init__.py +0 -0
  29. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/argo/__init__.py +0 -0
  30. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/argo/implementation.py +0 -0
  31. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/argo/specification.yaml +0 -0
  32. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/k8s_job/__init__.py +0 -0
  33. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/k8s_job/implementation_FF.py +0 -0
  34. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/k8s_job/integration_FF.py +0 -0
  35. {runnable-0.12.2/runnable/extensions/executor/local → runnable-0.13.0/runnable/extensions/executor/local_container}/__init__.py +0 -0
  36. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/local_container/implementation.py +0 -0
  37. {runnable-0.12.2/runnable/extensions/executor/local_container → runnable-0.13.0/runnable/extensions/executor/mocked}/__init__.py +0 -0
  38. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/mocked/implementation.py +0 -0
  39. {runnable-0.12.2/runnable/extensions/executor/mocked → runnable-0.13.0/runnable/extensions/executor/retry}/__init__.py +0 -0
  40. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/executor/retry/implementation.py +0 -0
  41. {runnable-0.12.2/runnable/extensions/executor/retry → runnable-0.13.0/runnable/extensions/run_log_store}/__init__.py +0 -0
  42. {runnable-0.12.2/runnable/extensions/run_log_store → runnable-0.13.0/runnable/extensions/run_log_store/chunked_file_system}/__init__.py +0 -0
  43. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/chunked_file_system/implementation.py +0 -0
  44. {runnable-0.12.2/runnable/extensions/run_log_store/chunked_file_system → runnable-0.13.0/runnable/extensions/run_log_store/chunked_k8s_pvc}/__init__.py +0 -0
  45. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +0 -0
  46. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +0 -0
  47. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/db/implementation_FF.py +0 -0
  48. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/db/integration_FF.py +0 -0
  49. {runnable-0.12.2/runnable/extensions/run_log_store/chunked_k8s_pvc → runnable-0.13.0/runnable/extensions/run_log_store/file_system}/__init__.py +0 -0
  50. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/file_system/implementation.py +0 -0
  51. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/generic_chunked.py +0 -0
  52. {runnable-0.12.2/runnable/extensions/run_log_store/file_system → runnable-0.13.0/runnable/extensions/run_log_store/k8s_pvc}/__init__.py +0 -0
  53. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/k8s_pvc/implementation.py +0 -0
  54. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/run_log_store/k8s_pvc/integration.py +0 -0
  55. {runnable-0.12.2/runnable/extensions/run_log_store/k8s_pvc → runnable-0.13.0/runnable/extensions/secrets}/__init__.py +0 -0
  56. {runnable-0.12.2/runnable/extensions/secrets → runnable-0.13.0/runnable/extensions/secrets/dotenv}/__init__.py +0 -0
  57. {runnable-0.12.2 → runnable-0.13.0}/runnable/extensions/secrets/dotenv/implementation.py +0 -0
  58. {runnable-0.12.2 → runnable-0.13.0}/runnable/graph.py +0 -0
  59. {runnable-0.12.2 → runnable-0.13.0}/runnable/integration.py +0 -0
  60. {runnable-0.12.2 → runnable-0.13.0}/runnable/names.py +0 -0
  61. {runnable-0.12.2 → runnable-0.13.0}/runnable/nodes.py +0 -0
  62. {runnable-0.12.2 → runnable-0.13.0}/runnable/parameters.py +0 -0
  63. {runnable-0.12.2 → runnable-0.13.0}/runnable/pickler.py +0 -0
  64. {runnable-0.12.2 → runnable-0.13.0}/runnable/secrets.py +0 -0
  65. {runnable-0.12.2 → runnable-0.13.0}/runnable/utils.py +0 -0
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.1
2
+ Name: runnable
3
+ Version: 0.13.0
4
+ Summary: A Compute agnostic pipelining software
5
+ Home-page: https://github.com/vijayvammi/runnable
6
+ License: Apache-2.0
7
+ Author: Vijay Vammi
8
+ Author-email: mesanthu@gmail.com
9
+ Requires-Python: >=3.9,<3.13
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Provides-Extra: database
17
+ Provides-Extra: docker
18
+ Provides-Extra: notebook
19
+ Requires-Dist: click
20
+ Requires-Dist: click-plugins (>=1.1.1,<2.0.0)
21
+ Requires-Dist: dill (>=0.3.8,<0.4.0)
22
+ Requires-Dist: docker ; extra == "docker"
23
+ Requires-Dist: mlflow-skinny
24
+ Requires-Dist: ploomber-engine (>=0.0.31,<0.0.32) ; extra == "notebook"
25
+ Requires-Dist: pydantic (>=2.5,<3.0)
26
+ Requires-Dist: rich (>=13.5.2,<14.0.0)
27
+ Requires-Dist: ruamel.yaml
28
+ Requires-Dist: ruamel.yaml.clib
29
+ Requires-Dist: sqlalchemy ; extra == "database"
30
+ Requires-Dist: stevedore (>=3.5.0,<4.0.0)
31
+ Requires-Dist: typing-extensions ; python_version < "3.8"
32
+ Project-URL: Documentation, https://github.com/vijayvammi/runnable
33
+ Project-URL: Repository, https://github.com/vijayvammi/runnable
34
+ Description-Content-Type: text/markdown
35
+
36
+
37
+
38
+
39
+
40
+
41
+ </p>
42
+ <hr style="border:2px dotted orange">
43
+
44
+ <p align="center">
45
+ <a href="https://pypi.org/project/runnable/"><img alt="python:" src="https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10-blue.svg"></a>
46
+ <a href="https://pypi.org/project/runnable/"><img alt="Pypi" src="https://badge.fury.io/py/runnable.svg"></a>
47
+ <a href="https://github.com/vijayvammi/runnable/blob/main/LICENSE"><img alt"License" src="https://img.shields.io/badge/license-Apache%202.0-blue.svg"></a>
48
+ <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
49
+ <a href="https://github.com/python/mypy"><img alt="MyPy Checked" src="https://www.mypy-lang.org/static/mypy_badge.svg"></a>
50
+ <a href="https://github.com/vijayvammi/runnable/actions/workflows/release.yaml"><img alt="Tests:" src="https://github.com/vijayvammi/runnable/actions/workflows/release.yaml/badge.svg">
51
+ </p>
52
+ <hr style="border:2px dotted orange">
53
+
54
+
55
+ [Please check here for complete documentation](https://astrazeneca.github.io/runnable/)
56
+
57
+ ## Example
58
+
59
+ The below data science flavored code is a well-known
60
+ [iris example from scikit-learn](https://scikit-learn.org/stable/auto_examples/linear_model/plot_iris_logistic.html).
61
+
62
+
63
+ ```python
64
+ """
65
+ Example of Logistic regression using scikit-learn
66
+ https://scikit-learn.org/stable/auto_examples/linear_model/plot_iris_logistic.html
67
+ """
68
+
69
+ import matplotlib.pyplot as plt
70
+ import numpy as np
71
+ from sklearn import datasets
72
+ from sklearn.inspection import DecisionBoundaryDisplay
73
+ from sklearn.linear_model import LogisticRegression
74
+
75
+
76
+ def load_data():
77
+ # import some data to play with
78
+ iris = datasets.load_iris()
79
+ X = iris.data[:, :2] # we only take the first two features.
80
+ Y = iris.target
81
+
82
+ return X, Y
83
+
84
+
85
+ def model_fit(X: np.ndarray, Y: np.ndarray, C: float = 1e5):
86
+ logreg = LogisticRegression(C=C)
87
+ logreg.fit(X, Y)
88
+
89
+ return logreg
90
+
91
+
92
+ def generate_plots(X: np.ndarray, Y: np.ndarray, logreg: LogisticRegression):
93
+ _, ax = plt.subplots(figsize=(4, 3))
94
+ DecisionBoundaryDisplay.from_estimator(
95
+ logreg,
96
+ X,
97
+ cmap=plt.cm.Paired,
98
+ ax=ax,
99
+ response_method="predict",
100
+ plot_method="pcolormesh",
101
+ shading="auto",
102
+ xlabel="Sepal length",
103
+ ylabel="Sepal width",
104
+ eps=0.5,
105
+ )
106
+
107
+ # Plot also the training points
108
+ plt.scatter(X[:, 0], X[:, 1], c=Y, edgecolors="k", cmap=plt.cm.Paired)
109
+
110
+ plt.xticks(())
111
+ plt.yticks(())
112
+
113
+ plt.savefig("iris_logistic.png")
114
+
115
+ # TODO: What is the right value?
116
+ return 0.6
117
+
118
+
119
+ ## Without any orchestration
120
+ def main():
121
+ X, Y = load_data()
122
+ logreg = model_fit(X, Y, C=1.0)
123
+ generate_plots(X, Y, logreg)
124
+
125
+
126
+ ## With runnable orchestration
127
+ def runnable_pipeline():
128
+ # The below code can be anywhere
129
+ from runnable import Catalog, Pipeline, PythonTask, metric, pickled
130
+
131
+ # X, Y = load_data()
132
+ load_data_task = PythonTask(
133
+ function=load_data,
134
+ name="load_data",
135
+ returns=[pickled("X"), pickled("Y")], # (1)
136
+ )
137
+
138
+ # logreg = model_fit(X, Y, C=1.0)
139
+ model_fit_task = PythonTask(
140
+ function=model_fit,
141
+ name="model_fit",
142
+ returns=[pickled("logreg")],
143
+ )
144
+
145
+ # generate_plots(X, Y, logreg)
146
+ generate_plots_task = PythonTask(
147
+ function=generate_plots,
148
+ name="generate_plots",
149
+ terminate_with_success=True,
150
+ catalog=Catalog(put=["iris_logistic.png"]), # (2)
151
+ returns=[metric("score")],
152
+ )
153
+
154
+ pipeline = Pipeline(
155
+ steps=[load_data_task, model_fit_task, generate_plots_task],
156
+ ) # (4)
157
+
158
+ pipeline.execute()
159
+
160
+ return pipeline
161
+
162
+
163
+ if __name__ == "__main__":
164
+ # main()
165
+ runnable_pipeline()
166
+
167
+ ```
168
+
169
+
170
+ 1. Return two serialized objects X and Y.
171
+ 2. Store the file `iris_logistic.png` for future reference.
172
+ 3. Define the sequence of tasks.
173
+ 4. Define a pipeline with the tasks
174
+
175
+ The difference between native driver and runnable orchestration:
176
+
177
+ !!! tip inline end "Notebooks and Shell scripts"
178
+
179
+ You can execute notebooks and shell scripts too!!
180
+
181
+ They can be written just as you would want them, *plain old notebooks and scripts*.
182
+
183
+
184
+
185
+
186
+ <div class="annotate" markdown>
187
+
188
+ ```diff
189
+
190
+ - X, Y = load_data()
191
+ +load_data_task = PythonTask(
192
+ + function=load_data,
193
+ + name="load_data",
194
+ + returns=[pickled("X"), pickled("Y")], (1)
195
+ + )
196
+
197
+ -logreg = model_fit(X, Y, C=1.0)
198
+ +model_fit_task = PythonTask(
199
+ + function=model_fit,
200
+ + name="model_fit",
201
+ + returns=[pickled("logreg")],
202
+ + )
203
+
204
+ -generate_plots(X, Y, logreg)
205
+ +generate_plots_task = PythonTask(
206
+ + function=generate_plots,
207
+ + name="generate_plots",
208
+ + terminate_with_success=True,
209
+ + catalog=Catalog(put=["iris_logistic.png"]), (2)
210
+ + )
211
+
212
+
213
+ +pipeline = Pipeline(
214
+ + steps=[load_data_task, model_fit_task, generate_plots_task], (3)
215
+
216
+ ```
217
+ </div>
218
+
219
+
220
+ ---
221
+
222
+ - [x] ```Domain``` code remains completely independent of ```driver``` code.
223
+ - [x] The ```driver``` function has an equivalent and intuitive runnable expression
224
+ - [x] Reproducible by default, runnable stores metadata about code/data/config for every execution.
225
+ - [x] The pipeline is `runnable` in any environment.
226
+
227
+
228
+ ## Documentation
229
+
230
+ [More details about the project and how to use it available here](https://astrazeneca.github.io/runnable/).
231
+
232
+ <hr style="border:2px dotted orange">
233
+
234
+ ## Installation
235
+
236
+ The minimum python version that runnable supports is 3.8
237
+
238
+ ```shell
239
+ pip install runnable
240
+ ```
241
+
242
+ Please look at the [installation guide](https://astrazeneca.github.io/runnable-core/usage)
243
+ for more information.
244
+
245
+
246
+ ## Pipelines can be:
247
+
248
+ ### Linear
249
+
250
+ A simple linear pipeline with tasks either
251
+ [python functions](https://astrazeneca.github.io/runnable-core/concepts/task/#python_functions),
252
+ [notebooks](https://astrazeneca.github.io/runnable-core/concepts/task/#notebooks), or [shell scripts](https://astrazeneca.github.io/runnable-core/concepts/task/#shell)
253
+
254
+ [![](https://mermaid.ink/img/pako:eNpl0bFuwyAQBuBXQVdZTqTESpxMDJ0ytkszhgwnOCcoNo4OaFVZfvcSx20tGSQ4fn0wHB3o1hBIyLJOWGeDFJ3Iq7r90lfkkA9HHfmTUpnX1hFyLvrHzDLl_qB4-1BOOZGGD3TfSikvTDSNFqdj2sT2vBTr9euQlXNWjqycsN2c7UZWFMUE7udwP0L3y6JenNKiyfvz8t8_b-gavT9QJYY0PcDtjeTLptrAChriBq1JzeoeWkG4UkMKZCoN8k2Bcn1yGEN7_HYaZOBIK4h3g4EOFi-MDcgKa59SMja0_P7s_vAJ_Q_YOH6o?type=png)](https://mermaid.live/edit#pako:eNpl0bFuwyAQBuBXQVdZTqTESpxMDJ0ytkszhgwnOCcoNo4OaFVZfvcSx20tGSQ4fn0wHB3o1hBIyLJOWGeDFJ3Iq7r90lfkkA9HHfmTUpnX1hFyLvrHzDLl_qB4-1BOOZGGD3TfSikvTDSNFqdj2sT2vBTr9euQlXNWjqycsN2c7UZWFMUE7udwP0L3y6JenNKiyfvz8t8_b-gavT9QJYY0PcDtjeTLptrAChriBq1JzeoeWkG4UkMKZCoN8k2Bcn1yGEN7_HYaZOBIK4h3g4EOFi-MDcgKa59SMja0_P7s_vAJ_Q_YOH6o)
255
+
256
+ ### [Parallel branches](https://astrazeneca.github.io/runnable-core/concepts/parallel)
257
+
258
+ Execute branches in parallel
259
+
260
+ [![](https://mermaid.ink/img/pako:eNp9k01rwzAMhv-K8S4ZtJCzDzuMLmWwwkh2KMQ7eImShiZ2sB1KKf3vs52PpsWNT7LySHqlyBeciRwwwUUtTtmBSY2-YsopR8MpQUfAdCdBBekWNBpvv6-EkFICzGAtWcUTDW3wYy20M7lr5QGBK2j-anBAkH4M1z6grnjpy17xAiTwDII07jj6HK8-VnVZBspITnpjztyoVkLLJOy3Qfrdm6gQEu2370Io7WLORo84PbRoA_oOl9BBg4UHbHR58UkMWq_fxjrOnhLRx1nH0SgkjlBjh7ekxNKGc0NelDLknhePI8qf7MVNr_31nm1wwNTeM2Ao6pmf-3y3Mp7WlqA7twOnXfKs17zt-6azmim1gQL1A0NKS3EE8hKZE4Yezm3chIVFiFe4AdmwKjdv7mIjKNYHaIBiYsycySPFlF8NxzotkjPPMNGygxXu2pxp2FSslKzBpGC1Ml7IKy3krn_E7i1f_wEayTcn?type=png)](https://mermaid.live/edit#pako:eNp9k01rwzAMhv-K8S4ZtJCzDzuMLmWwwkh2KMQ7eImShiZ2sB1KKf3vs52PpsWNT7LySHqlyBeciRwwwUUtTtmBSY2-YsopR8MpQUfAdCdBBekWNBpvv6-EkFICzGAtWcUTDW3wYy20M7lr5QGBK2j-anBAkH4M1z6grnjpy17xAiTwDII07jj6HK8-VnVZBspITnpjztyoVkLLJOy3Qfrdm6gQEu2370Io7WLORo84PbRoA_oOl9BBg4UHbHR58UkMWq_fxjrOnhLRx1nH0SgkjlBjh7ekxNKGc0NelDLknhePI8qf7MVNr_31nm1wwNTeM2Ao6pmf-3y3Mp7WlqA7twOnXfKs17zt-6azmim1gQL1A0NKS3EE8hKZE4Yezm3chIVFiFe4AdmwKjdv7mIjKNYHaIBiYsycySPFlF8NxzotkjPPMNGygxXu2pxp2FSslKzBpGC1Ml7IKy3krn_E7i1f_wEayTcn)
261
+
262
+ ### [loops or map](https://astrazeneca.github.io/runnable-core/concepts/map)
263
+
264
+ Execute a pipeline over an iterable parameter.
265
+
266
+ [![](https://mermaid.ink/img/pako:eNqVlF1rwjAUhv9KyG4qKNR-3AS2m8nuBgN3Z0Sy5tQG20SSdE7E_76kVVEr2CY3Ied9Tx6Sk3PAmeKACc5LtcsKpi36nlGZFbXciHwfLN79CuWiBLMcEULWGkBSaeosA2OCxbxdXMd89Get2bZASsLiSyuvQE2mJZXIjW27t2rOmQZ3Gp9rD6UjatWnwy7q6zPPukd50WTydmemEiS_QbQ79RwxGoQY9UaMuojRA8TCXexzyHgQZNwbMu5Cxl3IXNX6OWMyiDHpzZh0GZMHjOK3xz2mgxjT3oxplzG9MPp5_nVOhwJjteDwOg3HyFj3L1dCcvh7DUc-iftX18n6Waet1xX8cG908vpKHO6OW7cvkeHm5GR2b3drdvaSGTODHLW37mxabYC8fLgRhlfxpjNdwmEets-Dx7gCXTHBXQc8-D2KbQEVUEzckjO9oZjKo9Ox2qr5XmaYWF3DGNdbzizMBHOVVWGSs9K4XeDCKv3ZttSmsx7_AYa341E?type=png)](https://mermaid.live/edit#pako:eNqVlF1rwjAUhv9KyG4qKNR-3AS2m8nuBgN3Z0Sy5tQG20SSdE7E_76kVVEr2CY3Ied9Tx6Sk3PAmeKACc5LtcsKpi36nlGZFbXciHwfLN79CuWiBLMcEULWGkBSaeosA2OCxbxdXMd89Get2bZASsLiSyuvQE2mJZXIjW27t2rOmQZ3Gp9rD6UjatWnwy7q6zPPukd50WTydmemEiS_QbQ79RwxGoQY9UaMuojRA8TCXexzyHgQZNwbMu5Cxl3IXNX6OWMyiDHpzZh0GZMHjOK3xz2mgxjT3oxplzG9MPp5_nVOhwJjteDwOg3HyFj3L1dCcvh7DUc-iftX18n6Waet1xX8cG908vpKHO6OW7cvkeHm5GR2b3drdvaSGTODHLW37mxabYC8fLgRhlfxpjNdwmEets-Dx7gCXTHBXQc8-D2KbQEVUEzckjO9oZjKo9Ox2qr5XmaYWF3DGNdbzizMBHOVVWGSs9K4XeDCKv3ZttSmsx7_AYa341E)
267
+
268
+ ### [Arbitrary nesting](https://astrazeneca.github.io/runnable-core/concepts/nesting/)
269
+ Any nesting of parallel within map and so on.
270
+
@@ -0,0 +1,234 @@
1
+
2
+
3
+
4
+
5
+
6
+ </p>
7
+ <hr style="border:2px dotted orange">
8
+
9
+ <p align="center">
10
+ <a href="https://pypi.org/project/runnable/"><img alt="python:" src="https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10-blue.svg"></a>
11
+ <a href="https://pypi.org/project/runnable/"><img alt="Pypi" src="https://badge.fury.io/py/runnable.svg"></a>
12
+ <a href="https://github.com/vijayvammi/runnable/blob/main/LICENSE"><img alt"License" src="https://img.shields.io/badge/license-Apache%202.0-blue.svg"></a>
13
+ <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
14
+ <a href="https://github.com/python/mypy"><img alt="MyPy Checked" src="https://www.mypy-lang.org/static/mypy_badge.svg"></a>
15
+ <a href="https://github.com/vijayvammi/runnable/actions/workflows/release.yaml"><img alt="Tests:" src="https://github.com/vijayvammi/runnable/actions/workflows/release.yaml/badge.svg">
16
+ </p>
17
+ <hr style="border:2px dotted orange">
18
+
19
+
20
+ [Please check here for complete documentation](https://astrazeneca.github.io/runnable/)
21
+
22
+ ## Example
23
+
24
+ The below data science flavored code is a well-known
25
+ [iris example from scikit-learn](https://scikit-learn.org/stable/auto_examples/linear_model/plot_iris_logistic.html).
26
+
27
+
28
+ ```python
29
+ """
30
+ Example of Logistic regression using scikit-learn
31
+ https://scikit-learn.org/stable/auto_examples/linear_model/plot_iris_logistic.html
32
+ """
33
+
34
+ import matplotlib.pyplot as plt
35
+ import numpy as np
36
+ from sklearn import datasets
37
+ from sklearn.inspection import DecisionBoundaryDisplay
38
+ from sklearn.linear_model import LogisticRegression
39
+
40
+
41
+ def load_data():
42
+ # import some data to play with
43
+ iris = datasets.load_iris()
44
+ X = iris.data[:, :2] # we only take the first two features.
45
+ Y = iris.target
46
+
47
+ return X, Y
48
+
49
+
50
+ def model_fit(X: np.ndarray, Y: np.ndarray, C: float = 1e5):
51
+ logreg = LogisticRegression(C=C)
52
+ logreg.fit(X, Y)
53
+
54
+ return logreg
55
+
56
+
57
+ def generate_plots(X: np.ndarray, Y: np.ndarray, logreg: LogisticRegression):
58
+ _, ax = plt.subplots(figsize=(4, 3))
59
+ DecisionBoundaryDisplay.from_estimator(
60
+ logreg,
61
+ X,
62
+ cmap=plt.cm.Paired,
63
+ ax=ax,
64
+ response_method="predict",
65
+ plot_method="pcolormesh",
66
+ shading="auto",
67
+ xlabel="Sepal length",
68
+ ylabel="Sepal width",
69
+ eps=0.5,
70
+ )
71
+
72
+ # Plot also the training points
73
+ plt.scatter(X[:, 0], X[:, 1], c=Y, edgecolors="k", cmap=plt.cm.Paired)
74
+
75
+ plt.xticks(())
76
+ plt.yticks(())
77
+
78
+ plt.savefig("iris_logistic.png")
79
+
80
+ # TODO: What is the right value?
81
+ return 0.6
82
+
83
+
84
+ ## Without any orchestration
85
+ def main():
86
+ X, Y = load_data()
87
+ logreg = model_fit(X, Y, C=1.0)
88
+ generate_plots(X, Y, logreg)
89
+
90
+
91
+ ## With runnable orchestration
92
+ def runnable_pipeline():
93
+ # The below code can be anywhere
94
+ from runnable import Catalog, Pipeline, PythonTask, metric, pickled
95
+
96
+ # X, Y = load_data()
97
+ load_data_task = PythonTask(
98
+ function=load_data,
99
+ name="load_data",
100
+ returns=[pickled("X"), pickled("Y")], # (1)
101
+ )
102
+
103
+ # logreg = model_fit(X, Y, C=1.0)
104
+ model_fit_task = PythonTask(
105
+ function=model_fit,
106
+ name="model_fit",
107
+ returns=[pickled("logreg")],
108
+ )
109
+
110
+ # generate_plots(X, Y, logreg)
111
+ generate_plots_task = PythonTask(
112
+ function=generate_plots,
113
+ name="generate_plots",
114
+ terminate_with_success=True,
115
+ catalog=Catalog(put=["iris_logistic.png"]), # (2)
116
+ returns=[metric("score")],
117
+ )
118
+
119
+ pipeline = Pipeline(
120
+ steps=[load_data_task, model_fit_task, generate_plots_task],
121
+ ) # (4)
122
+
123
+ pipeline.execute()
124
+
125
+ return pipeline
126
+
127
+
128
+ if __name__ == "__main__":
129
+ # main()
130
+ runnable_pipeline()
131
+
132
+ ```
133
+
134
+
135
+ 1. Return two serialized objects X and Y.
136
+ 2. Store the file `iris_logistic.png` for future reference.
137
+ 3. Define the sequence of tasks.
138
+ 4. Define a pipeline with the tasks
139
+
140
+ The difference between native driver and runnable orchestration:
141
+
142
+ !!! tip inline end "Notebooks and Shell scripts"
143
+
144
+ You can execute notebooks and shell scripts too!!
145
+
146
+ They can be written just as you would want them, *plain old notebooks and scripts*.
147
+
148
+
149
+
150
+
151
+ <div class="annotate" markdown>
152
+
153
+ ```diff
154
+
155
+ - X, Y = load_data()
156
+ +load_data_task = PythonTask(
157
+ + function=load_data,
158
+ + name="load_data",
159
+ + returns=[pickled("X"), pickled("Y")], (1)
160
+ + )
161
+
162
+ -logreg = model_fit(X, Y, C=1.0)
163
+ +model_fit_task = PythonTask(
164
+ + function=model_fit,
165
+ + name="model_fit",
166
+ + returns=[pickled("logreg")],
167
+ + )
168
+
169
+ -generate_plots(X, Y, logreg)
170
+ +generate_plots_task = PythonTask(
171
+ + function=generate_plots,
172
+ + name="generate_plots",
173
+ + terminate_with_success=True,
174
+ + catalog=Catalog(put=["iris_logistic.png"]), (2)
175
+ + )
176
+
177
+
178
+ +pipeline = Pipeline(
179
+ + steps=[load_data_task, model_fit_task, generate_plots_task], (3)
180
+
181
+ ```
182
+ </div>
183
+
184
+
185
+ ---
186
+
187
+ - [x] ```Domain``` code remains completely independent of ```driver``` code.
188
+ - [x] The ```driver``` function has an equivalent and intuitive runnable expression
189
+ - [x] Reproducible by default, runnable stores metadata about code/data/config for every execution.
190
+ - [x] The pipeline is `runnable` in any environment.
191
+
192
+
193
+ ## Documentation
194
+
195
+ [More details about the project and how to use it available here](https://astrazeneca.github.io/runnable/).
196
+
197
+ <hr style="border:2px dotted orange">
198
+
199
+ ## Installation
200
+
201
+ The minimum python version that runnable supports is 3.8
202
+
203
+ ```shell
204
+ pip install runnable
205
+ ```
206
+
207
+ Please look at the [installation guide](https://astrazeneca.github.io/runnable-core/usage)
208
+ for more information.
209
+
210
+
211
+ ## Pipelines can be:
212
+
213
+ ### Linear
214
+
215
+ A simple linear pipeline with tasks either
216
+ [python functions](https://astrazeneca.github.io/runnable-core/concepts/task/#python_functions),
217
+ [notebooks](https://astrazeneca.github.io/runnable-core/concepts/task/#notebooks), or [shell scripts](https://astrazeneca.github.io/runnable-core/concepts/task/#shell)
218
+
219
+ [![](https://mermaid.ink/img/pako:eNpl0bFuwyAQBuBXQVdZTqTESpxMDJ0ytkszhgwnOCcoNo4OaFVZfvcSx20tGSQ4fn0wHB3o1hBIyLJOWGeDFJ3Iq7r90lfkkA9HHfmTUpnX1hFyLvrHzDLl_qB4-1BOOZGGD3TfSikvTDSNFqdj2sT2vBTr9euQlXNWjqycsN2c7UZWFMUE7udwP0L3y6JenNKiyfvz8t8_b-gavT9QJYY0PcDtjeTLptrAChriBq1JzeoeWkG4UkMKZCoN8k2Bcn1yGEN7_HYaZOBIK4h3g4EOFi-MDcgKa59SMja0_P7s_vAJ_Q_YOH6o?type=png)](https://mermaid.live/edit#pako:eNpl0bFuwyAQBuBXQVdZTqTESpxMDJ0ytkszhgwnOCcoNo4OaFVZfvcSx20tGSQ4fn0wHB3o1hBIyLJOWGeDFJ3Iq7r90lfkkA9HHfmTUpnX1hFyLvrHzDLl_qB4-1BOOZGGD3TfSikvTDSNFqdj2sT2vBTr9euQlXNWjqycsN2c7UZWFMUE7udwP0L3y6JenNKiyfvz8t8_b-gavT9QJYY0PcDtjeTLptrAChriBq1JzeoeWkG4UkMKZCoN8k2Bcn1yGEN7_HYaZOBIK4h3g4EOFi-MDcgKa59SMja0_P7s_vAJ_Q_YOH6o)
220
+
221
+ ### [Parallel branches](https://astrazeneca.github.io/runnable-core/concepts/parallel)
222
+
223
+ Execute branches in parallel
224
+
225
+ [![](https://mermaid.ink/img/pako:eNp9k01rwzAMhv-K8S4ZtJCzDzuMLmWwwkh2KMQ7eImShiZ2sB1KKf3vs52PpsWNT7LySHqlyBeciRwwwUUtTtmBSY2-YsopR8MpQUfAdCdBBekWNBpvv6-EkFICzGAtWcUTDW3wYy20M7lr5QGBK2j-anBAkH4M1z6grnjpy17xAiTwDII07jj6HK8-VnVZBspITnpjztyoVkLLJOy3Qfrdm6gQEu2370Io7WLORo84PbRoA_oOl9BBg4UHbHR58UkMWq_fxjrOnhLRx1nH0SgkjlBjh7ekxNKGc0NelDLknhePI8qf7MVNr_31nm1wwNTeM2Ao6pmf-3y3Mp7WlqA7twOnXfKs17zt-6azmim1gQL1A0NKS3EE8hKZE4Yezm3chIVFiFe4AdmwKjdv7mIjKNYHaIBiYsycySPFlF8NxzotkjPPMNGygxXu2pxp2FSslKzBpGC1Ml7IKy3krn_E7i1f_wEayTcn?type=png)](https://mermaid.live/edit#pako:eNp9k01rwzAMhv-K8S4ZtJCzDzuMLmWwwkh2KMQ7eImShiZ2sB1KKf3vs52PpsWNT7LySHqlyBeciRwwwUUtTtmBSY2-YsopR8MpQUfAdCdBBekWNBpvv6-EkFICzGAtWcUTDW3wYy20M7lr5QGBK2j-anBAkH4M1z6grnjpy17xAiTwDII07jj6HK8-VnVZBspITnpjztyoVkLLJOy3Qfrdm6gQEu2370Io7WLORo84PbRoA_oOl9BBg4UHbHR58UkMWq_fxjrOnhLRx1nH0SgkjlBjh7ekxNKGc0NelDLknhePI8qf7MVNr_31nm1wwNTeM2Ao6pmf-3y3Mp7WlqA7twOnXfKs17zt-6azmim1gQL1A0NKS3EE8hKZE4Yezm3chIVFiFe4AdmwKjdv7mIjKNYHaIBiYsycySPFlF8NxzotkjPPMNGygxXu2pxp2FSslKzBpGC1Ml7IKy3krn_E7i1f_wEayTcn)
226
+
227
+ ### [loops or map](https://astrazeneca.github.io/runnable-core/concepts/map)
228
+
229
+ Execute a pipeline over an iterable parameter.
230
+
231
+ [![](https://mermaid.ink/img/pako:eNqVlF1rwjAUhv9KyG4qKNR-3AS2m8nuBgN3Z0Sy5tQG20SSdE7E_76kVVEr2CY3Ied9Tx6Sk3PAmeKACc5LtcsKpi36nlGZFbXciHwfLN79CuWiBLMcEULWGkBSaeosA2OCxbxdXMd89Get2bZASsLiSyuvQE2mJZXIjW27t2rOmQZ3Gp9rD6UjatWnwy7q6zPPukd50WTydmemEiS_QbQ79RwxGoQY9UaMuojRA8TCXexzyHgQZNwbMu5Cxl3IXNX6OWMyiDHpzZh0GZMHjOK3xz2mgxjT3oxplzG9MPp5_nVOhwJjteDwOg3HyFj3L1dCcvh7DUc-iftX18n6Waet1xX8cG908vpKHO6OW7cvkeHm5GR2b3drdvaSGTODHLW37mxabYC8fLgRhlfxpjNdwmEets-Dx7gCXTHBXQc8-D2KbQEVUEzckjO9oZjKo9Ox2qr5XmaYWF3DGNdbzizMBHOVVWGSs9K4XeDCKv3ZttSmsx7_AYa341E?type=png)](https://mermaid.live/edit#pako:eNqVlF1rwjAUhv9KyG4qKNR-3AS2m8nuBgN3Z0Sy5tQG20SSdE7E_76kVVEr2CY3Ied9Tx6Sk3PAmeKACc5LtcsKpi36nlGZFbXciHwfLN79CuWiBLMcEULWGkBSaeosA2OCxbxdXMd89Get2bZASsLiSyuvQE2mJZXIjW27t2rOmQZ3Gp9rD6UjatWnwy7q6zPPukd50WTydmemEiS_QbQ79RwxGoQY9UaMuojRA8TCXexzyHgQZNwbMu5Cxl3IXNX6OWMyiDHpzZh0GZMHjOK3xz2mgxjT3oxplzG9MPp5_nVOhwJjteDwOg3HyFj3L1dCcvh7DUc-iftX18n6Waet1xX8cG908vpKHO6OW7cvkeHm5GR2b3drdvaSGTODHLW37mxabYC8fLgRhlfxpjNdwmEets-Dx7gCXTHBXQc8-D2KbQEVUEzckjO9oZjKo9Ox2qr5XmaYWF3DGNdbzizMBHOVVWGSs9K4XeDCKv3ZttSmsx7_AYa341E)
232
+
233
+ ### [Arbitrary nesting](https://astrazeneca.github.io/runnable-core/concepts/nesting/)
234
+ Any nesting of parallel within map and so on.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "runnable"
3
- version = "0.12.2"
3
+ version = "0.13.0"
4
4
  description = "A Compute agnostic pipelining software"
5
5
  authors = ["Vijay Vammi <mesanthu@gmail.com>"]
6
6
  license = "Apache-2.0"
@@ -25,43 +25,12 @@ mlflow-skinny = { version = "*", optional = true }
25
25
  ploomber-engine = "^0.0.31"
26
26
  dill = "^0.3.8"
27
27
 
28
- [tool.poetry.group.docs.dependencies]
29
- mkdocs = "*"
30
- mkdocs-material = "*"
31
- mkdocs-section-index = "^0.3.5"
32
- mkdocstrings = { extras = ["python"], version = "^0.24.0" }
33
- nbconvert = "^7.13.1"
34
- mkdocs-click = "^0.8.1"
35
- tensorflow = "^2.16.1"
36
-
37
- [tool.poetry.group.binary.dependencies]
38
- pyinstaller = "^5.13.2"
39
-
40
- [tool.poetry.group.perf.dependencies]
41
- # Run the performace tests poetry run python -m pyflame -p ./flamegraph.pl runnable/entrypoints.py
42
- pyflame = "^0.3.1"
43
-
44
-
45
- [tool.poetry.group.tutorial.dependencies]
46
- pandas = "^2.2.1"
47
- numpy = "^1.26.4"
48
- scikit-learn = "^1.4.1.post1"
49
- en-core-web-sm = { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1.tar.gz" }
50
- matplotlib = "^3.8.3"
51
-
52
-
53
- [tool.poetry.group.release.dependencies]
54
- python-semantic-release = "^9.4.2"
55
-
56
-
57
- [tool.poetry.group.examples.dependencies]
58
- pandas = "^2.2.2"
59
-
60
28
  [tool.poetry.extras]
61
29
  docker = ['docker']
62
30
  notebook = ['ploomber-engine']
63
31
  database = ["sqlalchemy"]
64
32
 
33
+
65
34
  [tool.poetry.group.dev.dependencies]
66
35
  pytest = "*"
67
36
  pytest-cov = "*"
@@ -73,16 +42,47 @@ ruff = "^0.0.259"
73
42
  commit-linter = "^1.0.2"
74
43
  black = "^23.3.0"
75
44
  gitlint = "^0.19.1"
76
- pandas = "^2.2.2"
45
+ pandas = "^2.2.2" # for testing examples
77
46
 
47
+ [tool.poetry.group.release.dependencies]
48
+ python-semantic-release = "^9.4.2"
49
+
50
+ [tool.poetry.group.docs.dependencies]
51
+ mkdocs = "*"
52
+ mkdocs-material = "*"
53
+ mkdocs-section-index = "^0.3.5"
54
+ mkdocstrings = { extras = ["python"], version = "^0.24.0" }
55
+ nbconvert = "^7.13.1"
56
+ mkdocs-click = "^0.8.1"
57
+
58
+ [tool.poetry.group.tutorial.dependencies]
59
+ pandas = "^2.2.1"
60
+ numpy = "^1.26.4"
61
+ scikit-learn = "^1.4.1.post1"
62
+ en-core-web-sm = { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1.tar.gz" }
63
+ matplotlib = "^3.8.3"
64
+
65
+
66
+ [tool.poetry.group.compare.dependencies]
67
+ torch = "^2.3.0"
68
+ torchvision = "^0.18.0"
69
+ tensorflow = "^2.16.1"
78
70
 
79
71
  [tool.poetry.scripts]
80
72
  runnable = 'runnable.cli:cli'
81
73
 
82
74
 
75
+ [tool.poetry.group.binary.dependencies]
76
+ pyinstaller = "^5.13.2"
77
+
78
+ [tool.poetry.group.perf.dependencies]
79
+ # Run the performace tests poetry run python -m pyflame -p ./flamegraph.pl runnable/entrypoints.py
80
+ pyflame = "^0.3.1"
81
+
82
+
83
83
  # Plugins for Executors
84
84
  [tool.poetry.plugins."executor"]
85
- "local" = "runnable.extensions.executor.local.implementation:LocalExecutor"
85
+ "local" = "runnable.extensions.executor.local:LocalExecutor"
86
86
  "local-container" = "runnable.extensions.executor.local_container.implementation:LocalContainerExecutor"
87
87
  "argo" = "runnable.extensions.executor.argo.implementation:ArgoExecutor"
88
88
  "mocked" = "runnable.extensions.executor.mocked.implementation:MockedExecutor"
@@ -19,8 +19,6 @@ class LocalExecutor(GenericExecutor):
19
19
  Example config:
20
20
  execution:
21
21
  type: local
22
- config:
23
- enable_parallel: True or False to enable parallel.
24
22
 
25
23
  """
26
24
 
@@ -797,12 +797,27 @@ class DagNode(CompositeNode):
797
797
  class StubNode(ExecutableNode):
798
798
  """
799
799
  Stub is a convenience design node.
800
-
801
800
  It always returns success in the attempt log and does nothing.
802
801
 
803
802
  This node is very similar to pass state in Step functions.
804
803
 
805
804
  This node type could be handy when designing the pipeline and stubbing functions
805
+ --8<-- [start:stub_reference]
806
+ An stub execution node of the pipeline.
807
+ Please refer to define pipeline/tasks/stub for more information.
808
+
809
+ As part of the dag definition, a stub task is defined as follows:
810
+
811
+ dag:
812
+ steps:
813
+ stub_task: # The name of the node
814
+ type: stub
815
+ on_failure: The name of the step to traverse in case of failure
816
+ next: The next node to execute after this task, use "success" to terminate the pipeline successfully
817
+ or "fail" to terminate the pipeline with an error.
818
+
819
+ It can take arbritary number of parameters, which is handy to temporarily silence a task node.
820
+ --8<-- [end:stub_reference]
806
821
  """
807
822
 
808
823
  node_type: str = Field(default="stub", serialization_alias="type")