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.
- metaflow/__init__.py +2 -0
- metaflow/cli.py +12 -4
- metaflow/extension_support/plugins.py +1 -0
- metaflow/flowspec.py +8 -1
- metaflow/lint.py +13 -0
- metaflow/metaflow_current.py +0 -8
- metaflow/plugins/__init__.py +12 -0
- metaflow/plugins/argo/argo_workflows.py +616 -46
- metaflow/plugins/argo/argo_workflows_cli.py +70 -3
- metaflow/plugins/argo/argo_workflows_decorator.py +38 -7
- metaflow/plugins/argo/argo_workflows_deployer.py +290 -0
- metaflow/plugins/argo/daemon.py +59 -0
- metaflow/plugins/argo/jobset_input_paths.py +16 -0
- metaflow/plugins/aws/batch/batch_decorator.py +16 -13
- metaflow/plugins/aws/step_functions/step_functions_cli.py +45 -3
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +251 -0
- metaflow/plugins/cards/card_cli.py +1 -1
- metaflow/plugins/kubernetes/kubernetes.py +279 -52
- metaflow/plugins/kubernetes/kubernetes_cli.py +26 -8
- metaflow/plugins/kubernetes/kubernetes_client.py +0 -1
- metaflow/plugins/kubernetes/kubernetes_decorator.py +56 -44
- metaflow/plugins/kubernetes/kubernetes_job.py +7 -6
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +511 -272
- metaflow/plugins/parallel_decorator.py +108 -8
- metaflow/plugins/secrets/secrets_decorator.py +12 -3
- metaflow/plugins/test_unbounded_foreach_decorator.py +39 -4
- metaflow/runner/deployer.py +386 -0
- metaflow/runner/metaflow_runner.py +1 -20
- metaflow/runner/nbdeploy.py +130 -0
- metaflow/runner/nbrun.py +4 -28
- metaflow/runner/utils.py +49 -0
- metaflow/runtime.py +246 -134
- metaflow/version.py +1 -1
- {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/METADATA +2 -2
- {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/RECORD +39 -32
- {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/WHEEL +1 -1
- {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/LICENSE +0 -0
- {metaflow-2.12.8.dist-info → metaflow-2.12.10.dist-info}/entry_points.txt +0 -0
- {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(
|
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)
|