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
metaflow/util.py CHANGED
@@ -4,12 +4,13 @@ import sys
4
4
  import tempfile
5
5
  import zlib
6
6
  import base64
7
+ import re
8
+
7
9
  from functools import wraps
8
10
  from io import BytesIO
9
11
  from itertools import takewhile
10
- import re
12
+ from typing import Dict, Any, Tuple, Optional, List, Generator
11
13
 
12
- from metaflow.exception import MetaflowUnknownUser, MetaflowInternalError
13
14
 
14
15
  try:
15
16
  # python2
@@ -51,21 +52,6 @@ except NameError:
51
52
  from shlex import quote as _quote
52
53
 
53
54
 
54
- from typing import NamedTuple
55
-
56
-
57
- def namedtuple_with_defaults(typename, field_descr, defaults=()):
58
- T = NamedTuple(typename, field_descr)
59
- T.__new__.__defaults__ = tuple(defaults)
60
-
61
- # Adding the following to ensure the named tuple can be (un)pickled correctly.
62
- import __main__
63
-
64
- setattr(__main__, T.__name__, T)
65
- T.__module__ = "__main__"
66
- return T
67
-
68
-
69
55
  class TempDir(object):
70
56
  # Provide a temporary directory since Python 2.7 does not have it inbuilt
71
57
  def __enter__(self):
@@ -177,6 +163,8 @@ def get_username():
177
163
 
178
164
 
179
165
  def resolve_identity_as_tuple():
166
+ from metaflow.exception import MetaflowUnknownUser
167
+
180
168
  prod_token = os.environ.get("METAFLOW_PRODUCTION_TOKEN")
181
169
  if prod_token:
182
170
  return "production", prod_token
@@ -192,6 +180,119 @@ def resolve_identity():
192
180
  return "%s:%s" % (identity_type, identity_value)
193
181
 
194
182
 
183
+ def parse_spin_pathspec(pathspec: str, flow_name: str) -> Tuple:
184
+ """
185
+ Parse various pathspec formats for the spin command.
186
+
187
+ Parameters
188
+ ----------
189
+ pathspec : str
190
+ The pathspec string in one of the following formats:
191
+ - step_name (e.g., 'start')
192
+ - run_id/step_name (e.g., '221165/start')
193
+ - run_id/step_name/task_id (e.g., '221165/start/1350987')
194
+ - flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')
195
+ - flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')
196
+ flow_name : str
197
+ The name of the current flow.
198
+
199
+ Returns
200
+ -------
201
+ Tuple
202
+ A tuple of (step_name, full_pathspec_or_none)
203
+
204
+ Raises
205
+ ------
206
+ CommandException
207
+ If the pathspec format is invalid or flow name doesn't match.
208
+ """
209
+ from .exception import CommandException
210
+
211
+ parts = pathspec.split("/")
212
+
213
+ if len(parts) == 1:
214
+ # Just step name: 'start'
215
+ step_name = parts[0]
216
+ parsed_pathspec = None
217
+ elif len(parts) == 2:
218
+ # run_id/step_name: '221165/start'
219
+ run_id, step_name = parts
220
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
221
+ elif len(parts) == 3:
222
+ # Could be run_id/step_name/task_id or flow_name/run_id/step_name
223
+ if parts[0] == flow_name:
224
+ # flow_name/run_id/step_name
225
+ _, run_id, step_name = parts
226
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
227
+ else:
228
+ # run_id/step_name/task_id
229
+ run_id, step_name, task_id = parts
230
+ parsed_pathspec = f"{flow_name}/{run_id}/{step_name}/{task_id}"
231
+ elif len(parts) == 4:
232
+ # flow_name/run_id/step_name/task_id
233
+ parsed_flow_name, run_id, step_name, task_id = parts
234
+ if parsed_flow_name != flow_name:
235
+ raise CommandException(
236
+ f"Flow name '{parsed_flow_name}' in pathspec does not match current flow '{flow_name}'."
237
+ )
238
+ parsed_pathspec = pathspec
239
+ else:
240
+ raise CommandException(
241
+ f"Invalid pathspec format: '{pathspec}'. \n"
242
+ "Expected formats:\n"
243
+ " - step_name (e.g., 'start')\n"
244
+ " - run_id/step_name (e.g., '221165/start')\n"
245
+ " - run_id/step_name/task_id (e.g., '221165/start/1350987')\n"
246
+ " - flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')\n"
247
+ " - flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')"
248
+ )
249
+
250
+ return step_name, parsed_pathspec
251
+
252
+
253
+ def get_latest_task_pathspec(
254
+ flow_name: str, step_name: str, run_id: str = None
255
+ ) -> "metaflow.Task":
256
+ """
257
+ Returns a task pathspec from the latest run (or specified run) of the flow for the queried step.
258
+ If the queried step has several tasks, the task pathspec of the first task is returned.
259
+
260
+ Parameters
261
+ ----------
262
+ flow_name : str
263
+ The name of the flow.
264
+ step_name : str
265
+ The name of the step.
266
+ run_id : str, optional
267
+ The run ID to use. If None, uses the latest run.
268
+
269
+ Returns
270
+ -------
271
+ Task
272
+ A Metaflow Task instance containing the latest task for the queried step.
273
+
274
+ Raises
275
+ ------
276
+ MetaflowNotFound
277
+ If no task or run is found for the queried step.
278
+ """
279
+ from metaflow import Flow, Step
280
+ from metaflow.exception import MetaflowNotFound
281
+
282
+ if not run_id:
283
+ flow = Flow(flow_name)
284
+ run = flow.latest_run
285
+ if run is None:
286
+ raise MetaflowNotFound(f"No run found for flow {flow_name}")
287
+ run_id = run.id
288
+
289
+ try:
290
+ task = Step(f"{flow_name}/{run_id}/{step_name}").task
291
+ return task
292
+ except:
293
+ raise MetaflowNotFound(f"No task found for step {step_name} in run {run_id}")
294
+
295
+
195
296
  def get_latest_run_id(echo, flow_name):
