ob-metaflow 2.15.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 (169) hide show
  1. metaflow/__init__.py +10 -3
  2. metaflow/_vendor/imghdr/__init__.py +186 -0
  3. metaflow/_vendor/yaml/__init__.py +427 -0
  4. metaflow/_vendor/yaml/composer.py +139 -0
  5. metaflow/_vendor/yaml/constructor.py +748 -0
  6. metaflow/_vendor/yaml/cyaml.py +101 -0
  7. metaflow/_vendor/yaml/dumper.py +62 -0
  8. metaflow/_vendor/yaml/emitter.py +1137 -0
  9. metaflow/_vendor/yaml/error.py +75 -0
  10. metaflow/_vendor/yaml/events.py +86 -0
  11. metaflow/_vendor/yaml/loader.py +63 -0
  12. metaflow/_vendor/yaml/nodes.py +49 -0
  13. metaflow/_vendor/yaml/parser.py +589 -0
  14. metaflow/_vendor/yaml/reader.py +185 -0
  15. metaflow/_vendor/yaml/representer.py +389 -0
  16. metaflow/_vendor/yaml/resolver.py +227 -0
  17. metaflow/_vendor/yaml/scanner.py +1435 -0
  18. metaflow/_vendor/yaml/serializer.py +111 -0
  19. metaflow/_vendor/yaml/tokens.py +104 -0
  20. metaflow/cards.py +4 -0
  21. metaflow/cli.py +125 -21
  22. metaflow/cli_components/init_cmd.py +1 -0
  23. metaflow/cli_components/run_cmds.py +204 -40
  24. metaflow/cli_components/step_cmd.py +160 -4
  25. metaflow/client/__init__.py +1 -0
  26. metaflow/client/core.py +198 -130
  27. metaflow/client/filecache.py +59 -32
  28. metaflow/cmd/code/__init__.py +2 -1
  29. metaflow/cmd/develop/stub_generator.py +49 -18
  30. metaflow/cmd/develop/stubs.py +9 -27
  31. metaflow/cmd/make_wrapper.py +30 -0
  32. metaflow/datastore/__init__.py +1 -0
  33. metaflow/datastore/content_addressed_store.py +40 -9
  34. metaflow/datastore/datastore_set.py +10 -1
  35. metaflow/datastore/flow_datastore.py +124 -4
  36. metaflow/datastore/spin_datastore.py +91 -0
  37. metaflow/datastore/task_datastore.py +92 -6
  38. metaflow/debug.py +5 -0
  39. metaflow/decorators.py +331 -82
  40. metaflow/extension_support/__init__.py +414 -356
  41. metaflow/extension_support/_empty_file.py +2 -2
  42. metaflow/flowspec.py +322 -82
  43. metaflow/graph.py +178 -15
  44. metaflow/includefile.py +25 -3
  45. metaflow/lint.py +94 -3
  46. metaflow/meta_files.py +13 -0
  47. metaflow/metadata_provider/metadata.py +13 -2
  48. metaflow/metaflow_config.py +66 -4
  49. metaflow/metaflow_environment.py +91 -25
  50. metaflow/metaflow_profile.py +18 -0
  51. metaflow/metaflow_version.py +16 -1
  52. metaflow/package/__init__.py +673 -0
  53. metaflow/packaging_sys/__init__.py +880 -0
  54. metaflow/packaging_sys/backend.py +128 -0
  55. metaflow/packaging_sys/distribution_support.py +153 -0
  56. metaflow/packaging_sys/tar_backend.py +99 -0
  57. metaflow/packaging_sys/utils.py +54 -0
  58. metaflow/packaging_sys/v1.py +527 -0
  59. metaflow/parameters.py +6 -2
  60. metaflow/plugins/__init__.py +6 -0
  61. metaflow/plugins/airflow/airflow.py +11 -1
  62. metaflow/plugins/airflow/airflow_cli.py +16 -5
  63. metaflow/plugins/argo/argo_client.py +42 -20
  64. metaflow/plugins/argo/argo_events.py +6 -6
  65. metaflow/plugins/argo/argo_workflows.py +1023 -344
  66. metaflow/plugins/argo/argo_workflows_cli.py +396 -94
  67. metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
  68. metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
  69. metaflow/plugins/argo/capture_error.py +5 -2
  70. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  71. metaflow/plugins/argo/exit_hooks.py +209 -0
  72. metaflow/plugins/argo/param_val.py +19 -0
  73. metaflow/plugins/aws/aws_client.py +6 -0
  74. metaflow/plugins/aws/aws_utils.py +33 -1
  75. metaflow/plugins/aws/batch/batch.py +72 -5
  76. metaflow/plugins/aws/batch/batch_cli.py +24 -3
  77. metaflow/plugins/aws/batch/batch_decorator.py +57 -6
  78. metaflow/plugins/aws/step_functions/step_functions.py +28 -3
  79. metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
  80. metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
  81. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
  82. metaflow/plugins/cards/card_cli.py +20 -1
  83. metaflow/plugins/cards/card_creator.py +24 -1
  84. metaflow/plugins/cards/card_datastore.py +21 -49
  85. metaflow/plugins/cards/card_decorator.py +58 -6
  86. metaflow/plugins/cards/card_modules/basic.py +38 -9
  87. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  88. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  89. metaflow/plugins/cards/card_modules/components.py +592 -3
  90. metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
  91. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  92. metaflow/plugins/cards/card_modules/main.css +1 -0
  93. metaflow/plugins/cards/card_modules/main.js +56 -41
  94. metaflow/plugins/cards/card_modules/test_cards.py +22 -6
  95. metaflow/plugins/cards/component_serializer.py +1 -8
  96. metaflow/plugins/cards/metadata.py +22 -0
  97. metaflow/plugins/catch_decorator.py +9 -0
  98. metaflow/plugins/datastores/local_storage.py +12 -6
  99. metaflow/plugins/datastores/spin_storage.py +12 -0
  100. metaflow/plugins/datatools/s3/s3.py +49 -17
  101. metaflow/plugins/datatools/s3/s3op.py +113 -66
  102. metaflow/plugins/env_escape/client_modules.py +102 -72
  103. metaflow/plugins/events_decorator.py +127 -121
  104. metaflow/plugins/exit_hook/__init__.py +0 -0
  105. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  106. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  107. metaflow/plugins/kubernetes/kubernetes.py +12 -1
  108. metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
  109. metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
  110. metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
  111. metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
  112. metaflow/plugins/metadata_providers/local.py +76 -82
  113. metaflow/plugins/metadata_providers/service.py +13 -9
  114. metaflow/plugins/metadata_providers/spin.py +16 -0
  115. metaflow/plugins/package_cli.py +36 -24
  116. metaflow/plugins/parallel_decorator.py +11 -2
  117. metaflow/plugins/parsers.py +16 -0
  118. metaflow/plugins/pypi/bootstrap.py +7 -1
  119. metaflow/plugins/pypi/conda_decorator.py +41 -82
  120. metaflow/plugins/pypi/conda_environment.py +14 -6
  121. metaflow/plugins/pypi/micromamba.py +9 -1
  122. metaflow/plugins/pypi/pip.py +41 -5
  123. metaflow/plugins/pypi/pypi_decorator.py +4 -4
  124. metaflow/plugins/pypi/utils.py +22 -0
  125. metaflow/plugins/secrets/__init__.py +3 -0
  126. metaflow/plugins/secrets/secrets_decorator.py +14 -178
  127. metaflow/plugins/secrets/secrets_func.py +49 -0
  128. metaflow/plugins/secrets/secrets_spec.py +101 -0
  129. metaflow/plugins/secrets/utils.py +74 -0
  130. metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
  131. metaflow/plugins/timeout_decorator.py +0 -1
  132. metaflow/plugins/uv/bootstrap.py +29 -1
  133. metaflow/plugins/uv/uv_environment.py +5 -3
  134. metaflow/pylint_wrapper.py +5 -1
  135. metaflow/runner/click_api.py +79 -26
  136. metaflow/runner/deployer.py +208 -6
  137. metaflow/runner/deployer_impl.py +32 -12
  138. metaflow/runner/metaflow_runner.py +266 -33
  139. metaflow/runner/subprocess_manager.py +21 -1
  140. metaflow/runner/utils.py +27 -16
  141. metaflow/runtime.py +660 -66
  142. metaflow/task.py +255 -26
  143. metaflow/user_configs/config_options.py +33 -21
  144. metaflow/user_configs/config_parameters.py +220 -58
  145. metaflow/user_decorators/__init__.py +0 -0
  146. metaflow/user_decorators/common.py +144 -0
  147. metaflow/user_decorators/mutable_flow.py +512 -0
  148. metaflow/user_decorators/mutable_step.py +424 -0
  149. metaflow/user_decorators/user_flow_decorator.py +264 -0
  150. metaflow/user_decorators/user_step_decorator.py +749 -0
  151. metaflow/util.py +197 -7
  152. metaflow/vendor.py +23 -7
  153. metaflow/version.py +1 -1
  154. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
  155. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
  156. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
  157. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
  158. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
  159. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  160. metaflow/_vendor/v3_5/__init__.py +0 -1
  161. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  162. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  163. metaflow/_vendor/v3_5/zipp.py +0 -329
  164. metaflow/info_file.py +0 -25
  165. metaflow/package.py +0 -203
  166. metaflow/user_configs/config_decorators.py +0 -568
  167. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
  168. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
  169. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.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/cards.py CHANGED
