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
@@ -7,6 +7,7 @@ from hashlib import sha1
7
7
  from metaflow import current, decorators
8
8
  from metaflow._vendor import click
9
9
  from metaflow.exception import MetaflowException, MetaflowInternalError
10
+ from metaflow.metaflow_config import FEAT_ALWAYS_UPLOAD_CODE_PACKAGE
10
11
  from metaflow.package import MetaflowPackage
11
12
  from metaflow.plugins.aws.step_functions.production_token import (
12
13
  load_token,
@@ -283,24 +284,35 @@ def make_flow(
283
284
  ):
284
285
  # Attach @kubernetes.
285
286
  decorators._attach_decorators(obj.flow, [KubernetesDecorator.name])
287
+ decorators._init(obj.flow)
286
288
 
287
289
  decorators._init_step_decorators(
288
290
  obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
289
291
  )
290
-
292
+ obj.graph = obj.flow._graph
291
293
  # Save the code package in the flow datastore so that both user code and
292
294
  # metaflow package can be retrieved during workflow execution.
293
295
  obj.package = MetaflowPackage(
294
- obj.flow, obj.environment, obj.echo, obj.package_suffixes
296
+ obj.flow,
297
+ obj.environment,
298
+ obj.echo,
299
+ suffixes=obj.package_suffixes,
300
+ flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
295
301
  )
296
- package_url, package_sha = obj.flow_datastore.save_data(
297
- [obj.package.blob], len_hint=1
298
- )[0]
302
+ # This blocks until the package is created
303
+ if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
304
+ package_url = obj.package.package_url()
305
+ package_sha = obj.package.package_sha()
306
+ else:
307
+ package_url, package_sha = obj.flow_datastore.save_data(
308
+ [obj.package.blob], len_hint=1
309
+ )[0]
299
310
 
300
311
  return Airflow(
301
312
  dag_name,
302
313
  obj.graph,
303
314
  obj.flow,
315
+ obj.package.package_metadata,
304
316
  package_sha,
305
317
  package_url,
306
318
  obj.metadata,
@@ -389,6 +401,11 @@ def _validate_workflow(flow, graph, flow_datastore, metadata, workflow_timeout):
389
401
  "Step *%s* is marked for execution on AWS Batch with Airflow which isn't currently supported."
390
402
  % node.name
391
403
  )
404
+ if any([d.name == "slurm" for d in node.decorators]):
405
+ raise NotSupportedException(
406
+ "Step *%s* is marked for execution on Slurm with Airflow which isn't currently supported."
407
+ % node.name
408
+ )
392
409
  SUPPORTED_DATASTORES = ("azure", "s3", "gs")
393
410
  if flow_datastore.TYPE not in SUPPORTED_DATASTORES:
394
411
  raise AirflowException(
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  from metaflow.decorators import StepDecorator
4
- from metaflow.metadata import MetaDatum
4
+ from metaflow.metadata_provider import MetaDatum
5
5
 
6
6
  from .airflow_utils import (
7
7
  TASK_ID_XCOM_KEY,
@@ -567,9 +567,11 @@ class AirflowTask(object):
567
567
  return cls(
568
568
  task_dict["name"],
569
569
  is_mapper_node=is_mapper_node,
570
- operator_type=task_dict["operator_type"]
571
- if "operator_type" in task_dict
572
- else "kubernetes",
570
+ operator_type=(
571
+ task_dict["operator_type"]
572
+ if "operator_type" in task_dict
573
+ else "kubernetes"
574
+ ),
573
575
  flow_name=flow_name,
574
576
  flow_contains_foreach=flow_contains_foreach,
575
577
  ).set_operator_args(**op_args)
@@ -1,5 +1,5 @@
1
1
  import uuid
2
- from metaflow.decorators import FlowDecorator
2
+ from metaflow.decorators import FlowDecorator, flow_decorators
3
3
  from ..exception import AirflowException
4
4
  from ..airflow_utils import AirflowTask, id_creator, TASK_ID_HASH_LEN
5
5
 
@@ -49,7 +49,7 @@ class AirflowSensorDecorator(FlowDecorator):
49
49
  operator_type=self.operator_type,
50
50
  ).set_operator_args(**{k: v for k, v in task_args.items() if v is not None})
51
51
 
52
- def validate(self):
52
+ def validate(self, flow):
53
53
  """
54
54
  Validate if the arguments for the sensor are correct.
55
55
  """
@@ -58,7 +58,7 @@ class AirflowSensorDecorator(FlowDecorator):
58
58
  if self.attributes["name"] is None:
59
59
  deco_index = [
60
60
  d._id
61
- for d in self._flow_decorators
61
+ for d in flow_decorators(flow)
62
62
  if issubclass(d.__class__, AirflowSensorDecorator)
63
63
  ].index(self._id)
64
64
  self._airflow_task_name = "%s-%s" % (
@@ -71,4 +71,4 @@ class AirflowSensorDecorator(FlowDecorator):
71
71
  def flow_init(
72
72
  self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
73
73
  ):
74
- self.validate()
74
+ self.validate(flow)
@@ -85,7 +85,7 @@ class ExternalTaskSensorDecorator(AirflowSensorDecorator):
85
85
  )
86
86
  return task_args
87
87
 
88
- def validate(self):
88
+ def validate(self, flow):
89
89
  if self.attributes["external_dag_id"] is None:
90
90
  raise AirflowException(
91
91
  "`%s` argument of `@%s`cannot be `None`."
@@ -131,4 +131,4 @@ class ExternalTaskSensorDecorator(AirflowSensorDecorator):
131
131
  self.name,
132
132
  )
133
133
  )
134
- super().validate()
134
+ super().validate(flow)
@@ -58,9 +58,9 @@ class S3KeySensorDecorator(AirflowSensorDecorator):
58
58
  # `verify` is a airflow variable.
59
59
  )