196
297
  from metaflow.plugins.datastores.local_storage import LocalStorage
197
298
 
@@ -251,6 +352,8 @@ def get_object_package_version(obj):
251
352
 
252
353
 
253
354
  def compress_list(lst, separator=",", rangedelim=":", zlibmarker="!", zlibmin=500):
355
+ from metaflow.exception import MetaflowInternalError
356
+
254
357
  bad_items = [x for x in lst if separator in x or rangedelim in x or zlibmarker in x]
255
358
  if bad_items:
256
359
  raise MetaflowInternalError(
@@ -311,6 +414,9 @@ def get_metaflow_root():
311
414
 
312
415
 
313
416
  def dict_to_cli_options(params):
417
+ # Prevent circular imports
418
+ from .user_configs.config_options import ConfigInput
419
+
314
420
  for k, v in params.items():
315
421
  # Omit boolean options set to false or None, but preserve options with an empty
316
422
  # string argument.
@@ -319,6 +425,20 @@ def dict_to_cli_options(params):
319
425
  # keyword in Python, so we call it 'decospecs' in click args
320
426
  if k == "decospecs":
321
427
  k = "with"
428
+ if k in ("config", "config_value"):
429
+ # Special handling here since we gather them all in one option but actually
430
+ # need to send them one at a time using --config-value <name> kv.<name>
431
+ # Note it can be either config or config_value depending
432
+ # on click processing order.
433
+ for config_name in v.keys():
434
+ yield "--config-value"
435
+ yield to_unicode(config_name)
436
+ yield to_unicode(ConfigInput.make_key_name(config_name))
437
+ continue
438
+ if k == "local_config_file":
439
+ # Skip this value -- it should only be used locally and never when
440
+ # forming another command line
441
+ continue
322
442
  k = k.replace("_", "-")
323
443
  v = v if isinstance(v, (list, tuple, set)) else [v]
324
444
  for value in v:
@@ -397,9 +517,9 @@ def to_camelcase(obj):
397
517
  if isinstance(obj, dict):
398
518
  res = obj.__class__()
399
519
  for k in obj:
400
- res[
401
- re.sub(r"(?!^)_([a-zA-Z])", lambda x: x.group(1).upper(), k)
402
- ] = to_camelcase(obj[k])
520
+ res[re.sub(r"(?!^)_([a-zA-Z])", lambda x: x.group(1).upper(), k)] = (
521
+ to_camelcase(obj[k])
522
+ )
403
523
  elif isinstance(obj, (list, set, tuple)):
404
524
  res = obj.__class__(to_camelcase(v) for v in obj)
405
525
  else:
@@ -416,9 +536,9 @@ def to_pascalcase(obj):
416
536
  if isinstance(obj, dict):
417
537
  res = obj.__class__()
418
538
  for k in obj:
419
- res[
420
- re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), k, 1)
421
- ] = to_pascalcase(obj[k])
539
+ res[re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), k, count=1)] = (
540
+ to_pascalcase(obj[k])
541
+ )
422
542
  elif isinstance(obj, (list, set, tuple)):
