ob-metaflow 2.15.21.3__py2.py3-none-any.whl → 2.15.21.5__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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/_vendor/click/core.py +3 -4
- metaflow/_vendor/imghdr/__init__.py +11 -0
- metaflow/_vendor/yaml/__init__.py +427 -0
- metaflow/_vendor/yaml/composer.py +139 -0
- metaflow/_vendor/yaml/constructor.py +748 -0
- metaflow/_vendor/yaml/cyaml.py +101 -0
- metaflow/_vendor/yaml/dumper.py +62 -0
- metaflow/_vendor/yaml/emitter.py +1137 -0
- metaflow/_vendor/yaml/error.py +75 -0
- metaflow/_vendor/yaml/events.py +86 -0
- metaflow/_vendor/yaml/loader.py +63 -0
- metaflow/_vendor/yaml/nodes.py +49 -0
- metaflow/_vendor/yaml/parser.py +589 -0
- metaflow/_vendor/yaml/reader.py +185 -0
- metaflow/_vendor/yaml/representer.py +389 -0
- metaflow/_vendor/yaml/resolver.py +227 -0
- metaflow/_vendor/yaml/scanner.py +1435 -0
- metaflow/_vendor/yaml/serializer.py +111 -0
- metaflow/_vendor/yaml/tokens.py +104 -0
- metaflow/cli.py +11 -2
- metaflow/plugins/argo/argo_client.py +31 -14
- metaflow/plugins/argo/argo_workflows.py +66 -17
- metaflow/plugins/argo/argo_workflows_cli.py +1 -2
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +69 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +7 -1
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +26 -28
- metaflow/runner/deployer.py +160 -7
- metaflow/vendor.py +23 -6
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/METADATA +2 -2
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/RECORD +39 -22
- {ob_metaflow-2.15.21.3.data → ob_metaflow-2.15.21.5.data}/data/share/metaflow/devtools/Makefile +0 -0
- {ob_metaflow-2.15.21.3.data → ob_metaflow-2.15.21.5.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {ob_metaflow-2.15.21.3.data → ob_metaflow-2.15.21.5.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.15.21.3.dist-info → ob_metaflow-2.15.21.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
__all__ = ['Serializer', 'SerializerError']
|
|
3
|
+
|
|
4
|
+
from .error import YAMLError
|
|
5
|
+
from .events import *
|
|
6
|
+
from .nodes import *
|
|
7
|
+
|
|
8
|
+
class SerializerError(YAMLError):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class Serializer:
|
|
12
|
+
|
|
13
|
+
ANCHOR_TEMPLATE = 'id%03d'
|
|
14
|
+
|
|
15
|
+
def __init__(self, encoding=None,
|
|
16
|
+
explicit_start=None, explicit_end=None, version=None, tags=None):
|
|
17
|
+
self.use_encoding = encoding
|
|
18
|
+
self.use_explicit_start = explicit_start
|
|
19
|
+
self.use_explicit_end = explicit_end
|
|
20
|
+
self.use_version = version
|
|
21
|
+
self.use_tags = tags
|
|
22
|
+
self.serialized_nodes = {}
|
|
23
|
+
self.anchors = {}
|
|
24
|
+
self.last_anchor_id = 0
|
|
25
|
+
self.closed = None
|
|
26
|
+
|
|
27
|
+
def open(self):
|
|
28
|
+
if self.closed is None:
|
|
29
|
+
self.emit(StreamStartEvent(encoding=self.use_encoding))
|
|
30
|
+
self.closed = False
|
|
31
|
+
elif self.closed:
|
|
32
|
+
raise SerializerError("serializer is closed")
|
|
33
|
+
else:
|
|
34
|
+
raise SerializerError("serializer is already opened")
|
|
35
|
+
|
|
36
|
+
def close(self):
|
|
37
|
+
if self.closed is None:
|
|
38
|
+
raise SerializerError("serializer is not opened")
|
|
39
|
+
elif not self.closed:
|
|
40
|
+
self.emit(StreamEndEvent())
|
|
41
|
+
self.closed = True
|
|
42
|
+
|
|
43
|
+
#def __del__(self):
|
|
44
|
+
# self.close()
|
|
45
|
+
|
|
46
|
+
def serialize(self, node):
|
|
47
|
+
if self.closed is None:
|
|
48
|
+
raise SerializerError("serializer is not opened")
|
|
49
|
+
elif self.closed:
|
|
50
|
+
raise SerializerError("serializer is closed")
|
|
51
|
+
self.emit(DocumentStartEvent(explicit=self.use_explicit_start,
|
|
52
|
+
version=self.use_version, tags=self.use_tags))
|
|
53
|
+
self.anchor_node(node)
|
|
54
|
+
self.serialize_node(node, None, None)
|
|
55
|
+
self.emit(DocumentEndEvent(explicit=self.use_explicit_end))
|
|
56
|
+
self.serialized_nodes = {}
|
|
57
|
+
self.anchors = {}
|
|
58
|
+
self.last_anchor_id = 0
|
|
59
|
+
|
|
60
|
+
def anchor_node(self, node):
|
|
61
|
+
if node in self.anchors:
|
|
62
|
+
if self.anchors[node] is None:
|
|
63
|
+
self.anchors[node] = self.generate_anchor(node)
|
|
64
|
+
else:
|
|
65
|
+
self.anchors[node] = None
|
|
66
|
+
if isinstance(node, SequenceNode):
|
|
67
|
+
for item in node.value:
|
|
68
|
+
self.anchor_node(item)
|
|
69
|
+
elif isinstance(node, MappingNode):
|
|
70
|
+
for key, value in node.value:
|
|
71
|
+
self.anchor_node(key)
|
|
72
|
+
self.anchor_node(value)
|
|
73
|
+
|
|
74
|
+
def generate_anchor(self, node):
|
|
75
|
+
self.last_anchor_id += 1
|
|
76
|
+
return self.ANCHOR_TEMPLATE % self.last_anchor_id
|
|
77
|
+
|
|
78
|
+
def serialize_node(self, node, parent, index):
|
|
79
|
+
alias = self.anchors[node]
|
|
80
|
+
if node in self.serialized_nodes:
|
|
81
|
+
self.emit(AliasEvent(alias))
|
|
82
|
+
else:
|
|
83
|
+
self.serialized_nodes[node] = True
|
|
84
|
+
self.descend_resolver(parent, index)
|
|
85
|
+
if isinstance(node, ScalarNode):
|
|
86
|
+
detected_tag = self.resolve(ScalarNode, node.value, (True, False))
|
|
87
|
+
default_tag = self.resolve(ScalarNode, node.value, (False, True))
|
|
88
|
+
implicit = (node.tag == detected_tag), (node.tag == default_tag)
|
|
89
|
+
self.emit(ScalarEvent(alias, node.tag, implicit, node.value,
|
|
90
|
+
style=node.style))
|
|
91
|
+
elif isinstance(node, SequenceNode):
|
|
92
|
+
implicit = (node.tag
|
|
93
|
+
== self.resolve(SequenceNode, node.value, True))
|
|
94
|
+
self.emit(SequenceStartEvent(alias, node.tag, implicit,
|
|
95
|
+
flow_style=node.flow_style))
|
|
96
|
+
index = 0
|
|
97
|
+
for item in node.value:
|
|
98
|
+
self.serialize_node(item, node, index)
|
|
99
|
+
index += 1
|
|
100
|
+
self.emit(SequenceEndEvent())
|
|
101
|
+
elif isinstance(node, MappingNode):
|
|
102
|
+
implicit = (node.tag
|
|
103
|
+
== self.resolve(MappingNode, node.value, True))
|
|
104
|
+
self.emit(MappingStartEvent(alias, node.tag, implicit,
|
|
105
|
+
flow_style=node.flow_style))
|
|
106
|
+
for key, value in node.value:
|
|
107
|
+
self.serialize_node(key, node, None)
|
|
108
|
+
self.serialize_node(value, node, key)
|
|
109
|
+
self.emit(MappingEndEvent())
|
|
110
|
+
self.ascend_resolver()
|
|
111
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
|
|
2
|
+
class Token(object):
|
|
3
|
+
def __init__(self, start_mark, end_mark):
|
|
4
|
+
self.start_mark = start_mark
|
|
5
|
+
self.end_mark = end_mark
|
|
6
|
+
def __repr__(self):
|
|
7
|
+
attributes = [key for key in self.__dict__
|
|
8
|
+
if not key.endswith('_mark')]
|
|
9
|
+
attributes.sort()
|
|
10
|
+
arguments = ', '.join(['%s=%r' % (key, getattr(self, key))
|
|
11
|
+
for key in attributes])
|
|
12
|
+
return '%s(%s)' % (self.__class__.__name__, arguments)
|
|
13
|
+
|
|
14
|
+
#class BOMToken(Token):
|
|
15
|
+
# id = '<byte order mark>'
|
|
16
|
+
|
|
17
|
+
class DirectiveToken(Token):
|
|
18
|
+
id = '<directive>'
|
|
19
|
+
def __init__(self, name, value, start_mark, end_mark):
|
|
20
|
+
self.name = name
|
|
21
|
+
self.value = value
|
|
22
|
+
self.start_mark = start_mark
|
|
23
|
+
self.end_mark = end_mark
|
|
24
|
+
|
|
25
|
+
class DocumentStartToken(Token):
|
|
26
|
+
id = '<document start>'
|
|
27
|
+
|
|
28
|
+
class DocumentEndToken(Token):
|
|
29
|
+
id = '<document end>'
|
|
30
|
+
|
|
31
|
+
class StreamStartToken(Token):
|
|
32
|
+
id = '<stream start>'
|
|
33
|
+
def __init__(self, start_mark=None, end_mark=None,
|
|
34
|
+
encoding=None):
|
|
35
|
+
self.start_mark = start_mark
|
|
36
|
+
self.end_mark = end_mark
|
|
37
|
+
self.encoding = encoding
|
|
38
|
+
|
|
39
|
+
class StreamEndToken(Token):
|
|
40
|
+
id = '<stream end>'
|
|
41
|
+
|
|
42
|
+
class BlockSequenceStartToken(Token):
|
|
43
|
+
id = '<block sequence start>'
|
|
44
|
+
|
|
45
|
+
class BlockMappingStartToken(Token):
|
|
46
|
+
id = '<block mapping start>'
|
|
47
|
+
|
|
48
|
+
class BlockEndToken(Token):
|
|
49
|
+
id = '<block end>'
|
|
50
|
+
|
|
51
|
+
class FlowSequenceStartToken(Token):
|
|
52
|
+
id = '['
|
|
53
|
+
|
|
54
|
+
class FlowMappingStartToken(Token):
|
|
55
|
+
id = '{'
|
|
56
|
+
|
|
57
|
+
class FlowSequenceEndToken(Token):
|
|
58
|
+
id = ']'
|
|
59
|
+
|
|
60
|
+
class FlowMappingEndToken(Token):
|
|
61
|
+
id = '}'
|
|
62
|
+
|
|
63
|
+
class KeyToken(Token):
|
|
64
|
+
id = '?'
|
|
65
|
+
|
|
66
|
+
class ValueToken(Token):
|
|
67
|
+
id = ':'
|
|
68
|
+
|
|
69
|
+
class BlockEntryToken(Token):
|
|
70
|
+
id = '-'
|
|
71
|
+
|
|
72
|
+
class FlowEntryToken(Token):
|
|
73
|
+
id = ','
|
|
74
|
+
|
|
75
|
+
class AliasToken(Token):
|
|
76
|
+
id = '<alias>'
|
|
77
|
+
def __init__(self, value, start_mark, end_mark):
|
|
78
|
+
self.value = value
|
|
79
|
+
self.start_mark = start_mark
|
|
80
|
+
self.end_mark = end_mark
|
|
81
|
+
|
|
82
|
+
class AnchorToken(Token):
|
|
83
|
+
id = '<anchor>'
|
|
84
|
+
def __init__(self, value, start_mark, end_mark):
|
|
85
|
+
self.value = value
|
|
86
|
+
self.start_mark = start_mark
|
|
87
|
+
self.end_mark = end_mark
|
|
88
|
+
|
|
89
|
+
class TagToken(Token):
|
|
90
|
+
id = '<tag>'
|
|
91
|
+
def __init__(self, value, start_mark, end_mark):
|
|
92
|
+
self.value = value
|
|
93
|
+
self.start_mark = start_mark
|
|
94
|
+
self.end_mark = end_mark
|
|
95
|
+
|
|
96
|
+
class ScalarToken(Token):
|
|
97
|
+
id = '<scalar>'
|
|
98
|
+
def __init__(self, value, plain, start_mark, end_mark, style=None):
|
|
99
|
+
self.value = value
|
|
100
|
+
self.plain = plain
|
|
101
|
+
self.start_mark = start_mark
|
|
102
|
+
self.end_mark = end_mark
|
|
103
|
+
self.style = style
|
|
104
|
+
|
metaflow/cli.py
CHANGED
|
@@ -7,6 +7,7 @@ from datetime import datetime
|
|
|
7
7
|
|
|
8
8
|
import metaflow.tracing as tracing
|
|
9
9
|
from metaflow._vendor import click
|
|
10
|
+
from metaflow.system import _system_logger, _system_monitor
|
|
10
11
|
|
|
11
12
|
from . import decorators, lint, metaflow_version, parameters, plugins
|
|
12
13
|
from .cli_args import cli_args
|
|
@@ -26,7 +27,6 @@ from .metaflow_config import (
|
|
|
26
27
|
DEFAULT_PACKAGE_SUFFIXES,
|
|
27
28
|
)
|
|
28
29
|
from .metaflow_current import current
|
|
29
|
-
from metaflow.system import _system_monitor, _system_logger
|
|
30
30
|
from .metaflow_environment import MetaflowEnvironment
|
|
31
31
|
from .plugins import (
|
|
32
32
|
DATASTORES,
|
|
@@ -37,9 +37,9 @@ from .plugins import (
|
|
|
37
37
|
)
|
|
38
38
|
from .pylint_wrapper import PyLint
|
|
39
39
|
from .R import metaflow_r_version, use_r
|
|
40
|
-
from .util import get_latest_run_id, resolve_identity
|
|
41
40
|
from .user_configs.config_options import LocalFileInput, config_options
|
|
42
41
|
from .user_configs.config_parameters import ConfigValue
|
|
42
|
+
from .util import get_latest_run_id, resolve_identity
|
|
43
43
|
|
|
44
44
|
ERASE_TO_EOL = "\033[K"
|
|
45
45
|
HIGHLIGHT = "red"
|
|
@@ -55,6 +55,15 @@ def echo_dev_null(*args, **kwargs):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def echo_always(line, **kwargs):
|
|
58
|
+
if kwargs.pop("wrap", False):
|
|
59
|
+
import textwrap
|
|
60
|
+
|
|
61
|
+
indent_str = INDENT if kwargs.get("indent", None) else ""
|
|
62
|
+
effective_width = 80 - len(indent_str)
|
|
63
|
+
wrapped = textwrap.wrap(line, width=effective_width, break_long_words=False)
|
|
64
|
+
line = "\n".join(indent_str + l for l in wrapped)
|
|
65
|
+
kwargs["indent"] = False
|
|
66
|
+
|
|
58
67
|
kwargs["err"] = kwargs.get("err", True)
|
|
59
68
|
if kwargs.pop("indent", None):
|
|
60
69
|
line = "\n".join(INDENT + x for x in line.splitlines())
|
|
@@ -58,21 +58,38 @@ class ArgoClient(object):
|
|
|
58
58
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
def get_workflow_templates(self):
|
|
61
|
+
def get_workflow_templates(self, page_size=100):
|
|
62
62
|
client = self._client.get()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
continue_token = None
|
|
64
|
+
|
|
65
|
+
while True:
|
|
66
|
+
try:
|
|
67
|
+
params = {"limit": page_size}
|
|
68
|
+
if continue_token:
|
|
69
|
+
params["_continue"] = continue_token
|
|
70
|
+
|
|
71
|
+
response = client.CustomObjectsApi().list_namespaced_custom_object(
|
|
72
|
+
group=self._group,
|
|
73
|
+
version=self._version,
|
|
74
|
+
namespace=self._namespace,
|
|
75
|
+
plural="workflowtemplates",
|
|
76
|
+
**params,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
for item in response.get("items", []):
|
|
80
|
+
yield item
|
|
81
|
+
|
|
82
|
+
metadata = response.get("metadata", {})
|
|
83
|
+
continue_token = metadata.get("continue")
|
|
84
|
+
|
|
85
|
+
if not continue_token:
|
|
86
|
+
break
|
|
87
|
+
except client.rest.ApiException as e:
|
|
88
|
+
if e.status == 404:
|
|
89
|
+
return None
|
|
90
|
+
raise ArgoClientException(
|
|
91
|
+
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
92
|
+
)
|
|
76
93
|
|
|
77
94
|
def register_workflow_template(self, name, workflow_template):
|
|
78
95
|
# Unfortunately, Kubernetes client does not handle optimistic
|
|
@@ -214,23 +214,14 @@ class ArgoWorkflows(object):
|
|
|
214
214
|
return name.replace(".", "-")
|
|
215
215
|
|
|
216
216
|
@staticmethod
|
|
217
|
-
def list_templates(flow_name, all=False):
|
|
217
|
+
def list_templates(flow_name, all=False, page_size=100):
|
|
218
218
|
client = ArgoClient(namespace=KUBERNETES_NAMESPACE)
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
template["metadata"]["name"]
|
|
226
|
-
for template in templates
|
|
227
|
-
if all
|
|
228
|
-
or flow_name
|
|
229
|
-
== template["metadata"]
|
|
230
|
-
.get("annotations", {})
|
|
231
|
-
.get("metaflow/flow_name", None)
|
|
232
|
-
]
|
|
233
|
-
return template_names
|
|
220
|
+
for template in client.get_workflow_templates(page_size=page_size):
|
|
221
|
+
if all or flow_name == template["metadata"].get("annotations", {}).get(
|
|
222
|
+
"metaflow/flow_name", None
|
|
223
|
+
):
|
|
224
|
+
yield template["metadata"]["name"]
|
|
234
225
|
|
|
235
226
|
@staticmethod
|
|
236
227
|
def delete(name):
|
|
@@ -3275,8 +3266,8 @@ class ArgoWorkflows(object):
|
|
|
3275
3266
|
Trigger().template(
|
|
3276
3267
|
TriggerTemplate(self.name)
|
|
3277
3268
|
# Trigger a deployed workflow template
|
|
3278
|
-
.
|
|
3279
|
-
|
|
3269
|
+
.k8s_trigger(
|
|
3270
|
+
StandardK8STrigger()
|
|
3280
3271
|
.source(
|
|
3281
3272
|
{
|
|
3282
3273
|
"resource": {
|
|
@@ -4254,6 +4245,10 @@ class TriggerTemplate(object):
|
|
|
4254
4245
|
self.payload = tree()
|
|
4255
4246
|
self.payload["name"] = name
|
|
4256
4247
|
|
|
4248
|
+
def k8s_trigger(self, k8s_trigger):
|
|
4249
|
+
self.payload["k8s"] = k8s_trigger.to_json()
|
|
4250
|
+
return self
|
|
4251
|
+
|
|
4257
4252
|
def argo_workflow_trigger(self, argo_workflow_trigger):
|
|
4258
4253
|
self.payload["argoWorkflow"] = argo_workflow_trigger.to_json()
|
|
4259
4254
|
return self
|
|
@@ -4328,3 +4323,57 @@ class TriggerParameter(object):
|
|
|
4328
4323
|
|
|
4329
4324
|
def __str__(self):
|
|
4330
4325
|
return json.dumps(self.payload, indent=4)
|
|
4326
|
+
|
|
4327
|
+
|
|
4328
|
+
class StandardK8STrigger(object):
|
|
4329
|
+
# https://pkg.go.dev/github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1#StandardK8STrigger
|
|
4330
|
+
|
|
4331
|
+
def __init__(self):
|
|
4332
|
+
tree = lambda: defaultdict(tree)
|
|
4333
|
+
self.payload = tree()
|
|
4334
|
+
self.payload["operation"] = "create"
|
|
4335
|
+
|
|
4336
|
+
def operation(self, operation):
|
|
4337
|
+
self.payload["operation"] = operation
|
|
4338
|
+
return self
|
|
4339
|
+
|
|
4340
|
+
def group(self, group):
|
|
4341
|
+
self.payload["group"] = group
|
|
4342
|
+
return self
|
|
4343
|
+
|
|
4344
|
+
def version(self, version):
|
|
4345
|
+
self.payload["version"] = version
|
|
4346
|
+
return self
|
|
4347
|
+
|
|
4348
|
+
def resource(self, resource):
|
|
4349
|
+
self.payload["resource"] = resource
|
|
4350
|
+
return self
|
|
4351
|
+
|
|
4352
|
+
def namespace(self, namespace):
|
|
4353
|
+
self.payload["namespace"] = namespace
|
|
4354
|
+
return self
|
|
4355
|
+
|
|
4356
|
+
def source(self, source):
|
|
4357
|
+
self.payload["source"] = source
|
|
4358
|
+
return self
|
|
4359
|
+
|
|
4360
|
+
def parameters(self, trigger_parameters):
|
|
4361
|
+
if "parameters" not in self.payload:
|
|
4362
|
+
self.payload["parameters"] = []
|
|
4363
|
+
for trigger_parameter in trigger_parameters:
|
|
4364
|
+
self.payload["parameters"].append(trigger_parameter.to_json())
|
|
4365
|
+
return self
|
|
4366
|
+
|
|
4367
|
+
def live_object(self, live_object=True):
|
|
4368
|
+
self.payload["liveObject"] = live_object
|
|
4369
|
+
return self
|
|
4370
|
+
|
|
4371
|
+
def patch_strategy(self, patch_strategy):
|
|
4372
|
+
self.payload["patchStrategy"] = patch_strategy
|
|
4373
|
+
return self
|
|
4374
|
+
|
|
4375
|
+
def to_json(self):
|
|
4376
|
+
return self.payload
|
|
4377
|
+
|
|
4378
|
+
def __str__(self):
|
|
4379
|
+
return json.dumps(self.payload, indent=4)
|
|
@@ -998,8 +998,7 @@ def terminate(obj, run_id, authorize=None):
|
|
|
998
998
|
)
|
|
999
999
|
@click.pass_obj
|
|
1000
1000
|
def list_workflow_templates(obj, all=None):
|
|
1001
|
-
|
|
1002
|
-
for template_name in templates:
|
|
1001
|
+
for template_name in ArgoWorkflows.list_templates(obj.flow.name, all):
|
|
1003
1002
|
obj.echo_always(template_name)
|
|
1004
1003
|
|
|
1005
1004
|
|
|
@@ -203,6 +203,38 @@ class ArgoWorkflowsDeployedFlow(DeployedFlow):
|
|
|
203
203
|
|
|
204
204
|
TYPE: ClassVar[Optional[str]] = "argo-workflows"
|
|
205
205
|
|
|
206
|
+
@classmethod
|
|
207
|
+
def list_deployed_flows(cls, flow_name: Optional[str] = None):
|
|
208
|
+
"""
|
|
209
|
+
List all deployed Argo Workflow templates.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
flow_name : str, optional, default None
|
|
214
|
+
If specified, only list deployed flows for this specific flow name.
|
|
215
|
+
If None, list all deployed flows.
|
|
216
|
+
|
|
217
|
+
Yields
|
|
218
|
+
------
|
|
219
|
+
ArgoWorkflowsDeployedFlow
|
|
220
|
+
`ArgoWorkflowsDeployedFlow` objects representing deployed
|
|
221
|
+
workflow templates on Argo Workflows.
|
|
222
|
+
"""
|
|
223
|
+
from metaflow.plugins.argo.argo_workflows import ArgoWorkflows
|
|
224
|
+
|
|
225
|
+
# When flow_name is None, use all=True to get all templates
|
|
226
|
+
# When flow_name is specified, use all=False to filter by flow_name
|
|
227
|
+
all_templates = flow_name is None
|
|
228
|
+
for template_name in ArgoWorkflows.list_templates(
|
|
229
|
+
flow_name=flow_name, all=all_templates
|
|
230
|
+
):
|
|
231
|
+
try:
|
|
232
|
+
deployed_flow = cls.from_deployment(template_name)
|
|
233
|
+
yield deployed_flow
|
|
234
|
+
except Exception:
|
|
235
|
+
# Skip templates that can't be converted to DeployedFlow objects
|
|
236
|
+
continue
|
|
237
|
+
|
|
206
238
|
@classmethod
|
|
207
239
|
def from_deployment(cls, identifier: str, metadata: Optional[str] = None):
|
|
208
240
|
"""
|
|
@@ -278,6 +310,43 @@ class ArgoWorkflowsDeployedFlow(DeployedFlow):
|
|
|
278
310
|
|
|
279
311
|
return cls(deployer=d)
|
|
280
312
|
|
|
313
|
+
@classmethod
|
|
314
|
+
def get_triggered_run(
|
|
315
|
+
cls, identifier: str, run_id: str, metadata: Optional[str] = None
|
|
316
|
+
):
|
|
317
|
+
"""
|
|
318
|
+
Retrieves a `ArgoWorkflowsTriggeredRun` object from an identifier, a run id and
|
|
319
|
+
optional metadata.
|
|
320
|
+
|
|
321
|
+
Parameters
|
|
322
|
+
----------
|
|
323
|
+
identifier : str
|
|
324
|
+
Deployer specific identifier for the workflow to retrieve
|
|
325
|
+
run_id : str
|
|
326
|
+
Run ID for the which to fetch the triggered run object
|
|
327
|
+
metadata : str, optional, default None
|
|
328
|
+
Optional deployer specific metadata.
|
|
329
|
+
|
|
330
|
+
Returns
|
|
331
|
+
-------
|
|
332
|
+
ArgoWorkflowsTriggeredRun
|
|
333
|
+
A `ArgoWorkflowsTriggeredRun` object representing the
|
|
334
|
+
triggered run on argo workflows.
|
|
335
|
+
"""
|
|
336
|
+
deployed_flow_obj = cls.from_deployment(identifier, metadata)
|
|
337
|
+
return ArgoWorkflowsTriggeredRun(
|
|
338
|
+
deployer=deployed_flow_obj.deployer,
|
|
339
|
+
content=json.dumps(
|
|
340
|
+
{
|
|
341
|
+
"metadata": deployed_flow_obj.deployer.metadata,
|
|
342
|
+
"pathspec": "/".join(
|
|
343
|
+
(deployed_flow_obj.deployer.flow_name, run_id)
|
|
344
|
+
),
|
|
345
|
+
"name": run_id,
|
|
346
|
+
}
|
|
347
|
+
),
|
|
348
|
+
)
|
|
349
|
+
|
|
281
350
|
@property
|
|
282
351
|
def production_token(self) -> Optional[str]:
|
|
283
352
|
"""
|
|
@@ -56,6 +56,20 @@ class StepFunctionsDeployedFlow(DeployedFlow):
|
|
|
56
56
|
|
|
57
57
|
TYPE: ClassVar[Optional[str]] = "step-functions"
|
|
58
58
|
|
|
59
|
+
@classmethod
|
|
60
|
+
def list_deployed_flows(cls, flow_name: Optional[str] = None):
|
|
61
|
+
"""
|
|
62
|
+
This method is not currently implemented for Step Functions.
|
|
63
|
+
|
|
64
|
+
Raises
|
|
65
|
+
------
|
|
66
|
+
NotImplementedError
|
|
67
|
+
This method is not implemented for Step Functions.
|
|
68
|
+
"""
|
|
69
|
+
raise NotImplementedError(
|
|
70
|
+
"list_deployed_flows is not implemented for StepFunctions"
|
|
71
|
+
)
|
|
72
|
+
|
|
59
73
|
@classmethod
|
|
60
74
|
def from_deployment(cls, identifier: str, metadata: Optional[str] = None):
|
|
61
75
|
"""
|
|
@@ -70,6 +84,22 @@ class StepFunctionsDeployedFlow(DeployedFlow):
|
|
|
70
84
|
"from_deployment is not implemented for StepFunctions"
|
|
71
85
|
)
|
|
72
86
|
|
|
87
|
+
@classmethod
|
|
88
|
+
def get_triggered_run(
|
|
89
|
+
cls, identifier: str, run_id: str, metadata: Optional[str] = None
|
|
90
|
+
):
|
|
91
|
+
"""
|
|
92
|
+
This method is not currently implemented for Step Functions.
|
|
93
|
+
|
|
94
|
+
Raises
|
|
95
|
+
------
|
|
96
|
+
NotImplementedError
|
|
97
|
+
This method is not implemented for Step Functions.
|
|
98
|
+
"""
|
|
99
|
+
raise NotImplementedError(
|
|
100
|
+
"get_triggered_run is not implemented for StepFunctions"
|
|
101
|
+
)
|
|
102
|
+
|
|
73
103
|
@property
|
|
74
104
|
def production_token(self: DeployedFlow) -> Optional[str]:
|
|
75
105
|
"""
|
|
@@ -146,7 +146,13 @@ class TaskToDict:
|
|
|
146
146
|
# Python 3.13 removes the standard ``imghdr`` module. Metaflow
|
|
147
147
|
# vendors a copy so we can keep using ``what`` to detect image
|
|
148
148
|
# formats irrespective of the Python version.
|
|
149
|
-
|
|
149
|
+
import warnings
|
|
150
|
+
|
|
151
|
+
with warnings.catch_warnings():
|
|
152
|
+
warnings.filterwarnings(
|
|
153
|
+
"ignore", category=DeprecationWarning, module="imghdr"
|
|
154
|
+
)
|
|
155
|
+
from metaflow._vendor import imghdr
|
|
150
156
|
|
|
151
157
|
resp = imghdr.what(None, h=data_object)
|
|
152
158
|
# Only accept types supported on the web
|
|
@@ -6,6 +6,7 @@ from collections import namedtuple
|
|
|
6
6
|
from metaflow.exception import MetaflowException
|
|
7
7
|
from metaflow.metaflow_config import KUBERNETES_JOBSET_GROUP, KUBERNETES_JOBSET_VERSION
|
|
8
8
|
from metaflow.tracing import inject_tracing_vars
|
|
9
|
+
from metaflow._vendor import yaml
|
|
9
10
|
|
|
10
11
|
from .kube_utils import qos_requests_and_limits
|
|
11
12
|
|
|
@@ -1025,34 +1026,32 @@ class KubernetesArgoJobSet(object):
|
|
|
1025
1026
|
|
|
1026
1027
|
def dump(self):
|
|
1027
1028
|
client = self._kubernetes_sdk
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
),
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
),
|
|
1052
|
-
status=None,
|
|
1053
|
-
)
|
|
1029
|
+
js_dict = client.ApiClient().sanitize_for_serialization(
|
|
1030
|
+
dict(
|
|
1031
|
+
apiVersion=self._group + "/" + self._version,
|
|
1032
|
+
kind="JobSet",
|
|
1033
|
+
metadata=client.api_client.ApiClient().sanitize_for_serialization(
|
|
1034
|
+
client.V1ObjectMeta(
|
|
1035
|
+
name=self.name,
|
|
1036
|
+
labels=self._labels,
|
|
1037
|
+
annotations=self._annotations,
|
|
1038
|
+
)
|
|
1039
|
+
),
|
|
1040
|
+
spec=dict(
|
|
1041
|
+
replicatedJobs=[self.control.dump(), self.worker.dump()],
|
|
1042
|
+
suspend=False,
|
|
1043
|
+
startupPolicy=None,
|
|
1044
|
+
successPolicy=None,
|
|
1045
|
+
# The Failure Policy helps setting the number of retries for the jobset.
|
|
1046
|
+
# but we don't rely on it and instead rely on either the local scheduler
|
|
1047
|
+
# or the Argo Workflows to handle retries.
|
|
1048
|
+
failurePolicy=None,
|
|
1049
|
+
network=None,
|
|
1050
|
+
),
|
|
1051
|
+
status=None,
|
|
1054
1052
|
)
|
|
1055
1053
|
)
|
|
1054
|
+
data = yaml.dump(js_dict, default_flow_style=False, indent=2)
|
|
1056
1055
|
# The values we populate in the Jobset manifest (for Argo Workflows) piggybacks on the Argo Workflow's templating engine.
|
|
1057
1056
|
# Even though Argo Workflows's templating helps us constructing all the necessary IDs and populating the fields
|
|
1058
1057
|
# required by Metaflow, we run into one glitch. When we construct JSON/YAML serializable objects,
|
|
@@ -1067,7 +1066,6 @@ class KubernetesArgoJobSet(object):
|
|
|
1067
1066
|
# Since the value of `num_parallel` can be dynamic and can change from run to run, we need to ensure that the
|
|
1068
1067
|
# value can be passed-down dynamically and is **explicitly set as a integer** in the Jobset Manifest submitted as a
|
|
1069
1068
|
# part of the Argo Workflow
|
|
1070
|
-
|
|
1071
|
-
quoted_substring = '"{{=asInt(inputs.parameters.workerCount)}}"'
|
|
1069
|
+
quoted_substring = "'{{=asInt(inputs.parameters.workerCount)}}'"
|
|
1072
1070
|
unquoted_substring = "{{=asInt(inputs.parameters.workerCount)}}"
|
|
1073
1071
|
return data.replace(quoted_substring, unquoted_substring)
|