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,470 @@
1
+ import os
2
+ import json
3
+ import time
4
+
5
+ from typing import ClassVar, Dict, Optional, TYPE_CHECKING
6
+
7
+ from metaflow.exception import MetaflowNotFound
8
+ from metaflow.metaflow_config import DEFAULT_FROM_DEPLOYMENT_IMPL
9
+
10
+
11
+ def generate_fake_flow_file_contents(
12
+ flow_name: str, param_info: dict, project_name: Optional[str] = None
13
+ ):
14
+ params_code = ""
15
+ for _, param_details in param_info.items():
16
+ param_python_var_name = param_details.get(
17
+ "python_var_name", param_details["name"]
18
+ )
19
+ param_name = param_details["name"]
20
+ param_type = param_details["type"]
21
+ param_help = param_details["description"]
22
+ param_required = param_details["is_required"]
23
+
24
+ if param_type == "JSON":
25
+ params_code += (
26
+ f" {param_python_var_name} = Parameter('{param_name}', "
27
+ f"type=JSONType, help='''{param_help}''', required={param_required})\n"
28
+ )
29
+ elif param_type == "FilePath":
30
+ is_text = param_details.get("is_text", True)
31
+ encoding = param_details.get("encoding", "utf-8")
32
+ params_code += (
33
+ f" {param_python_var_name} = IncludeFile('{param_name}', "
34
+ f"is_text={is_text}, encoding='{encoding}', help='''{param_help}''', "
35
+ f"required={param_required})\n"
36
+ )
37
+ else:
38
+ params_code += (
39
+ f" {param_python_var_name} = Parameter('{param_name}', "
40
+ f"type={param_type}, help='''{param_help}''', required={param_required})\n"
41
+ )
42
+
43
+ project_decorator = f"@project(name='{project_name}')\n" if project_name else ""
44
+
45
+ contents = f"""\
46
+ from metaflow import FlowSpec, Parameter, IncludeFile, JSONType, step, project
47
+ {project_decorator}class {flow_name}(FlowSpec):
48
+ {params_code}
49
+ @step
50
+ def start(self):
51
+ self.next(self.end)
52
+ @step
53
+ def end(self):
54
+ pass
55
+ if __name__ == '__main__':
56
+ {flow_name}()
57
+ """
58
+ return contents
59
+
60
+
61
+ if TYPE_CHECKING:
62
+ import metaflow
63
+ import metaflow.runner.deployer_impl
64
+
65
+
66
+ class DeployerMeta(type):
67
+ def __new__(mcs, name, bases, dct):
68
+ cls = super().__new__(mcs, name, bases, dct)
69
+
70
+ from metaflow.plugins import DEPLOYER_IMPL_PROVIDERS
71
+
72
+ def _injected_method(method_name, deployer_class):
73
+ def f(self, **deployer_kwargs):
74
+ return deployer_class(
75
+ deployer_kwargs=deployer_kwargs,
76
+ flow_file=self.flow_file,
77
+ show_output=self.show_output,
78
+ profile=self.profile,
79
+ env=self.env,
80
+ cwd=self.cwd,
81
+ file_read_timeout=self.file_read_timeout,
82
+ **self.top_level_kwargs,
83
+ )
84
+
85
+ f.__doc__ = provider_class.__doc__ or ""
86
+ f.__name__ = method_name
87
+ return f
88
+
89
+ for provider_class in DEPLOYER_IMPL_PROVIDERS:
90
+ # TYPE is the name of the CLI groups i.e.
91
+ # `argo-workflows` instead of `argo_workflows`
92
+ # The injected method names replace '-' by '_' though.
93
+ method_name = provider_class.TYPE.replace("-", "_")
94
+ setattr(cls, method_name, _injected_method(method_name, provider_class))
95
+
96
+ return cls
97
+
98
+
99
+ class Deployer(metaclass=DeployerMeta):
100
+ """
101
+ Use the `Deployer` class to configure and access one of the production
102
+ orchestrators supported by Metaflow.
103
+
104
+ Parameters
105
+ ----------
106
+ flow_file : str
107
+ Path to the flow file to deploy, relative to current directory.
108
+ show_output : bool, default True
109
+ Show the 'stdout' and 'stderr' to the console by default.
110
+ profile : Optional[str], default None
111
+ Metaflow profile to use for the deployment. If not specified, the default
112
+ profile is used.
113
+ env : Optional[Dict[str, str]], default None
114
+ Additional environment variables to set for the deployment.
115
+ cwd : Optional[str], default None
116
+ The directory to run the subprocess in; if not specified, the current
117
+ directory is used.
118
+ file_read_timeout : int, default 3600
119
+ The timeout until which we try to read the deployer attribute file (in seconds).
120
+ **kwargs : Any
121
+ Additional arguments that you would pass to `python myflow.py` before
122
+ the deployment command.
123
+ """
124
+
125
+ def __init__(
126
+ self,
127
+ flow_file: str,
128
+ show_output: bool = True,
129
+ profile: Optional[str] = None,
130
+ env: Optional[Dict] = None,
131
+ cwd: Optional[str] = None,
132
+ file_read_timeout: int = 3600,
133
+ **kwargs,
134
+ ):
135
+ # Convert flow_file to absolute path if it's relative
136
+ if not os.path.isabs(flow_file):
137
+ self.flow_file = os.path.abspath(flow_file)
138
+ else:
139
+ self.flow_file = flow_file
140
+
141
+ self.show_output = show_output
142
+ self.profile = profile
143
+ self.env = env
144
+ self.cwd = cwd
145
+ self.file_read_timeout = file_read_timeout
146
+ self.top_level_kwargs = kwargs
147
+
148
+
149
+ class TriggeredRun(object):
150
+ """
151
+ TriggeredRun class represents a run that has been triggered on a
152
+ production orchestrator.
153
+ """
154
+
155
+ def __init__(
156
+ self,
157
+ deployer: "metaflow.runner.deployer_impl.DeployerImpl",
158
+ content: str,
159
+ ):
160
+ self.deployer = deployer
161
+ content_json = json.loads(content)
162
+ self.metadata_for_flow = content_json.get("metadata")
163
+ self.pathspec = content_json.get("pathspec")
164
+ self.name = content_json.get("name")
165
+
166
+ def wait_for_run(self, check_interval: int = 5, timeout: Optional[int] = None):
167
+ """
168
+ Wait for the `run` property to become available.
169
+
170
+ The `run` property becomes available only after the `start` task of the triggered
171
+ flow starts running.
172
+
173
+ Parameters
174
+ ----------
175
+ check_interval: int, default: 5
176
+ Frequency of checking for the `run` to become available, in seconds.
177
+ timeout : int, optional, default None
178
+ Maximum time to wait for the `run` to become available, in seconds. If
179
+ None, wait indefinitely.
180
+
181
+ Raises
182
+ ------
183
+ TimeoutError
184
+ If the `run` is not available within the specified timeout.
185
+ """
186
+ start_time = time.time()
187
+ while True:
188
+ if self.run is not None:
189
+ return self.run
190
+
191
+ if timeout is not None and (time.time() - start_time) > timeout:
192
+ raise TimeoutError(
193
+ "Timed out waiting for the run object to become available."
194
+ )
195
+
196
+ time.sleep(check_interval)
197
+
198
+ @property
199
+ def run(self) -> Optional["metaflow.Run"]:
200
+ """
201
+ Retrieve the `Run` object for the triggered run.
202
+
203
+ Note that Metaflow `Run` becomes available only when the `start` task
204
+ has started executing.
205
+
206
+ Returns
207
+ -------
208
+ Run, optional
209
+ Metaflow Run object if the `start` step has started executing, otherwise None.
210
+ """
211
+ from metaflow import Run
212
+
213
+ try:
214
+ return Run(self.pathspec, _namespace_check=False)
215
+ except MetaflowNotFound:
216
+ return None
217
+
218
+
219
+ class DeployedFlowMeta(type):
220
+ def __new__(mcs, name, bases, dct):
221
+ cls = super().__new__(mcs, name, bases, dct)
222
+ if not bases:
223
+ # Inject methods only in DeployedFlow and not any of its
224
+ # subclasses
225
+ from metaflow.plugins import DEPLOYER_IMPL_PROVIDERS
226
+
227
+ allowed_providers = dict(
228
+ {
229
+ provider.TYPE.replace("-", "_"): provider
230
+ for provider in DEPLOYER_IMPL_PROVIDERS
231
+ }
232
+ )
233
+
234
+ def _get_triggered_run_injected_method():
235
+ def f(
236
+ cls,
237
+ identifier: str,
238
+ run_id: str,
239
+ metadata: Optional[str] = None,
240
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
241
+ ) -> "TriggeredRun":
242
+ """
243
+ Retrieves a `TriggeredRun` object from an identifier, a run id and optional
244
+ metadata. The `impl` parameter specifies the deployer implementation
245
+ to use (like `argo-workflows`).
246
+
247
+ Parameters
248
+ ----------
249
+ identifier : str
250
+ Deployer specific identifier for the workflow to retrieve
251
+ run_id : str
252
+ Run ID for the which to fetch the triggered run object
253
+ metadata : str, optional, default None
254
+ Optional deployer specific metadata.
255
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
256
+ The default implementation to use if not specified
257
+
258
+ Returns
259
+ -------
260
+ TriggeredRun
261
+ A `TriggeredRun` object representing the triggered run corresponding
262
+ to the identifier and the run id.
263
+ """
264
+ if impl in allowed_providers:
265
+ return (
266
+ allowed_providers[impl]
267
+ .deployed_flow_type()
268
+ .get_triggered_run(identifier, run_id, metadata)
269
+ )
270
+ else:
271
+ raise ValueError(
272
+ f"No deployer '{impl}' exists; valid deployers are: "
273
+ f"{list(allowed_providers.keys())}"
274
+ )
275
+
276
+ f.__name__ = "get_triggered_run"
277
+ return f
278
+
279
+ def _per_type_get_triggered_run_injected_method(method_name, impl):
280
+ def f(
281
+ cls,
282
+ identifier: str,
283
+ run_id: str,
284
+ metadata: Optional[str] = None,
285
+ ):
286
+ return (
287
+ allowed_providers[impl]
288
+ .deployed_flow_type()
289
+ .get_triggered_run(identifier, run_id, metadata)
290
+ )
291
+
292
+ f.__name__ = method_name
293
+ return f
294
+
295
+ def _from_deployment_injected_method():
296
+ def f(
297
+ cls,
298
+ identifier: str,
299
+ metadata: Optional[str] = None,
300
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
301
+ ) -> "DeployedFlow":
302
+ """
303
+ Retrieves a `DeployedFlow` object from an identifier and optional
304
+ metadata. The `impl` parameter specifies the deployer implementation
305
+ to use (like `argo-workflows`).
306
+
307
+ Parameters
308
+ ----------
309
+ identifier : str
310
+ Deployer specific identifier for the workflow to retrieve
311
+ metadata : str, optional, default None
312
+ Optional deployer specific metadata.
313
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
314
+ The default implementation to use if not specified
315
+
316
+ Returns
317
+ -------
318
+ DeployedFlow
319
+ A `DeployedFlow` object representing the deployed flow corresponding
320
+ to the identifier
321
+ """
322
+ if impl in allowed_providers:
323
+ return (
324
+ allowed_providers[impl]
325
+ .deployed_flow_type()
326
+ .from_deployment(identifier, metadata)
327
+ )
328
+ else:
329
+ raise ValueError(
330
+ f"No deployer '{impl}' exists; valid deployers are: "
331
+ f"{list(allowed_providers.keys())}"
332
+ )
333
+
334
+ f.__name__ = "from_deployment"
335
+ return f
336
+
337
+ def _per_type_from_deployment_injected_method(method_name, impl):
338
+ def f(
339
+ cls,
340
+ identifier: str,
341
+ metadata: Optional[str] = None,
342
+ ):
343
+ return (
344
+ allowed_providers[impl]
345
+ .deployed_flow_type()
346
+ .from_deployment(identifier, metadata)
347
+ )
348
+
349
+ f.__name__ = method_name
350
+ return f
351
+
352
+ def _list_deployed_flows_injected_method():
353
+ def f(
354
+ cls,
355
+ flow_name: Optional[str] = None,
356
+ impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
357
+ ):
358
+ """
359
+ List all deployed flows for the specified implementation.
360
+
361
+ Parameters
362
+ ----------
363
+ flow_name : str, optional, default None
364
+ If specified, only list deployed flows for this specific flow name.
365
+ If None, list all deployed flows.
366
+ impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
367
+ The default implementation to use if not specified
368
+
369
+ Yields
370
+ ------
371
+ DeployedFlow
372
+ `DeployedFlow` objects representing deployed flows.
373
+ """
374
+ if impl in allowed_providers:
375
+ return (
376
+ allowed_providers[impl]
377
+ .deployed_flow_type()
378
+ .list_deployed_flows(flow_name)
379
+ )
380
+ else:
381
+ raise ValueError(
382
+ f"No deployer '{impl}' exists; valid deployers are: "
383
+ f"{list(allowed_providers.keys())}"
384
+ )
385
+
386
+ f.__name__ = "list_deployed_flows"
387
+ return f
388
+
389
+ def _per_type_list_deployed_flows_injected_method(method_name, impl):
390
+ def f(
391
+ cls,
392
+ flow_name: Optional[str] = None,
393
+ ):
394
+ return (
395
+ allowed_providers[impl]
396
+ .deployed_flow_type()
397
+ .list_deployed_flows(flow_name)
398
+ )
399
+
400
+ f.__name__ = method_name
401
+ return f
402
+
403
+ setattr(
404
+ cls, "from_deployment", classmethod(_from_deployment_injected_method())
405
+ )
406
+ setattr(
407
+ cls,
408
+ "list_deployed_flows",
409
+ classmethod(_list_deployed_flows_injected_method()),
410
+ )
411
+ setattr(
412
+ cls,
413
+ "get_triggered_run",
414
+ classmethod(_get_triggered_run_injected_method()),
415
+ )
416
+
417
+ for impl in allowed_providers:
418
+ from_deployment_method_name = f"from_{impl}"
419
+ list_deployed_flows_method_name = f"list_{impl}"
420
+ get_triggered_run_method_name = f"get_triggered_{impl}_run"
421
+
422
+ setattr(
423
+ cls,
424
+ from_deployment_method_name,
425
+ classmethod(
426
+ _per_type_from_deployment_injected_method(
427
+ from_deployment_method_name, impl
428
+ )
429
+ ),
430
+ )
431
+
432
+ setattr(
433
+ cls,
434
+ list_deployed_flows_method_name,
435
+ classmethod(
436
+ _per_type_list_deployed_flows_injected_method(
437
+ list_deployed_flows_method_name, impl
438
+ )
439
+ ),
440
+ )
441
+
442
+ setattr(
443
+ cls,
444
+ get_triggered_run_method_name,
445
+ classmethod(
446
+ _per_type_get_triggered_run_injected_method(
447
+ get_triggered_run_method_name, impl
448
+ )
449
+ ),
450
+ )
451
+
452
+ return cls
453
+
454
+
455
+ class DeployedFlow(metaclass=DeployedFlowMeta):
456
+ """
457
+ DeployedFlow class represents a flow that has been deployed.
458
+
459
+ This class is not meant to be instantiated directly. Instead, it is returned from
460
+ methods of `Deployer`.
461
+ """
462
+
463
+ # This should match the TYPE value in DeployerImpl for proper stub generation
464
+ TYPE: ClassVar[Optional[str]] = None
465
+
466
+ def __init__(self, deployer: "metaflow.runner.deployer_impl.DeployerImpl"):
467
+ self.deployer = deployer
468
+ self.name = self.deployer.name
469
+ self.flow_name = self.deployer.flow_name
470
+ self.metadata = self.deployer.metadata
@@ -0,0 +1,201 @@
1
+ import importlib
2
+ import json
3
+ import os
4
+ import sys
5
+
6
+ from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type, List
7
+
8
+ from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
9
+
10
+ from .subprocess_manager import SubprocessManager
11
+ from .utils import get_lower_level_group, handle_timeout, temporary_fifo, with_dir
12
+
13
+ if TYPE_CHECKING:
14
+ import metaflow.runner.deployer
15
+
16
+ # NOTE: This file is separate from the deployer.py file to prevent circular imports.
17
+ # This file is needed in any of the DeployerImpl implementations
18
+ # (like argo_workflows_deployer.py) which is in turn needed to create the Deployer
19
+ # class (ie: it uses ArgoWorkflowsDeployer to create the Deployer class).
20
+
21
+
22
+ class DeployerImpl(object):
23
+ """
24
+ Base class for deployer implementations. Each implementation should define a TYPE
25
+ class variable that matches the name of the CLI group.
26
+
27
+ Parameters
28
+ ----------
29
+ flow_file : str
30
+ Path to the flow file to deploy, relative to current directory.
31
+ show_output : bool, default True
32
+ Show the 'stdout' and 'stderr' to the console by default.
33
+ profile : Optional[str], default None
34
+ Metaflow profile to use for the deployment. If not specified, the default
35
+ profile is used.
36
+ env : Optional[Dict], default None
37
+ Additional environment variables to set for the deployment.
38
+ cwd : Optional[str], default None
39
+ The directory to run the subprocess in; if not specified, the current
40
+ directory is used.
41
+ file_read_timeout : int, default 3600
42
+ The timeout until which we try to read the deployer attribute file (in seconds).
43
+ **kwargs : Any
44
+ Additional arguments that you would pass to `python myflow.py` before
45
+ the deployment command.
46
+ """
47
+
48
+ TYPE: ClassVar[Optional[str]] = None
49
+
50
+ def __init__(
51
+ self,
52
+ flow_file: str,
53
+ show_output: bool = True,
54
+ profile: Optional[str] = None,
55
+ env: Optional[Dict] = None,
56
+ cwd: Optional[str] = None,
57
+ file_read_timeout: int = 3600,
58
+ **kwargs
59
+ ):
60
+ if self.TYPE is None:
61
+ raise ValueError(
62
+ "DeployerImpl doesn't have a 'TYPE' to target. Please use a sub-class "
63
+ "of DeployerImpl."
64
+ )
65
+
66
+ from metaflow.parameters import flow_context
67
+
68
+ # Reload the CLI with an "empty" flow -- this will remove any configuration
69
+ # and parameter options. They are re-added in from_cli (called below).
70
+ with flow_context(None):
71
+ [
72
+ importlib.reload(sys.modules[module])
73
+ for module in self.to_reload
74
+ if module in sys.modules
75
+ ]
76
+
77
+ from metaflow.cli import start
78
+ from metaflow.runner.click_api import MetaflowAPI
79
+
80
+ # Convert flow_file to absolute path if it's relative
81
+ if not os.path.isabs(flow_file):
82
+ self.flow_file = os.path.abspath(flow_file)
83
+ else:
84
+ self.flow_file = flow_file
85
+ self.show_output = show_output
86
+ self.profile = profile
87
+ self.env = env
88
+ self.cwd = cwd or os.getcwd()
89
+ self.file_read_timeout = file_read_timeout
90
+
91
+ self.env_vars = os.environ.copy()
92
+ self.env_vars.update(self.env or {})
93
+ if self.profile:
94
+ self.env_vars["METAFLOW_PROFILE"] = profile
95
+
96
+ self.spm = SubprocessManager()
97
+ self.top_level_kwargs = kwargs
98
+ self.api = MetaflowAPI.from_cli(self.flow_file, start)
99
+
100
+ @property
101
+ def to_reload(self) -> List[str]:
102
+ """
103
+ List of modules to reload when the deployer is initialized.
104
+ This is used to ensure that the CLI is in a clean state before
105
+ deploying the flow.
106
+ """
107
+ return [
108
+ "metaflow.cli",
109
+ "metaflow.cli_components.run_cmds",
110
+ "metaflow.cli_components.init_cmd",
111
+ ]
112
+
113
+ @property
114
+ def deployer_kwargs(self) -> Dict[str, Any]:
115
+ raise NotImplementedError
116
+
117
+ @staticmethod
118
+ def deployed_flow_type() -> Type["metaflow.runner.deployer.DeployedFlow"]:
119
+ raise NotImplementedError
120
+
121
+ def __enter__(self) -> "DeployerImpl":
122
+ return self
123
+
124
+ def create(self, **kwargs) -> "metaflow.runner.deployer.DeployedFlow":
125
+ """
126
+ Create a sub-class of a `DeployedFlow` depending on the deployer implementation.
127
+
128
+ Parameters
129
+ ----------
130
+ **kwargs : Any
131
+ Additional arguments to pass to `create` corresponding to the
132
+ command line arguments of `create`
133
+
134
+ Returns
135
+ -------
136
+ DeployedFlow
137
+ DeployedFlow object representing the deployed flow.
138
+
139
+ Raises
140
+ ------
141
+ Exception
142
+ If there is an error during deployment.
143
+ """
144
+ # Sub-classes should implement this by simply calling _create and pass the
145
+ # proper class as the DeployedFlow to return.
146
+ raise NotImplementedError
147
+
148
+ def _create(
149
+ self, create_class: Type["metaflow.runner.deployer.DeployedFlow"], **kwargs
150
+ ) -> "metaflow.runner.deployer.DeployedFlow":
151
+ with temporary_fifo() as (attribute_file_path, attribute_file_fd):
152
+ # every subclass needs to have `self.deployer_kwargs`
153
+ # TODO: Get rid of CLICK_API_PROCESS_CONFIG in the near future
154
+ if CLICK_API_PROCESS_CONFIG:
155
+ # We need to run this in the cwd because configs depend on files
156
+ # that may be located in paths relative to the directory the user
157
+ # wants to run in
158
+ with with_dir(self.cwd):
159
+ command = get_lower_level_group(
160
+ self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
161
+ ).create(deployer_attribute_file=attribute_file_path, **kwargs)
162
+ else:
163
+ command = get_lower_level_group(
164
+ self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
165
+ ).create(deployer_attribute_file=attribute_file_path, **kwargs)
166
+
167
+ pid = self.spm.run_command(
168
+ [sys.executable, *command],
169
+ env=self.env_vars,
170
+ cwd=self.cwd,
171
+ show_output=self.show_output,
172
+ )
173
+
174
+ command_obj = self.spm.get(pid)
175
+ content = handle_timeout(
176
+ attribute_file_fd, command_obj, self.file_read_timeout
177
+ )
178
+ content = json.loads(content)
179
+ self.name = content.get("name")
180
+ self.flow_name = content.get("flow_name")
181
+ self.metadata = content.get("metadata")
182
+ # Additional info is used to pass additional deployer specific information.
183
+ # It is used in non-OSS deployers (extensions).
184
+ self.additional_info = content.get("additional_info", {})
185
+ command_obj.sync_wait()
186
+ if command_obj.process.returncode == 0:
187
+ return create_class(deployer=self)
188
+
189
+ raise RuntimeError("Error deploying %s to %s" % (self.flow_file, self.TYPE))
190
+
191
+ def __exit__(self, exc_type, exc_value, traceback):
192
+ """
193
+ Cleanup resources on exit.
194
+ """
195
+ self.cleanup()
196
+
197
+ def cleanup(self):
198
+ """
199
+ Cleanup resources.
200
+ """
201
+ self.spm.cleanup()