@@ -8,7 +8,11 @@ from metaflow.plugins.cards.card_modules.components import (
8
8
  Markdown,
9
9
  VegaChart,
10
10
  ProgressBar,
11
+ ValueBox,
11
12
  PythonCode,
13
+ EventsTimeline,
14
+ JSONViewer,
15
+ YAMLViewer,
12
16
  )
13
17
  from metaflow.plugins.cards.card_modules.basic import (
14
18
  DefaultCard,
metaflow/cli.py CHANGED
@@ -1,5 +1,7 @@
1
+ import os
1
2
  import functools
2
3
  import inspect
4
+ import os
3
5
  import sys
4
6
  import traceback
5
7
  from datetime import datetime
@@ -13,7 +15,7 @@ from .cli_components.utils import LazyGroup, LazyPluginCommandCollection
13
15
  from .datastore import FlowDataStore, TaskDataStoreSet
14
16
  from .debug import debug
15
17
  from .exception import CommandException, MetaflowException
16
- from .flowspec import _FlowState
18
+ from .flowspec import FlowStateItems
17
19
  from .graph import FlowGraph
18
20
  from .metaflow_config import (
19
21
  DEFAULT_DATASTORE,
@@ -25,8 +27,10 @@ from .metaflow_config import (
25
27
  DEFAULT_PACKAGE_SUFFIXES,
26
28
  )
27
29
  from .metaflow_current import current
30
+ from .metaflow_profile import from_start
28
31
  from metaflow.system import _system_monitor, _system_logger
29
32
  from .metaflow_environment import MetaflowEnvironment
33
+ from .packaging_sys import MetaflowCodeContent
30
34
  from .plugins import (
31
35
  DATASTORES,
32
36
  ENVIRONMENTS,
@@ -36,7 +40,7 @@ from .plugins import (
36
40
  )
37
41
  from .pylint_wrapper import PyLint
38
42
  from .R import metaflow_r_version, use_r
39
- from .util import get_latest_run_id, resolve_identity
43
+ from .util import get_latest_run_id, resolve_identity, decompress_list
40
44
  from .user_configs.config_options import LocalFileInput, config_options
41
45
  from .user_configs.config_parameters import ConfigValue
42
46
 
@@ -54,6 +58,15 @@ def echo_dev_null(*args, **kwargs):
54
58
 
55
59
 
56
60
  def echo_always(line, **kwargs):
61
+ if kwargs.pop("wrap", False):
62
+ import textwrap
63
+
64
+ indent_str = INDENT if kwargs.get("indent", None) else ""
65
+ effective_width = 80 - len(indent_str)
66
+ wrapped = textwrap.wrap(line, width=effective_width, break_long_words=False)
67
+ line = "\n".join(indent_str + l for l in wrapped)
68
+ kwargs["indent"] = False
69
+
57
70
  kwargs["err"] = kwargs.get("err", True)
58
71
  if kwargs.pop("indent", None):
59
72
  line = "\n".join(INDENT + x for x in line.splitlines())
@@ -114,6 +127,8 @@ def logger(body="", system_msg=False, head="", bad=False, timestamp=True, nl=Tru
114
127
  "step": "metaflow.cli_components.step_cmd.step",
115
128
  "run": "metaflow.cli_components.run_cmds.run",
116
129
  "resume": "metaflow.cli_components.run_cmds.resume",
130
+ "spin": "metaflow.cli_components.run_cmds.spin",
131
+ "spin-step": "metaflow.cli_components.step_cmd.spin_step",
117
132
  },
118
133
  )
119
134
  def cli(ctx):
@@ -151,8 +166,13 @@ def check(obj, warnings=False):
151
166
  def show(obj):
152
167
  echo_always("\n%s" % obj.graph.doc)
153
168
  for node_name in obj.graph.sorted_nodes:
169
+ echo_always("")
154
170
  node = obj.graph[node_name]
155
- echo_always("\nStep *%s*" % node.name, err=False)
171
+ for deco in node.decorators:
172
+ echo_always("@%s" % deco.name, err=False)
173
+ for deco in node.wrappers:
174
+ echo_always("@%s" % deco.decorator_name, err=False)
175
+ echo_always("Step *%s*" % node.name, err=False)
156
176
  echo_always(node.doc if node.doc else "?", indent=True, err=False)
157
177
  if node.type != "end":
158
178
  echo_always(
@@ -242,6 +262,14 @@ def version(obj):
242
262
  type=click.Choice(["local"] + [m.TYPE for m in ENVIRONMENTS]),
243
263
  help="Execution environment type",
244
264
  )
265
+ @click.option(
266
+ "--force-rebuild-environments/--no-force-rebuild-environments",
267
+ is_flag=True,
268
+ default=False,
269
+ hidden=True,
270
+ type=bool,
271
+ help="Explicitly rebuild the execution environments",
272
+ )
245
273
  # See comment for --quiet
246
274
  @click.option(
247
275
  "--datastore",
@@ -294,12 +322,20 @@ def version(obj):
294
322
  hidden=True,
295
323
  is_eager=True,
296
324
  )
325
+ @click.option(
326
+ "--mode",
327
+ type=click.Choice(["spin"]),
328
+ default=None,
329
+ help="Execution mode for metaflow CLI commands. Use 'spin' to enable "
330
+ "spin metadata and spin datastore for executions",
331
+ )
297
332
  @click.pass_context
298
333
  def start(
299
334
  ctx,
300
335
  quiet=False,
301
336
  metadata=None,
302
337
  environment=None,
338
+ force_rebuild_environments=False,
303
339
  datastore=None,
304
340
  datastore_root=None,
305
341
  decospecs=None,
@@ -310,6 +346,7 @@ def start(
310
346
  local_config_file=None,
311
347
  config=None,
312
348
  config_value=None,
349
+ mode=None,
313
350
  **deco_options
314
351
  ):
315
352
  if quiet:
@@ -322,10 +359,16 @@ def start(
322
359
  if use_r():
323
360
  version = metaflow_r_version()
324
361
 
362
+ from_start("MetaflowCLI: Starting")
325
363
  echo("Metaflow %s" % version, fg="magenta", bold=True, nl=False)
326
364
  echo(" executing *%s*" % ctx.obj.flow.name, fg="magenta", nl=False)
327
365
  echo(" for *%s*" % resolve_identity(), fg="magenta")
328
366
 
367
+ # Check if we need to setup the distribution finder (if running )
368
+ dist_info = MetaflowCodeContent.get_distribution_finder()
369
+ if dist_info:
370
+ sys.meta_path.append(dist_info)
371
+
329
372
  # Setup the context
330
373
  cli_args._set_top_kwargs(ctx.params)
331
374
  ctx.obj.echo = echo
@@ -336,6 +379,7 @@ def start(
336
379
  ctx.obj.check = functools.partial(_check, echo)
337
380
  ctx.obj.top_cli = cli
338
381
  ctx.obj.package_suffixes = package_suffixes.split(",")
382
+ ctx.obj.spin_mode = mode == "spin"
339
383
 
340
384
  ctx.obj.datastore_impl = [d for d in DATASTORES if d.TYPE == datastore][0]
341
385
 
@@ -414,8 +458,8 @@ def start(
414
458
  # We can now set the the CONFIGS value in the flow properly. This will overwrite
415
459
  # anything that may have been passed in by default and we will use exactly what
416
460
  # the original flow had. Note that these are accessed through the parameter name
417
- ctx.obj.flow._flow_state[_FlowState.CONFIGS].clear()
418
- d = ctx.obj.flow._flow_state[_FlowState.CONFIGS]
461
+ ctx.obj.flow._flow_state[FlowStateItems.CONFIGS].clear()
462
+ d = ctx.obj.flow._flow_state[FlowStateItems.CONFIGS]
419
463
  for param_name, var_name in zip(config_param_names, config_var_names):
420
464
  val = param_ds[var_name]
421
465
  debug.userconf_exec("Loaded config %s as: %s" % (param_name, val))
@@ -426,6 +470,10 @@ def start(
426
470
  # be raised. For resume, since we ignore those options, we ignore the error.
427
471
  raise ctx.obj.delayed_config_exception
428
472
 
473
+ # Init all values in the flow mutators and then process them
474
+ for decorator in ctx.obj.flow._flow_state[FlowStateItems.FLOW_MUTATORS]:
475
+ decorator.external_init()
476
+
429
477
  new_cls = ctx.obj.flow._process_config_decorators(config_options)
430
478
  if new_cls:
431
479
  ctx.obj.flow = new_cls(use_cli=False)
@@ -435,20 +483,15 @@ def start(
435
483
  ctx.obj.environment = [
436
484
  e for e in ENVIRONMENTS + [MetaflowEnvironment] if e.TYPE == environment
437
485
  ][0](ctx.obj.flow)
486
+ # set force rebuild flag for environments that support it.
487
+ ctx.obj.environment._force_rebuild = force_rebuild_environments
438
488
  ctx.obj.environment.validate_environment(ctx.obj.logger, datastore)
439
-
440
489
  ctx.obj.event_logger = LOGGING_SIDECARS[event_logger](
441
490
  flow=ctx.obj.flow, env=ctx.obj.environment
442
491
  )
443
- ctx.obj.event_logger.start()
444
- _system_logger.init_system_logger(ctx.obj.flow.name, ctx.obj.event_logger)
445
-
446
492
  ctx.obj.monitor = MONITOR_SIDECARS[monitor](
447
493
  flow=ctx.obj.flow, env=ctx.obj.environment
448
494
  )
449
- ctx.obj.monitor.start()
450
- _system_monitor.init_system_monitor(ctx.obj.flow.name, ctx.obj.monitor)
451
-
452
495
  ctx.obj.metadata = [m for m in METADATA_PROVIDERS if m.TYPE == metadata][0](
453
496
  ctx.obj.environment, ctx.obj.flow, ctx.obj.event_logger, ctx.obj.monitor
454
497
  )
@@ -462,6 +505,57 @@ def start(
462
505
  )
463
506
 
464
507
  ctx.obj.config_options = config_options
508
+ ctx.obj.is_spin = False
509
+ ctx.obj.skip_decorators = False
510
+
511
+ # Override values for spin steps, or if we are in spin mode
512
+ if (
513
+ hasattr(ctx, "saved_args")
514
+ and ctx.saved_args
515
+ and "spin" in ctx.saved_args[0]
516
+ or ctx.obj.spin_mode
517
+ ):
518
+ # To minimize side effects for spin, we will only use the following:
519
+ # - local metadata provider,
520
+ # - local datastore,
521
+ # - local environment,
522
+ # - null event logger,
523
+ # - null monitor
524
+ ctx.obj.is_spin = True
525
+ if "--skip-decorators" in ctx.saved_args:
526
+ ctx.obj.skip_decorators = True
527
+
528
+ ctx.obj.event_logger = LOGGING_SIDECARS["nullSidecarLogger"](
529
+ flow=ctx.obj.flow, env=ctx.obj.environment
530
+ )
531
+ ctx.obj.monitor = MONITOR_SIDECARS["nullSidecarMonitor"](
532
+ flow=ctx.obj.flow, env=ctx.obj.environment
533
+ )
534
+ # Use spin metadata, spin datastore, and spin datastore root
535
+ ctx.obj.metadata = [m for m in METADATA_PROVIDERS if m.TYPE == "spin"][0](
536
+ ctx.obj.environment, ctx.obj.flow, ctx.obj.event_logger, ctx.obj.monitor
537
+ )
538
+ ctx.obj.datastore_impl = [d for d in DATASTORES if d.TYPE == "spin"][0]
539
+ datastore_root = ctx.obj.datastore_impl.get_datastore_root_from_config(
540
+ ctx.obj.echo, create_on_absent=True
541
+ )
542
+ ctx.obj.datastore_impl.datastore_root = datastore_root
543
+
544
+ ctx.obj.flow_datastore = FlowDataStore(
545
+ ctx.obj.flow.name,
546
+ ctx.obj.environment, # Same environment as run/resume
547
+ ctx.obj.metadata, # local metadata
548
+ ctx.obj.event_logger, # null event logger
549
+ ctx.obj.monitor, # null monitor
550
+ storage_impl=ctx.obj.datastore_impl,
551
+ )
552
+
553
+ # Start event logger and monitor
554
+ ctx.obj.event_logger.start()
555
+ _system_logger.init_system_logger(ctx.obj.flow.name, ctx.obj.event_logger)
556
+
557
+ ctx.obj.monitor.start()
558
+ _system_monitor.init_system_monitor(ctx.obj.flow.name, ctx.obj.monitor)
465
559
 
466
560
  decorators._init(ctx.obj.flow)
467
561
 
@@ -476,9 +570,11 @@ def start(
476
570
  ctx.obj.logger,
477
571
  echo,
478
572
  deco_options,
573
+ ctx.obj.is_spin,
574
+ ctx.obj.skip_decorators,
479
575
  )
480
576
 
481
- # In the case of run/resume, we will want to apply the TL decospecs
577
+ # In the case of run/resume/spin, we will want to apply the TL decospecs
482
578
  # *after* the run decospecs so that they don't take precedence. In other
483
579
  # words, for the same decorator, we want `myflow.py run --with foo` to
484
580
  # take precedence over any other `foo` decospec
@@ -496,21 +592,20 @@ def start(
496
592
  ctx.obj.echo,
497
593
  ctx.obj.flow_datastore,
498
594
  {
499
- k: ConfigValue(v)
500
- for k, v in ctx.obj.flow.__class__._flow_state.get(
501
- _FlowState.CONFIGS, {}
502
- ).items()
595
+ k: ConfigValue(v) if v is not None else None
596
+ for k, v in ctx.obj.flow.__class__._flow_state[
597
+ FlowStateItems.CONFIGS
598
+ ].items()
503
599
  },
504
600
  )
505
601
 
506
602
  if (
507
603
  hasattr(ctx, "saved_args")
508
604
  and ctx.saved_args
509
- and ctx.saved_args[0] not in ("run", "resume")
605
+ and ctx.saved_args[0] not in ("run", "resume", "spin")
510
606
  ):
511
- # run/resume are special cases because they can add more decorators with --with,
607
+ # run/resume/spin are special cases because they can add more decorators with --with,
512
608
  # so they have to take care of themselves.
513
-
514
609
  all_decospecs = ctx.obj.tl_decospecs + list(
515
610
  ctx.obj.environment.decospecs() or []
516
611
  )
@@ -520,11 +615,14 @@ def start(
520
615
  # or a scheduler setting them up in their own way.
521
616
  if ctx.saved_args[0] not in ("step", "init"):
522
617
  all_decospecs += DEFAULT_DECOSPECS.split()
618
+ elif ctx.saved_args[0] == "spin-step":
619
+ # If we are in spin-args, we will not attach any decorators
620
+ all_decospecs = []
523
621
  if all_decospecs:
524
622
  decorators._attach_decorators(ctx.obj.flow, all_decospecs)
525
623
  decorators._init(ctx.obj.flow)
526
624
  # Regenerate graph if we attached more decorators
527
- ctx.obj.flow.__class__._init_attrs()
625
+ ctx.obj.flow.__class__._init_graph()
528
626
  ctx.obj.graph = ctx.obj.flow._graph
529
627
 
530
628
  decorators._init_step_decorators(
@@ -533,8 +631,14 @@ def start(
533
631
  ctx.obj.environment,
534
632
  ctx.obj.flow_datastore,
535
633
  ctx.obj.logger,
634
+ # The last two arguments are only used for spin steps
635
+ ctx.obj.is_spin,
636
+ ctx.obj.skip_decorators,
536
637
  )
537
638
 
639
+ # Check the graph again (mutators may have changed it)
640
+ ctx.obj.graph = ctx.obj.flow._graph
641
+
538
642
  # TODO (savin): Enable lazy instantiation of package
539
643
  ctx.obj.package = None
540
644
 
@@ -46,6 +46,7 @@ def init(obj, run_id=None, task_id=None, tags=None, **kwargs):
46
46
  obj.event_logger,
47
47
  obj.monitor,
48
48
  run_id=run_id,
49
+ skip_decorator_hooks=True,
49
50
  )
50
51
  obj.flow._set_constants(obj.graph, kwargs, obj.config_options)
51
52
  runtime.persist_constants(task_id=task_id)