ob-metaflow 2.16.8.2rc1__py2.py3-none-any.whl → 2.17.0.1__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 +7 -1
- 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/cli_components/run_cmds.py +0 -15
- metaflow/client/core.py +6 -1
- metaflow/extension_support/__init__.py +4 -3
- metaflow/flowspec.py +1 -113
- metaflow/graph.py +10 -134
- metaflow/lint.py +3 -70
- metaflow/metaflow_environment.py +14 -6
- metaflow/package/__init__.py +18 -9
- metaflow/packaging_sys/__init__.py +53 -43
- metaflow/packaging_sys/backend.py +21 -6
- metaflow/packaging_sys/tar_backend.py +16 -3
- metaflow/packaging_sys/v1.py +21 -21
- metaflow/plugins/argo/argo_client.py +31 -14
- metaflow/plugins/argo/argo_workflows.py +67 -22
- metaflow/plugins/argo/argo_workflows_cli.py +348 -85
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +69 -0
- metaflow/plugins/aws/step_functions/step_functions.py +0 -6
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_modules/basic.py +3 -14
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +7 -1
- metaflow/plugins/kubernetes/kubernetes_decorator.py +1 -1
- metaflow/plugins/kubernetes/kubernetes_job.py +8 -2
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +26 -28
- metaflow/plugins/pypi/conda_decorator.py +4 -2
- metaflow/runner/click_api.py +14 -7
- metaflow/runner/deployer.py +160 -7
- metaflow/runner/subprocess_manager.py +20 -12
- metaflow/runtime.py +27 -102
- metaflow/task.py +25 -46
- metaflow/user_decorators/mutable_flow.py +3 -1
- metaflow/util.py +0 -29
- metaflow/vendor.py +23 -6
- metaflow/version.py +1 -1
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.dist-info}/METADATA +2 -2
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.dist-info}/RECORD +62 -45
- {ob_metaflow-2.16.8.2rc1.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/Makefile +0 -0
- {ob_metaflow-2.16.8.2rc1.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {ob_metaflow-2.16.8.2rc1.data → ob_metaflow-2.17.0.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.16.8.2rc1.dist-info → ob_metaflow-2.17.0.1.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 .packaging_sys import MetaflowCodeContent
|
|
32
32
|
from .plugins import (
|
|
@@ -38,9 +38,9 @@ from .plugins import (
|
|
|
38
38
|
)
|
|
39
39
|
from .pylint_wrapper import PyLint
|
|
40
40
|
from .R import metaflow_r_version, use_r
|
|
41
|
-
from .util import get_latest_run_id, resolve_identity
|
|
42
41
|
from .user_configs.config_options import LocalFileInput, config_options
|
|
43
42
|
from .user_configs.config_parameters import ConfigValue
|
|
43
|
+
from .util import get_latest_run_id, resolve_identity
|
|
44
44
|
|
|
45
45
|
ERASE_TO_EOL = "\033[K"
|
|
46
46
|
HIGHLIGHT = "red"
|
|
@@ -56,6 +56,15 @@ def echo_dev_null(*args, **kwargs):
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def echo_always(line, **kwargs):
|
|
59
|
+
if kwargs.pop("wrap", False):
|
|
60
|
+
import textwrap
|
|
61
|
+
|
|
62
|
+
indent_str = INDENT if kwargs.get("indent", None) else ""
|
|
63
|
+
effective_width = 80 - len(indent_str)
|
|
64
|
+
wrapped = textwrap.wrap(line, width=effective_width, break_long_words=False)
|
|
65
|
+
line = "\n".join(indent_str + l for l in wrapped)
|
|
66
|
+
kwargs["indent"] = False
|
|
67
|
+
|
|
59
68
|
kwargs["err"] = kwargs.get("err", True)
|
|
60
69
|
if kwargs.pop("indent", None):
|
|
61
70
|
line = "\n".join(INDENT + x for x in line.splitlines())
|
|
@@ -13,8 +13,6 @@ from ..package import MetaflowPackage
|
|
|
13
13
|
from ..runtime import NativeRuntime
|
|
14
14
|
from ..system import _system_logger
|
|
15
15
|
|
|
16
|
-
# from ..client.core import Run
|
|
17
|
-
|
|
18
16
|
from ..tagging_util import validate_tags
|
|
19
17
|
from ..util import get_latest_run_id, write_latest_run_id
|
|
20
18
|
|
|
@@ -232,19 +230,6 @@ def resume(
|
|
|
232
230
|
step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
|
|
233
231
|
)
|
|
234
232
|
)
|
|
235
|
-
|
|
236
|
-
## TODO: instead of checking execution path here, can add a warning later
|
|
237
|
-
## instead of throwing an error. This is for resuming a step which was not
|
|
238
|
-
## taken inside a branch i.e. not present in the execution path.
|
|
239
|
-
|
|
240
|
-
# origin_run = Run(f"{obj.flow.name}/{origin_run_id}", _namespace_check=False)
|
|
241
|
-
# executed_steps = {step.path_components[-1] for step in origin_run}
|
|
242
|
-
# if step_to_rerun not in executed_steps:
|
|
243
|
-
# raise CommandException(
|
|
244
|
-
# f"Cannot resume from step '{step_to_rerun}'. This step was not "
|
|
245
|
-
# f"part of the original execution path for run '{origin_run_id}'."
|
|
246
|
-
# )
|
|
247
|
-
|
|
248
233
|
steps_to_rerun = {step_to_rerun}
|
|
249
234
|
|
|
250
235
|
if run_id:
|
metaflow/client/core.py
CHANGED
|
@@ -831,10 +831,12 @@ class MetaflowCode(object):
|
|
|
831
831
|
)
|
|
832
832
|
self._code_obj = BytesIO(blobdata)
|
|
833
833
|
self._info = MetaflowPackage.cls_get_info(self._code_metadata, self._code_obj)
|
|
834
|
+
self._code_obj.seek(0)
|
|
834
835
|
if self._info:
|
|
835
836
|
self._flowspec = MetaflowPackage.cls_get_content(
|
|
836
837
|
self._code_metadata, self._code_obj, self._info["script"]
|
|
837
838
|
)
|
|
839
|
+
self._code_obj.seek(0)
|
|
838
840
|
else:
|
|
839
841
|
raise MetaflowInternalError("Code package metadata is invalid.")
|
|
840
842
|
|
|
@@ -885,7 +887,9 @@ class MetaflowCode(object):
|
|
|
885
887
|
TarFile for everything in this code package
|
|
886
888
|
"""
|
|
887
889
|
if self._backend.type == "tgz":
|
|
888
|
-
|
|
890
|
+
to_return = self._backend.cls_open(self._code_obj)
|
|
891
|
+
self._code_obj.seek(0)
|
|
892
|
+
return to_return
|
|
889
893
|
raise RuntimeError("Archive is not a tarball")
|
|
890
894
|
|
|
891
895
|
def extract(self) -> TemporaryDirectory:
|
|
@@ -921,6 +925,7 @@ class MetaflowCode(object):
|
|
|
921
925
|
MetaflowPackage.cls_extract_into(
|
|
922
926
|
self._code_metadata, self._code_obj, tmp.name, ContentType.USER_CONTENT
|
|
923
927
|
)
|
|
928
|
+
self._code_obj.seek(0)
|
|
924
929
|
return tmp
|
|
925
930
|
|
|
926
931
|
@property
|
|
@@ -205,9 +205,10 @@ def package_mfext_all():
|
|
|
205
205
|
# the packaged metaflow_extensions directory "self-contained" so that
|
|
206
206
|
# python doesn't go and search other parts of the system for more
|
|
207
207
|
# metaflow_extensions.
|
|
208
|
-
|
|
209
|
-
os.path.
|
|
210
|
-
|
|
208
|
+
if _all_packages:
|
|
209
|
+
yield os.path.join(
|
|
210
|
+
os.path.dirname(os.path.abspath(__file__)), "_empty_file.py"
|
|
211
|
+
), os.path.join(EXT_PKG, "__init__.py")
|
|
211
212
|
|
|
212
213
|
for p in _all_packages:
|
|
213
214
|
for path_tuple in package_mfext_package(p):
|
metaflow/flowspec.py
CHANGED
|
@@ -788,35 +788,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
788
788
|
value = item if _is_primitive_type(item) else reprlib.Repr().repr(item)
|
|
789
789
|
return basestring(value)[:MAXIMUM_FOREACH_VALUE_CHARS]
|
|
790
790
|
|
|
791
|
-
def _validate_switch_cases(self, switch_cases, step):
|
|
792
|
-
resolved_cases = {}
|
|
793
|
-
for case_key, step_method in switch_cases.items():
|
|
794
|
-
if isinstance(case_key, str) and case_key.startswith("config:"):
|
|
795
|
-
full_path = case_key[len("config:") :]
|
|
796
|
-
parts = full_path.split(".", 1)
|
|
797
|
-
if len(parts) == 2:
|
|
798
|
-
config_var_name, config_key_name = parts
|
|
799
|
-
try:
|
|
800
|
-
config_obj = getattr(self, config_var_name)
|
|
801
|
-
resolved_key = str(getattr(config_obj, config_key_name))
|
|
802
|
-
except AttributeError:
|
|
803
|
-
msg = (
|
|
804
|
-
"Step *{step}* references unknown config '{path}' "
|
|
805
|
-
"in switch case.".format(step=step, path=full_path)
|
|
806
|
-
)
|
|
807
|
-
raise InvalidNextException(msg)
|
|
808
|
-
else:
|
|
809
|
-
raise MetaflowInternalError(
|
|
810
|
-
"Invalid config path format in switch case."
|
|
811
|
-
)
|
|
812
|
-
else:
|
|
813
|
-
resolved_key = case_key
|
|
814
|
-
|
|
815
|
-
func_name = step_method.__func__.__name__
|
|
816
|
-
resolved_cases[resolved_key] = func_name
|
|
817
|
-
|
|
818
|
-
return resolved_cases
|
|
819
|
-
|
|
820
791
|
def next(self, *dsts: Callable[..., None], **kwargs) -> None:
|
|
821
792
|
"""
|
|
822
793
|
Indicates the next step to execute after this step has completed.
|
|
@@ -841,15 +812,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
841
812
|
evaluates to an iterator. A task will be launched for each value in the iterator and
|
|
842
813
|
each task will execute the code specified by the step `foreach_step`.
|
|
843
814
|
|
|
844
|
-
- Switch statement:
|
|
845
|
-
```
|
|
846
|
-
self.next({"case1": self.step_a, "case2": self.step_b}, condition='condition_variable')
|
|
847
|
-
```
|
|
848
|
-
In this situation, `step_a` and `step_b` are methods in the current class decorated
|
|
849
|
-
with the `@step` decorator and `condition_variable` is a variable name in the current
|
|
850
|
-
class. The value of the condition variable determines which step to execute. If the
|
|
851
|
-
value doesn't match any of the dictionary keys, a RuntimeError is raised.
|
|
852
|
-
|
|
853
815
|
Parameters
|
|
854
816
|
----------
|
|
855
817
|
dsts : Callable[..., None]
|
|
@@ -865,7 +827,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
865
827
|
|
|
866
828
|
foreach = kwargs.pop("foreach", None)
|
|
867
829
|
num_parallel = kwargs.pop("num_parallel", None)
|
|
868
|
-
condition = kwargs.pop("condition", None)
|
|
869
830
|
if kwargs:
|
|
870
831
|
kw = next(iter(kwargs))
|
|
871
832
|
msg = (
|
|
@@ -882,79 +843,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
882
843
|
)
|
|
883
844
|
raise InvalidNextException(msg)
|
|
884
845
|
|
|
885
|
-
# check: switch case using condition
|
|
886
|
-
if condition is not None:
|
|
887
|
-
if len(dsts) != 1 or not isinstance(dsts[0], dict) or not dsts[0]:
|
|
888
|
-
msg = (
|
|
889
|
-
"Step *{step}* has an invalid self.next() transition. "
|
|
890
|
-
"When using 'condition', the transition must be to a single, "
|
|
891
|
-
"non-empty dictionary mapping condition values to step methods.".format(
|
|
892
|
-
step=step
|
|
893
|
-
)
|
|
894
|
-
)
|
|
895
|
-
raise InvalidNextException(msg)
|
|
896
|
-
|
|
897
|
-
if not isinstance(condition, basestring):
|
|
898
|
-
msg = (
|
|
899
|
-
"Step *{step}* has an invalid self.next() transition. "
|
|
900
|
-
"The argument to 'condition' must be a string.".format(step=step)
|
|
901
|
-
)
|
|
902
|
-
raise InvalidNextException(msg)
|
|
903
|
-
|
|
904
|
-
if foreach is not None or num_parallel is not None:
|
|
905
|
-
msg = (
|
|
906
|
-
"Step *{step}* has an invalid self.next() transition. "
|
|
907
|
-
"Switch statements cannot be combined with foreach or num_parallel.".format(
|
|
908
|
-
step=step
|
|
909
|
-
)
|
|
910
|
-
)
|
|
911
|
-
raise InvalidNextException(msg)
|
|
912
|
-
|
|
913
|
-
switch_cases = dsts[0]
|
|
914
|
-
|
|
915
|
-
# Validate that condition variable exists
|
|
916
|
-
try:
|
|
917
|
-
condition_value = getattr(self, condition)
|
|
918
|
-
except AttributeError:
|
|
919
|
-
msg = (
|
|
920
|
-
"Condition variable *self.{var}* in step *{step}* "
|
|
921
|
-
"does not exist. Make sure you set self.{var} in this step.".format(
|
|
922
|
-
step=step, var=condition
|
|
923
|
-
)
|
|
924
|
-
)
|
|
925
|
-
raise InvalidNextException(msg)
|
|
926
|
-
|
|
927
|
-
resolved_switch_cases = self._validate_switch_cases(switch_cases, step)
|
|
928
|
-
|
|
929
|
-
if str(condition_value) not in resolved_switch_cases:
|
|
930
|
-
available_cases = list(resolved_switch_cases.keys())
|
|
931
|
-
raise RuntimeError(
|
|
932
|
-
f"Switch condition variable '{condition}' has value '{condition_value}' "
|
|
933
|
-
f"which is not in the available cases: {available_cases}"
|
|
934
|
-
)
|
|
935
|
-
|
|
936
|
-
# Get the chosen step and set transition directly
|
|
937
|
-
chosen_step = resolved_switch_cases[str(condition_value)]
|
|
938
|
-
|
|
939
|
-
# Validate that the chosen step exists
|
|
940
|
-
if not hasattr(self, chosen_step):
|
|
941
|
-
msg = (
|
|
942
|
-
"Step *{step}* specifies a switch transition to an "
|
|
943
|
-
"unknown step, *{name}*.".format(step=step, name=chosen_step)
|
|
944
|
-
)
|
|
945
|
-
raise InvalidNextException(msg)
|
|
946
|
-
|
|
947
|
-
self._transition = ([chosen_step], None)
|
|
948
|
-
return
|
|
949
|
-
|
|
950
|
-
# Check for an invalid transition: a dictionary used without a 'condition' parameter.
|
|
951
|
-
if len(dsts) == 1 and isinstance(dsts[0], dict):
|
|
952
|
-
msg = (
|
|
953
|
-
"Step *{step}* has an invalid self.next() transition. "
|
|
954
|
-
"Dictionary argument requires 'condition' parameter.".format(step=step)
|
|
955
|
-
)
|
|
956
|
-
raise InvalidNextException(msg)
|
|
957
|
-
|
|
958
846
|
# check: all destinations are methods of this object
|
|
959
847
|
funcs = []
|
|
960
848
|
for i, dst in enumerate(dsts):
|
|
@@ -1045,7 +933,7 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
1045
933
|
self._foreach_var = foreach
|
|
1046
934
|
|
|
1047
935
|
# check: non-keyword transitions are valid
|
|
1048
|
-
if foreach is None
|
|
936
|
+
if foreach is None:
|
|
1049
937
|
if len(dsts) < 1:
|
|
1050
938
|
msg = (
|
|
1051
939
|
"Step *{step}* has an invalid self.next() transition. "
|