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
metaflow/runner/click_api.py
CHANGED
|
@@ -43,10 +43,10 @@ from metaflow._vendor.click.types import (
|
|
|
43
43
|
)
|
|
44
44
|
from metaflow.decorators import add_decorator_options
|
|
45
45
|
from metaflow.exception import MetaflowException
|
|
46
|
+
from metaflow.flowspec import FlowStateItems
|
|
46
47
|
from metaflow.includefile import FilePathClass
|
|
47
48
|
from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
|
|
48
49
|
from metaflow.parameters import JSONTypeClass, flow_context
|
|
49
|
-
from metaflow.user_configs.config_decorators import CustomFlowDecorator
|
|
50
50
|
from metaflow.user_configs.config_options import (
|
|
51
51
|
ConfigValue,
|
|
52
52
|
ConvertDictOrStr,
|
|
@@ -55,6 +55,7 @@ from metaflow.user_configs.config_options import (
|
|
|
55
55
|
MultipleTuple,
|
|
56
56
|
config_options_with_config_input,
|
|
57
57
|
)
|
|
58
|
+
from metaflow.user_decorators.user_flow_decorator import FlowMutator
|
|
58
59
|
|
|
59
60
|
# Define a recursive type alias for JSON
|
|
60
61
|
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
|
|
@@ -165,13 +166,23 @@ def _method_sanity_check(
|
|
|
165
166
|
return method_params
|
|
166
167
|
|
|
167
168
|
|
|
169
|
+
def _cleanup_flow_parameters(cmd_obj: Union[click.Command, click.Group]):
|
|
170
|
+
if hasattr(cmd_obj, "original_params"):
|
|
171
|
+
cmd_obj.params = list(cmd_obj.original_params)
|
|
172
|
+
|
|
173
|
+
if isinstance(cmd_obj, click.Group):
|
|
174
|
+
for sub_cmd_name in cmd_obj.list_commands(None):
|
|
175
|
+
sub_cmd = cmd_obj.get_command(None, sub_cmd_name)
|
|
176
|
+
if sub_cmd:
|
|
177
|
+
_cleanup_flow_parameters(sub_cmd)
|
|
178
|
+
|
|
179
|
+
|
|
168
180
|
def _lazy_load_command(
|
|
169
181
|
cli_collection: click.Group,
|
|
170
182
|
flow_parameters: Union[str, List[Parameter]],
|
|
171
183
|
_self,
|
|
172
184
|
name: str,
|
|
173
185
|
):
|
|
174
|
-
|
|
175
186
|
# Context is not used in get_command so we can pass None. Since we pin click,
|
|
176
187
|
# this won't change from under us.
|
|
177
188
|
|
|
@@ -181,6 +192,7 @@ def _lazy_load_command(
|
|
|
181
192
|
flow_parameters = getattr(_self, flow_parameters)()
|
|
182
193
|
cmd_obj = cli_collection.get_command(None, name)
|
|
183
194
|
if cmd_obj:
|
|
195
|
+
_cleanup_flow_parameters(cmd_obj)
|
|
184
196
|
if isinstance(cmd_obj, click.Group):
|
|
185
197
|
# TODO: possibly check for fake groups with cmd_obj.name in ["cli", "main"]
|
|
186
198
|
result = functools.partial(extract_group(cmd_obj, flow_parameters), _self)
|
|
@@ -264,12 +276,12 @@ def extract_flow_class_from_file(flow_file: str) -> FlowSpec:
|
|
|
264
276
|
loaded_modules[flow_file] = module
|
|
265
277
|
|
|
266
278
|
classes = inspect.getmembers(
|
|
267
|
-
module, lambda x: inspect.isclass(x) or isinstance(x,
|
|
279
|
+
module, lambda x: inspect.isclass(x) or isinstance(x, FlowMutator)
|
|
268
280
|
)
|
|
269
281
|
flow_cls = None
|
|
270
282
|
|
|
271
283
|
for _, kls in classes:
|
|
272
|
-
if isinstance(kls,
|
|
284
|
+
if isinstance(kls, FlowMutator):
|
|
273
285
|
kls = kls._flow_cls
|
|
274
286
|
if (
|
|
275
287
|
kls is not FlowSpec
|
|
@@ -339,8 +351,10 @@ class MetaflowAPI(object):
|
|
|
339
351
|
class_dict = {
|
|
340
352
|
"__module__": "metaflow",
|
|
341
353
|
"_API_NAME": flow_file,
|
|
342
|
-
"_internal_getattr":
|
|
343
|
-
|
|
354
|
+
"_internal_getattr": staticmethod(
|
|
355
|
+
functools.partial(
|
|
356
|
+
_lazy_load_command, cli_collection, "_compute_flow_parameters"
|
|
357
|
+
)
|
|
344
358
|
),
|
|
345
359
|
"__getattr__": getattr_wrapper,
|
|
346
360
|
}
|
|
@@ -460,31 +474,62 @@ class MetaflowAPI(object):
|
|
|
460
474
|
|
|
461
475
|
ds = opts.get("datastore", defaults["datastore"])
|
|
462
476
|
quiet = opts.get("quiet", defaults["quiet"])
|
|
477
|
+
|
|
478
|
+
# Order to find config or config_value:
|
|
479
|
+
# 1. Passed directly to the Click API
|
|
480
|
+
# 2. If not found, check if passed through an environment variable
|
|
481
|
+
# 3. If not found, use the default value
|
|
463
482
|
is_default = False
|
|
464
483
|
config_file = opts.get("config")
|
|
465
484
|
if config_file is None:
|
|
466
|
-
|
|
467
|
-
|
|
485
|
+
# Check if it was set through an environment variable -- we
|
|
486
|
+
# don't have click process them here so we need to "fake" it.
|
|
487
|
+
env_config_file = os.environ.get("METAFLOW_FLOW_CONFIG")
|
|
488
|
+
if env_config_file:
|
|
489
|
+
# Convert dict items to list of tuples
|
|
490
|
+
config_file = list(json.loads(env_config_file).items())
|
|
491
|
+
is_default = False
|
|
492
|
+
else:
|
|
493
|
+
is_default = True
|
|
494
|
+
config_file = defaults.get("config")
|
|
468
495
|
|
|
469
496
|
if config_file:
|
|
470
|
-
config_file =
|
|
471
|
-
|
|
472
|
-
|
|
497
|
+
config_file = dict(
|
|
498
|
+
map(
|
|
499
|
+
lambda x: (
|
|
500
|
+
x[0],
|
|
501
|
+
ConvertPath.convert_value(x[1], is_default),
|
|
502
|
+
),
|
|
503
|
+
config_file,
|
|
504
|
+
)
|
|
473
505
|
)
|
|
474
506
|
|
|
475
507
|
is_default = False
|
|
476
508
|
config_value = opts.get("config-value")
|
|
477
509
|
if config_value is None:
|
|
478
|
-
|
|
479
|
-
|
|
510
|
+
env_config_value = os.environ.get("METAFLOW_FLOW_CONFIG_VALUE")
|
|
511
|
+
if env_config_value:
|
|
512
|
+
# Parse environment variable using MultipleTuple logic
|
|
513
|
+
loaded = json.loads(env_config_value)
|
|
514
|
+
# Convert dict items to list of tuples with JSON-serialized values
|
|
515
|
+
config_value = [
|
|
516
|
+
(k, json.dumps(v) if not isinstance(v, str) else v)
|
|
517
|
+
for k, v in loaded.items()
|
|
518
|
+
]
|
|
519
|
+
is_default = False
|
|
520
|
+
else:
|
|
521
|
+
is_default = True
|
|
522
|
+
config_value = defaults.get("config_value")
|
|
480
523
|
|
|
481
524
|
if config_value:
|
|
482
|
-
config_value =
|
|
483
|
-
|
|
484
|
-
x
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
525
|
+
config_value = dict(
|
|
526
|
+
map(
|
|
527
|
+
lambda x: (
|
|
528
|
+
x[0],
|
|
529
|
+
ConvertDictOrStr.convert_value(x[1], is_default),
|
|
530
|
+
),
|
|
531
|
+
config_value,
|
|
532
|
+
)
|
|
488
533
|
)
|
|
489
534
|
|
|
490
535
|
if (config_file is None) ^ (config_value is None):
|
|
@@ -505,10 +550,15 @@ class MetaflowAPI(object):
|
|
|
505
550
|
|
|
506
551
|
# At this point, we are like in start() in cli.py -- we obtained the
|
|
507
552
|
# properly processed config_options which we can now use to process
|
|
508
|
-
# the config decorators (including
|
|
553
|
+
# the config decorators (including StepMutator/FlowMutator)
|
|
509
554
|
# Note that if CLICK_API_PROCESS_CONFIG is False, we still do this because
|
|
510
555
|
# it will init all parameters (config_options will be None)
|
|
511
556
|
# We ignore any errors if we don't check the configs in the click API.
|
|
557
|
+
|
|
558
|
+
# Init all values in the flow mutators and then process them
|
|
559
|
+
for decorator in self._flow_cls._flow_state[FlowStateItems.FLOW_MUTATORS]:
|
|
560
|
+
decorator.external_init()
|
|
561
|
+
|
|
512
562
|
new_cls = self._flow_cls._process_config_decorators(
|
|
513
563
|
config_options, process_configs=CLICK_API_PROCESS_CONFIG
|
|
514
564
|
)
|
|
@@ -534,14 +584,16 @@ def extract_all_params(cmd_obj: Union[click.Command, click.Group]):
|
|
|
534
584
|
|
|
535
585
|
for each_param in cmd_obj.params:
|
|
536
586
|
if isinstance(each_param, click.Argument):
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
587
|
+
(
|
|
588
|
+
arg_params_sigs[each_param.name],
|
|
589
|
+
annotations[each_param.name],
|
|
590
|
+
) = get_inspect_param_obj(each_param, inspect.Parameter.POSITIONAL_ONLY)
|
|
540
591
|
arg_parameters[each_param.name] = each_param
|
|
541
592
|
elif isinstance(each_param, click.Option):
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
593
|
+
(
|
|
594
|
+
opt_params_sigs[each_param.name],
|
|
595
|
+
annotations[each_param.name],
|
|
596
|
+
) = get_inspect_param_obj(each_param, inspect.Parameter.KEYWORD_ONLY)
|
|
545
597
|
opt_parameters[each_param.name] = each_param
|
|
546
598
|
|
|
547
599
|
defaults[each_param.name] = each_param.default
|
|
@@ -651,6 +703,7 @@ if __name__ == "__main__":
|
|
|
651
703
|
.kubernetes()
|
|
652
704
|
.step(
|
|
653
705
|
step_name="process",
|
|
706
|
+
code_package_metadata="some_version",
|
|
654
707
|
code_package_sha="some_sha",
|
|
655
708
|
code_package_url="some_url",
|
|
656
709
|
)
|
metaflow/runner/deployer.py
CHANGED
|
@@ -7,6 +7,57 @@ from typing import ClassVar, Dict, Optional, TYPE_CHECKING
|
|
|
7
7
|
from metaflow.exception import MetaflowNotFound
|
|
8
8
|
from metaflow.metaflow_config import DEFAULT_FROM_DEPLOYMENT_IMPL
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
def generate_fake_flow_file_contents(
|
|
12
|
+
flow_name: str, param_info: dict, project_name: Optional[str] = None
|
|
13
|
+
):
|
|
14
|
+
params_code = ""
|
|
15
|
+
for _, param_details in param_info.items():
|
|
16
|
+
param_python_var_name = param_details.get(
|
|
17
|
+
"python_var_name", param_details["name"]
|
|
18
|
+
)
|
|
19
|
+
param_name = param_details["name"]
|
|
20
|
+
param_type = param_details["type"]
|
|
21
|
+
param_help = param_details["description"]
|
|
22
|
+
param_required = param_details["is_required"]
|
|
23
|
+
|
|
24
|
+
if param_type == "JSON":
|
|
25
|
+
params_code += (
|
|
26
|
+
f" {param_python_var_name} = Parameter('{param_name}', "
|
|
27
|
+
f"type=JSONType, help='''{param_help}''', required={param_required})\n"
|
|
28
|
+
)
|
|
29
|
+
elif param_type == "FilePath":
|
|
30
|
+
is_text = param_details.get("is_text", True)
|
|
31
|
+
encoding = param_details.get("encoding", "utf-8")
|
|
32
|
+
params_code += (
|
|
33
|
+
f" {param_python_var_name} = IncludeFile('{param_name}', "
|
|
34
|
+
f"is_text={is_text}, encoding='{encoding}', help='''{param_help}''', "
|
|
35
|
+
f"required={param_required})\n"
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
params_code += (
|
|
39
|
+
f" {param_python_var_name} = Parameter('{param_name}', "
|
|
40
|
+
f"type={param_type}, help='''{param_help}''', required={param_required})\n"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
project_decorator = f"@project(name='{project_name}')\n" if project_name else ""
|
|
44
|
+
|
|
45
|
+
contents = f"""\
|
|
46
|
+
from metaflow import FlowSpec, Parameter, IncludeFile, JSONType, step, project
|
|
47
|
+
{project_decorator}class {flow_name}(FlowSpec):
|
|
48
|
+
{params_code}
|
|
49
|
+
@step
|
|
50
|
+
def start(self):
|
|
51
|
+
self.next(self.end)
|
|
52
|
+
@step
|
|
53
|
+
def end(self):
|
|
54
|
+
pass
|
|
55
|
+
if __name__ == '__main__':
|
|
56
|
+
{flow_name}()
|
|
57
|
+
"""
|
|
58
|
+
return contents
|
|
59
|
+
|
|
60
|
+
|
|
10
61
|
if TYPE_CHECKING:
|
|
11
62
|
import metaflow
|
|
12
63
|
import metaflow.runner.deployer_impl
|
|
@@ -180,7 +231,68 @@ class DeployedFlowMeta(type):
|
|
|
180
231
|
}
|
|
181
232
|
)
|
|
182
233
|
|
|
183
|
-
def
|
|
234
|
+
def _get_triggered_run_injected_method():
|
|
235
|
+
def f(
|
|
236
|
+
cls,
|
|
237
|
+
identifier: str,
|
|
238
|
+
run_id: str,
|
|
239
|
+
metadata: Optional[str] = None,
|
|
240
|
+
impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
|
|
241
|
+
) -> "TriggeredRun":
|
|
242
|
+
"""
|
|
243
|
+
Retrieves a `TriggeredRun` object from an identifier, a run id and optional
|
|
244
|
+
metadata. The `impl` parameter specifies the deployer implementation
|
|
245
|
+
to use (like `argo-workflows`).
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
identifier : str
|
|
250
|
+
Deployer specific identifier for the workflow to retrieve
|
|
251
|
+
run_id : str
|
|
252
|
+
Run ID for the which to fetch the triggered run object
|
|
253
|
+
metadata : str, optional, default None
|
|
254
|
+
Optional deployer specific metadata.
|
|
255
|
+
impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
|
|
256
|
+
The default implementation to use if not specified
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
TriggeredRun
|
|
261
|
+
A `TriggeredRun` object representing the triggered run corresponding
|
|
262
|
+
to the identifier and the run id.
|
|
263
|
+
"""
|
|
264
|
+
if impl in allowed_providers:
|
|
265
|
+
return (
|
|
266
|
+
allowed_providers[impl]
|
|
267
|
+
.deployed_flow_type()
|
|
268
|
+
.get_triggered_run(identifier, run_id, metadata)
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"No deployer '{impl}' exists; valid deployers are: "
|
|
273
|
+
f"{list(allowed_providers.keys())}"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
f.__name__ = "get_triggered_run"
|
|
277
|
+
return f
|
|
278
|
+
|
|
279
|
+
def _per_type_get_triggered_run_injected_method(method_name, impl):
|
|
280
|
+
def f(
|
|
281
|
+
cls,
|
|
282
|
+
identifier: str,
|
|
283
|
+
run_id: str,
|
|
284
|
+
metadata: Optional[str] = None,
|
|
285
|
+
):
|
|
286
|
+
return (
|
|
287
|
+
allowed_providers[impl]
|
|
288
|
+
.deployed_flow_type()
|
|
289
|
+
.get_triggered_run(identifier, run_id, metadata)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
f.__name__ = method_name
|
|
293
|
+
return f
|
|
294
|
+
|
|
295
|
+
def _from_deployment_injected_method():
|
|
184
296
|
def f(
|
|
185
297
|
cls,
|
|
186
298
|
identifier: str,
|
|
@@ -222,7 +334,7 @@ class DeployedFlowMeta(type):
|
|
|
222
334
|
f.__name__ = "from_deployment"
|
|
223
335
|
return f
|
|
224
336
|
|
|
225
|
-
def
|
|
337
|
+
def _per_type_from_deployment_injected_method(method_name, impl):
|
|
226
338
|
def f(
|
|
227
339
|
cls,
|
|
228
340
|
identifier: str,
|
|
@@ -237,14 +349,104 @@ class DeployedFlowMeta(type):
|
|
|
237
349
|
f.__name__ = method_name
|
|
238
350
|
return f
|
|
239
351
|
|
|
240
|
-
|
|
352
|
+
def _list_deployed_flows_injected_method():
|
|
353
|
+
def f(
|
|
354
|
+
cls,
|
|
355
|
+
flow_name: Optional[str] = None,
|
|
356
|
+
impl: str = DEFAULT_FROM_DEPLOYMENT_IMPL.replace("-", "_"),
|
|
357
|
+
):
|
|
358
|
+
"""
|
|
359
|
+
List all deployed flows for the specified implementation.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
flow_name : str, optional, default None
|
|
364
|
+
If specified, only list deployed flows for this specific flow name.
|
|
365
|
+
If None, list all deployed flows.
|
|
366
|
+
impl : str, optional, default given by METAFLOW_DEFAULT_FROM_DEPLOYMENT_IMPL
|
|
367
|
+
The default implementation to use if not specified
|
|
368
|
+
|
|
369
|
+
Yields
|
|
370
|
+
------
|
|
371
|
+
DeployedFlow
|
|
372
|
+
`DeployedFlow` objects representing deployed flows.
|
|
373
|
+
"""
|
|
374
|
+
if impl in allowed_providers:
|
|
375
|
+
return (
|
|
376
|
+
allowed_providers[impl]
|
|
377
|
+
.deployed_flow_type()
|
|
378
|
+
.list_deployed_flows(flow_name)
|
|
379
|
+
)
|
|
380
|
+
else:
|
|
381
|
+
raise ValueError(
|
|
382
|
+
f"No deployer '{impl}' exists; valid deployers are: "
|
|
383
|
+
f"{list(allowed_providers.keys())}"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
f.__name__ = "list_deployed_flows"
|
|
387
|
+
return f
|
|
388
|
+
|
|
389
|
+
def _per_type_list_deployed_flows_injected_method(method_name, impl):
|
|
390
|
+
def f(
|
|
391
|
+
cls,
|
|
392
|
+
flow_name: Optional[str] = None,
|
|
393
|
+
):
|
|
394
|
+
return (
|
|
395
|
+
allowed_providers[impl]
|
|
396
|
+
.deployed_flow_type()
|
|
397
|
+
.list_deployed_flows(flow_name)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
f.__name__ = method_name
|
|
401
|
+
return f
|
|
402
|
+
|
|
403
|
+
setattr(
|
|
404
|
+
cls, "from_deployment", classmethod(_from_deployment_injected_method())
|
|
405
|
+
)
|
|
406
|
+
setattr(
|
|
407
|
+
cls,
|
|
408
|
+
"list_deployed_flows",
|
|
409
|
+
classmethod(_list_deployed_flows_injected_method()),
|
|
410
|
+
)
|
|
411
|
+
setattr(
|
|
412
|
+
cls,
|
|
413
|
+
"get_triggered_run",
|
|
414
|
+
classmethod(_get_triggered_run_injected_method()),
|
|
415
|
+
)
|
|
241
416
|
|
|
242
417
|
for impl in allowed_providers:
|
|
243
|
-
|
|
418
|
+
from_deployment_method_name = f"from_{impl}"
|
|
419
|
+
list_deployed_flows_method_name = f"list_{impl}"
|
|
420
|
+
get_triggered_run_method_name = f"get_triggered_{impl}_run"
|
|
421
|
+
|
|
422
|
+
setattr(
|
|
423
|
+
cls,
|
|
424
|
+
from_deployment_method_name,
|
|
425
|
+
classmethod(
|
|
426
|
+
_per_type_from_deployment_injected_method(
|
|
427
|
+
from_deployment_method_name, impl
|
|
428
|
+
)
|
|
429
|
+
),
|
|
430
|
+
)
|
|
431
|
+
|
|
244
432
|
setattr(
|
|
245
433
|
cls,
|
|
246
|
-
|
|
247
|
-
classmethod(
|
|
434
|
+
list_deployed_flows_method_name,
|
|
435
|
+
classmethod(
|
|
436
|
+
_per_type_list_deployed_flows_injected_method(
|
|
437
|
+
list_deployed_flows_method_name, impl
|
|
438
|
+
)
|
|
439
|
+
),
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
setattr(
|
|
443
|
+
cls,
|
|
444
|
+
get_triggered_run_method_name,
|
|
445
|
+
classmethod(
|
|
446
|
+
_per_type_get_triggered_run_injected_method(
|
|
447
|
+
get_triggered_run_method_name, impl
|
|
448
|
+
)
|
|
449
|
+
),
|
|
248
450
|
)
|
|
249
451
|
|
|
250
452
|
return cls
|
metaflow/runner/deployer_impl.py
CHANGED
|
@@ -3,10 +3,12 @@ import json
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
|
-
from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type
|
|
6
|
+
from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Type, List
|
|
7
|
+
|
|
8
|
+
from metaflow.metaflow_config import CLICK_API_PROCESS_CONFIG
|
|
7
9
|
|
|
8
10
|
from .subprocess_manager import SubprocessManager
|
|
9
|
-
from .utils import get_lower_level_group, handle_timeout, temporary_fifo
|
|
11
|
+
from .utils import get_lower_level_group, handle_timeout, temporary_fifo, with_dir
|
|
10
12
|
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
import metaflow.runner.deployer
|
|
@@ -65,15 +67,10 @@ class DeployerImpl(object):
|
|
|
65
67
|
|
|
66
68
|
# Reload the CLI with an "empty" flow -- this will remove any configuration
|
|
67
69
|
# and parameter options. They are re-added in from_cli (called below).
|
|
68
|
-
to_reload = [
|
|
69
|
-
"metaflow.cli",
|
|
70
|
-
"metaflow.cli_components.run_cmds",
|
|
71
|
-
"metaflow.cli_components.init_cmd",
|
|
72
|
-
]
|
|
73
70
|
with flow_context(None):
|
|
74
71
|
[
|
|
75
72
|
importlib.reload(sys.modules[module])
|
|
76
|
-
for module in to_reload
|
|
73
|
+
for module in self.to_reload
|
|
77
74
|
if module in sys.modules
|
|
78
75
|
]
|
|
79
76
|
|
|
@@ -88,7 +85,7 @@ class DeployerImpl(object):
|
|
|
88
85
|
self.show_output = show_output
|
|
89
86
|
self.profile = profile
|
|
90
87
|
self.env = env
|
|
91
|
-
self.cwd = cwd
|
|
88
|
+
self.cwd = cwd or os.getcwd()
|
|
92
89
|
self.file_read_timeout = file_read_timeout
|
|
93
90
|
|
|
94
91
|
self.env_vars = os.environ.copy()
|
|
@@ -100,6 +97,19 @@ class DeployerImpl(object):
|
|
|
100
97
|
self.top_level_kwargs = kwargs
|
|
101
98
|
self.api = MetaflowAPI.from_cli(self.flow_file, start)
|
|
102
99
|
|
|
100
|
+
@property
|
|
101
|
+
def to_reload(self) -> List[str]:
|
|
102
|
+
"""
|
|
103
|
+
List of modules to reload when the deployer is initialized.
|
|
104
|
+
This is used to ensure that the CLI is in a clean state before
|
|
105
|
+
deploying the flow.
|
|
106
|
+
"""
|
|
107
|
+
return [
|
|
108
|
+
"metaflow.cli",
|
|
109
|
+
"metaflow.cli_components.run_cmds",
|
|
110
|
+
"metaflow.cli_components.init_cmd",
|
|
111
|
+
]
|
|
112
|
+
|
|
103
113
|
@property
|
|
104
114
|
def deployer_kwargs(self) -> Dict[str, Any]:
|
|
105
115
|
raise NotImplementedError
|
|
@@ -140,9 +150,19 @@ class DeployerImpl(object):
|
|
|
140
150
|
) -> "metaflow.runner.deployer.DeployedFlow":
|
|
141
151
|
with temporary_fifo() as (attribute_file_path, attribute_file_fd):
|
|
142
152
|
# every subclass needs to have `self.deployer_kwargs`
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
# TODO: Get rid of CLICK_API_PROCESS_CONFIG in the near future
|
|
154
|
+
if CLICK_API_PROCESS_CONFIG:
|
|
155
|
+
# We need to run this in the cwd because configs depend on files
|
|
156
|
+
# that may be located in paths relative to the directory the user
|
|
157
|
+
# wants to run in
|
|
158
|
+
with with_dir(self.cwd):
|
|
159
|
+
command = get_lower_level_group(
|
|
160
|
+
self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
|
|
161
|
+
).create(deployer_attribute_file=attribute_file_path, **kwargs)
|
|
162
|
+
else:
|
|
163
|
+
command = get_lower_level_group(
|
|
164
|
+
self.api, self.top_level_kwargs, self.TYPE, self.deployer_kwargs
|
|
165
|
+
).create(deployer_attribute_file=attribute_file_path, **kwargs)
|
|
146
166
|
|
|
147
167
|
pid = self.spm.run_command(
|
|
148
168
|
[sys.executable, *command],
|