60
60
 
61
- def validate(self):
61
+ def validate(self, flow):
62
62
  if self.attributes["bucket_key"] is None:
63
63
  raise AirflowException(
64
64
  "`bucket_key` for `@%s`cannot be empty." % (self.name)
65
65
  )
66
- super().validate()
66
+ super().validate(flow)
@@ -1,7 +1,6 @@
1
1
  import json
2
- import os
3
- import sys
4
2
 
3
+ from metaflow.metaflow_config import ARGO_EVENTS_SENSOR_NAMESPACE
5
4
  from metaflow.exception import MetaflowException
6
5
  from metaflow.plugins.kubernetes.kubernetes_client import KubernetesClient
7
6
 
@@ -10,6 +9,14 @@ class ArgoClientException(MetaflowException):
10
9
  headline = "Argo Client error"
11
10
 
12
11
 
12
+ class ArgoResourceNotFound(MetaflowException):
13
+ headline = "Resource not found"
14
+
15
+
16
+ class ArgoNotPermitted(MetaflowException):
17
+ headline = "Operation not permitted"
18
+
19
+
13
20
  class ArgoClient(object):
14
21
  def __init__(self, namespace=None):
15
22
  self._client = KubernetesClient()
@@ -52,21 +59,38 @@ class ArgoClient(object):
52
59
  json.loads(e.body)["message"] if e.body is not None else e.reason
53
60
  )
54
61
 
55
- def get_workflow_templates(self):
62
+ def get_workflow_templates(self, page_size=100):
56
63
  client = self._client.get()
57
- try:
58
- return client.CustomObjectsApi().list_namespaced_custom_object(
59
- group=self._group,
60
- version=self._version,
61
- namespace=self._namespace,
62
- plural="workflowtemplates",
63
- )["items"]
64
- except client.rest.ApiException as e:
65
- if e.status == 404:
66
- return None
67
- raise ArgoClientException(
68
- json.loads(e.body)["message"] if e.body is not None else e.reason
69
- )
64
+ continue_token = None
65
+
66
+ while True:
67
+ try:
68
+ params = {"limit": page_size}
69
+ if continue_token:
70
+ params["_continue"] = continue_token
71
+
72
+ response = client.CustomObjectsApi().list_namespaced_custom_object(
73
+ group=self._group,
74
+ version=self._version,
75
+ namespace=self._namespace,
76
+ plural="workflowtemplates",
77
+ **params,
78
+ )
79
+
80
+ for item in response.get("items", []):
81
+ yield item
82
+
83
+ metadata = response.get("metadata", {})
84
+ continue_token = metadata.get("continue")
85
+
86
+ if not continue_token:
87
+ break
88
+ except client.rest.ApiException as e:
89
+ if e.status == 404:
90
+ return None
91
+ raise ArgoClientException(
92
+ json.loads(e.body)["message"] if e.body is not None else e.reason
93
+ )
70
94
 
