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,546 @@
1
+ import json
2
+
3
+ from functools import wraps
4
+
5
+ from metaflow._vendor import click
6
+
7
+ from .. import decorators, namespace, parameters, tracing
8
+ from ..exception import CommandException
9
+ from ..graph import FlowGraph
10
+ from ..metaflow_current import current
11
+ from ..metaflow_config import (
12
+ DEFAULT_DECOSPECS,
13
+ FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
14
+ SPIN_PERSIST,
15
+ )
16
+ from ..metaflow_profile import from_start
17
+ from ..package import MetaflowPackage
18
+ from ..runtime import NativeRuntime, SpinRuntime
19
+ from ..system import _system_logger
20
+
21
+ # from ..client.core import Run
22
+
23
+ from ..tagging_util import validate_tags
24
+ from ..util import get_latest_run_id, write_latest_run_id, parse_spin_pathspec
25
+
26
+
27
+ def before_run(obj, tags, decospecs, skip_decorators=False):
28
+ validate_tags(tags)
29
+
30
+ # There's a --with option both at the top-level and for the run/resume/spin
31
+ # subcommand. Why?
32
+ #
33
+ # "run --with shoes" looks so much better than "--with shoes run".
34
+ # This is a very common use case of --with.
35
+ #
36
+ # A downside is that we need to have the following decorators handling
37
+ # in two places in this module and make sure _init_step_decorators
38
+ # doesn't get called twice.
39
+
40
+ # We want the order to be the following:
41
+ # - run level decospecs
42
+ # - top level decospecs
43
+ # - environment decospecs
44
+ from_start(
45
+ f"Inside before_run, skip_decorators={skip_decorators}, is_spin={obj.is_spin}"
46
+ )
47
+ if not skip_decorators:
48
+ all_decospecs = (
49
+ list(decospecs or [])
50
+ + obj.tl_decospecs
51
+ + list(obj.environment.decospecs() or [])
52
+ )
53
+ if all_decospecs:
54
+ # These decospecs are the ones from run/resume/spin PLUS the ones from the
55
+ # environment (for example the @conda)
56
+ decorators._attach_decorators(obj.flow, all_decospecs)
57
+ decorators._init(obj.flow)
58
+ # Regenerate graph if we attached more decorators
59
+ obj.flow.__class__._init_graph()
60
+ obj.graph = obj.flow._graph
61
+
62
+ obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
63
+ # obj.environment.init_environment(obj.logger)
64
+
65
+ decorators._init_step_decorators(
66
+ obj.flow,
67
+ obj.graph,
68
+ obj.environment,
69
+ obj.flow_datastore,
70
+ obj.logger,
71
+ obj.is_spin,
72
+ skip_decorators,
73
+ )
74
+ # Re-read graph since it may have been modified by mutators
75
+ obj.graph = obj.flow._graph
76
+
77
+ obj.metadata.add_sticky_tags(tags=tags)
78
+
79
+ # Package working directory only once per run.
80
+ # We explicitly avoid doing this in `start` since it is invoked for every
81
+ # step in the run.
82
+ obj.package = MetaflowPackage(
83
+ obj.flow,
84
+ obj.environment,
85
+ obj.echo,
86
+ suffixes=obj.package_suffixes,
87
+ flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
88
+ )
89
+
90
+
91
+ def common_runner_options(func):
92
+ @click.option(
93
+ "--run-id-file",
94
+ default=None,
95
+ show_default=True,
96
+ type=str,
97
+ help="Write the ID of this run to the file specified.",
98
+ )
99
+ @click.option(
100
+ "--runner-attribute-file",
101
+ default=None,
102
+ show_default=True,
103
+ type=str,
104
+ help="Write the metadata and pathspec of this run to the file specified. Used internally "
105
+ "for Metaflow's Runner API.",
106
+ )
107
+ @wraps(func)
108
+ def wrapper(*args, **kwargs):
109
+ return func(*args, **kwargs)
110
+
111
+ return wrapper
112
+
113
+
114
+ def write_file(file_path, content):
115
+ if file_path is not None:
116
+ with open(file_path, "w", encoding="utf-8") as f:
117
+ f.write(str(content))
118
+
119
+
120
+ def config_callback(ctx, param, value):
121
+ # Callback to:
122
+ # - read the Click auto_envvar variable from both the
123
+ # environment AND the configuration
124
+ # - merge that value with the value passed in the command line (value)
125
+ # - return the value as a tuple
126
+ # Note that this function gets called even if there is no option passed on the
127
+ # command line.
128
+ # NOTE: Assumes that ctx.auto_envvar_prefix is set to METAFLOW (same as in
129
+ # from_conf)
130
+
131
+ # Read decospecs options from the environment (METAFLOW_DEFAULT_DECOSPECS=...)
132
+ # and merge them with the one provided as --with.
133
+ splits = DEFAULT_DECOSPECS.split()
134
+ return tuple(list(value) + splits)
135
+
136
+
137
+ def common_run_options(func):
138
+ @click.option(
139
+ "--tag",
140
+ "tags",
141
+ multiple=True,
142
+ default=None,
143
+ help="Annotate this run with the given tag. You can specify "
144
+ "this option multiple times to attach multiple tags in "
145
+ "the run.",
146
+ )
147
+ @click.option(
148
+ "--max-workers",
149
+ default=16,
150
+ show_default=True,
151
+ help="Maximum number of parallel processes.",
152
+ )
153
+ @click.option(
154
+ "--max-num-splits",
155
+ default=100,
156
+ show_default=True,
157
+ help="Maximum number of splits allowed in a foreach. This "
158
+ "is a safety check preventing bugs from triggering "
159
+ "thousands of steps inadvertently.",
160
+ )
161
+ @click.option(
162
+ "--max-log-size",
163
+ default=10,
164
+ show_default=True,
165
+ help="Maximum size of stdout and stderr captured in "
166
+ "megabytes. If a step outputs more than this to "
167
+ "stdout/stderr, its output will be truncated.",
168
+ )
169
+ @click.option(
170
+ "--with",
171
+ "decospecs",
172
+ multiple=True,
173
+ help="Add a decorator to all steps. You can specify this "
174
+ "option multiple times to attach multiple decorators "
175
+ "in steps.",
176
+ callback=config_callback,
177
+ )
178
+ @wraps(func)
179
+ def wrapper(*args, **kwargs):
180
+ return func(*args, **kwargs)
181
+
182
+ return wrapper
183
+
184
+
185
+ @click.option(
186
+ "--origin-run-id",
187
+ default=None,
188
+ help="ID of the run that should be resumed. By default, the "
189
+ "last run executed locally.",
190
+ )
191
+ @click.option(
192
+ "--run-id",
193
+ default=None,
194
+ help="Run ID for the new run. By default, a new run-id will be generated",
195
+ hidden=True,
196
+ )
197
+ @click.option(
198
+ "--clone-only/--no-clone-only",
199
+ default=False,
200
+ show_default=True,
201
+ help="Only clone tasks without continuing execution",
202
+ hidden=True,
203
+ )
204
+ @click.option(
205
+ "--reentrant/--no-reentrant",
206
+ default=False,
207
+ show_default=True,
208
+ hidden=True,
209
+ help="If specified, allows this call to be called in parallel",
210
+ )
211
+ @click.option(
212
+ "--resume-identifier",
213
+ default=None,
214
+ show_default=True,
215
+ hidden=True,
216
+ help="If specified, it identifies the task that started this resume call. It is in the form of {step_name}-{task_id}",
217
+ )
218
+ @click.argument("step-to-rerun", required=False)
219
+ @click.command(help="Resume execution of a previous run of this flow.")
220
+ @tracing.cli("cli/resume")
221
+ @common_run_options
222
+ @common_runner_options
223
+ @click.pass_obj
224
+ def resume(
225
+ obj,
226
+ tags=None,
227
+ step_to_rerun=None,
228
+ origin_run_id=None,
229
+ run_id=None,
230
+ clone_only=False,
231
+ reentrant=False,
232
+ max_workers=None,
233
+ max_num_splits=None,
234
+ max_log_size=None,
235
+ decospecs=None,
236
+ run_id_file=None,
237
+ resume_identifier=None,
238
+ runner_attribute_file=None,
239
+ ):
240
+ before_run(obj, tags, decospecs)
241
+
242
+ if origin_run_id is None:
243
+ origin_run_id = get_latest_run_id(obj.echo, obj.flow.name)
244
+ if origin_run_id is None:
245
+ raise CommandException(
246
+ "A previous run id was not found. Specify --origin-run-id."
247
+ )
248
+
249
+ if step_to_rerun is None:
250
+ steps_to_rerun = set()
251
+ else:
252
+ # validate step name
253
+ if step_to_rerun not in obj.graph.nodes:
254
+ raise CommandException(
255
+ "invalid step name {0} specified, must be step present in "
256
+ "current form of execution graph. Valid step names include: {1}".format(
257
+ step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
258
+ )
259
+ )
260
+
261
+ ## TODO: instead of checking execution path here, can add a warning later
262
+ ## instead of throwing an error. This is for resuming a step which was not
263
+ ## taken inside a branch i.e. not present in the execution path.
264
+
265
+ # origin_run = Run(f"{obj.flow.name}/{origin_run_id}", _namespace_check=False)
266
+ # executed_steps = {step.path_components[-1] for step in origin_run}
267
+ # if step_to_rerun not in executed_steps:
268
+ # raise CommandException(
269
+ # f"Cannot resume from step '{step_to_rerun}'. This step was not "
270
+ # f"part of the original execution path for run '{origin_run_id}'."
271
+ # )
272
+
273
+ steps_to_rerun = {step_to_rerun}
274
+
275
+ if run_id:
276
+ # Run-ids that are provided by the metadata service are always integers.
277
+ # External providers or run-ids (like external schedulers) always need to
278
+ # be non-integers to avoid any clashes. This condition ensures this.
279
+ try:
280
+ int(run_id)
281
+ except:
282
+ pass
283
+ else:
284
+ raise CommandException("run-id %s cannot be an integer" % run_id)
285
+
286
+ runtime = NativeRuntime(
287
+ obj.flow,
288
+ obj.graph,
289
+ obj.flow_datastore,
290
+ obj.metadata,
291
+ obj.environment,
292
+ obj.package,
293
+ obj.logger,
294
+ obj.entrypoint,
295
+ obj.event_logger,
296
+ obj.monitor,
297
+ run_id=run_id,
298
+ clone_run_id=origin_run_id,
299
+ clone_only=clone_only,
300
+ reentrant=reentrant,
301
+ steps_to_rerun=steps_to_rerun,
302
+ max_workers=max_workers,
303
+ max_num_splits=max_num_splits,
304
+ max_log_size=max_log_size * 1024 * 1024,
305
+ resume_identifier=resume_identifier,
306
+ )
307
+ write_file(run_id_file, runtime.run_id)
308
+ runtime.print_workflow_info()
309
+
310
+ runtime.persist_constants()
311
+
312
+ if runner_attribute_file:
313
+ with open(runner_attribute_file, "w", encoding="utf-8") as f:
314
+ json.dump(
315
+ {
316
+ "run_id": runtime.run_id,
317
+ "flow_name": obj.flow.name,
318
+ "metadata": obj.metadata.metadata_str(),
319
+ },
320
+ f,
321
+ )
322
+
323
+ # We may skip clone-only resume if this is not a resume leader,
324
+ # and clone is already complete.
325
+ if runtime.should_skip_clone_only_execution():
326
+ return
327
+
328
+ current._update_env(
329
+ {
330
+ "run_id": runtime.run_id,
331
+ }
332
+ )
333
+ _system_logger.log_event(
334
+ level="info",
335
+ module="metaflow.resume",
336
+ name="start",
337
+ payload={
338
+ "msg": "Resuming run",
339
+ },
340
+ )
341
+
342
+ with runtime.run_heartbeat():
343
+ if clone_only:
344
+ runtime.clone_original_run()
345
+ else:
346
+ runtime.clone_original_run(generate_task_obj=True, verbose=False)
347
+ runtime.execute()
348
+
349
+
350
+ @parameters.add_custom_parameters(deploy_mode=True)
351
+ @click.command(help="Run the workflow locally.")
352
+ @tracing.cli("cli/run")
353
+ @common_run_options
354
+ @common_runner_options
355
+ @click.option(
356
+ "--namespace",
357
+ "user_namespace",
358
+ default=None,
359
+ help="Change namespace from the default (your username) to "
360
+ "the specified tag. Note that this option does not alter "
361
+ "tags assigned to the objects produced by this run, just "
362
+ "what existing objects are visible in the client API. You "
363
+ "can enable the global namespace with an empty string."
364
+ "--namespace=",
365
+ )
366
+ @click.pass_obj
367
+ def run(
368
+ obj,
369
+ tags=None,
370
+ max_workers=None,
371
+ max_num_splits=None,
372
+ max_log_size=None,
373
+ decospecs=None,
374
+ run_id_file=None,
375
+ runner_attribute_file=None,
376
+ user_namespace=None,
377
+ **kwargs,
378
+ ):
379
+ if user_namespace is not None:
380
+ namespace(user_namespace or None)
381
+ before_run(obj, tags, decospecs)
382
+
383
+ runtime = NativeRuntime(
384
+ obj.flow,
385
+ obj.graph,
386
+ obj.flow_datastore,
387
+ obj.metadata,
388
+ obj.environment,
389
+ obj.package,
390
+ obj.logger,
391
+ obj.entrypoint,
392
+ obj.event_logger,
393
+ obj.monitor,
394
+ max_workers=max_workers,
395
+ max_num_splits=max_num_splits,
396
+ max_log_size=max_log_size * 1024 * 1024,
397
+ )
398
+ write_latest_run_id(obj, runtime.run_id)
399
+ write_file(run_id_file, runtime.run_id)
400
+
401
+ obj.flow._set_constants(obj.graph, kwargs, obj.config_options)
402
+ current._update_env(
403
+ {
404
+ "run_id": runtime.run_id,
405
+ }
406
+ )
407
+ _system_logger.log_event(
408
+ level="info",
409
+ module="metaflow.run",
410
+ name="start",
411
+ payload={
412
+ "msg": "Starting run",
413
+ },
414
+ )
415
+
416
+ runtime.print_workflow_info()
417
+ runtime.persist_constants()
418
+ if runner_attribute_file:
419
+ with open(runner_attribute_file, "w", encoding="utf-8") as f:
420
+ json.dump(
421
+ {
422
+ "run_id": runtime.run_id,
423
+ "flow_name": obj.flow.name,
424
+ "metadata": obj.metadata.metadata_str(),
425
+ },
426
+ f,
427
+ )
428
+ with runtime.run_heartbeat():
429
+ runtime.execute()
430
+
431
+
432
+ # @parameters.add_custom_parameters(deploy_mode=True)
433
+ @click.command(help="Spins up a task for a given step from a previous run locally.")
434
+ @tracing.cli("cli/spin")
435
+ @click.argument("pathspec")
436
+ @click.option(
437
+ "--skip-decorators/--no-skip-decorators",
438
+ is_flag=True,
439
+ # Default False matches the saved_args check in cli.py for spin steps - skip_decorators
440
+ # only becomes True when explicitly passed, otherwise decorators are applied by default
441
+ default=False,
442
+ show_default=True,
443
+ help="Skip decorators attached to the step or flow.",
444
+ )
445
+ @click.option(
446
+ "--artifacts-module",
447
+ default=None,
448
+ show_default=True,
449
+ help="Path to a module that contains artifacts to be used in the spun step. "
450
+ "The artifacts should be defined as a dictionary called ARTIFACTS with keys as "
451
+ "the artifact names and values as the artifact values. The artifact values will "
452
+ "overwrite the default values of the artifacts used in the spun step.",
453
+ )
454
+ @click.option(
455
+ "--persist/--no-persist",
456
+ "persist",
457
+ default=SPIN_PERSIST,
458
+ show_default=True,
459
+ help="Whether to persist the artifacts in the spun step. If set to False, "
460
+ "the artifacts will not be persisted and will not be available in the spun step's "
461
+ "datastore.",
462
+ )
463
+ @click.option(
464
+ "--max-log-size",
465
+ default=10,
466
+ show_default=True,
467
+ help="Maximum size of stdout and stderr captured in "
468
+ "megabytes. If a step outputs more than this to "
469
+ "stdout/stderr, its output will be truncated.",
470
+ )
471
+ @common_runner_options
472
+ @click.pass_obj
473
+ def spin(
474
+ obj,
475
+ pathspec,
476
+ persist=True,
477
+ artifacts_module=None,
478
+ skip_decorators=False,
479
+ max_log_size=None,
480
+ run_id_file=None,
481
+ runner_attribute_file=None,
482
+ **kwargs,
483
+ ):
484
+ # Parse the pathspec argument to extract step name and full pathspec
485
+ step_name, parsed_pathspec = parse_spin_pathspec(pathspec, obj.flow.name)
486
+
487
+ before_run(obj, [], [], skip_decorators)
488
+ obj.echo(f"Spinning up step *{step_name}* locally for flow *{obj.flow.name}*")
489
+ # For spin, flow parameters come from the original run, but _set_constants
490
+ # requires them in kwargs. Use parameter defaults as placeholders - they'll be
491
+ # overwritten when the spin step loads artifacts from the original run.
492
+ flow_param_defaults = {}
493
+ for var, param in obj.flow._get_parameters():
494
+ if not param.IS_CONFIG_PARAMETER:
495
+ default_value = param.kwargs.get("default")
496
+ # Use None for required parameters without defaults
497
+ flow_param_defaults[param.name.replace("-", "_").lower()] = default_value
498
+ obj.flow._set_constants(obj.graph, flow_param_defaults, obj.config_options)
499
+ step_func = getattr(obj.flow, step_name, None)
500
+ if step_func is None:
501
+ raise CommandException(
502
+ f"Step '{step_name}' not found in flow '{obj.flow.name}'. "
503
+ "Please provide a valid step name."
504
+ )
505
+ from_start("Spin: before spin runtime init")
506
+ spin_runtime = SpinRuntime(
507
+ obj.flow,
508
+ obj.graph,
509
+ obj.flow_datastore,
510
+ obj.metadata,
511
+ obj.environment,
512
+ obj.package,
513
+ obj.logger,
514
+ obj.entrypoint,
515
+ obj.event_logger,
516
+ obj.monitor,
517
+ step_func,
518
+ step_name,
519
+ parsed_pathspec,
520
+ skip_decorators,
521
+ artifacts_module,
522
+ persist,
523
+ max_log_size * 1024 * 1024,
524
+ )
525
+ write_latest_run_id(obj, spin_runtime.run_id)
526
+ write_file(run_id_file, spin_runtime.run_id)
527
+ # We only need the root for the metadata, i.e. the portion before DATASTORE_LOCAL_DIR
528
+ datastore_root = spin_runtime._flow_datastore._storage_impl.datastore_root
529
+ orig_task_metadata_root = datastore_root.rsplit("/", 1)[0]
530
+ from_start("Spin: going to execute")
531
+ spin_runtime.execute()
532
+ from_start("Spin: after spin runtime execute")
533
+
534
+ if runner_attribute_file:
535
+ with open(runner_attribute_file, "w") as f:
536
+ json.dump(
537
+ {
538
+ "task_id": spin_runtime.task.task_id,
539
+ "step_name": step_name,
540
+ "run_id": spin_runtime.run_id,
541
+ "flow_name": obj.flow.name,
542
+ # Store metadata in a format that can be used by the Runner API
543
+ "metadata": f"{obj.metadata.__class__.TYPE}@{orig_task_metadata_root}",
544
+ },
545
+ f,
546
+ )