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.
- metaflow/__init__.py +10 -3
- metaflow/_vendor/imghdr/__init__.py +186 -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/cards.py +4 -0
- metaflow/cli.py +125 -21
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +204 -40
- metaflow/cli_components/step_cmd.py +160 -4
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +198 -130
- metaflow/client/filecache.py +59 -32
- metaflow/cmd/code/__init__.py +2 -1
- metaflow/cmd/develop/stub_generator.py +49 -18
- metaflow/cmd/develop/stubs.py +9 -27
- metaflow/cmd/make_wrapper.py +30 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +40 -9
- metaflow/datastore/datastore_set.py +10 -1
- metaflow/datastore/flow_datastore.py +124 -4
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +92 -6
- metaflow/debug.py +5 -0
- metaflow/decorators.py +331 -82
- metaflow/extension_support/__init__.py +414 -356
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +322 -82
- metaflow/graph.py +178 -15
- metaflow/includefile.py +25 -3
- metaflow/lint.py +94 -3
- metaflow/meta_files.py +13 -0
- metaflow/metadata_provider/metadata.py +13 -2
- metaflow/metaflow_config.py +66 -4
- metaflow/metaflow_environment.py +91 -25
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +16 -1
- metaflow/package/__init__.py +673 -0
- metaflow/packaging_sys/__init__.py +880 -0
- metaflow/packaging_sys/backend.py +128 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +99 -0
- metaflow/packaging_sys/utils.py +54 -0
- metaflow/packaging_sys/v1.py +527 -0
- metaflow/parameters.py +6 -2
- metaflow/plugins/__init__.py +6 -0
- metaflow/plugins/airflow/airflow.py +11 -1
- metaflow/plugins/airflow/airflow_cli.py +16 -5
- metaflow/plugins/argo/argo_client.py +42 -20
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +1023 -344
- metaflow/plugins/argo/argo_workflows_cli.py +396 -94
- metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
- metaflow/plugins/argo/capture_error.py +5 -2
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +6 -0
- metaflow/plugins/aws/aws_utils.py +33 -1
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +24 -3
- metaflow/plugins/aws/batch/batch_decorator.py +57 -6
- metaflow/plugins/aws/step_functions/step_functions.py +28 -3
- metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +58 -6
- metaflow/plugins/cards/card_modules/basic.py +38 -9
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +592 -3
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
- metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
- metaflow/plugins/cards/card_modules/main.css +1 -0
- metaflow/plugins/cards/card_modules/main.js +56 -41
- metaflow/plugins/cards/card_modules/test_cards.py +22 -6
- metaflow/plugins/cards/component_serializer.py +1 -8
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/s3/s3.py +49 -17
- metaflow/plugins/datatools/s3/s3op.py +113 -66
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/events_decorator.py +127 -121
- metaflow/plugins/exit_hook/__init__.py +0 -0
- metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
- metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
- metaflow/plugins/kubernetes/kubernetes.py +12 -1
- metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
- metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
- metaflow/plugins/metadata_providers/local.py +76 -82
- metaflow/plugins/metadata_providers/service.py +13 -9
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +11 -2
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/pypi/bootstrap.py +7 -1
- metaflow/plugins/pypi/conda_decorator.py +41 -82
- metaflow/plugins/pypi/conda_environment.py +14 -6
- metaflow/plugins/pypi/micromamba.py +9 -1
- metaflow/plugins/pypi/pip.py +41 -5
- metaflow/plugins/pypi/pypi_decorator.py +4 -4
- metaflow/plugins/pypi/utils.py +22 -0
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +14 -178
- metaflow/plugins/secrets/secrets_func.py +49 -0
- metaflow/plugins/secrets/secrets_spec.py +101 -0
- metaflow/plugins/secrets/utils.py +74 -0
- metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
- metaflow/plugins/timeout_decorator.py +0 -1
- metaflow/plugins/uv/bootstrap.py +29 -1
- metaflow/plugins/uv/uv_environment.py +5 -3
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +79 -26
- metaflow/runner/deployer.py +208 -6
- metaflow/runner/deployer_impl.py +32 -12
- metaflow/runner/metaflow_runner.py +266 -33
- metaflow/runner/subprocess_manager.py +21 -1
- metaflow/runner/utils.py +27 -16
- metaflow/runtime.py +660 -66
- metaflow/task.py +255 -26
- metaflow/user_configs/config_options.py +33 -21
- metaflow/user_configs/config_parameters.py +220 -58
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +512 -0
- metaflow/user_decorators/mutable_step.py +424 -0
- metaflow/user_decorators/user_flow_decorator.py +264 -0
- metaflow/user_decorators/user_step_decorator.py +749 -0
- metaflow/util.py +197 -7
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- metaflow/_vendor/v3_5/__init__.py +0 -1
- metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
- metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
- metaflow/_vendor/v3_5/zipp.py +0 -329
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
|
@@ -8,19 +8,26 @@ from .. import decorators, namespace, parameters, tracing
|
|
|
8
8
|
from ..exception import CommandException
|
|
9
9
|
from ..graph import FlowGraph
|
|
10
10
|
from ..metaflow_current import current
|
|
11
|
-
from ..metaflow_config import
|
|
11
|
+
from ..metaflow_config import (
|
|
12
|
+
DEFAULT_DECOSPECS,
|
|
13
|
+
FEAT_ALWAYS_UPLOAD_CODE_PACKAGE,
|
|
14
|
+
SPIN_PERSIST,
|
|
15
|
+
)
|
|
16
|
+
from ..metaflow_profile import from_start
|
|
12
17
|
from ..package import MetaflowPackage
|
|
13
|
-
from ..runtime import NativeRuntime
|
|
18
|
+
from ..runtime import NativeRuntime, SpinRuntime
|
|
14
19
|
from ..system import _system_logger
|
|
15
20
|
|
|
21
|
+
# from ..client.core import Run
|
|
22
|
+
|
|
16
23
|
from ..tagging_util import validate_tags
|
|
17
|
-
from ..util import get_latest_run_id, write_latest_run_id
|
|
24
|
+
from ..util import get_latest_run_id, write_latest_run_id, parse_spin_pathspec
|
|
18
25
|
|
|
19
26
|
|
|
20
|
-
def before_run(obj, tags, decospecs):
|
|
27
|
+
def before_run(obj, tags, decospecs, skip_decorators=False):
|
|
21
28
|
validate_tags(tags)
|
|
22
29
|
|
|
23
|
-
# There's a --with option both at the top-level and for the run
|
|
30
|
+
# There's a --with option both at the top-level and for the run/resume/spin
|
|
24
31
|
# subcommand. Why?
|
|
25
32
|
#
|
|
26
33
|
# "run --with shoes" looks so much better than "--with shoes run".
|
|
@@ -34,26 +41,38 @@ def before_run(obj, tags, decospecs):
|
|
|
34
41
|
# - run level decospecs
|
|
35
42
|
# - top level decospecs
|
|
36
43
|
# - environment decospecs
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
+ obj.tl_decospecs
|
|
40
|
-
+ list(obj.environment.decospecs() or [])
|
|
41
|
-
)
|
|
42
|
-
if all_decospecs:
|
|
43
|
-
# These decospecs are the ones from run/resume PLUS the ones from the
|
|
44
|
-
# environment (for example the @conda)
|
|
45
|
-
decorators._attach_decorators(obj.flow, all_decospecs)
|
|
46
|
-
decorators._init(obj.flow)
|
|
47
|
-
# Regenerate graph if we attached more decorators
|
|
48
|
-
obj.flow.__class__._init_attrs()
|
|
49
|
-
obj.graph = obj.flow._graph
|
|
50
|
-
|
|
51
|
-
obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
|
|
52
|
-
# obj.environment.init_environment(obj.logger)
|
|
53
|
-
|
|
54
|
-
decorators._init_step_decorators(
|
|
55
|
-
obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
|
|
44
|
+
from_start(
|
|
45
|
+
f"Inside before_run, skip_decorators={skip_decorators}, is_spin={obj.is_spin}"
|
|
56
46
|
)
|
|
47
|
+
if not skip_decorators:
|
|
48
|
+
all_decospecs = (
|
|
49
|
+
list(decospecs or [])
|
|
50
|
+
+ obj.tl_decospecs
|
|
51
|
+
+ list(obj.environment.decospecs() or [])
|
|
52
|
+
)
|
|
53
|
+
if all_decospecs:
|
|
54
|
+
# These decospecs are the ones from run/resume/spin PLUS the ones from the
|
|
55
|
+
# environment (for example the @conda)
|
|
56
|
+
decorators._attach_decorators(obj.flow, all_decospecs)
|
|
57
|
+
decorators._init(obj.flow)
|
|
58
|
+
# Regenerate graph if we attached more decorators
|
|
59
|
+
obj.flow.__class__._init_graph()
|
|
60
|
+
obj.graph = obj.flow._graph
|
|
61
|
+
|
|
62
|
+
obj.check(obj.graph, obj.flow, obj.environment, pylint=obj.pylint)
|
|
63
|
+
# obj.environment.init_environment(obj.logger)
|
|
64
|
+
|
|
65
|
+
decorators._init_step_decorators(
|
|
66
|
+
obj.flow,
|
|
67
|
+
obj.graph,
|
|
68
|
+
obj.environment,
|
|
69
|
+
obj.flow_datastore,
|
|
70
|
+
obj.logger,
|
|
71
|
+
obj.is_spin,
|
|
72
|
+
skip_decorators,
|
|
73
|
+
)
|
|
74
|
+
# Re-read graph since it may have been modified by mutators
|
|
75
|
+
obj.graph = obj.flow._graph
|
|
57
76
|
|
|
58
77
|
obj.metadata.add_sticky_tags(tags=tags)
|
|
59
78
|
|
|
@@ -61,10 +80,37 @@ def before_run(obj, tags, decospecs):
|
|
|
61
80
|
# We explicitly avoid doing this in `start` since it is invoked for every
|
|
62
81
|
# step in the run.
|
|
63
82
|
obj.package = MetaflowPackage(
|
|
64
|
-
obj.flow,
|
|
83
|
+
obj.flow,
|
|
84
|
+
obj.environment,
|
|
85
|
+
obj.echo,
|
|
86
|
+
suffixes=obj.package_suffixes,
|
|
87
|
+
flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
|
|
65
88
|
)
|
|
66
89
|
|
|
67
90
|
|
|
91
|
+
def common_runner_options(func):
|
|
92
|
+
@click.option(
|
|
93
|
+
"--run-id-file",
|
|
94
|
+
default=None,
|
|
95
|
+
show_default=True,
|
|
96
|
+
type=str,
|
|
97
|
+
help="Write the ID of this run to the file specified.",
|
|
98
|
+
)
|
|
99
|
+
@click.option(
|
|
100
|
+
"--runner-attribute-file",
|
|
101
|
+
default=None,
|
|
102
|
+
show_default=True,
|
|
103
|
+
type=str,
|
|
104
|
+
help="Write the metadata and pathspec of this run to the file specified. Used internally "
|
|
105
|
+
"for Metaflow's Runner API.",
|
|
106
|
+
)
|
|
107
|
+
@wraps(func)
|
|
108
|
+
def wrapper(*args, **kwargs):
|
|
109
|
+
return func(*args, **kwargs)
|
|
110
|
+
|
|
111
|
+
return wrapper
|
|
112
|
+
|
|
113
|
+
|
|
68
114
|
def write_file(file_path, content):
|
|
69
115
|
if file_path is not None:
|
|
70
116
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
@@ -129,20 +175,6 @@ def common_run_options(func):
|
|
|
129
175
|
"in steps.",
|
|
130
176
|
callback=config_callback,
|
|
131
177
|
)
|
|
132
|
-
@click.option(
|
|
133
|
-
"--run-id-file",
|
|
134
|
-
default=None,
|
|
135
|
-
show_default=True,
|
|
136
|
-
type=str,
|
|
137
|
-
help="Write the ID of this run to the file specified.",
|
|
138
|
-
)
|
|
139
|
-
@click.option(
|
|
140
|
-
"--runner-attribute-file",
|
|
141
|
-
default=None,
|
|
142
|
-
show_default=True,
|
|
143
|
-
type=str,
|
|
144
|
-
help="Write the metadata and pathspec of this run to the file specified. Used internally for Metaflow's Runner API.",
|
|
145
|
-
)
|
|
146
178
|
@wraps(func)
|
|
147
179
|
def wrapper(*args, **kwargs):
|
|
148
180
|
return func(*args, **kwargs)
|
|
@@ -187,6 +219,7 @@ def common_run_options(func):
|
|
|
187
219
|
@click.command(help="Resume execution of a previous run of this flow.")
|
|
188
220
|
@tracing.cli("cli/resume")
|
|
189
221
|
@common_run_options
|
|
222
|
+
@common_runner_options
|
|
190
223
|
@click.pass_obj
|
|
191
224
|
def resume(
|
|
192
225
|
obj,
|
|
@@ -224,6 +257,19 @@ def resume(
|
|
|
224
257
|
step_to_rerun, ",".join(list(obj.graph.nodes.keys()))
|
|
225
258
|
)
|
|
226
259
|
)
|
|
260
|
+
|
|
261
|
+
## TODO: instead of checking execution path here, can add a warning later
|
|
262
|
+
## instead of throwing an error. This is for resuming a step which was not
|
|
263
|
+
## taken inside a branch i.e. not present in the execution path.
|
|
264
|
+
|
|
265
|
+
# origin_run = Run(f"{obj.flow.name}/{origin_run_id}", _namespace_check=False)
|
|
266
|
+
# executed_steps = {step.path_components[-1] for step in origin_run}
|
|
267
|
+
# if step_to_rerun not in executed_steps:
|
|
268
|
+
# raise CommandException(
|
|
269
|
+
# f"Cannot resume from step '{step_to_rerun}'. This step was not "
|
|
270
|
+
# f"part of the original execution path for run '{origin_run_id}'."
|
|
271
|
+
# )
|
|
272
|
+
|
|
227
273
|
steps_to_rerun = {step_to_rerun}
|
|
228
274
|
|
|
229
275
|
if run_id:
|
|
@@ -305,6 +351,7 @@ def resume(
|
|
|
305
351
|
@click.command(help="Run the workflow locally.")
|
|
306
352
|
@tracing.cli("cli/run")
|
|
307
353
|
@common_run_options
|
|
354
|
+
@common_runner_options
|
|
308
355
|
@click.option(
|
|
309
356
|
"--namespace",
|
|
310
357
|
"user_namespace",
|
|
@@ -327,7 +374,7 @@ def run(
|
|
|
327
374
|
run_id_file=None,
|
|
328
375
|
runner_attribute_file=None,
|
|
329
376
|
user_namespace=None,
|
|
330
|
-
**kwargs
|
|
377
|
+
**kwargs,
|
|
331
378
|
):
|
|
332
379
|
if user_namespace is not None:
|
|
333
380
|
namespace(user_namespace or None)
|
|
@@ -380,3 +427,120 @@ def run(
|
|
|
380
427
|
)
|
|
381
428
|
with runtime.run_heartbeat():
|
|
382
429
|
runtime.execute()
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# @parameters.add_custom_parameters(deploy_mode=True)
|
|
433
|
+
@click.command(help="Spins up a task for a given step from a previous run locally.")
|
|
434
|
+
@tracing.cli("cli/spin")
|
|
435
|
+
@click.argument("pathspec")
|
|
436
|
+
@click.option(
|
|
437
|
+
"--skip-decorators/--no-skip-decorators",
|
|
438
|
+
is_flag=True,
|
|
439
|
+
# Default False matches the saved_args check in cli.py for spin steps - skip_decorators
|
|
440
|
+
# only becomes True when explicitly passed, otherwise decorators are applied by default
|
|
441
|
+
default=False,
|
|
442
|
+
show_default=True,
|
|
443
|
+
help="Skip decorators attached to the step or flow.",
|
|
444
|
+
)
|
|
445
|
+
@click.option(
|
|
446
|
+
"--artifacts-module",
|
|
447
|
+
default=None,
|
|
448
|
+
show_default=True,
|
|
449
|
+
help="Path to a module that contains artifacts to be used in the spun step. "
|
|
450
|
+
"The artifacts should be defined as a dictionary called ARTIFACTS with keys as "
|
|
451
|
+
"the artifact names and values as the artifact values. The artifact values will "
|
|
452
|
+
"overwrite the default values of the artifacts used in the spun step.",
|
|
453
|
+
)
|
|
454
|
+
@click.option(
|
|
455
|
+
"--persist/--no-persist",
|
|
456
|
+
"persist",
|
|
457
|
+
default=SPIN_PERSIST,
|
|
458
|
+
show_default=True,
|
|
459
|
+
help="Whether to persist the artifacts in the spun step. If set to False, "
|
|
460
|
+
"the artifacts will not be persisted and will not be available in the spun step's "
|
|
461
|
+
"datastore.",
|
|
462
|
+
)
|
|
463
|
+
@click.option(
|
|
464
|
+
"--max-log-size",
|
|
465
|
+
default=10,
|
|
466
|
+
show_default=True,
|
|
467
|
+
help="Maximum size of stdout and stderr captured in "
|
|
468
|
+
"megabytes. If a step outputs more than this to "
|
|
469
|
+
"stdout/stderr, its output will be truncated.",
|
|
470
|
+
)
|
|
471
|
+
@common_runner_options
|
|
472
|
+
@click.pass_obj
|
|
473
|
+
def spin(
|
|
474
|
+
obj,
|
|
475
|
+
pathspec,
|
|
476
|
+
persist=True,
|
|
477
|
+
artifacts_module=None,
|
|
478
|
+
skip_decorators=False,
|
|
479
|
+
max_log_size=None,
|
|
480
|
+
run_id_file=None,
|
|
481
|
+
runner_attribute_file=None,
|
|
482
|
+
**kwargs,
|
|
483
|
+
):
|
|
484
|
+
# Parse the pathspec argument to extract step name and full pathspec
|
|
485
|
+
step_name, parsed_pathspec = parse_spin_pathspec(pathspec, obj.flow.name)
|
|
486
|
+
|
|
487
|
+
before_run(obj, [], [], skip_decorators)
|
|
488
|
+
obj.echo(f"Spinning up step *{step_name}* locally for flow *{obj.flow.name}*")
|
|
489
|
+
# For spin, flow parameters come from the original run, but _set_constants
|
|
490
|
+
# requires them in kwargs. Use parameter defaults as placeholders - they'll be
|
|
491
|
+
# overwritten when the spin step loads artifacts from the original run.
|
|
492
|
+
flow_param_defaults = {}
|
|
493
|
+
for var, param in obj.flow._get_parameters():
|
|
494
|
+
if not param.IS_CONFIG_PARAMETER:
|
|
495
|
+
default_value = param.kwargs.get("default")
|
|
496
|
+
# Use None for required parameters without defaults
|
|
497
|
+
flow_param_defaults[param.name.replace("-", "_").lower()] = default_value
|
|
498
|
+
obj.flow._set_constants(obj.graph, flow_param_defaults, obj.config_options)
|
|
499
|
+
step_func = getattr(obj.flow, step_name, None)
|
|
500
|
+
if step_func is None:
|
|
501
|
+
raise CommandException(
|
|
502
|
+
f"Step '{step_name}' not found in flow '{obj.flow.name}'. "
|
|
503
|
+
"Please provide a valid step name."
|
|
504
|
+
)
|
|
505
|
+
from_start("Spin: before spin runtime init")
|
|
506
|
+
spin_runtime = SpinRuntime(
|
|
507
|
+
obj.flow,
|
|
508
|
+
obj.graph,
|
|
509
|
+
obj.flow_datastore,
|
|
510
|
+
obj.metadata,
|
|
511
|
+
obj.environment,
|
|
512
|
+
obj.package,
|
|
513
|
+
obj.logger,
|
|
514
|
+
obj.entrypoint,
|
|
515
|
+
obj.event_logger,
|
|
516
|
+
obj.monitor,
|
|
517
|
+
step_func,
|
|
518
|
+
step_name,
|
|
519
|
+
parsed_pathspec,
|
|
520
|
+
skip_decorators,
|
|
521
|
+
artifacts_module,
|
|
522
|
+
persist,
|
|
523
|
+
max_log_size * 1024 * 1024,
|
|
524
|
+
)
|
|
525
|
+
write_latest_run_id(obj, spin_runtime.run_id)
|
|
526
|
+
write_file(run_id_file, spin_runtime.run_id)
|
|
527
|
+
# We only need the root for the metadata, i.e. the portion before DATASTORE_LOCAL_DIR
|
|
528
|
+
datastore_root = spin_runtime._flow_datastore._storage_impl.datastore_root
|
|
529
|
+
orig_task_metadata_root = datastore_root.rsplit("/", 1)[0]
|
|
530
|
+
from_start("Spin: going to execute")
|
|
531
|
+
spin_runtime.execute()
|
|
532
|
+
from_start("Spin: after spin runtime execute")
|
|
533
|
+
|
|
534
|
+
if runner_attribute_file:
|
|
535
|
+
with open(runner_attribute_file, "w") as f:
|
|
536
|
+
json.dump(
|
|
537
|
+
{
|
|
538
|
+
"task_id": spin_runtime.task.task_id,
|
|
539
|
+
"step_name": step_name,
|
|
540
|
+
"run_id": spin_runtime.run_id,
|
|
541
|
+
"flow_name": obj.flow.name,
|
|
542
|
+
# Store metadata in a format that can be used by the Runner API
|
|
543
|
+
"metadata": f"{obj.metadata.__class__.TYPE}@{orig_task_metadata_root}",
|
|
544
|
+
},
|
|
545
|
+
f,
|
|
546
|
+
)
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
from metaflow._vendor import click
|
|
2
2
|
|
|
3
|
-
from .. import
|
|
3
|
+
from .. import namespace
|
|
4
4
|
from ..cli import echo_always, echo_dev_null
|
|
5
5
|
from ..cli_args import cli_args
|
|
6
|
+
from ..datastore.flow_datastore import FlowDataStore
|
|
6
7
|
from ..exception import CommandException
|
|
8
|
+
from ..client.filecache import FileCache, FileBlobCache, TaskMetadataCache
|
|
9
|
+
from ..metaflow_config import SPIN_ALLOWED_DECORATORS
|
|
10
|
+
from ..metaflow_profile import from_start
|
|
11
|
+
from ..plugins import DATASTORES
|
|
7
12
|
from ..task import MetaflowTask
|
|
8
13
|
from ..unbounded_foreach import UBF_CONTROL, UBF_TASK
|
|
9
|
-
from ..util import decompress_list
|
|
14
|
+
from ..util import decompress_list, read_artifacts_module
|
|
10
15
|
import metaflow.tracing as tracing
|
|
11
16
|
|
|
12
17
|
|
|
@@ -109,7 +114,6 @@ def step(
|
|
|
109
114
|
ubf_context="none",
|
|
110
115
|
num_parallel=None,
|
|
111
116
|
):
|
|
112
|
-
|
|
113
117
|
if ctx.obj.is_quiet:
|
|
114
118
|
echo = echo_dev_null
|
|
115
119
|
else:
|
|
@@ -118,7 +122,7 @@ def step(
|
|
|
118
122
|
if ubf_context == "none":
|
|
119
123
|
ubf_context = None
|
|
120
124
|
if opt_namespace is not None:
|
|
121
|
-
namespace(opt_namespace
|
|
125
|
+
namespace(opt_namespace)
|
|
122
126
|
|
|
123
127
|
func = None
|
|
124
128
|
try:
|
|
@@ -176,3 +180,155 @@ def step(
|
|
|
176
180
|
)
|
|
177
181
|
|
|
178
182
|
echo("Success", fg="green", bold=True, indent=True)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@click.command(help="Internal command to spin a single task.", hidden=True)
|
|
186
|
+
@click.argument("step-name")
|
|
187
|
+
@click.option(
|
|
188
|
+
"--run-id",
|
|
189
|
+
default=None,
|
|
190
|
+
required=True,
|
|
191
|
+
help="Original run ID for the step that will be spun",
|
|
192
|
+
)
|
|
193
|
+
@click.option(
|
|
194
|
+
"--task-id",
|
|
195
|
+
default=None,
|
|
196
|
+
required=True,
|
|
197
|
+
help="Original Task ID for the step that will be spun",
|
|
198
|
+
)
|
|
199
|
+
@click.option(
|
|
200
|
+
"--orig-flow-datastore",
|
|
201
|
+
show_default=True,
|
|
202
|
+
help="Original datastore for the flow from which a task is being spun",
|
|
203
|
+
)
|
|
204
|
+
@click.option(
|
|
205
|
+
"--input-paths",
|
|
206
|
+
help="A comma-separated list of pathspecs specifying inputs for this step.",
|
|
207
|
+
)
|
|
208
|
+
@click.option(
|
|
209
|
+
"--split-index",
|
|
210
|
+
type=int,
|
|
211
|
+
default=None,
|
|
212
|
+
show_default=True,
|
|
213
|
+
help="Index of this foreach split.",
|
|
214
|
+
)
|
|
215
|
+
@click.option(
|
|
216
|
+
"--retry-count",
|
|
217
|
+
default=0,
|
|
218
|
+
help="How many times we have attempted to run this task.",
|
|
219
|
+
)
|
|
220
|
+
@click.option(
|
|
221
|
+
"--max-user-code-retries",
|
|
222
|
+
default=0,
|
|
223
|
+
help="How many times we should attempt running the user code.",
|
|
224
|
+
)
|
|
225
|
+
@click.option(
|
|
226
|
+
"--namespace",
|
|
227
|
+
"opt_namespace",
|
|
228
|
+
default=None,
|
|
229
|
+
help="Change namespace from the default (your username) to the specified tag.",
|
|
230
|
+
)
|
|
231
|
+
@click.option(
|
|
232
|
+
"--skip-decorators/--no-skip-decorators",
|
|
233
|
+
is_flag=True,
|
|
234
|
+
default=False,
|
|
235
|
+
show_default=True,
|
|
236
|
+
help="Skip decorators attached to the step or flow.",
|
|
237
|
+
)
|
|
238
|
+
@click.option(
|
|
239
|
+
"--persist/--no-persist",
|
|
240
|
+
"persist",
|
|
241
|
+
default=True,
|
|
242
|
+
show_default=True,
|
|
243
|
+
help="Whether to persist the artifacts in the spun step. If set to false, the artifacts will not"
|
|
244
|
+
" be persisted and will not be available in the spun step's datastore.",
|
|
245
|
+
)
|
|
246
|
+
@click.option(
|
|
247
|
+
"--artifacts-module",
|
|
248
|
+
default=None,
|
|
249
|
+
show_default=True,
|
|
250
|
+
help="Path to a module that contains artifacts to be used in the spun step. The artifacts should "
|
|
251
|
+
"be defined as a dictionary called ARTIFACTS with keys as the artifact names and values as the "
|
|
252
|
+
"artifact values. The artifact values will overwrite the default values of the artifacts used in "
|
|
253
|
+
"the spun step.",
|
|
254
|
+
)
|
|
255
|
+
@click.pass_context
|
|
256
|
+
def spin_step(
|
|
257
|
+
ctx,
|
|
258
|
+
step_name,
|
|
259
|
+
orig_flow_datastore,
|
|
260
|
+
run_id=None,
|
|
261
|
+
task_id=None,
|
|
262
|
+
input_paths=None,
|
|
263
|
+
split_index=None,
|
|
264
|
+
retry_count=None,
|
|
265
|
+
max_user_code_retries=None,
|
|
266
|
+
opt_namespace=None,
|
|
267
|
+
skip_decorators=False,
|
|
268
|
+
artifacts_module=None,
|
|
269
|
+
persist=True,
|
|
270
|
+
):
|
|
271
|
+
import time
|
|
272
|
+
|
|
273
|
+
if ctx.obj.is_quiet:
|
|
274
|
+
echo = echo_dev_null
|
|
275
|
+
else:
|
|
276
|
+
echo = echo_always
|
|
277
|
+
|
|
278
|
+
if opt_namespace is not None:
|
|
279
|
+
namespace(opt_namespace)
|
|
280
|
+
|
|
281
|
+
input_paths = decompress_list(input_paths) if input_paths else []
|
|
282
|
+
|
|
283
|
+
skip_decorators = skip_decorators
|
|
284
|
+
whitelist_decorators = [] if skip_decorators else SPIN_ALLOWED_DECORATORS
|
|
285
|
+
from_start("SpinStep: initialized decorators")
|
|
286
|
+
spin_artifacts = read_artifacts_module(artifacts_module) if artifacts_module else {}
|
|
287
|
+
from_start("SpinStep: read artifacts module")
|
|
288
|
+
|
|
289
|
+
ds_type, ds_root = orig_flow_datastore.split("@")
|
|
290
|
+
orig_datastore_impl = [d for d in DATASTORES if d.TYPE == ds_type][0]
|
|
291
|
+
orig_datastore_impl.datastore_root = ds_root
|
|
292
|
+
orig_flow_datastore = FlowDataStore(
|
|
293
|
+
ctx.obj.flow.name,
|
|
294
|
+
environment=None,
|
|
295
|
+
storage_impl=orig_datastore_impl,
|
|
296
|
+
ds_root=ds_root,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
filecache = FileCache()
|
|
300
|
+
orig_flow_datastore.set_metadata_cache(
|
|
301
|
+
TaskMetadataCache(filecache, ds_type, ds_root, ctx.obj.flow.name)
|
|
302
|
+
)
|
|
303
|
+
orig_flow_datastore.ca_store.set_blob_cache(
|
|
304
|
+
FileBlobCache(
|
|
305
|
+
filecache, FileCache.flow_ds_id(ds_type, ds_root, ctx.obj.flow.name)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
task = MetaflowTask(
|
|
310
|
+
ctx.obj.flow,
|
|
311
|
+
ctx.obj.flow_datastore,
|
|
312
|
+
ctx.obj.metadata,
|
|
313
|
+
ctx.obj.environment,
|
|
314
|
+
echo,
|
|
315
|
+
ctx.obj.event_logger,
|
|
316
|
+
ctx.obj.monitor,
|
|
317
|
+
None, # no unbounded foreach context
|
|
318
|
+
orig_flow_datastore=orig_flow_datastore,
|
|
319
|
+
spin_artifacts=spin_artifacts,
|
|
320
|
+
)
|
|
321
|
+
from_start("SpinStep: initialized task")
|
|
322
|
+
task.run_step(
|
|
323
|
+
step_name,
|
|
324
|
+
run_id,
|
|
325
|
+
task_id,
|
|
326
|
+
None,
|
|
327
|
+
input_paths,
|
|
328
|
+
split_index,
|
|
329
|
+
retry_count,
|
|
330
|
+
max_user_code_retries,
|
|
331
|
+
whitelist_decorators,
|
|
332
|
+
persist,
|
|
333
|
+
)
|
|
334
|
+
from_start("SpinStep: ran step")
|