71
95
  def register_workflow_template(self, name, workflow_template):
72
96
  # Unfortunately, Kubernetes client does not handle optimistic
@@ -140,9 +164,7 @@ class ArgoClient(object):
140
164
  if e.status == 404:
141
165
  return None
142
166
  else:
143
- raise ArgoClientException(
144
- json.loads(e.body)["message"] if e.body is not None else e.reason
145
- )
167
+ raise wrap_api_error(e)
146
168
 
147
169
  def delete_workflow_template(self, name):
148
170
  """
@@ -164,9 +186,7 @@ class ArgoClient(object):
164
186
  if e.status == 404:
165
187
  return None
166
188
  else:
167
- raise ArgoClientException(
168
- json.loads(e.body)["message"] if e.body is not None else e.reason
169
- )
189
+ raise wrap_api_error(e)
170
190
 
171
191
  def terminate_workflow(self, name):
172
192
  client = self._client.get()
@@ -254,12 +274,19 @@ class ArgoClient(object):
254
274
  json.loads(e.body)["message"] if e.body is not None else e.reason
255
275
  )
256
276
 
257
- def trigger_workflow_template(self, name, parameters={}):
277
+ def trigger_workflow_template(self, name, usertype, username, parameters={}):
258
278
  client = self._client.get()
259
279
  body = {
260
280
  "apiVersion": "argoproj.io/v1alpha1",
261
281
  "kind": "Workflow",
262
- "metadata": {"generateName": name + "-"},
282
+ "metadata": {
283
+ "generateName": name + "-",
284
+ "annotations": {
285
+ "metaflow/triggered_by_user": json.dumps(
286
+ {"type": usertype, "name": username}
287
+ )
288
+ },
289
+ },
263
290
  "spec": {
264
291
  "workflowTemplateRef": {"name": name},
265
292
  "arguments": {
@@ -295,7 +322,10 @@ class ArgoClient(object):
295
322
  "suspend": schedule is None,
296
323
  "schedule": schedule,
297
324
  "timezone": timezone,
325
+ "failedJobsHistoryLimit": 10000, # default is unfortunately 1
326
+ "successfulJobsHistoryLimit": 10000, # default is unfortunately 3
298
327
  "workflowSpec": {"workflowTemplateRef": {"name": name}},
328
+ "startingDeadlineSeconds": 3540, # configuring this to 59 minutes so a failed trigger of cron workflow can succeed at most 59 mins after scheduled execution
299
329
  },
300
330
  }
301
331
  try:
@@ -349,12 +379,15 @@ class ArgoClient(object):
349
379
  json.loads(e.body)["message"] if e.body is not None else e.reason
350
380
  )
351
381
 
352
- def register_sensor(self, name, sensor=None):
382
+ def register_sensor(
383
+ self, name, sensor=None, sensor_namespace=ARGO_EVENTS_SENSOR_NAMESPACE
384
+ ):
353
385
  if sensor is None:
354
386
  sensor = {}
355
387
  # Unfortunately, Kubernetes client does not handle optimistic
356
388
  # concurrency control by itself unlike kubectl
357
389
  client = self._client.get()
390
+
358
391
  if not sensor:
359
392
  sensor["metadata"] = {}
360
393
 
@@ -364,7 +397,7 @@ class ArgoClient(object):
364
397
  ] = client.CustomObjectsApi().get_namespaced_custom_object(
365
398
  group=self._group,
366
399
  version=self._version,
367
- namespace=self._namespace,
400
+ namespace=sensor_namespace,
368
401
  plural="sensors",
369
402
  name=name,
370
403
  )[
@@ -379,7 +412,7 @@ class ArgoClient(object):
379
412
  return client.CustomObjectsApi().create_namespaced_custom_object(
380
413
  group=self._group,
381
414
  version=self._version,
382
- namespace=self._namespace,
415
+ namespace=sensor_namespace,
383
416
  plural="sensors",
384
417
  body=sensor,
385
418
  )
@@ -397,7 +430,7 @@ class ArgoClient(object):
397
430
  return client.CustomObjectsApi().replace_namespaced_custom_object(
398
431
  group=self._group,
399
432
  version=self._version,
400
- namespace=self._namespace,
433
+ namespace=sensor_namespace,
401
434
  plural="sensors",
402
435
  body=sensor,
403
436
  name=name,
@@ -407,7 +440,7 @@ class ArgoClient(object):
407
440
  json.loads(e.body)["message"] if e.body is not None else e.reason
408
441
  )
409
442
 
410
- def delete_sensor(self, name):
443
+ def delete_sensor(self, name, sensor_namespace):
411
444
  """