423
543
  res = obj.__class__(to_pascalcase(v) for v in obj)
424
544
  else:
@@ -441,7 +561,103 @@ def tar_safe_extract(tar, path=".", members=None, *, numeric_owner=False):
441
561
  tar.extractall(path, members, numeric_owner=numeric_owner)
442
562
 
443
563
 
444
- if sys.version_info[:2] > (3, 5):
445
- from metaflow._vendor.packaging.version import parse as version_parse
446
- else:
447
- from distutils.version import LooseVersion as version_parse
564
+ def to_pod(value):
565
+ """
566
+ Convert a python object to plain-old-data (POD) format.
567
+
568
+ Parameters
569
+ ----------
570
+ value : Any
571
+ Value to convert to POD format. The value can be a string, number, list,
572
+ dictionary, or a nested structure of these types.
573
+ """
574
+ # Prevent circular imports
575
+ from metaflow.parameters import DeployTimeField
576
+
577
+ if isinstance(value, (str, int, float)):
578
+ return value
579
+ if isinstance(value, dict):
580
+ return {to_pod(k): to_pod(v) for k, v in value.items()}
581
+ if isinstance(value, (list, set, tuple)):
582
+ return [to_pod(v) for v in value]
583
+ if isinstance(value, DeployTimeField):
584
+ return value.print_representation
585
+ return str(value)
586
+
587
+
588
+ from metaflow._vendor.packaging.version import parse as version_parse
589
+
590
+
591
+ def read_artifacts_module(file_path: str) -> Dict[str, Any]:
592
+ """
593
+ Read a Python module from the given file path and return its ARTIFACTS variable.
594
+
595
+ Parameters
596
+ ----------
597
+ file_path : str
598
+ The path to the Python file containing the ARTIFACTS variable.
599
+
600
+ Returns
601
+ -------
602
+ Dict[str, Any]
603
+ A dictionary containing the ARTIFACTS variable from the module.
604
+
605
+ Raises
606
+ -------
607
+ MetaflowInternalError
608
+ If the file cannot be read or does not contain the ARTIFACTS variable.
609
+ """
610
+ import importlib.util
611
+ import os
612
+
613
+ try:
614
+ module_name = os.path.splitext(os.path.basename(file_path))[0]
615
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
616
+ module = importlib.util.module_from_spec(spec)
617
+ spec.loader.exec_module(module)
618
+ variables = vars(module)
619
+ if "ARTIFACTS" not in variables:
620
+ raise MetaflowInternalError(
621
+ f"Module {file_path} does not contain ARTIFACTS variable"
622
+ )
623
+ return variables.get("ARTIFACTS")
624
+ except Exception as e:
625
+ raise MetaflowInternalError(f"Error reading file {file_path}") from e
626
+
627
+
628
+ # this is os.walk(follow_symlinks=True) with cycle detection
629
+ def walk_without_cycles(
630
+ top_root: str,
631
+ exclude_dirs: Optional[List[str]] = None,
632
+ ) -> Generator[Tuple[str, List[str], List[str]], None, None]:
633
+ seen = set()
634
+
635
+ default_skip_dirs = ["__pycache__"]
636
+
637
+ def _recurse(root, skip_dirs):
638
+ for parent, dirs, files in os.walk(root):
639
+ dirs[:] = [d for d in dirs if d not in skip_dirs]
640
+ for d in dirs:
641
+ path = os.path.join(parent, d)
642
+ if os.path.islink(path):
643
+ # Breaking loops: never follow the same symlink twice
644
+ #
645
+ # NOTE: this also means that links to sibling links are
646
+ # not followed. In this case:
647
+ #
648
+ # x -> y
649
+ # y -> oo
650
+ # oo/real_file
651
+ #
652
+ # real_file is only included twice, not three times
653
+ reallink = os.path.realpath(path)
654
+ if reallink not in seen:
655
+ seen.add(reallink)
656
+ for x in _recurse(path, default_skip_dirs):
657
+ yield x
658
+ yield parent, dirs, files
659
+
660
+ skip_dirs = set(default_skip_dirs + (exclude_dirs or []))
661
+ for x in _recurse(top_root, skip_dirs):
662
+ skip_dirs = default_skip_dirs
663
+ yield x
metaflow/vendor.py CHANGED
@@ -11,7 +11,6 @@ WHITELIST = {
11
11
  "README.txt",
12
12
  "__init__.py",
13
13
  "vendor_any.txt",
14
- "vendor_v3_5.txt",
15
14
  "vendor_v3_6.txt",
16
15
  "vendor_v3_7.txt",
17
16
  "pip.LICENSE",
@@ -64,14 +63,29 @@ def find_vendored_libs(vendor_dir, whitelist, whitelist_dirs):
64
63
  return vendored_libs, paths
65
64
 
66
65
 
67
- def fetch_licenses(*info_dir, vendor_dir):
68
- for file in chain.from_iterable(map(iter_subtree, info_dir)):
69
- if "LICENSE" in file.name:
70
- library = file.parent.name.split("-")[0]
71
- shutil.copy(file, vendor_dir / ("%s.LICENSE" % library))
72
- else:
66
+ def fetch_licenses(*info_dirs, vendor_dir):
67
+ for dist_info in info_dirs:
68
+ metadata_file = dist_info / "METADATA"
69
+ if not metadata_file.exists():
70
+ continue
71
+
72
+ project_name = None
73
+ for line in metadata_file.read_text("utf-8").splitlines():
74
+ if line.startswith("Name: "):
75
+ project_name = line.split("Name: ", 1)[1].strip()
76
+ break
77
+ if not project_name:
73
78
  continue
74
79
 
80
+ for item in dist_info.iterdir():
81
+ if item.is_file() and re.search(r"(LICENSE|COPYING)", item.name, re.I):
82
+ shutil.copy(item, vendor_dir / f"{project_name}.LICENSE")
83
+ elif item.is_dir() and item.name.lower() == "licenses":
84
+ for license_file in item.iterdir():
85
+ if license_file.is_file():
86
+ dest_name = f"{project_name}.{license_file.name}"
87
+ shutil.copy(license_file, vendor_dir / dest_name)
88
+
75
89
 
76
90
  def vendor(vendor_dir):
77
91
  # remove everything
@@ -109,6 +123,8 @@ def vendor(vendor_dir):
109
123
  "-r",
110
124
  "_vendor/vendor_%s.txt" % subdir,
111
125
  "--no-compile",
126
+ "--no-binary",
127
+ ":all:",
112
128
  ]
113
129
  )
114
130
 
metaflow/version.py CHANGED
@@ -1 +1 @@
1
- metaflow_version = "2.11.13.1"
1
+ metaflow_version = "2.19.7.1rc0"