metaflow 2.12.8__py2.py3-none-any.whl → 2.12.10__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 (39) hide show
  1. metaflow/__init__.py +2 -0
  2. metaflow/cli.py +12 -4
  3. metaflow/extension_support/plugins.py +1 -0
  4. metaflow/flowspec.py +8 -1
  5. metaflow/lint.py +13 -0
  6. metaflow/metaflow_current.py +0 -8
  7. metaflow/plugins/__init__.py +12 -0
  8. metaflow/plugins/argo/argo_workflows.py +616 -46
  9. metaflow/plugins/argo/argo_workflows_cli.py +70 -3
  10. metaflow/plugins/argo/argo_workflows_decorator.py +38 -7
  11. metaflow/plugins/argo/argo_workflows_deployer.py +290 -0
  12. metaflow/plugins/argo/daemon.py +59 -0
  13. metaflow/plugins/argo/jobset_input_paths.py +16 -0
  14. metaflow/plugins/aws/batch/batch_decorator.py +16 -13
  15. metaflow/plugins/aws/step_functions/step_functions_cli.py +45 -3
  16. metaflow/plugins/aws/step_functions/step_functions_deployer.py +251 -0
  17. metaflow/plugins/cards/card_cli.py +1 -1
  18. metaflow/plugins/kubernetes/kubernetes.py +279 -52
  19. metaflow/plugins/kubernetes/kubernetes_cli.py +26 -8
  20. metaflow/plugins/kubernetes/kubernetes_client.py +0 -1
  21. metaflow/plugins/kubernetes/kubernetes_decorator.py +56 -44
  22. metaflow/plugins/kubernetes/kubernetes_job.py +7 -6
  23. metaflow/plugins/kubernetes/kubernetes_jobsets.py +511 -272
  24. metaflow/plugins/parallel_decorator.py +108 -8
  25. metaflow/plugins/secrets/secrets_decorator.py +12 -3
  26. metaflow/plugins/test_unbounded_foreach_decorator.py +39 -4
  27. metaflow/runner/deployer.py +386 -0
  28. metaflow/runner/metaflow_runner.py +1 -20
  29. metaflow/runner/nbdeploy.py +130 -0
  30. metaflow/runner/nbrun.py +4 -28
  31. metaflow/runner/utils.py +49 -0
  32. metaflow/runtime.py +246 -134
  33. metaflow/version.py +1 -1
  34. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/METADATA +2 -2
  35. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/RECORD +39 -32
  36. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/WHEEL +1 -1
  37. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/LICENSE +0 -0
  38. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/entry_points.txt +0 -0
  39. {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import re
4
4
  from hashlib import sha1
5
5
 
6
6
  from metaflow import JSONType, current, decorators, parameters
7
+ from metaflow.client.core import get_metadata
7
8
  from metaflow._vendor import click
8
9
  from metaflow.exception import MetaflowException, MetaflowInternalError
9
10
  from metaflow.metaflow_config import (
@@ -130,6 +131,14 @@ def step_functions(obj, name=None):
130
131
  help="Use AWS Step Functions Distributed Map instead of Inline Map for "
131
132
  "defining foreach tasks in Amazon State Language.",
132
133
  )
134
+ @click.option(
135
+ "--deployer-attribute-file",
136
+ default=None,
137
+ show_default=True,
138
+ type=str,
139
+ help="Write the workflow name to the file specified. Used internally for Metaflow's Deployer API.",
140
+ hidden=True,
141
+ )
133
142
  @click.pass_obj
134
143
  def create(
135
144
  obj,
@@ -143,9 +152,21 @@ def create(
143
152
  workflow_timeout=None,
144
153
  log_execution_history=False,
145
154
  use_distributed_map=False,
155
+ deployer_attribute_file=None,
146
156
  ):
147
157
  validate_tags(tags)
148
158
 
159
+ if deployer_attribute_file:
160
+ with open(deployer_attribute_file, "w") as f:
161
+ json.dump(
162
+ {
163
+ "name": obj.state_machine_name,
164
+ "flow_name": obj.flow.name,
165
+ "metadata": get_metadata(),
166
+ },
167
+ f,
168
+ )
169
+
149
170
  obj.echo(
150
171
  "Deploying *%s* to AWS Step Functions..." % obj.state_machine_name, bold=True
151
172
  )
@@ -231,8 +252,10 @@ def check_metadata_service_version(obj):
231
252
 
232
253
 
233
254
  def resolve_state_machine_name(obj, name):
234
- def attach_prefix(name):
235
- if SFN_STATE_MACHINE_PREFIX is not None:
255
+ def attach_prefix(name: str):
256
+ if SFN_STATE_MACHINE_PREFIX is not None and (
257
+ not name.startswith(SFN_STATE_MACHINE_PREFIX)
258
+ ):
236
259
  return SFN_STATE_MACHINE_PREFIX + "_" + name
237
260
  return name
238
261
 
@@ -440,8 +463,16 @@ def resolve_token(
440
463
  type=str,
441
464
  help="Write the ID of this run to the file specified.",
442
465
  )
466
+ @click.option(
467
+ "--deployer-attribute-file",
468
+ default=None,
469
+ show_default=True,
470
+ type=str,
471
+ help="Write the metadata and pathspec of this run to the file specified.\nUsed internally for Metaflow's Deployer API.",
472
+ hidden=True,
473
+ )
443
474
  @click.pass_obj
444
- def trigger(obj, run_id_file=None, **kwargs):
475
+ def trigger(obj, run_id_file=None, deployer_attribute_file=None, **kwargs):
445
476
  def _convert_value(param):
446
477
  # Swap `-` with `_` in parameter name to match click's behavior
447
478
  val = kwargs.get(param.name.replace("-", "_").lower())
@@ -466,6 +497,17 @@ def trigger(obj, run_id_file=None, **kwargs):
466
497
  with open(run_id_file, "w") as f:
467
498
  f.write(str(run_id))
468
499
 
500
+ if deployer_attribute_file:
501
+ with open(deployer_attribute_file, "w") as f:
502
+ json.dump(
503
+ {
504
+ "name": obj.state_machine_name,
505
+ "metadata": get_metadata(),
506
+ "pathspec": "/".join((obj.flow.name, run_id)),
507
+ },
508
+ f,
509
+ )
510
+
469
511
  obj.echo(
470
512
  "Workflow *{name}* triggered on AWS Step Functions "
471
513
  "(run-id *{run_id}*).".format(name=obj.state_machine_name, run_id=run_id),
@@ -0,0 +1,251 @@
1
+ import sys
2
+ import json
3
+ import tempfile
4
+ from typing import Optional, ClassVar, List
5
+
6
+ from metaflow.plugins.aws.step_functions.step_functions import StepFunctions
7
+ from metaflow.runner.deployer import (
8
+ DeployerImpl,
9
+ DeployedFlow,
10
+ TriggeredRun,
11
+ get_lower_level_group,
12
+ handle_timeout,
13
+ )
14
+
15
+
16
+ def terminate(instance: TriggeredRun, **kwargs):
17
+ """
18
+ Terminate the running workflow.
19
+
20
+ Parameters
21
+ ----------
22
+ **kwargs : Any
23
+ Additional arguments to pass to the terminate command.
24
+
25
+ Returns
26
+ -------
27
+ bool
28
+ True if the command was successful, False otherwise.
29
+ """
30
+ _, run_id = instance.pathspec.split("/")
31
+
32
+ # every subclass needs to have `self.deployer_kwargs`
33
+ command = get_lower_level_group(
34
+ instance.deployer.api,
35
+ instance.deployer.top_level_kwargs,
36
+ instance.deployer.TYPE,
37
+ instance.deployer.deployer_kwargs,
38
+ ).terminate(run_id=run_id, **kwargs)
39
+
40
+ pid = instance.deployer.spm.run_command(
41
+ [sys.executable, *command],
42
+ env=instance.deployer.env_vars,
43
+ cwd=instance.deployer.cwd,
44
+ show_output=instance.deployer.show_output,
45
+ )
46
+
47
+ command_obj = instance.deployer.spm.get(pid)
48
+ return command_obj.process.returncode == 0
49
+
50
+
51
+ def production_token(instance: DeployedFlow):
52
+ """
53
+ Get the production token for the deployed flow.
54
+
55
+ Returns
56
+ -------
57
+ str, optional
58
+ The production token, None if it cannot be retrieved.
59
+ """
60
+ try:
61
+ _, production_token = StepFunctions.get_existing_deployment(
62
+ instance.deployer.name
63
+ )
64
+ return production_token
65
+ except TypeError:
66
+ return None
67
+
68
+
69
+ def list_runs(instance: DeployedFlow, states: Optional[List[str]] = None):
70
+ """
71
+ List runs of the deployed flow.
72
+
73
+ Parameters
74
+ ----------
75
+ states : Optional[List[str]], optional
76
+ A list of states to filter the runs by. Allowed values are:
77
+ RUNNING, SUCCEEDED, FAILED, TIMED_OUT, ABORTED.
78
+ If not provided, all states will be considered.
79
+
80
+ Returns
81
+ -------
82
+ List[TriggeredRun]
83
+ A list of TriggeredRun objects representing the runs of the deployed flow.
84
+
85
+ Raises
86
+ ------
87
+ ValueError
88
+ If any of the provided states are invalid or if there are duplicate states.
89
+ """
90
+ VALID_STATES = {"RUNNING", "SUCCEEDED", "FAILED", "TIMED_OUT", "ABORTED"}
91
+
92
+ if states is None:
93
+ states = []
94
+
95
+ unique_states = set(states)
96
+ if not unique_states.issubset(VALID_STATES):
97
+ invalid_states = unique_states - VALID_STATES
98
+ raise ValueError(
99
+ f"Invalid states found: {invalid_states}. Valid states are: {VALID_STATES}"
100
+ )
101
+
102
+ if len(states) != len(unique_states):
103
+ raise ValueError("Duplicate states are not allowed")
104
+
105
+ triggered_runs = []
106
+ executions = StepFunctions.list(instance.deployer.name, states)
107
+
108
+ for e in executions:
109
+ run_id = "sfn-%s" % e["name"]
110
+ tr = TriggeredRun(
111
+ deployer=instance.deployer,
112
+ content=json.dumps(
113
+ {
114
+ "metadata": instance.deployer.metadata,
115
+ "pathspec": "/".join((instance.deployer.flow_name, run_id)),
116
+ "name": run_id,
117
+ }
118
+ ),
119
+ )
120
+ tr._enrich_object({"terminate": terminate})
121
+ triggered_runs.append(tr)
122
+
123
+ return triggered_runs
124
+
125
+
126
+ def delete(instance: DeployedFlow, **kwargs):
127
+ """
128
+ Delete the deployed flow.
129
+
130
+ Parameters
131
+ ----------
132
+ **kwargs : Any
133
+ Additional arguments to pass to the delete command.
134
+
135
+ Returns
136
+ -------
137
+ bool
138
+ True if the command was successful, False otherwise.
139
+ """
140
+ command = get_lower_level_group(
141
+ instance.deployer.api,
142
+ instance.deployer.top_level_kwargs,
143
+ instance.deployer.TYPE,
144
+ instance.deployer.deployer_kwargs,
145
+ ).delete(**kwargs)
146
+
147
+ pid = instance.deployer.spm.run_command(
148
+ [sys.executable, *command],
149
+ env=instance.deployer.env_vars,
150
+ cwd=instance.deployer.cwd,
151
+ show_output=instance.deployer.show_output,
152
+ )
153
+
154
+ command_obj = instance.deployer.spm.get(pid)
155
+ return command_obj.process.returncode == 0
156
+
157
+
158
+ def trigger(instance: DeployedFlow, **kwargs):
159
+ """
160
+ Trigger a new run for the deployed flow.
161
+
162
+ Parameters
163
+ ----------
164
+ **kwargs : Any
165
+ Additional arguments to pass to the trigger command, `Parameters` in particular
166
+
167
+ Returns
168
+ -------
169
+ StepFunctionsTriggeredRun
170
+ The triggered run instance.
171
+
172
+ Raises
173
+ ------
174
+ Exception
175
+ If there is an error during the trigger process.
176
+ """
177
+ with tempfile.TemporaryDirectory() as temp_dir:
178
+ tfp_runner_attribute = tempfile.NamedTemporaryFile(dir=temp_dir, delete=False)
179
+
180
+ # every subclass needs to have `self.deployer_kwargs`
181
+ command = get_lower_level_group(
182
+ instance.deployer.api,
183
+ instance.deployer.top_level_kwargs,
184
+ instance.deployer.TYPE,
185
+ instance.deployer.deployer_kwargs,
186
+ ).trigger(deployer_attribute_file=tfp_runner_attribute.name, **kwargs)
187
+
188
+ pid = instance.deployer.spm.run_command(
189
+ [sys.executable, *command],
190
+ env=instance.deployer.env_vars,
191
+ cwd=instance.deployer.cwd,
192
+ show_output=instance.deployer.show_output,
193
+ )
194
+
195
+ command_obj = instance.deployer.spm.get(pid)
196
+ content = handle_timeout(tfp_runner_attribute, command_obj)
197
+
198
+ if command_obj.process.returncode == 0:
199
+ triggered_run = TriggeredRun(deployer=instance.deployer, content=content)
200
+ triggered_run._enrich_object({"terminate": terminate})
201
+ return triggered_run
202
+
203
+ raise Exception(
204
+ "Error triggering %s on %s for %s"
205
+ % (instance.deployer.name, instance.deployer.TYPE, instance.deployer.flow_file)
206
+ )
207
+
208
+
209
+ class StepFunctionsDeployer(DeployerImpl):
210
+ """
211
+ Deployer implementation for AWS Step Functions.
212
+
213
+ Attributes
214
+ ----------
215
+ TYPE : ClassVar[Optional[str]]
216
+ The type of the deployer, which is "step-functions".
217
+ """
218
+
219
+ TYPE: ClassVar[Optional[str]] = "step-functions"
220
+
221
+ def __init__(self, deployer_kwargs, **kwargs):
222
+ """
223
+ Initialize the StepFunctionsDeployer.
224
+
225
+ Parameters
226
+ ----------
227
+ deployer_kwargs : dict
228
+ The deployer-specific keyword arguments.
229
+ **kwargs : Any
230
+ Additional arguments to pass to the superclass constructor.
231
+ """
232
+ self.deployer_kwargs = deployer_kwargs
233
+ super().__init__(**kwargs)
234
+
235
+ def _enrich_deployed_flow(self, deployed_flow: DeployedFlow):
236
+ """
237
+ Enrich the DeployedFlow object with additional properties and methods.
238
+
239
+ Parameters
240
+ ----------
241
+ deployed_flow : DeployedFlow
242
+ The deployed flow object to enrich.
243
+ """
244
+ deployed_flow._enrich_object(
245
+ {
246
+ "production_token": property(production_token),
247
+ "trigger": trigger,
248
+ "delete": delete,
249
+ "list_runs": list_runs,
250
+ }
251
+ )
@@ -752,7 +752,7 @@ def create(
752
752
  return _card.render(
753
753
  task,
754
754
  stack_trace=stack_trace,
755
- ).replace(mf_card.RELOAD_POLICY_TOKEN, token)
755
+ ).replace(_card.RELOAD_POLICY_TOKEN, token)
756
756
 
757
757
  if error_stack_trace is not None and mode != "refresh":
758
758
  rendered_content = _render_error_card(error_stack_trace)