ob-metaflow 2.11.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/R.py +10 -7
- metaflow/__init__.py +40 -25
- metaflow/_vendor/imghdr/__init__.py +186 -0
- metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/typeguard/__init__.py +48 -0
- metaflow/_vendor/typeguard/_checkers.py +1070 -0
- metaflow/_vendor/typeguard/_config.py +108 -0
- metaflow/_vendor/typeguard/_decorators.py +233 -0
- metaflow/_vendor/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/typeguard/_functions.py +308 -0
- metaflow/_vendor/typeguard/_importhook.py +213 -0
- metaflow/_vendor/typeguard/_memo.py +48 -0
- metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
- metaflow/_vendor/typeguard/_suppression.py +86 -0
- metaflow/_vendor/typeguard/_transformer.py +1229 -0
- metaflow/_vendor/typeguard/_union_transformer.py +55 -0
- metaflow/_vendor/typeguard/_utils.py +173 -0
- metaflow/_vendor/typeguard/py.typed +0 -0
- metaflow/_vendor/typing_extensions.py +3641 -0
- metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
- metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
- metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
- metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
- metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
- metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
- metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
- metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
- metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
- metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
- metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
- metaflow/_vendor/v3_7/typing_extensions.py +3072 -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 +5 -0
- metaflow/cli.py +331 -785
- metaflow/cli_args.py +17 -0
- metaflow/cli_components/__init__.py +0 -0
- metaflow/cli_components/dump_cmd.py +96 -0
- metaflow/cli_components/init_cmd.py +52 -0
- metaflow/cli_components/run_cmds.py +546 -0
- metaflow/cli_components/step_cmd.py +334 -0
- metaflow/cli_components/utils.py +140 -0
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +467 -73
- metaflow/client/filecache.py +75 -35
- metaflow/clone_util.py +7 -1
- metaflow/cmd/code/__init__.py +231 -0
- metaflow/cmd/develop/stub_generator.py +756 -288
- metaflow/cmd/develop/stubs.py +12 -28
- metaflow/cmd/main_cli.py +6 -4
- metaflow/cmd/make_wrapper.py +78 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +41 -10
- metaflow/datastore/datastore_set.py +11 -2
- metaflow/datastore/flow_datastore.py +156 -10
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +154 -39
- metaflow/debug.py +5 -0
- metaflow/decorators.py +404 -78
- metaflow/exception.py +8 -2
- metaflow/extension_support/__init__.py +527 -376
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/extension_support/plugins.py +49 -31
- metaflow/flowspec.py +482 -33
- metaflow/graph.py +210 -42
- metaflow/includefile.py +84 -40
- metaflow/lint.py +141 -22
- metaflow/meta_files.py +13 -0
- metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
- metaflow/{metadata → metadata_provider}/metadata.py +86 -1
- metaflow/metaflow_config.py +175 -28
- metaflow/metaflow_config_funcs.py +51 -3
- metaflow/metaflow_current.py +4 -10
- metaflow/metaflow_environment.py +139 -53
- metaflow/metaflow_git.py +115 -0
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +150 -66
- metaflow/mflog/__init__.py +4 -3
- metaflow/mflog/save_logs.py +2 -2
- metaflow/multicore_utils.py +31 -14
- 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 +149 -28
- metaflow/plugins/__init__.py +74 -5
- metaflow/plugins/airflow/airflow.py +40 -25
- metaflow/plugins/airflow/airflow_cli.py +22 -5
- metaflow/plugins/airflow/airflow_decorator.py +1 -1
- metaflow/plugins/airflow/airflow_utils.py +5 -3
- metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
- metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
- metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
- metaflow/plugins/argo/argo_client.py +78 -33
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +2410 -527
- metaflow/plugins/argo/argo_workflows_cli.py +571 -121
- metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
- metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
- metaflow/plugins/argo/capture_error.py +73 -0
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/jobset_input_paths.py +15 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +10 -3
- metaflow/plugins/aws/aws_utils.py +55 -2
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +33 -10
- metaflow/plugins/aws/batch/batch_client.py +4 -3
- metaflow/plugins/aws/batch/batch_decorator.py +102 -35
- metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
- metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
- metaflow/plugins/aws/step_functions/production_token.py +1 -1
- metaflow/plugins/aws/step_functions/step_functions.py +65 -8
- metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
- metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
- metaflow/plugins/azure/azure_exceptions.py +1 -1
- metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
- metaflow/plugins/azure/azure_tail.py +1 -1
- metaflow/plugins/azure/includefile_support.py +2 -0
- metaflow/plugins/cards/card_cli.py +66 -30
- metaflow/plugins/cards/card_creator.py +25 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +132 -8
- metaflow/plugins/cards/card_modules/basic.py +112 -17
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/card.py +16 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +665 -28
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
- 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 +68 -49
- metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
- metaflow/plugins/cards/card_modules/test_cards.py +26 -12
- metaflow/plugins/cards/card_server.py +39 -14
- metaflow/plugins/cards/component_serializer.py +2 -9
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/azure_storage.py +10 -1
- metaflow/plugins/datastores/gs_storage.py +6 -2
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/local.py +2 -0
- metaflow/plugins/datatools/s3/s3.py +126 -75
- metaflow/plugins/datatools/s3/s3op.py +254 -121
- metaflow/plugins/env_escape/__init__.py +3 -3
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/env_escape/server.py +7 -0
- metaflow/plugins/env_escape/stub.py +24 -5
- metaflow/plugins/events_decorator.py +343 -185
- 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/gcp/__init__.py +1 -1
- metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
- metaflow/plugins/gcp/gs_tail.py +10 -6
- metaflow/plugins/gcp/includefile_support.py +3 -0
- metaflow/plugins/kubernetes/kube_utils.py +108 -0
- metaflow/plugins/kubernetes/kubernetes.py +411 -130
- metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
- metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
- metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
- metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
- metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
- metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
- metaflow/plugins/logs_cli.py +359 -0
- metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
- metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +128 -11
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/project_decorator.py +51 -5
- metaflow/plugins/pypi/bootstrap.py +357 -105
- metaflow/plugins/pypi/conda_decorator.py +82 -81
- metaflow/plugins/pypi/conda_environment.py +187 -52
- metaflow/plugins/pypi/micromamba.py +157 -47
- metaflow/plugins/pypi/parsers.py +268 -0
- metaflow/plugins/pypi/pip.py +88 -13
- metaflow/plugins/pypi/pypi_decorator.py +37 -1
- metaflow/plugins/pypi/utils.py +48 -2
- metaflow/plugins/resources_decorator.py +2 -2
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +26 -181
- 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/tag_cli.py +4 -7
- metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
- metaflow/plugins/timeout_decorator.py +3 -3
- metaflow/plugins/uv/__init__.py +0 -0
- metaflow/plugins/uv/bootstrap.py +128 -0
- metaflow/plugins/uv/uv_environment.py +72 -0
- metaflow/procpoll.py +1 -1
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/__init__.py +0 -0
- metaflow/runner/click_api.py +717 -0
- metaflow/runner/deployer.py +470 -0
- metaflow/runner/deployer_impl.py +201 -0
- metaflow/runner/metaflow_runner.py +714 -0
- metaflow/runner/nbdeploy.py +132 -0
- metaflow/runner/nbrun.py +225 -0
- metaflow/runner/subprocess_manager.py +650 -0
- metaflow/runner/utils.py +335 -0
- metaflow/runtime.py +1078 -260
- metaflow/sidecar/sidecar_worker.py +1 -1
- metaflow/system/__init__.py +5 -0
- metaflow/system/system_logger.py +85 -0
- metaflow/system/system_monitor.py +108 -0
- metaflow/system/system_utils.py +19 -0
- metaflow/task.py +521 -225
- metaflow/tracing/__init__.py +7 -7
- metaflow/tracing/span_exporter.py +31 -38
- metaflow/tracing/tracing_modules.py +38 -43
- metaflow/tuple_util.py +27 -0
- metaflow/user_configs/__init__.py +0 -0
- metaflow/user_configs/config_options.py +563 -0
- metaflow/user_configs/config_parameters.py +598 -0
- 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 +243 -27
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
- ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
- ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
- 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/package.py +0 -188
- ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
- ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
- /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
- /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
- /metaflow/{metadata → metadata_provider}/util.py +0 -0
- /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
from metaflow._vendor import click
|
|
2
|
+
|
|
3
|
+
from .. import namespace
|
|
4
|
+
from ..cli import echo_always, echo_dev_null
|
|
5
|
+
from ..cli_args import cli_args
|
|
6
|
+
from ..datastore.flow_datastore import FlowDataStore
|
|
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
|
|
12
|
+
from ..task import MetaflowTask
|
|
13
|
+
from ..unbounded_foreach import UBF_CONTROL, UBF_TASK
|
|
14
|
+
from ..util import decompress_list, read_artifacts_module
|
|
15
|
+
import metaflow.tracing as tracing
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command(help="Internal command to execute a single task.", hidden=True)
|
|
19
|
+
@tracing.cli("cli/step")
|
|
20
|
+
@click.argument("step-name")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--run-id",
|
|
23
|
+
default=None,
|
|
24
|
+
required=True,
|
|
25
|
+
help="ID for one execution of all steps in the flow.",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--task-id",
|
|
29
|
+
default=None,
|
|
30
|
+
required=True,
|
|
31
|
+
show_default=True,
|
|
32
|
+
help="ID for this instance of the step.",
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--input-paths",
|
|
36
|
+
help="A comma-separated list of pathspecs specifying inputs for this step.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--input-paths-filename",
|
|
40
|
+
type=click.Path(exists=True, readable=True, dir_okay=False, resolve_path=True),
|
|
41
|
+
help="A filename containing the argument typically passed to `input-paths`",
|
|
42
|
+
hidden=True,
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--split-index",
|
|
46
|
+
type=int,
|
|
47
|
+
default=None,
|
|
48
|
+
show_default=True,
|
|
49
|
+
help="Index of this foreach split.",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--tag",
|
|
53
|
+
"opt_tag",
|
|
54
|
+
multiple=True,
|
|
55
|
+
default=None,
|
|
56
|
+
help="Annotate this run with the given tag. You can specify "
|
|
57
|
+
"this option multiple times to attach multiple tags in "
|
|
58
|
+
"the task.",
|
|
59
|
+
)
|
|
60
|
+
@click.option(
|
|
61
|
+
"--namespace",
|
|
62
|
+
"opt_namespace",
|
|
63
|
+
default=None,
|
|
64
|
+
help="Change namespace from the default (your username) to the specified tag.",
|
|
65
|
+
)
|
|
66
|
+
@click.option(
|
|
67
|
+
"--retry-count",
|
|
68
|
+
default=0,
|
|
69
|
+
help="How many times we have attempted to run this task.",
|
|
70
|
+
)
|
|
71
|
+
@click.option(
|
|
72
|
+
"--max-user-code-retries",
|
|
73
|
+
default=0,
|
|
74
|
+
help="How many times we should attempt running the user code.",
|
|
75
|
+
)
|
|
76
|
+
@click.option(
|
|
77
|
+
"--clone-only",
|
|
78
|
+
default=None,
|
|
79
|
+
help="Pathspec of the origin task for this task to clone. Do "
|
|
80
|
+
"not execute anything.",
|
|
81
|
+
)
|
|
82
|
+
@click.option(
|
|
83
|
+
"--clone-run-id",
|
|
84
|
+
default=None,
|
|
85
|
+
help="Run id of the origin flow, if this task is part of a flow being resumed.",
|
|
86
|
+
)
|
|
87
|
+
@click.option(
|
|
88
|
+
"--ubf-context",
|
|
89
|
+
default="none",
|
|
90
|
+
type=click.Choice(["none", UBF_CONTROL, UBF_TASK]),
|
|
91
|
+
help="Provides additional context if this task is of type unbounded foreach.",
|
|
92
|
+
)
|
|
93
|
+
@click.option(
|
|
94
|
+
"--num-parallel",
|
|
95
|
+
default=0,
|
|
96
|
+
type=int,
|
|
97
|
+
help="Number of parallel instances of a step. Ignored in local mode (see parallel decorator code).",
|
|
98
|
+
)
|
|
99
|
+
@click.pass_context
|
|
100
|
+
def step(
|
|
101
|
+
ctx,
|
|
102
|
+
step_name,
|
|
103
|
+
opt_tag=None,
|
|
104
|
+
run_id=None,
|
|
105
|
+
task_id=None,
|
|
106
|
+
input_paths=None,
|
|
107
|
+
input_paths_filename=None,
|
|
108
|
+
split_index=None,
|
|
109
|
+
opt_namespace=None,
|
|
110
|
+
retry_count=None,
|
|
111
|
+
max_user_code_retries=None,
|
|
112
|
+
clone_only=None,
|
|
113
|
+
clone_run_id=None,
|
|
114
|
+
ubf_context="none",
|
|
115
|
+
num_parallel=None,
|
|
116
|
+
):
|
|
117
|
+
if ctx.obj.is_quiet:
|
|
118
|
+
echo = echo_dev_null
|
|
119
|
+
else:
|
|
120
|
+
echo = echo_always
|
|
121
|
+
|
|
122
|
+
if ubf_context == "none":
|
|
123
|
+
ubf_context = None
|
|
124
|
+
if opt_namespace is not None:
|
|
125
|
+
namespace(opt_namespace)
|
|
126
|
+
|
|
127
|
+
func = None
|
|
128
|
+
try:
|
|
129
|
+
func = getattr(ctx.obj.flow, step_name)
|
|
130
|
+
except:
|
|
131
|
+
raise CommandException("Step *%s* doesn't exist." % step_name)
|
|
132
|
+
if not func.is_step:
|
|
133
|
+
raise CommandException("Function *%s* is not a step." % step_name)
|
|
134
|
+
echo("Executing a step, *%s*" % step_name, fg="magenta", bold=False)
|
|
135
|
+
|
|
136
|
+
step_kwargs = ctx.params
|
|
137
|
+
# Remove argument `step_name` from `step_kwargs`.
|
|
138
|
+
step_kwargs.pop("step_name", None)
|
|
139
|
+
# Remove `opt_*` prefix from (some) option keys.
|
|
140
|
+
step_kwargs = dict(
|
|
141
|
+
[(k[4:], v) if k.startswith("opt_") else (k, v) for k, v in step_kwargs.items()]
|
|
142
|
+
)
|
|
143
|
+
cli_args._set_step_kwargs(step_kwargs)
|
|
144
|
+
|
|
145
|
+
ctx.obj.metadata.add_sticky_tags(tags=opt_tag)
|
|
146
|
+
if not input_paths and input_paths_filename:
|
|
147
|
+
with open(input_paths_filename, mode="r", encoding="utf-8") as f:
|
|
148
|
+
input_paths = f.read().strip(" \n\"'")
|
|
149
|
+
|
|
150
|
+
paths = decompress_list(input_paths) if input_paths else []
|
|
151
|
+
|
|
152
|
+
task = MetaflowTask(
|
|
153
|
+
ctx.obj.flow,
|
|
154
|
+
ctx.obj.flow_datastore,
|
|
155
|
+
ctx.obj.metadata,
|
|
156
|
+
ctx.obj.environment,
|
|
157
|
+
ctx.obj.echo,
|
|
158
|
+
ctx.obj.event_logger,
|
|
159
|
+
ctx.obj.monitor,
|
|
160
|
+
ubf_context,
|
|
161
|
+
)
|
|
162
|
+
if clone_only:
|
|
163
|
+
task.clone_only(
|
|
164
|
+
step_name,
|
|
165
|
+
run_id,
|
|
166
|
+
task_id,
|
|
167
|
+
clone_only,
|
|
168
|
+
retry_count,
|
|
169
|
+
)
|
|
170
|
+
else:
|
|
171
|
+
task.run_step(
|
|
172
|
+
step_name,
|
|
173
|
+
run_id,
|
|
174
|
+
task_id,
|
|
175
|
+
clone_run_id,
|
|
176
|
+
paths,
|
|
177
|
+
split_index,
|
|
178
|
+
retry_count,
|
|
179
|
+
max_user_code_retries,
|
|
180
|
+
)
|
|
181
|
+
|
|
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")
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from metaflow._vendor import click
|
|
3
|
+
from metaflow.extension_support.plugins import get_plugin
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LazyPluginCommandCollection(click.CommandCollection):
|
|
7
|
+
# lazy_source should only point to things that are resolved as CLI plugins.
|
|
8
|
+
def __init__(self, *args, lazy_sources=None, **kwargs):
|
|
9
|
+
super().__init__(*args, **kwargs)
|
|
10
|
+
# lazy_sources is a list of strings in the form
|
|
11
|
+
# "{plugin_name}" -> "{module-name}.{command-object-name}"
|
|
12
|
+
self.lazy_sources = lazy_sources or {}
|
|
13
|
+
self._lazy_loaded = {}
|
|
14
|
+
|
|
15
|
+
def invoke(self, ctx):
|
|
16
|
+
# NOTE: This is copied from MultiCommand.invoke. The change is that we
|
|
17
|
+
# behave like chain in the sense that we evaluate the subcommand *after*
|
|
18
|
+
# invoking the base command but we don't chain the commands like self.chain
|
|
19
|
+
# would otherwise indicate.
|
|
20
|
+
# The goal of this is to make sure that the first command is properly executed
|
|
21
|
+
# *first* prior to loading the other subcommands. It's more a lazy_subcommand_load
|
|
22
|
+
# than a chain.
|
|
23
|
+
# Look for CHANGE HERE in this code to see where the changes are made.
|
|
24
|
+
# If click is updated, this may also need to be updated. This version is for
|
|
25
|
+
# click 7.1.2.
|
|
26
|
+
def _process_result(value):
|
|
27
|
+
if self.result_callback is not None:
|
|
28
|
+
value = ctx.invoke(self.result_callback, value, **ctx.params)
|
|
29
|
+
return value
|
|
30
|
+
|
|
31
|
+
if not ctx.protected_args:
|
|
32
|
+
# If we are invoked without command the chain flag controls
|
|
33
|
+
# how this happens. If we are not in chain mode, the return
|
|
34
|
+
# value here is the return value of the command.
|
|
35
|
+
# If however we are in chain mode, the return value is the
|
|
36
|
+
# return value of the result processor invoked with an empty
|
|
37
|
+
# list (which means that no subcommand actually was executed).
|
|
38
|
+
if self.invoke_without_command:
|
|
39
|
+
# CHANGE HERE: We behave like self.chain = False here
|
|
40
|
+
|
|
41
|
+
# if not self.chain:
|
|
42
|
+
return click.Command.invoke(self, ctx)
|
|
43
|
+
# with ctx:
|
|
44
|
+
# click.Command.invoke(self, ctx)
|
|
45
|
+
# return _process_result([])
|
|
46
|
+
|
|
47
|
+
ctx.fail("Missing command.")
|
|
48
|
+
|
|
49
|
+
# Fetch args back out
|
|
50
|
+
args = ctx.protected_args + ctx.args
|
|
51
|
+
ctx.args = []
|
|
52
|
+
ctx.protected_args = []
|
|
53
|
+
# CHANGE HERE: Add saved_args so we have access to it in the command to be
|
|
54
|
+
# able to infer what we are calling next
|
|
55
|
+
ctx.saved_args = args
|
|
56
|
+
|
|
57
|
+
# If we're not in chain mode, we only allow the invocation of a
|
|
58
|
+
# single command but we also inform the current context about the
|
|
59
|
+
# name of the command to invoke.
|
|
60
|
+
# CHANGE HERE: We change this block to do the invoke *before* the resolve_command
|
|
61
|
+
# Make sure the context is entered so we do not clean up
|
|
62
|
+
# resources until the result processor has worked.
|
|
63
|
+
with ctx:
|
|
64
|
+
ctx.invoked_subcommand = "*" if args else None
|
|
65
|
+
click.Command.invoke(self, ctx)
|
|
66
|
+
cmd_name, cmd, args = self.resolve_command(ctx, args)
|
|
67
|
+
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
|
|
68
|
+
with sub_ctx:
|
|
69
|
+
return _process_result(sub_ctx.command.invoke(sub_ctx))
|
|
70
|
+
|
|
71
|
+
# CHANGE HERE: Removed all the part of chain mode.
|
|
72
|
+
|
|
73
|
+
def list_commands(self, ctx):
|
|
74
|
+
base = super().list_commands(ctx)
|
|
75
|
+
for source_name, source in self.lazy_sources.items():
|
|
76
|
+
subgroup = self._lazy_load(source_name, source)
|
|
77
|
+
base.extend(subgroup.list_commands(ctx))
|
|
78
|
+
return base
|
|
79
|
+
|
|
80
|
+
def get_command(self, ctx, cmd_name):
|
|
81
|
+
base_cmd = super().get_command(ctx, cmd_name)
|
|
82
|
+
if base_cmd is not None:
|
|
83
|
+
return base_cmd
|
|
84
|
+
for source_name, source in self.lazy_sources.items():
|
|
85
|
+
subgroup = self._lazy_load(source_name, source)
|
|
86
|
+
cmd = subgroup.get_command(ctx, cmd_name)
|
|
87
|
+
if cmd is not None:
|
|
88
|
+
return cmd
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def _lazy_load(self, source_name, source_path):
|
|
92
|
+
if source_name in self._lazy_loaded:
|
|
93
|
+
return self._lazy_loaded[source_name]
|
|
94
|
+
cmd_object = get_plugin("cli", source_path, source_name)
|
|
95
|
+
if not isinstance(cmd_object, click.Group):
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"Lazy loading of {source_name} failed by returning "
|
|
98
|
+
"a non-group object"
|
|
99
|
+
)
|
|
100
|
+
self._lazy_loaded[source_name] = cmd_object
|
|
101
|
+
return cmd_object
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class LazyGroup(click.Group):
|
|
105
|
+
def __init__(self, *args, lazy_subcommands=None, **kwargs):
|
|
106
|
+
super().__init__(*args, **kwargs)
|
|
107
|
+
# lazy_subcommands is a list of strings in the form
|
|
108
|
+
# "{command} -> "{module-name}.{command-object-name}"
|
|
109
|
+
self.lazy_subcommands = lazy_subcommands or {}
|
|
110
|
+
self._lazy_loaded = {}
|
|
111
|
+
|
|
112
|
+
def list_commands(self, ctx):
|
|
113
|
+
base = super().list_commands(ctx)
|
|
114
|
+
lazy = sorted(self.lazy_subcommands.keys())
|
|
115
|
+
return base + lazy
|
|
116
|
+
|
|
117
|
+
def get_command(self, ctx, cmd_name):
|
|
118
|
+
if cmd_name in self.lazy_subcommands:
|
|
119
|
+
return self._lazy_load(cmd_name)
|
|
120
|
+
return super().get_command(ctx, cmd_name)
|
|
121
|
+
|
|
122
|
+
def _lazy_load(self, cmd_name):
|
|
123
|
+
if cmd_name in self._lazy_loaded:
|
|
124
|
+
return self._lazy_loaded[cmd_name]
|
|
125
|
+
|
|
126
|
+
import_path = self.lazy_subcommands[cmd_name]
|
|
127
|
+
modname, cmd = import_path.rsplit(".", 1)
|
|
128
|
+
# do the import
|
|
129
|
+
mod = importlib.import_module(modname)
|
|
130
|
+
# get the Command object from that module
|
|
131
|
+
cmd_object = getattr(mod, cmd)
|
|
132
|
+
# check the result to make debugging easier. note that wrapped BaseCommand
|
|
133
|
+
# can be functions
|
|
134
|
+
if not isinstance(cmd_object, click.BaseCommand):
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"Lazy loading of {import_path} failed by returning "
|
|
137
|
+
f"a non-command object {type(cmd_object)}"
|
|
138
|
+
)
|
|
139
|
+
self._lazy_loaded[cmd_name] = cmd_object
|
|
140
|
+
return cmd_object
|