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,101 @@
1
+ from metaflow.exception import MetaflowException
2
+ from metaflow.plugins.secrets.utils import get_default_secrets_backend_type
3
+
4
+
5
+ class SecretSpec:
6
+ def __init__(self, secrets_backend_type, secret_id, options={}, role=None):
7
+ self._secrets_backend_type = secrets_backend_type
8
+ self._secret_id = secret_id
9
+ self._options = options
10
+ self._role = role
11
+
12
+ @property
13
+ def secrets_backend_type(self):
14
+ return self._secrets_backend_type
15
+
16
+ @property
17
+ def secret_id(self):
18
+ return self._secret_id
19
+
20
+ @property
21
+ def options(self):
22
+ return self._options
23
+
24
+ @property
25
+ def role(self):
26
+ return self._role
27
+
28
+ def to_json(self):
29
+ """Mainly used for testing... not the same as the input dict in secret_spec_from_dict()!"""
30
+ return {
31
+ "secrets_backend_type": self.secrets_backend_type,
32
+ "secret_id": self.secret_id,
33
+ "options": self.options,
34
+ "role": self.role,
35
+ }
36
+
37
+ def __str__(self):
38
+ return "%s (%s)" % (self._secret_id, self._secrets_backend_type)
39
+
40
+ @staticmethod
41
+ def secret_spec_from_str(secret_spec_str, role):
42
+ # "." may be used in secret_id one day (provider specific). HOWEVER, it provides the best UX for
43
+ # non-conflicting cases (i.e. for secret ids that don't contain "."). This is true for all AWS
44
+ # Secrets Manager secrets.
45
+ #
46
+ # So we skew heavily optimize for best upfront UX for the present (1/2023).
47
+ #
48
+ # If/when a certain secret backend supports "." secret names, we can figure out a solution at that time.
49
+ # At a minimum, dictionary style secret spec may be used with no code changes (see secret_spec_from_dict()).
50
+ # Other options could be:
51
+ # - accept and document that "." secret_ids don't work in Metaflow (across all possible providers)
52
+ # - add a Metaflow config variable that specifies the separator (default ".")
53
+ # - smarter spec parsing, that errors on secrets that look ambiguous. "aws-secrets-manager.XYZ" could mean:
54
+ # + secret_id "XYZ" in aws-secrets-manager backend, OR
55
+ # + secret_id "aws-secrets-manager.XYZ" default backend (if it is defined).
56
+ # + in this case, user can simply set "azure-key-vault.aws-secrets-manager.XYZ" instead!
57
+ parts = secret_spec_str.split(".", maxsplit=1)
58
+ if len(parts) == 1:
59
+ secrets_backend_type = get_default_secrets_backend_type()
60
+ secret_id = parts[0]
61
+ else:
62
+ secrets_backend_type = parts[0]
63
+ secret_id = parts[1]
64
+ return SecretSpec(
65
+ secrets_backend_type, secret_id=secret_id, options={}, role=role
66
+ )
67
+
68
+ @staticmethod
69
+ def secret_spec_from_dict(secret_spec_dict, role):
70
+ if "type" not in secret_spec_dict:
71
+ secrets_backend_type = get_default_secrets_backend_type()
72
+ else:
73
+ secrets_backend_type = secret_spec_dict["type"]
74
+ if not isinstance(secrets_backend_type, str):
75
+ raise MetaflowException(
76
+ "Bad @secrets specification - 'type' must be a string - found %s"
77
+ % type(secrets_backend_type)
78
+ )
79
+ secret_id = secret_spec_dict.get("id")
80
+ if not isinstance(secret_id, str):
81
+ raise MetaflowException(
82
+ "Bad @secrets specification - 'id' must be a string - found %s"
83
+ % type(secret_id)
84
+ )
85
+ options = secret_spec_dict.get("options", {})
86
+ if not isinstance(options, dict):
87
+ raise MetaflowException(
88
+ "Bad @secrets specification - 'option' must be a dict - found %s"
89
+ % type(options)
90
+ )
91
+ role_for_source = secret_spec_dict.get("role", None)
92
+ if role_for_source is not None:
93
+ if not isinstance(role_for_source, str):
94
+ raise MetaflowException(
95
+ "Bad @secrets specification - 'role' must be a str - found %s"
96
+ % type(role_for_source)
97
+ )
98
+ role = role_for_source
99
+ return SecretSpec(
100
+ secrets_backend_type, secret_id=secret_id, options=options, role=role
101
+ )
@@ -0,0 +1,74 @@
1
+ import os
2
+ import re
3
+ from metaflow.exception import MetaflowException
4
+
5
+ DISALLOWED_SECRETS_ENV_VAR_PREFIXES = ["METAFLOW_"]
6
+
7
+
8
+ def get_default_secrets_backend_type():
9
+ from metaflow.metaflow_config import DEFAULT_SECRETS_BACKEND_TYPE
10
+
11
+ if DEFAULT_SECRETS_BACKEND_TYPE is None:
12
+ raise MetaflowException(
13
+ "No default secrets backend type configured, but needed by @secrets. "
14
+ "Set METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE."
15
+ )
16
+ return DEFAULT_SECRETS_BACKEND_TYPE
17
+
18
+
19
+ def validate_env_vars_across_secrets(all_secrets_env_vars):
20
+ vars_injected_by = {}
21
+ for secret_spec, env_vars in all_secrets_env_vars:
22
+ for k in env_vars:
23
+ if k in vars_injected_by:
24
+ raise MetaflowException(
25
+ "Secret '%s' will inject '%s' as env var, and it is also added by '%s'"
26
+ % (secret_spec, k, vars_injected_by[k])
27
+ )
28
+ vars_injected_by[k] = secret_spec
29
+
30
+
31
+ def validate_env_vars_vs_existing_env(all_secrets_env_vars):
32
+ for secret_spec, env_vars in all_secrets_env_vars:
33
+ for k in env_vars:
34
+ if k in os.environ:
35
+ raise MetaflowException(
36
+ "Secret '%s' will inject '%s' as env var, but it already exists in env"
37
+ % (secret_spec, k)
38
+ )
39
+
40
+
41
+ def validate_env_vars(env_vars):
42
+ for k, v in env_vars.items():
43
+ if not isinstance(k, str):
44
+ raise MetaflowException("Found non string key %s (%s)" % (str(k), type(k)))
45
+ if not isinstance(v, str):
46
+ raise MetaflowException(
47
+ "Found non string value %s (%s)" % (str(v), type(v))
48
+ )
49
+ if not re.fullmatch("[a-zA-Z_][a-zA-Z0-9_]*", k):
50
+ raise MetaflowException("Found invalid env var name '%s'." % k)
51
+ for disallowed_prefix in DISALLOWED_SECRETS_ENV_VAR_PREFIXES:
52
+ if k.startswith(disallowed_prefix):
53
+ raise MetaflowException(
54
+ "Found disallowed env var name '%s' (starts with '%s')."
55
+ % (k, disallowed_prefix)
56
+ )
57
+
58
+
59
+ def get_secrets_backend_provider(secrets_backend_type):
60
+ from metaflow.plugins import SECRETS_PROVIDERS
61
+
62
+ try:
63
+ provider_cls = [
64
+ pc for pc in SECRETS_PROVIDERS if pc.TYPE == secrets_backend_type
65
+ ][0]
66
+ return provider_cls()
67
+ except IndexError:
68
+ raise MetaflowException(
69
+ "Unknown secrets backend type %s (available types: %s)"
70
+ % (
71
+ secrets_backend_type,
72
+ ", ".join(pc.TYPE for pc in SECRETS_PROVIDERS if pc.TYPE != "inline"),
73
+ )
74
+ )
@@ -225,10 +225,7 @@ def _get_client_run_obj(obj, run_id, user_namespace):
225
225
 