412
445
  Issues an API call for deleting a sensor
413
446
 
@@ -419,13 +452,25 @@ class ArgoClient(object):
419
452
  return client.CustomObjectsApi().delete_namespaced_custom_object(
420
453
  group=self._group,
421
454
  version=self._version,
422
- namespace=self._namespace,
455
+ namespace=sensor_namespace,
423
456
  plural="sensors",
424
457
  name=name,
425
458
  )
426
459
  except client.rest.ApiException as e:
427
460
  if e.status == 404:
428
461
  return None
429
- raise ArgoClientException(
430
- json.loads(e.body)["message"] if e.body is not None else e.reason
431
- )
462
+ raise wrap_api_error(e)
463
+
464
+
465
+ def wrap_api_error(error):
466
+ message = (
467
+ json.loads(error.body)["message"] if error.body is not None else error.reason
468
+ )
469
+ # catch all
470
+ ex = ArgoClientException(message)
471
+ if error.status == 404:
472
+ # usually handled outside this function as most cases want to return None instead.
473
+ ex = ArgoResourceNotFound(message)
474
+ if error.status == 403:
475
+ ex = ArgoNotPermitted(message)
476
+ return ex
@@ -11,6 +11,7 @@ from metaflow.metaflow_config import (
11
11
  ARGO_EVENTS_WEBHOOK_AUTH,
12
12
  ARGO_EVENTS_WEBHOOK_URL,
13
13
  SERVICE_HEADERS,
14
+ SERVICE_RETRY_COUNT,
14
15
  )
15
16
 
16
17
 
@@ -127,12 +128,11 @@ class ArgoEvent(object):
127
128
  headers={"Content-Type": "application/json", **headers},
128
129
  data=json.dumps(data).encode("utf-8"),
129
130
  )
130
- retries = 3
131
- backoff_factor = 2
132
131
 
133
- for i in range(retries):
132
+ for i in range(SERVICE_RETRY_COUNT):
134
133
  try:
135
- urllib.request.urlopen(request, timeout=10.0)
134
+ # we do not want to wait indefinitely for a response on the event broadcast, as this will keep the task running.
135
+ urllib.request.urlopen(request, timeout=60)
136
136
  print(
137
137
  "Argo Event (%s) published." % self._name, file=sys.stderr
138
138
  )
@@ -141,10 +141,10 @@ class ArgoEvent(object):
141
141
  # TODO: Retry retryable HTTP error codes
142
142
  raise e
143
143
  except urllib.error.URLError as e:
144
- if i == retries - 1:
144
+ if i == SERVICE_RETRY_COUNT - 1:
145
145
  raise e
146
146
  else:
147
- time.sleep(backoff_factor**i)
147
+ time.sleep(2**i)
148
148
  except Exception as e:
149
149
  msg = "Unable to publish Argo Event (%s): %s" % (self._name, e)
150
150
  if ignore_errors: