ob-metaflow 2.11.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__py2.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 (289) hide show
  1. metaflow/R.py +10 -7
  2. metaflow/__init__.py +40 -25
  3. metaflow/_vendor/imghdr/__init__.py +186 -0
  4. metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
  5. metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
  6. metaflow/_vendor/importlib_metadata/_collections.py +30 -0
  7. metaflow/_vendor/importlib_metadata/_compat.py +71 -0
  8. metaflow/_vendor/importlib_metadata/_functools.py +104 -0
  9. metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
  10. metaflow/_vendor/importlib_metadata/_meta.py +48 -0
  11. metaflow/_vendor/importlib_metadata/_text.py +99 -0
  12. metaflow/_vendor/importlib_metadata/py.typed +0 -0
  13. metaflow/_vendor/typeguard/__init__.py +48 -0
  14. metaflow/_vendor/typeguard/_checkers.py +1070 -0
  15. metaflow/_vendor/typeguard/_config.py +108 -0
  16. metaflow/_vendor/typeguard/_decorators.py +233 -0
  17. metaflow/_vendor/typeguard/_exceptions.py +42 -0
  18. metaflow/_vendor/typeguard/_functions.py +308 -0
  19. metaflow/_vendor/typeguard/_importhook.py +213 -0
  20. metaflow/_vendor/typeguard/_memo.py +48 -0
  21. metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
  22. metaflow/_vendor/typeguard/_suppression.py +86 -0
  23. metaflow/_vendor/typeguard/_transformer.py +1229 -0
  24. metaflow/_vendor/typeguard/_union_transformer.py +55 -0
  25. metaflow/_vendor/typeguard/_utils.py +173 -0
  26. metaflow/_vendor/typeguard/py.typed +0 -0
  27. metaflow/_vendor/typing_extensions.py +3641 -0
  28. metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
  29. metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
  30. metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
  31. metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
  32. metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
  33. metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
  34. metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
  35. metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
  36. metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
  37. metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
  38. metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
  39. metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
  40. metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
  41. metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
  42. metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
  43. metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
  44. metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
  45. metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
  46. metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
  47. metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
  48. metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
  49. metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
  50. metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
  51. metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
  52. metaflow/_vendor/yaml/__init__.py +427 -0
  53. metaflow/_vendor/yaml/composer.py +139 -0
  54. metaflow/_vendor/yaml/constructor.py +748 -0
  55. metaflow/_vendor/yaml/cyaml.py +101 -0
  56. metaflow/_vendor/yaml/dumper.py +62 -0
  57. metaflow/_vendor/yaml/emitter.py +1137 -0
  58. metaflow/_vendor/yaml/error.py +75 -0
  59. metaflow/_vendor/yaml/events.py +86 -0
  60. metaflow/_vendor/yaml/loader.py +63 -0
  61. metaflow/_vendor/yaml/nodes.py +49 -0
  62. metaflow/_vendor/yaml/parser.py +589 -0
  63. metaflow/_vendor/yaml/reader.py +185 -0
  64. metaflow/_vendor/yaml/representer.py +389 -0
  65. metaflow/_vendor/yaml/resolver.py +227 -0
  66. metaflow/_vendor/yaml/scanner.py +1435 -0
  67. metaflow/_vendor/yaml/serializer.py +111 -0
  68. metaflow/_vendor/yaml/tokens.py +104 -0
  69. metaflow/cards.py +5 -0
  70. metaflow/cli.py +331 -785
  71. metaflow/cli_args.py +17 -0
  72. metaflow/cli_components/__init__.py +0 -0
  73. metaflow/cli_components/dump_cmd.py +96 -0
  74. metaflow/cli_components/init_cmd.py +52 -0
  75. metaflow/cli_components/run_cmds.py +546 -0
  76. metaflow/cli_components/step_cmd.py +334 -0
  77. metaflow/cli_components/utils.py +140 -0
  78. metaflow/client/__init__.py +1 -0
  79. metaflow/client/core.py +467 -73
  80. metaflow/client/filecache.py +75 -35
  81. metaflow/clone_util.py +7 -1
  82. metaflow/cmd/code/__init__.py +231 -0
  83. metaflow/cmd/develop/stub_generator.py +756 -288
  84. metaflow/cmd/develop/stubs.py +12 -28
  85. metaflow/cmd/main_cli.py +6 -4
  86. metaflow/cmd/make_wrapper.py +78 -0
  87. metaflow/datastore/__init__.py +1 -0
  88. metaflow/datastore/content_addressed_store.py +41 -10
  89. metaflow/datastore/datastore_set.py +11 -2
  90. metaflow/datastore/flow_datastore.py +156 -10
  91. metaflow/datastore/spin_datastore.py +91 -0
  92. metaflow/datastore/task_datastore.py +154 -39
  93. metaflow/debug.py +5 -0
  94. metaflow/decorators.py +404 -78
  95. metaflow/exception.py +8 -2
  96. metaflow/extension_support/__init__.py +527 -376
  97. metaflow/extension_support/_empty_file.py +2 -2
  98. metaflow/extension_support/plugins.py +49 -31
  99. metaflow/flowspec.py +482 -33
  100. metaflow/graph.py +210 -42
  101. metaflow/includefile.py +84 -40
  102. metaflow/lint.py +141 -22
  103. metaflow/meta_files.py +13 -0
  104. metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
  105. metaflow/{metadata → metadata_provider}/metadata.py +86 -1
  106. metaflow/metaflow_config.py +175 -28
  107. metaflow/metaflow_config_funcs.py +51 -3
  108. metaflow/metaflow_current.py +4 -10
  109. metaflow/metaflow_environment.py +139 -53
  110. metaflow/metaflow_git.py +115 -0
  111. metaflow/metaflow_profile.py +18 -0
  112. metaflow/metaflow_version.py +150 -66
  113. metaflow/mflog/__init__.py +4 -3
  114. metaflow/mflog/save_logs.py +2 -2
  115. metaflow/multicore_utils.py +31 -14
  116. metaflow/package/__init__.py +673 -0
  117. metaflow/packaging_sys/__init__.py +880 -0
  118. metaflow/packaging_sys/backend.py +128 -0
  119. metaflow/packaging_sys/distribution_support.py +153 -0
  120. metaflow/packaging_sys/tar_backend.py +99 -0
  121. metaflow/packaging_sys/utils.py +54 -0
  122. metaflow/packaging_sys/v1.py +527 -0
  123. metaflow/parameters.py +149 -28
  124. metaflow/plugins/__init__.py +74 -5
  125. metaflow/plugins/airflow/airflow.py +40 -25
  126. metaflow/plugins/airflow/airflow_cli.py +22 -5
  127. metaflow/plugins/airflow/airflow_decorator.py +1 -1
  128. metaflow/plugins/airflow/airflow_utils.py +5 -3
  129. metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
  130. metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
  131. metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
  132. metaflow/plugins/argo/argo_client.py +78 -33
  133. metaflow/plugins/argo/argo_events.py +6 -6
  134. metaflow/plugins/argo/argo_workflows.py +2410 -527
  135. metaflow/plugins/argo/argo_workflows_cli.py +571 -121
  136. metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
  137. metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
  138. metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
  139. metaflow/plugins/argo/capture_error.py +73 -0
  140. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  141. metaflow/plugins/argo/exit_hooks.py +209 -0
  142. metaflow/plugins/argo/jobset_input_paths.py +15 -0
  143. metaflow/plugins/argo/param_val.py +19 -0
  144. metaflow/plugins/aws/aws_client.py +10 -3
  145. metaflow/plugins/aws/aws_utils.py +55 -2
  146. metaflow/plugins/aws/batch/batch.py +72 -5
  147. metaflow/plugins/aws/batch/batch_cli.py +33 -10
  148. metaflow/plugins/aws/batch/batch_client.py +4 -3
  149. metaflow/plugins/aws/batch/batch_decorator.py +102 -35
  150. metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
  151. metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
  152. metaflow/plugins/aws/step_functions/production_token.py +1 -1
  153. metaflow/plugins/aws/step_functions/step_functions.py +65 -8
  154. metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
  155. metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
  156. metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
  157. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
  158. metaflow/plugins/azure/azure_exceptions.py +1 -1
  159. metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
  160. metaflow/plugins/azure/azure_tail.py +1 -1
  161. metaflow/plugins/azure/includefile_support.py +2 -0
  162. metaflow/plugins/cards/card_cli.py +66 -30
  163. metaflow/plugins/cards/card_creator.py +25 -1
  164. metaflow/plugins/cards/card_datastore.py +21 -49
  165. metaflow/plugins/cards/card_decorator.py +132 -8
  166. metaflow/plugins/cards/card_modules/basic.py +112 -17
  167. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  168. metaflow/plugins/cards/card_modules/card.py +16 -1
  169. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  170. metaflow/plugins/cards/card_modules/components.py +665 -28
  171. metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
  172. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  173. metaflow/plugins/cards/card_modules/main.css +1 -0
  174. metaflow/plugins/cards/card_modules/main.js +68 -49
  175. metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
  176. metaflow/plugins/cards/card_modules/test_cards.py +26 -12
  177. metaflow/plugins/cards/card_server.py +39 -14
  178. metaflow/plugins/cards/component_serializer.py +2 -9
  179. metaflow/plugins/cards/metadata.py +22 -0
  180. metaflow/plugins/catch_decorator.py +9 -0
  181. metaflow/plugins/datastores/azure_storage.py +10 -1
  182. metaflow/plugins/datastores/gs_storage.py +6 -2
  183. metaflow/plugins/datastores/local_storage.py +12 -6
  184. metaflow/plugins/datastores/spin_storage.py +12 -0
  185. metaflow/plugins/datatools/local.py +2 -0
  186. metaflow/plugins/datatools/s3/s3.py +126 -75
  187. metaflow/plugins/datatools/s3/s3op.py +254 -121
  188. metaflow/plugins/env_escape/__init__.py +3 -3
  189. metaflow/plugins/env_escape/client_modules.py +102 -72
  190. metaflow/plugins/env_escape/server.py +7 -0
  191. metaflow/plugins/env_escape/stub.py +24 -5
  192. metaflow/plugins/events_decorator.py +343 -185
  193. metaflow/plugins/exit_hook/__init__.py +0 -0
  194. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  195. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  196. metaflow/plugins/gcp/__init__.py +1 -1
  197. metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
  198. metaflow/plugins/gcp/gs_tail.py +10 -6
  199. metaflow/plugins/gcp/includefile_support.py +3 -0
  200. metaflow/plugins/kubernetes/kube_utils.py +108 -0
  201. metaflow/plugins/kubernetes/kubernetes.py +411 -130
  202. metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
  203. metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
  204. metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
  205. metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
  206. metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
  207. metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
  208. metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
  209. metaflow/plugins/logs_cli.py +359 -0
  210. metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
  211. metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
  212. metaflow/plugins/metadata_providers/spin.py +16 -0
  213. metaflow/plugins/package_cli.py +36 -24
  214. metaflow/plugins/parallel_decorator.py +128 -11
  215. metaflow/plugins/parsers.py +16 -0
  216. metaflow/plugins/project_decorator.py +51 -5
  217. metaflow/plugins/pypi/bootstrap.py +357 -105
  218. metaflow/plugins/pypi/conda_decorator.py +82 -81
  219. metaflow/plugins/pypi/conda_environment.py +187 -52
  220. metaflow/plugins/pypi/micromamba.py +157 -47
  221. metaflow/plugins/pypi/parsers.py +268 -0
  222. metaflow/plugins/pypi/pip.py +88 -13
  223. metaflow/plugins/pypi/pypi_decorator.py +37 -1
  224. metaflow/plugins/pypi/utils.py +48 -2
  225. metaflow/plugins/resources_decorator.py +2 -2
  226. metaflow/plugins/secrets/__init__.py +3 -0
  227. metaflow/plugins/secrets/secrets_decorator.py +26 -181
  228. metaflow/plugins/secrets/secrets_func.py +49 -0
  229. metaflow/plugins/secrets/secrets_spec.py +101 -0
  230. metaflow/plugins/secrets/utils.py +74 -0
  231. metaflow/plugins/tag_cli.py +4 -7
  232. metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
  233. metaflow/plugins/timeout_decorator.py +3 -3
  234. metaflow/plugins/uv/__init__.py +0 -0
  235. metaflow/plugins/uv/bootstrap.py +128 -0
  236. metaflow/plugins/uv/uv_environment.py +72 -0
  237. metaflow/procpoll.py +1 -1
  238. metaflow/pylint_wrapper.py +5 -1
  239. metaflow/runner/__init__.py +0 -0
  240. metaflow/runner/click_api.py +717 -0
  241. metaflow/runner/deployer.py +470 -0
  242. metaflow/runner/deployer_impl.py +201 -0
  243. metaflow/runner/metaflow_runner.py +714 -0
  244. metaflow/runner/nbdeploy.py +132 -0
  245. metaflow/runner/nbrun.py +225 -0
  246. metaflow/runner/subprocess_manager.py +650 -0
  247. metaflow/runner/utils.py +335 -0
  248. metaflow/runtime.py +1078 -260
  249. metaflow/sidecar/sidecar_worker.py +1 -1
  250. metaflow/system/__init__.py +5 -0
  251. metaflow/system/system_logger.py +85 -0
  252. metaflow/system/system_monitor.py +108 -0
  253. metaflow/system/system_utils.py +19 -0
  254. metaflow/task.py +521 -225
  255. metaflow/tracing/__init__.py +7 -7
  256. metaflow/tracing/span_exporter.py +31 -38
  257. metaflow/tracing/tracing_modules.py +38 -43
  258. metaflow/tuple_util.py +27 -0
  259. metaflow/user_configs/__init__.py +0 -0
  260. metaflow/user_configs/config_options.py +563 -0
  261. metaflow/user_configs/config_parameters.py +598 -0
  262. metaflow/user_decorators/__init__.py +0 -0
  263. metaflow/user_decorators/common.py +144 -0
  264. metaflow/user_decorators/mutable_flow.py +512 -0
  265. metaflow/user_decorators/mutable_step.py +424 -0
  266. metaflow/user_decorators/user_flow_decorator.py +264 -0
  267. metaflow/user_decorators/user_step_decorator.py +749 -0
  268. metaflow/util.py +243 -27
  269. metaflow/vendor.py +23 -7
  270. metaflow/version.py +1 -1
  271. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
  272. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
  273. ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
  274. ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
  275. ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
  276. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  277. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
  278. metaflow/_vendor/v3_5/__init__.py +0 -1
  279. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  280. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  281. metaflow/package.py +0 -188
  282. ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
  283. ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
  284. /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
  285. /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
  286. /metaflow/{metadata → metadata_provider}/util.py +0 -0
  287. /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
  288. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
  289. {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,714 @@
1
+ import importlib
2
+ import inspect
3
+ import os
4
+ import sys
5
+ import json
6
+
7
+ from typing import Dict, Iterator, Optional, Tuple
8
+
9
+ from metaflow import Run, Task
10
+
11
+ from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
12
+
13
+ from metaflow.plugins import get_runner_cli
14
+
15
+ from .utils import (
16
+ temporary_fifo,
17
+ handle_timeout,
18
+ async_handle_timeout,
19
+ with_dir,
20
+ )
21
+ from .subprocess_manager import CommandManager, SubprocessManager
22
+
23
+
24
+ class ExecutingProcess(object):
25
+ """
26
+ This is a base class for `ExecutingRun` and `ExecutingTask` classes.
27
+ The `ExecutingRun` and `ExecutingTask` classes are returned by methods
28
+ in `Runner` and `NBRunner`, and they are subclasses of this class.
29
+
30
+ The `ExecutingRun` class for instance contains a reference to a `metaflow.Run`
31
+ object representing the currently executing or finished run, as well as the metadata
32
+ related to the process.
33
+
34
+ Similarly, the `ExecutingTask` class contains a reference to a `metaflow.Task`
35
+ object representing the currently executing or finished task, as well as the metadata
36
+ related to the process.
37
+
38
+ This class or its subclasses are not meant to be instantiated directly. The class
39
+ works as a context manager, allowing you to use a pattern like:
40
+
41
+ ```python
42
+ with Runner(...).run() as running:
43
+ ...
44
+ ```
45
+
46
+ Note that you should use either this object as the context manager or `Runner`, not both
47
+ in a nested manner.
48
+ """
49
+
50
+ def __init__(self, runner: "Runner", command_obj: CommandManager) -> None:
51
+ """
52
+ Create a new ExecutingRun -- this should not be done by the user directly but
53
+ instead use Runner.run()
54
+
55
+ Parameters
56
+ ----------
57
+ runner : Runner
58
+ Parent runner for this run.
59
+ command_obj : CommandManager
60
+ CommandManager containing the subprocess executing this run.
61
+ run_obj : Run
62
+ Run object corresponding to this run.
63
+ """
64
+ self.runner = runner
65
+ self.command_obj = command_obj
66
+
67
+ def __enter__(self) -> "ExecutingProcess":
68
+ return self
69
+
70
+ def __exit__(self, exc_type, exc_value, traceback):
71
+ self.runner.__exit__(exc_type, exc_value, traceback)
72
+
73
+ async def wait(
74
+ self, timeout: Optional[float] = None, stream: Optional[str] = None
75
+ ) -> "ExecutingProcess":
76
+ """
77
+ Wait for this run to finish, optionally with a timeout
78
+ and optionally streaming its output.
79
+
80
+ Note that this method is asynchronous and needs to be `await`ed.
81
+
82
+ Parameters
83
+ ----------
84
+ timeout : float, optional, default None
85
+ The maximum time, in seconds, to wait for the run to finish.
86
+ If the timeout is reached, the run is terminated. If not specified, wait
87
+ forever.
88
+ stream : str, optional, default None
89
+ If specified, the specified stream is printed to stdout. `stream` can
90
+ be one of `stdout` or `stderr`.
91
+
92
+ Returns
93
+ -------
94
+ ExecutingProcess
95
+ This object, allowing you to chain calls.
96
+ """
97
+ await self.command_obj.wait(timeout, stream)
98
+ return self
99
+
100
+ @property
101
+ def returncode(self) -> Optional[int]:
102
+ """
103
+ Gets the return code of the underlying subprocess. A non-zero
104
+ code indicates a failure, `None` a currently executing run.
105
+
106
+ Returns
107
+ -------
108
+ Optional[int]
109
+ The return code of the underlying subprocess.
110
+ """
111
+ return self.command_obj.process.returncode
112
+
113
+ @property
114
+ def status(self) -> str:
115
+ """
116
+ Returns the status of the underlying subprocess that is responsible
117
+ for executing the run.
118
+
119
+ The return value is one of the following strings:
120
+ - `timeout` indicates that the run timed out.
121
+ - `running` indicates a currently executing run.
122
+ - `failed` indicates a failed run.
123
+ - `successful` indicates a successful run.
124
+
125
+ Returns
126
+ -------
127
+ str
128
+ The current status of the run.
129
+ """
130
+ if self.command_obj.timeout:
131
+ return "timeout"
132
+ elif self.command_obj.process.returncode is None:
133
+ return "running"
134
+ elif self.command_obj.process.returncode != 0:
135
+ return "failed"
136
+ else:
137
+ return "successful"
138
+
139
+ @property
140
+ def stdout(self) -> str:
141
+ """
142
+ Returns the current stdout of the run. If the run is finished, this will
143
+ contain the entire stdout output. Otherwise, it will contain the
144
+ stdout up until this point.
145
+
146
+ Returns
147
+ -------
148
+ str
149
+ The current snapshot of stdout.
150
+ """
151
+ with open(
152
+ self.command_obj.log_files.get("stdout"), "r", encoding="utf-8"
153
+ ) as fp:
154
+ return fp.read()
155
+
156
+ @property
157
+ def stderr(self) -> str:
158
+ """
159
+ Returns the current stderr of the run. If the run is finished, this will
160
+ contain the entire stderr output. Otherwise, it will contain the
161
+ stderr up until this point.
162
+
163
+ Returns
164
+ -------
165
+ str
166
+ The current snapshot of stderr.
167
+ """
168
+ with open(
169
+ self.command_obj.log_files.get("stderr"), "r", encoding="utf-8"
170
+ ) as fp:
171
+ return fp.read()
172
+
173
+ async def stream_log(
174
+ self, stream: str, position: Optional[int] = None
175
+ ) -> Iterator[Tuple[int, str]]:
176
+ """
177
+ Asynchronous iterator to stream logs from the subprocess line by line.
178
+
179
+ Note that this method is asynchronous and needs to be `await`ed.
180
+
181
+ Parameters
182
+ ----------
183
+ stream : str
184
+ The stream to stream logs from. Can be one of `stdout` or `stderr`.
185
+ position : int, optional, default None
186
+ The position in the log file to start streaming from. If None, it starts
187
+ from the beginning of the log file. This allows resuming streaming from
188
+ a previously known position
189
+
190
+ Yields
191
+ ------
192
+ Tuple[int, str]
193
+ A tuple containing the position in the log file and the line read. The
194
+ position returned can be used to feed into another `stream_logs` call
195
+ for example.
196
+ """
197
+ async for position, line in self.command_obj.stream_log(stream, position):
198
+ yield position, line
199
+
200
+
201
+ class ExecutingTask(ExecutingProcess):
202
+ """
203
+ This class contains a reference to a `metaflow.Task` object representing
204
+ the currently executing or finished task, as well as metadata related
205
+ to the process.
206
+ `ExecutingTask` is returned by methods in `Runner` and `NBRunner`. It is not
207
+ meant to be instantiated directly.
208
+ This class works as a context manager, allowing you to use a pattern like
209
+ ```python
210
+ with Runner(...).spin() as running:
211
+ ...
212
+ ```
213
+ Note that you should use either this object as the context manager or
214
+ `Runner`, not both in a nested manner.
215
+ """
216
+
217
+ def __init__(
218
+ self, runner: "Runner", command_obj: CommandManager, task_obj: Task
219
+ ) -> None:
220
+ """
221
+ Create a new ExecutingTask -- this should not be done by the user directly but
222
+ instead use Runner.spin()
223
+ Parameters
224
+ ----------
225
+ runner : Runner
226
+ Parent runner for this task.
227
+ command_obj : CommandManager
228
+ CommandManager containing the subprocess executing this task.
229
+ task_obj : Task
230
+ Task object corresponding to this task.
231
+ """
232
+ super().__init__(runner, command_obj)
233
+ self.task = task_obj
234
+
235
+
236
+ class ExecutingRun(ExecutingProcess):
237
+ """
238
+ This class contains a reference to a `metaflow.Run` object representing
239
+ the currently executing or finished run, as well as metadata related
240
+ to the process.
241
+ `ExecutingRun` is returned by methods in `Runner` and `NBRunner`. It is not
242
+ meant to be instantiated directly.
243
+ This class works as a context manager, allowing you to use a pattern like
244
+ ```python
245
+ with Runner(...).run() as running:
246
+ ...
247
+ ```
248
+ Note that you should use either this object as the context manager or
249
+ `Runner`, not both in a nested manner.
250
+ """
251
+
252
+ def __init__(
253
+ self, runner: "Runner", command_obj: CommandManager, run_obj: Run
254
+ ) -> None:
255
+ """
256
+ Create a new ExecutingRun -- this should not be done by the user directly but
257
+ instead use Runner.run()
258
+ Parameters
259
+ ----------
260
+ runner : Runner
261
+ Parent runner for this run.
262
+ command_obj : CommandManager
263
+ CommandManager containing the subprocess executing this run.
264
+ run_obj : Run
265
+ Run object corresponding to this run.
266
+ """
267
+ super().__init__(runner, command_obj)
268
+ self.run = run_obj
269
+
270
+
271
+ class RunnerMeta(type):
272
+ def __new__(mcs, name, bases, dct):
273
+ cls = super().__new__(mcs, name, bases, dct)
274
+
275
+ def _injected_method(subcommand_name, runner_subcommand):
276
+ def f(self, *args, **kwargs):
277
+ return runner_subcommand(self, *args, **kwargs)
278
+
279
+ f.__doc__ = runner_subcommand.__init__.__doc__ or ""
280
+ f.__name__ = subcommand_name
281
+ sig = inspect.signature(runner_subcommand)
282
+ # We take all the same parameters except replace the first with
283
+ # simple "self"
284
+ new_parameters = {}
285
+ for name, param in sig.parameters.items():
286
+ if new_parameters:
287
+ new_parameters[name] = param
288
+ else:
289
+ new_parameters["self"] = inspect.Parameter(
290
+ "self", inspect.Parameter.POSITIONAL_OR_KEYWORD
291
+ )
292
+ f.__signature__ = inspect.Signature(
293
+ list(new_parameters.values()), return_annotation=runner_subcommand
294
+ )
295
+
296
+ return f
297
+
298
+ for runner_subcommand in get_runner_cli():
299
+ method_name = runner_subcommand.name.replace("-", "_")
300
+ setattr(cls, method_name, _injected_method(method_name, runner_subcommand))
301
+
302
+ return cls
303
+
304
+
305
+ class Runner(metaclass=RunnerMeta):
306
+ """
307
+ Metaflow's Runner API that presents a programmatic interface
308
+ to run flows and perform other operations either synchronously or asynchronously.
309
+ The class expects a path to the flow file along with optional arguments
310
+ that match top-level options on the command-line.
311
+
312
+ This class works as a context manager, calling `cleanup()` to remove
313
+ temporary files at exit.
314
+
315
+ Example:
316
+ ```python
317
+ with Runner('slowflow.py', pylint=False) as runner:
318
+ result = runner.run(alpha=5, tags=["abc", "def"], max_workers=5)
319
+ print(result.run.finished)
320
+ ```
321
+
322
+ Parameters
323
+ ----------
324
+ flow_file : str
325
+ Path to the flow file to run, relative to current directory.
326
+ show_output : bool, default True
327
+ Show the 'stdout' and 'stderr' to the console by default,
328
+ Only applicable for synchronous 'run' and 'resume' functions.
329
+ profile : str, optional, default None
330
+ Metaflow profile to use to run this run. If not specified, the default
331
+ profile is used (or the one already set using `METAFLOW_PROFILE`)
332
+ env : Dict[str, str], optional, default None
333
+ Additional environment variables to set for the Run. This overrides the
334
+ environment set for this process.
335
+ cwd : str, optional, default None
336
+ The directory to run the subprocess in; if not specified, the current
337
+ directory is used.
338
+ file_read_timeout : int, default 3600
339
+ The timeout until which we try to read the runner attribute file (in seconds).
340
+ **kwargs : Any
341
+ Additional arguments that you would pass to `python myflow.py` before
342
+ the `run` command.
343
+ """
344
+
345
+ def __init__(
346
+ self,
347
+ flow_file: str,
348
+ show_output: bool = True,
349
+ profile: Optional[str] = None,
350
+ env: Optional[Dict[str, str]] = None,
351
+ cwd: Optional[str] = None,
352
+ file_read_timeout: int = 3600,
353
+ **kwargs,
354
+ ):
355
+ # these imports are required here and not at the top
356
+ # since they interfere with the user defined Parameters
357
+ # in the flow file, this is related to the ability of
358
+ # importing 'Runner' directly i.e.
359
+ # from metaflow import Runner
360
+ # This ability is made possible by the statement:
361
+ # 'from .metaflow_runner import Runner' in '__init__.py'
362
+
363
+ from metaflow.parameters import flow_context
364
+
365
+ # Reload the CLI with an "empty" flow -- this will remove any configuration
366
+ # and parameter options. They are re-added in from_cli (called below).
367
+ to_reload = [
368
+ "metaflow.cli",
369
+ "metaflow.cli_components.run_cmds",
370
+ "metaflow.cli_components.init_cmd",
371
+ ]
372
+ with flow_context(None):
373
+ [
374
+ importlib.reload(sys.modules[module])
375
+ for module in to_reload
376
+ if module in sys.modules
377
+ ]
378
+
379
+ from metaflow.cli import start
380
+ from metaflow.runner.click_api import MetaflowAPI
381
+
382
+ # Convert flow_file to absolute path if it's relative
383
+ if not os.path.isabs(flow_file):
384
+ self.flow_file = os.path.abspath(flow_file)
385
+ else:
386
+ self.flow_file = flow_file
387
+
388
+ self.show_output = show_output
389
+
390
+ self.env_vars = os.environ.copy()
391
+ self.env_vars.update(env or {})
392
+ if profile:
393
+ self.env_vars["METAFLOW_PROFILE"] = profile
394
+
395
+ self.cwd = cwd or os.getcwd()
396
+ self.file_read_timeout = file_read_timeout
397
+ self.spm = SubprocessManager()
398
+ self.top_level_kwargs = kwargs
399
+ self.api = MetaflowAPI.from_cli(self.flow_file, start)
400
+
401
+ def __enter__(self) -> "Runner":
402
+ return self
403
+
404
+ async def __aenter__(self) -> "Runner":
405
+ return self
406
+
407
+ def __get_executing_run(self, attribute_file_fd, command_obj):
408
+ content = handle_timeout(attribute_file_fd, command_obj, self.file_read_timeout)
409
+
410
+ command_obj.sync_wait()
411
+
412
+ content = json.loads(content)
413
+ pathspec = "%s/%s" % (content.get("flow_name"), content.get("run_id"))
414
+
415
+ # Set the correct metadata from the runner_attribute file corresponding to this run.
416
+ metadata_for_flow = content.get("metadata")
417
+
418
+ run_object = Run(
419
+ pathspec, _namespace_check=False, _current_metadata=metadata_for_flow
420
+ )
421
+ return ExecutingRun(self, command_obj, run_object)
422
+
423
+ async def __async_get_executing_run(self, attribute_file_fd, command_obj):
424
+ content = await async_handle_timeout(
425
+ attribute_file_fd, command_obj, self.file_read_timeout
426
+ )
427
+ content = json.loads(content)
428
+ pathspec = "%s/%s" % (content.get("flow_name"), content.get("run_id"))
429
+
430
+ # Set the correct metadata from the runner_attribute file corresponding to this run.
431
+ metadata_for_flow = content.get("metadata")
432
+
433
+ run_object = Run(
434
+ pathspec, _namespace_check=False, _current_metadata=metadata_for_flow
435
+ )
436
+ return ExecutingRun(self, command_obj, run_object)
437
+
438
+ def run(self, **kwargs) -> ExecutingRun:
439
+ """
440
+ Blocking execution of the run. This method will wait until
441
+ the run has completed execution.
442
+
443
+ Parameters
444
+ ----------
445
+ **kwargs : Any
446
+ Additional arguments that you would pass to `python myflow.py` after
447
+ the `run` command, in particular, any parameters accepted by the flow.
448
+
449
+ Returns
450
+ -------
451
+ ExecutingRun
452
+ ExecutingRun containing the results of the run.
453
+ """
454
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
455
+ if CLICK_API_PROCESS_CONFIG:
456
+ with with_dir(self.cwd):
457
+ command = self.api(**self.top_level_kwargs).run(
458
+ runner_attribute_file=attribute_file_path, **kwargs
459
+ )
460
+ else:
461
+ command = self.api(**self.top_level_kwargs).run(
462
+ runner_attribute_file=attribute_file_path, **kwargs
463
+ )
464
+
465
+ pid = self.spm.run_command(
466
+ [sys.executable, *command],
467
+ env=self.env_vars,
468
+ cwd=self.cwd,
469
+ show_output=self.show_output,
470
+ )
471
+ command_obj = self.spm.get(pid)
472
+
473
+ return self.__get_executing_run(attribute_file_fd, command_obj)
474
+
475
+ def __get_executing_task(self, attribute_file_fd, command_obj):
476
+ content = handle_timeout(attribute_file_fd, command_obj, self.file_read_timeout)
477
+
478
+ command_obj.sync_wait()
479
+
480
+ content = json.loads(content)
481
+ pathspec = f"{content.get('flow_name')}/{content.get('run_id')}/{content.get('step_name')}/{content.get('task_id')}"
482
+
483
+ # Set the correct metadata from the runner_attribute file corresponding to this run.
484
+ metadata_for_flow = content.get("metadata")
485
+
486
+ task_object = Task(
487
+ pathspec, _namespace_check=False, _current_metadata=metadata_for_flow
488
+ )
489
+ return ExecutingTask(self, command_obj, task_object)
490
+
491
+ async def __async_get_executing_task(self, attribute_file_fd, command_obj):
492
+ content = await async_handle_timeout(
493
+ attribute_file_fd, command_obj, self.file_read_timeout
494
+ )
495
+ content = json.loads(content)
496
+ pathspec = f"{content.get('flow_name')}/{content.get('run_id')}/{content.get('step_name')}/{content.get('task_id')}"
497
+
498
+ # Set the correct metadata from the runner_attribute file corresponding to this run.
499
+ metadata_for_flow = content.get("metadata")
500
+
501
+ task_object = Task(
502
+ pathspec, _namespace_check=False, _current_metadata=metadata_for_flow
503
+ )
504
+ return ExecutingTask(self, command_obj, task_object)
505
+
506
+ def spin(self, pathspec, **kwargs) -> ExecutingTask:
507
+ """
508
+ Blocking spin execution of the run.
509
+ This method will wait until the spun run has completed execution.
510
+ Parameters
511
+ ----------
512
+ pathspec : str
513
+ The pathspec of the step/task to spin.
514
+ **kwargs : Any
515
+ Additional arguments that you would pass to `python ./myflow.py` after
516
+ the `spin` command.
517
+ Returns
518
+ -------
519
+ ExecutingTask
520
+ ExecutingTask containing the results of the spun task.
521
+ """
522
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
523
+ if CLICK_API_PROCESS_CONFIG:
524
+ with with_dir(self.cwd):
525
+ command = self.api(**self.top_level_kwargs).spin(
526
+ pathspec=pathspec,
527
+ runner_attribute_file=attribute_file_path,
528
+ **kwargs,
529
+ )
530
+ else:
531
+ command = self.api(**self.top_level_kwargs).spin(
532
+ pathspec=pathspec,
533
+ runner_attribute_file=attribute_file_path,
534
+ **kwargs,
535
+ )
536
+
537
+ pid = self.spm.run_command(
538
+ [sys.executable, *command],
539
+ env=self.env_vars,
540
+ cwd=self.cwd,
541
+ show_output=self.show_output,
542
+ )
543
+ command_obj = self.spm.get(pid)
544
+
545
+ return self.__get_executing_task(attribute_file_fd, command_obj)
546
+
547
+ def resume(self, **kwargs) -> ExecutingRun:
548
+ """
549
+ Blocking resume execution of the run.
550
+ This method will wait until the resumed run has completed execution.
551
+
552
+ Parameters
553
+ ----------
554
+ **kwargs : Any
555
+ Additional arguments that you would pass to `python ./myflow.py` after
556
+ the `resume` command.
557
+
558
+ Returns
559
+ -------
560
+ ExecutingRun
561
+ ExecutingRun containing the results of the resumed run.
562
+ """
563
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
564
+ if CLICK_API_PROCESS_CONFIG:
565
+ with with_dir(self.cwd):
566
+ command = self.api(**self.top_level_kwargs).resume(
567
+ runner_attribute_file=attribute_file_path, **kwargs
568
+ )
569
+ else:
570
+ command = self.api(**self.top_level_kwargs).resume(
571
+ runner_attribute_file=attribute_file_path, **kwargs
572
+ )
573
+
574
+ pid = self.spm.run_command(
575
+ [sys.executable, *command],
576
+ env=self.env_vars,
577
+ cwd=self.cwd,
578
+ show_output=self.show_output,
579
+ )
580
+ command_obj = self.spm.get(pid)
581
+
582
+ return self.__get_executing_run(attribute_file_fd, command_obj)
583
+
584
+ async def async_run(self, **kwargs) -> ExecutingRun:
585
+ """
586
+ Non-blocking execution of the run. This method will return as soon as the
587
+ run has launched.
588
+
589
+ Note that this method is asynchronous and needs to be `await`ed.
590
+
591
+ Parameters
592
+ ----------
593
+ **kwargs : Any
594
+ Additional arguments that you would pass to `python myflow.py` after
595
+ the `run` command, in particular, any parameters accepted by the flow.
596
+
597
+ Returns
598
+ -------
599
+ ExecutingRun
600
+ ExecutingRun representing the run that was started.
601
+ """
602
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
603
+ if CLICK_API_PROCESS_CONFIG:
604
+ with with_dir(self.cwd):
605
+ command = self.api(**self.top_level_kwargs).run(
606
+ runner_attribute_file=attribute_file_path, **kwargs
607
+ )
608
+ else:
609
+ command = self.api(**self.top_level_kwargs).run(
610
+ runner_attribute_file=attribute_file_path, **kwargs
611
+ )
612
+
613
+ pid = await self.spm.async_run_command(
614
+ [sys.executable, *command],
615
+ env=self.env_vars,
616
+ cwd=self.cwd,
617
+ )
618
+ command_obj = self.spm.get(pid)
619
+
620
+ return await self.__async_get_executing_run(attribute_file_fd, command_obj)
621
+
622
+ async def async_resume(self, **kwargs) -> ExecutingRun:
623
+ """
624
+ Non-blocking resume execution of the run.
625
+ This method will return as soon as the resume has launched.
626
+
627
+ Note that this method is asynchronous and needs to be `await`ed.
628
+
629
+ Parameters
630
+ ----------
631
+ **kwargs : Any
632
+ Additional arguments that you would pass to `python myflow.py` after
633
+ the `resume` command.
634
+
635
+ Returns
636
+ -------
637
+ ExecutingRun
638
+ ExecutingRun representing the resumed run that was started.
639
+ """
640
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
641
+ if CLICK_API_PROCESS_CONFIG:
642
+ with with_dir(self.cwd):
643
+ command = self.api(**self.top_level_kwargs).resume(
644
+ runner_attribute_file=attribute_file_path, **kwargs
645
+ )
646
+ else:
647
+ command = self.api(**self.top_level_kwargs).resume(
648
+ runner_attribute_file=attribute_file_path, **kwargs
649
+ )
650
+
651
+ pid = await self.spm.async_run_command(
652
+ [sys.executable, *command],
653
+ env=self.env_vars,
654
+ cwd=self.cwd,
655
+ )
656
+ command_obj = self.spm.get(pid)
657
+
658
+ return await self.__async_get_executing_run(attribute_file_fd, command_obj)
659
+
660
+ async def async_spin(self, pathspec, **kwargs) -> ExecutingTask:
661
+ """
662
+ Non-blocking spin execution of the run.
663
+ This method will return as soon as the spun task has launched.
664
+
665
+ Note that this method is asynchronous and needs to be `await`ed.
666
+
667
+ Parameters
668
+ ----------
669
+ pathspec : str
670
+ The pathspec of the step/task to spin.
671
+ **kwargs : Any
672
+ Additional arguments that you would pass to `python ./myflow.py` after
673
+ the `spin` command.
674
+
675
+ Returns
676
+ -------
677
+ ExecutingTask
678
+ ExecutingTask representing the spun task that was started.
679
+ """
680
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
681
+ if CLICK_API_PROCESS_CONFIG:
682
+ with with_dir(self.cwd):
683
+ command = self.api(**self.top_level_kwargs).spin(
684
+ pathspec=pathspec,
685
+ runner_attribute_file=attribute_file_path,
686
+ **kwargs,
687
+ )
688
+ else:
689
+ command = self.api(**self.top_level_kwargs).spin(
690
+ pathspec=pathspec,
691
+ runner_attribute_file=attribute_file_path,
692
+ **kwargs,
693
+ )
694
+
695
+ pid = await self.spm.async_run_command(
696
+ [sys.executable, *command],
697
+ env=self.env_vars,
698
+ cwd=self.cwd,
699
+ )
700
+ command_obj = self.spm.get(pid)
701
+
702
+ return await self.__async_get_executing_task(attribute_file_fd, command_obj)
703
+
704
+ def __exit__(self, exc_type, exc_value, traceback):
705
+ self.spm.cleanup()
706
+
707
+ async def __aexit__(self, exc_type, exc_value, traceback):
708
+ self.spm.cleanup()
709
+
710
+ def cleanup(self):
711
+ """
712
+ Delete any temporary files created during execution.
713
+ """
714
+ self.spm.cleanup()