226
226
 
227
227
  def _set_current(obj):
228
- current._set_env(
229
- metadata_str="%s@%s"
230
- % (obj.metadata.__class__.TYPE, obj.metadata.__class__.INFO)
231
- )
228
+ current._set_env(metadata_str=obj.metadata.metadata_str())
232
229
 
233
230
 
234
231
  @click.group()
@@ -507,9 +504,9 @@ def tag_list(
507
504
 
508
505
  if not group_by_run and not group_by_tag:
509
506
  # We list all the runs that match to print them out if needed.
510
- system_tags_by_some_grouping[
511
- ",".join(pathspecs)
512
- ] = system_tags_by_some_grouping.get("_", set())
507
+ system_tags_by_some_grouping[",".join(pathspecs)] = (
508
+ system_tags_by_some_grouping.get("_", set())
509
+ )
513
510
  all_tags_by_some_grouping[",".join(pathspecs)] = all_tags_by_some_grouping.get(
514
511
  "_", set()
515
512
  )
@@ -8,8 +8,14 @@ import sys
8
8
  from metaflow.cli_args import cli_args
9
9
  from metaflow.decorators import StepDecorator
10
10
  from metaflow.exception import MetaflowException
11
- from metaflow.unbounded_foreach import UnboundedForeachInput, UBF_CONTROL, UBF_TASK
11
+ from metaflow.unbounded_foreach import (
12
+ UnboundedForeachInput,
13
+ UBF_CONTROL,
14
+ UBF_TASK,
15
+ CONTROL_TASK_TAG,
16
+ )
12
17
  from metaflow.util import to_unicode
18
+ from metaflow.metadata_provider import MetaDatum
13
19
 
14
20
 
15
21
  class InternalTestUnboundedForeachInput(UnboundedForeachInput):
@@ -50,9 +56,9 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
50
56
  name = "unbounded_test_foreach_internal"
51
57
  results_dict = {}
52
58
 
53
- def __init__(self, attributes=None, statically_defined=False):
59
+ def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
54
60
  super(InternalTestUnboundedForeachDecorator, self).__init__(
55
- attributes, statically_defined
61
+ attributes, statically_defined, inserted_by
56
62
  )
57
63
 
58
64
  def step_init(
@@ -60,13 +66,42 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
60
66
  ):
61
67
  self.environment = environment
62
68
 
69
+ def task_pre_step(
70
+ self,
71
+ step_name,
72
+ task_datastore,
73
+ metadata,
74
+ run_id,
75
+ task_id,
76
+ flow,
77
+ graph,
78
+ retry_count,
79
+ max_user_code_retries,
80
+ ubf_context,
81
+ inputs,
82
+ ):
83
+ if ubf_context == UBF_CONTROL:
84
+ metadata.register_metadata(
85
+ run_id,
86
+ step_name,
87
+ task_id,
88
+ [
89
+ MetaDatum(
90
+ field="internal_task_type",
91
+ value=CONTROL_TASK_TAG,
92
+ type="internal_task_type",
93
+ tags=["attempt_id:{0}".format(0)],
94
+ )
95
+ ],
96
+ )
97
+ self.input_paths = [obj.pathspec for obj in inputs]
98
+
63
99
  def control_task_step_func(self, flow, graph, retry_count):
64
100
  from metaflow import current
65
101
 
66
102
  run_id = current.run_id
67
103
  step_name = current.step_name
68
104
  control_task_id = current.task_id
69
- (_, split_step_name, split_task_id) = control_task_id.split("-")[1:]
70
105
  # If we are running inside Conda, we use the base executable FIRST;
71
106
  # the conda environment will then be used when runtime_step_cli is
72
107
  # called. This is so that it can properly set up all the metaflow
@@ -94,10 +129,10 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
94
129
  mapper_tasks = []
95
130
 
96
131
  for i in range(foreach_num_splits):
97
- task_id = "%s-%d" % (control_task_id.replace("control-", "test-ubf-"), i)
132
+ task_id = "%s-%d" % (control_task_id, i)
98
133
  pathspec = "%s/%s/%s" % (run_id, step_name, task_id)
99
134
  mapper_tasks.append(to_unicode(pathspec))
100
- input_paths = "%s/%s/%s" % (run_id, split_step_name, split_task_id)
135
+ input_paths = ",".join(self.input_paths)
101
136
 
102
137
  # Override specific `step` kwargs.
103
138
  kwargs = cli_args.step_kwargs
@@ -4,6 +4,7 @@ import traceback
4
4
  from metaflow.exception import MetaflowException
5
5
  from metaflow.decorators import StepDecorator
6
6
  from metaflow.unbounded_foreach import UBF_CONTROL
7
+ from metaflow.metaflow_config import DEFAULT_RUNTIME_LIMIT
7
8
 
8
9
 
9
10
  class TimeoutException(MetaflowException):
@@ -36,8 +37,7 @@ class TimeoutDecorator(StepDecorator):
36
37
  name = "timeout"
37
38
  defaults = {"seconds": 0, "minutes": 0, "hours": 0}
38
39
 
39
- def __init__(self, *args, **kwargs):
40
- super(TimeoutDecorator, self).__init__(*args, **kwargs)
40
+ def init(self):
41
41
  # Initialize secs in __init__ so other decorators could safely use this
42
42
  # value without worrying about decorator order.
43
43
  # Convert values in attributes to type:int since they can be type:str
@@ -99,7 +99,7 @@ class TimeoutDecorator(StepDecorator):
99
99
 
100
100
 
101
101
  def get_run_time_limit_for_task(step_decos):
102
- run_time_limit = 5 * 24 * 60 * 60 # 5 days.
102
+ run_time_limit = DEFAULT_RUNTIME_LIMIT
103
103
  for deco in step_decos:
104
104
  if isinstance(deco, TimeoutDecorator):
105
105
  run_time_limit = deco.secs
File without changes
@@ -0,0 +1,128 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import sys
5
+ import time
6
+
7
+ from metaflow.util import which
8
+ from metaflow.meta_files import read_info_file
9
+ from metaflow.metaflow_config import get_pinned_conda_libs
10
+ from metaflow.packaging_sys import MetaflowCodeContent, ContentType
11
+ from urllib.request import Request, urlopen
12
+ from urllib.error import URLError
13
+
14
+ # TODO: support version/platform/architecture selection.
15
+ UV_URL = "https://github.com/astral-sh/uv/releases/download/0.6.11/uv-x86_64-unknown-linux-gnu.tar.gz"
16
+
17
+ if __name__ == "__main__":
18
+
19
+ def run_cmd(cmd, stdin_str=None):
20
+ result = subprocess.run(
21
+ cmd,
22
+ shell=True,
23
+ input=stdin_str,
24
+ stdout=subprocess.PIPE,
25
+ stderr=subprocess.PIPE,
26
+ text=True,
27
+ )
28
+ if result.returncode != 0:
29
+ print(f"Bootstrap failed while executing: {cmd}")
30
+ print("Stdout:", result.stdout)
31
+ print("Stderr:", result.stderr)
32
+ sys.exit(1)
33
+
34
+ def install_uv():
35
+ import tarfile
36
+
37
+ uv_install_path = os.path.join(os.getcwd(), "uv_install")
38
+ if which("uv"):
39
+ return
40
+
41
+ print("Installing uv...")
42
+
43
+ # Prepare directory once
44
+ os.makedirs(uv_install_path, exist_ok=True)
45
+
46
+ # Download and decompress in one go
47
+ headers = {
48
+ "Accept-Encoding": "gzip, deflate, br",
49
+ "Connection": "keep-alive",
50
+ "User-Agent": "python-urllib",
51
+ }
52
+
53
+ def _tar_filter(member: tarfile.TarInfo, path):
54
+ if os.path.basename(member.name) != "uv":
55
+ return None # skip
56
+ member.path = os.path.basename(member.path)
57
+ return member
58
+
59
+ max_retries = 3
60
+ for attempt in range(max_retries):
61
+ try:
62
+ req = Request(UV_URL, headers=headers)
63
+ with urlopen(req) as response:
64
+ with tarfile.open(fileobj=response, mode="r:gz") as tar:
65
+ tar.extractall(uv_install_path, filter=_tar_filter)
66
+ break
67
+ except (URLError, IOError) as e:
68
+ if attempt == max_retries - 1:
69
+ raise Exception(
70
+ f"Failed to download UV after {max_retries} attempts: {e}"
71
+ )
72
+ time.sleep(2**attempt)
73
+
74
+ # Update PATH only once at the end
75
+ os.environ["PATH"] += os.pathsep + uv_install_path
76
+
77
+ def get_dependencies(datastore_type):
78
+ # return required dependencies for Metaflow that must be added to the UV environment.
79
+ pinned = get_pinned_conda_libs(None, datastore_type)
80
+
81
+ # return only dependency names instead of pinned versions
82
+ return pinned.keys()
83
+
84
+ def skip_metaflow_dependencies():
85
+ skip_pkgs = ["metaflow", "ob-metaflow"]
86
+ info = read_info_file()
87
+ if info is not None:
88
+ try:
89
+ skip_pkgs.extend([ext_name for ext_name in info["ext_info"][0].keys()])
90
+ except Exception:
91
+ print(
92
+ "Failed to read INFO. Metaflow-related packages might get installed during runtime."
93
+ )
94
+
95
+ return skip_pkgs
96
+
97
+ def sync_uv_project(datastore_type):
98
+ # Move the files to the current directory so uv can find them.
99
+ for filename in ["uv.lock", "pyproject.toml"]:
100
+ path_to_file = MetaflowCodeContent.get_filename(
101
+ filename, ContentType.OTHER_CONTENT
102
+ )
103
+ if path_to_file is None:
104
+ raise RuntimeError(f"Could not find {filename} in the package.")
105
+ shutil.move(path_to_file, os.path.join(os.getcwd(), filename))
106
+
107
+ print("Syncing uv project...")
108
+ dependencies = " ".join(get_dependencies(datastore_type))
109
+ skip_pkgs = " ".join(
110
+ [f"--no-install-package {dep}" for dep in skip_metaflow_dependencies()]
111
+ )
112
+ cmd = f"""set -e;
113
+ uv sync --frozen {skip_pkgs};
114
+ uv pip install {dependencies} --strict
115
+ """
116
+ run_cmd(cmd)
117
+
118
+ if len(sys.argv) != 2:
119
+ print("Usage: bootstrap.py <datastore_type>")
120
+ sys.exit(1)
121
+
122
+ try:
123
+ datastore_type = sys.argv[1]
124
+ install_uv()
125
+ sync_uv_project(datastore_type)
126
+ except Exception as e:
127
+ print(f"Error: {str(e)}", file=sys.stderr)
128
+ sys.exit(1)
@@ -0,0 +1,72 @@
1
+ import os
2
+
3
+ from metaflow.exception import MetaflowException
4
+ from metaflow.metaflow_environment import MetaflowEnvironment
5
+ from metaflow.packaging_sys import ContentType
6
+
7
+
8
+ class UVException(MetaflowException):
9
+ headline = "uv error"
10
+
11
+
12
+ class UVEnvironment(MetaflowEnvironment):
13
+ TYPE = "uv"
14
+
15
+ def __init__(self, flow):
16
+ super().__init__(flow)
17
+ self.flow = flow
18
+
19
+ def validate_environment(self, logger, datastore_type):
20
+ self.datastore_type = datastore_type
21
+ self.logger = logger
22
+
23
+ def init_environment(self, echo, only_steps=None):
24
+ self.logger("Bootstrapping uv...")
25
+
26
+ def executable(self, step_name, default=None):
27
+ return "uv run --no-sync python"
28
+
29
+ def add_to_package(self):
30
+ # NOTE: We treat uv.lock and pyproject.toml as regular project assets and ship these along user code as part of the code package
31
+ # These are the minimal required files to reproduce the UV environment on the remote platform.
32
+ def _find(filename):
33
+ current_dir = os.getcwd()
34
+ while True:
35
+ file_path = os.path.join(current_dir, filename)
36
+ if os.path.isfile(file_path):
37
+ return file_path
38
+ parent_dir = os.path.dirname(current_dir)
39
+ if parent_dir == current_dir: # Reached root
40
+ raise UVException(
41
+ f"Could not find {filename} in current directory or any parent directory"
42
+ )
43
+ current_dir = parent_dir
44
+
45
+ pyproject_path = _find("pyproject.toml")
46
+ uv_lock_path = _find("uv.lock")
47
+ files = [
48
+ (uv_lock_path, "uv.lock", ContentType.OTHER_CONTENT),
49
+ (pyproject_path, "pyproject.toml", ContentType.OTHER_CONTENT),
50
+ ]
51
+ return files
52
+
53
+ def pylint_config(self):
54
+ config = super().pylint_config()
55
+ # Disable (import-error) in pylint
56
+ config.append("--disable=F0401")
57
+ return config
58
+
59
+ def bootstrap_commands(self, step_name, datastore_type):
60
+ return [
61
+ "echo 'Bootstrapping uv project...'",
62
+ "flush_mflogs",
63
+ # We have to prevent the tracing module from loading, as the bootstrapping process
64
+ # uses the internal S3 client which would fail to import tracing due to the required
65
+ # dependencies being bundled into the conda environment, which is yet to be
66
+ # initialized at this point.
67
+ 'DISABLE_TRACING=True python -m metaflow.plugins.uv.bootstrap "%s"'
68
+ % datastore_type,
69
+ "echo 'uv project bootstrapped.'",
70
+ "flush_mflogs",
71
+ "export PATH=$PATH:$(pwd)/uv_install",
72
+ ]
metaflow/procpoll.py CHANGED
@@ -31,7 +31,7 @@ class LinuxProcPoll(ProcPoll):
31
31
  self._poll.unregister(fd)
32
32
 
33
33
  def poll(self, timeout):
34
- for (fd, event) in self._poll.poll(timeout):
34
+ for fd, event in self._poll.poll(timeout):
35
35
  yield ProcPollEvent(
36
36
  fd=fd,
37
37
  can_read=bool(event & select.POLLIN),
@@ -28,7 +28,11 @@ class PyLint(object):
28
28
  return self._run is not None
29
29
 
30
30
  def run(self, logger=None, warnings=False, pylint_config=[]):
31
- args = [self._fname]
31
+ args = [
32
+ self._fname,
33
+ "--signature-mutators",
34
+ "metaflow.user_decorators.user_step_decorator.user_step_decorator",
35
+ ]
32
36
  if not warnings:
33
37
  args.append("--errors-only")
34
38
  if pylint_config:
File without changes