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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from metaflow.client import Task
|
|
2
|
-
from metaflow import
|
|
2
|
+
from metaflow.parameters import JSONTypeClass
|
|
3
|
+
from metaflow import namespace
|
|
3
4
|
from metaflow.util import resolve_identity
|
|
4
5
|
from metaflow.exception import (
|
|
5
6
|
CommandException,
|
|
@@ -29,7 +30,7 @@ from .exception import (
|
|
|
29
30
|
)
|
|
30
31
|
import traceback
|
|
31
32
|
from collections import namedtuple
|
|
32
|
-
|
|
33
|
+
from .metadata import _save_metadata
|
|
33
34
|
from .card_resolver import resolve_paths_from_task, resumed_info
|
|
34
35
|
|
|
35
36
|
id_func = id
|
|
@@ -388,6 +389,22 @@ def card_read_options_and_arguments(func):
|
|
|
388
389
|
return wrapper
|
|
389
390
|
|
|
390
391
|
|
|
392
|
+
def _extract_reload_token(data, task, mf_card):
|
|
393
|
+
if "render_seq" not in data:
|
|
394
|
+
return "never"
|
|
395
|
+
|
|
396
|
+
if data["render_seq"] == "final":
|
|
397
|
+
# final data update should always trigger a card reload to show
|
|
398
|
+
# the final card, hence a different token for the final update
|
|
399
|
+
return "final"
|
|
400
|
+
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ALWAYS:
|
|
401
|
+
return "render-seq-%s" % data["render_seq"]
|
|
402
|
+
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_NEVER:
|
|
403
|
+
return "never"
|
|
404
|
+
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ONCHANGE:
|
|
405
|
+
return mf_card.reload_content_token(task, data)
|
|
406
|
+
|
|
407
|
+
|
|
391
408
|
def update_card(mf_card, mode, task, data, timeout_value=None):
|
|
392
409
|
"""
|
|
393
410
|
This method will be responsible for creating a card/data-update based on the `mode`.
|
|
@@ -432,31 +449,21 @@ def update_card(mf_card, mode, task, data, timeout_value=None):
|
|
|
432
449
|
- `timeout_stack_trace` : stack trace of the function if it timed out.
|
|
433
450
|
"""
|
|
434
451
|
|
|
435
|
-
def _reload_token():
|
|
436
|
-
if "render_seq" not in data:
|
|
437
|
-
return "never"
|
|
438
|
-
|
|
439
|
-
if data["render_seq"] == "final":
|
|
440
|
-
# final data update should always trigger a card reload to show
|
|
441
|
-
# the final card, hence a different token for the final update
|
|
442
|
-
return "final"
|
|
443
|
-
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ALWAYS:
|
|
444
|
-
return "render-seq-%s" % data["render_seq"]
|
|
445
|
-
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_NEVER:
|
|
446
|
-
return "never"
|
|
447
|
-
elif mf_card.RELOAD_POLICY == mf_card.RELOAD_POLICY_ONCHANGE:
|
|
448
|
-
return mf_card.reload_content_token(task, data)
|
|
449
|
-
|
|
450
452
|
def _add_token_html(html):
|
|
451
453
|
if html is None:
|
|
452
454
|
return None
|
|
453
|
-
return html.replace(
|
|
455
|
+
return html.replace(
|
|
456
|
+
mf_card.RELOAD_POLICY_TOKEN,
|
|
457
|
+
_extract_reload_token(data=data, task=task, mf_card=mf_card),
|
|
458
|
+
)
|
|
454
459
|
|
|
455
460
|
def _add_token_json(json_msg):
|
|
456
461
|
if json_msg is None:
|
|
457
462
|
return None
|
|
458
463
|
return {
|
|
459
|
-
"reload_token":
|
|
464
|
+
"reload_token": _extract_reload_token(
|
|
465
|
+
data=data, task=task, mf_card=mf_card
|
|
466
|
+
),
|
|
460
467
|
"data": json_msg,
|
|
461
468
|
"created_on": time.time(),
|
|
462
469
|
}
|
|
@@ -545,7 +552,7 @@ def update_card(mf_card, mode, task, data, timeout_value=None):
|
|
|
545
552
|
"--options",
|
|
546
553
|
default=None,
|
|
547
554
|
show_default=True,
|
|
548
|
-
type=
|
|
555
|
+
type=JSONTypeClass(),
|
|
549
556
|
help="arguments of the card being created.",
|
|
550
557
|
)
|
|
551
558
|
@click.option(
|
|
@@ -606,6 +613,14 @@ def update_card(mf_card, mode, task, data, timeout_value=None):
|
|
|
606
613
|
hidden=True,
|
|
607
614
|
help="Delete data-file and component-file after reading. (internal)",
|
|
608
615
|
)
|
|
616
|
+
@click.option(
|
|
617
|
+
"--save-metadata",
|
|
618
|
+
default=None,
|
|
619
|
+
show_default=True,
|
|
620
|
+
type=JSONTypeClass(),
|
|
621
|
+
hidden=True,
|
|
622
|
+
help="JSON string containing metadata to be saved. (internal)",
|
|
623
|
+
)
|
|
609
624
|
@click.pass_context
|
|
610
625
|
def create(
|
|
611
626
|
ctx,
|
|
@@ -620,6 +635,7 @@ def create(
|
|
|
620
635
|
card_uuid=None,
|
|
621
636
|
delete_input_files=None,
|
|
622
637
|
id=None,
|
|
638
|
+
save_metadata=None,
|
|
623
639
|
):
|
|
624
640
|
card_id = id
|
|
625
641
|
rendered_info = None # Variable holding all the information which will be rendered
|
|
@@ -684,10 +700,15 @@ def create(
|
|
|
684
700
|
try:
|
|
685
701
|
if options is not None:
|
|
686
702
|
mf_card = filtered_card(
|
|
687
|
-
options=options,
|
|
703
|
+
options=options,
|
|
704
|
+
components=component_arr,
|
|
705
|
+
graph=graph_dict,
|
|
706
|
+
flow=ctx.obj.flow,
|
|
688
707
|
)
|
|
689
708
|
else:
|
|
690
|
-
mf_card = filtered_card(
|
|
709
|
+
mf_card = filtered_card(
|
|
710
|
+
components=component_arr, graph=graph_dict, flow=ctx.obj.flow
|
|
711
|
+
)
|
|
691
712
|
except TypeError as e:
|
|
692
713
|
if render_error_card:
|
|
693
714
|
mf_card = None
|
|
@@ -740,8 +761,16 @@ def create(
|
|
|
740
761
|
# 3. `refresh` is implemented but it raises an exception. (We do nothing. Don't store anything.)
|
|
741
762
|
# 4. `refresh` is implemented but it times out. (We do nothing. Don't store anything.)
|
|
742
763
|
|
|
764
|
+
def _render_error_card(stack_trace):
|
|
765
|
+
_card = error_card()
|
|
766
|
+
token = _extract_reload_token(data, task, _card)
|
|
767
|
+
return _card.render(
|
|
768
|
+
task,
|
|
769
|
+
stack_trace=stack_trace,
|
|
770
|
+
).replace(_card.RELOAD_POLICY_TOKEN, token)
|
|
771
|
+
|
|
743
772
|
if error_stack_trace is not None and mode != "refresh":
|
|
744
|
-
rendered_content =
|
|
773
|
+
rendered_content = _render_error_card(error_stack_trace)
|
|
745
774
|
elif (
|
|
746
775
|
rendered_info.is_implemented
|
|
747
776
|
and rendered_info.timed_out
|
|
@@ -753,18 +782,15 @@ def create(
|
|
|
753
782
|
"To increase the timeout duration for card rendering, please set the `timeout` parameter in the @card decorator. "
|
|
754
783
|
"\nStack Trace : \n%s"
|
|
755
784
|
) % (timeout, rendered_info.timeout_stack_trace)
|
|
756
|
-
rendered_content =
|
|
757
|
-
task,
|
|
758
|
-
stack_trace=timeout_stack_trace,
|
|
759
|
-
)
|
|
785
|
+
rendered_content = _render_error_card(timeout_stack_trace)
|
|
760
786
|
elif (
|
|
761
787
|
rendered_info.is_implemented
|
|
762
788
|
and rendered_info.data is None
|
|
763
789
|
and render_error_card
|
|
764
790
|
and mode != "refresh"
|
|
765
791
|
):
|
|
766
|
-
rendered_content =
|
|
767
|
-
|
|
792
|
+
rendered_content = _render_error_card(
|
|
793
|
+
"No information rendered from card of type %s" % type
|
|
768
794
|
)
|
|
769
795
|
elif (
|
|
770
796
|
not rendered_info.is_implemented
|
|
@@ -775,7 +801,7 @@ def create(
|
|
|
775
801
|
"Card of type %s is a runtime time card with no `render_runtime` implemented. "
|
|
776
802
|
"Please implement `render_runtime` method to allow rendering this card at runtime."
|
|
777
803
|
) % type
|
|
778
|
-
rendered_content =
|
|
804
|
+
rendered_content = _render_error_card(message)
|
|
779
805
|
|
|
780
806
|
# todo : should we save native type for error card or error type ?
|
|
781
807
|
if type is not None and re.match(CARD_ID_PATTERN, type) is not None:
|
|
@@ -807,6 +833,16 @@ def create(
|
|
|
807
833
|
% (card_info.type, card_info.hash[:NUM_SHORT_HASH_CHARS]),
|
|
808
834
|
fg="green",
|
|
809
835
|
)
|
|
836
|
+
if save_metadata:
|
|
837
|
+
_save_metadata(
|
|
838
|
+
ctx.obj.metadata,
|
|
839
|
+
task.parent.parent.id,
|
|
840
|
+
task.parent.id,
|
|
841
|
+
task.id,
|
|
842
|
+
task.current_attempt,
|
|
843
|
+
card_uuid,
|
|
844
|
+
save_metadata,
|
|
845
|
+
)
|
|
810
846
|
|
|
811
847
|
|
|
812
848
|
@card.command()
|
|
@@ -5,6 +5,8 @@ import json
|
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
7
|
from metaflow import current
|
|
8
|
+
from typing import Callable, Tuple, Dict
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
ASYNC_TIMEOUT = 30
|
|
10
12
|
|
|
@@ -44,8 +46,18 @@ class CardProcessManager:
|
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class CardCreator:
|
|
47
|
-
def __init__(
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
top_level_options,
|
|
52
|
+
should_save_metadata_lambda: Callable[[str], Tuple[bool, Dict]],
|
|
53
|
+
):
|
|
54
|
+
# should_save_metadata_lambda is a lambda that provides a flag to indicate if
|
|
55
|
+
# card metadata should be written to the metadata store.
|
|
56
|
+
# It gets called only once when the card is created inside the subprocess.
|
|
57
|
+
# The intent is that this is a stateful lambda that will ensure that we only end
|
|
58
|
+
# up writing to the metadata store once.
|
|
48
59
|
self._top_level_options = top_level_options
|
|
60
|
+
self._should_save_metadata = should_save_metadata_lambda
|
|
49
61
|
|
|
50
62
|
def create(
|
|
51
63
|
self,
|
|
@@ -62,6 +74,8 @@ class CardCreator:
|
|
|
62
74
|
# Setting `final` will affect the Reload token set during the card refresh
|
|
63
75
|
# data creation along with synchronous execution of subprocess.
|
|
64
76
|
# Setting `sync` will only cause synchronous execution of subprocess.
|
|
77
|
+
save_metadata = False
|
|
78
|
+
metadata_dict = {}
|
|
65
79
|
if mode != "render" and not runtime_card:
|
|
66
80
|
# silently ignore runtime updates for cards that don't support them
|
|
67
81
|
return
|
|
@@ -71,6 +85,8 @@ class CardCreator:
|
|
|
71
85
|
component_strings = []
|
|
72
86
|
else:
|
|
73
87
|
component_strings = current.card._serialize_components(card_uuid)
|
|
88
|
+
# Since the mode is a render, we can check if we need to write to the metadata store.
|
|
89
|
+
save_metadata, metadata_dict = self._should_save_metadata(card_uuid)
|
|
74
90
|
data = current.card._get_latest_data(card_uuid, final=final, mode=mode)
|
|
75
91
|
runspec = "/".join([current.run_id, current.step_name, current.task_id])
|
|
76
92
|
self._run_cards_subprocess(
|
|
@@ -85,6 +101,8 @@ class CardCreator:
|
|
|
85
101
|
data,
|
|
86
102
|
final=final,
|
|
87
103
|
sync=sync,
|
|
104
|
+
save_metadata=save_metadata,
|
|
105
|
+
metadata_dict=metadata_dict,
|
|
88
106
|
)
|
|
89
107
|
|
|
90
108
|
def _run_cards_subprocess(
|
|
@@ -100,6 +118,8 @@ class CardCreator:
|
|
|
100
118
|
data=None,
|
|
101
119
|
final=False,
|
|
102
120
|
sync=False,
|
|
121
|
+
save_metadata=False,
|
|
122
|
+
metadata_dict=None,
|
|
103
123
|
):
|
|
104
124
|
components_file = data_file = None
|
|
105
125
|
wait = final or sync
|
|
@@ -122,6 +142,7 @@ class CardCreator:
|
|
|
122
142
|
executable,
|
|
123
143
|
sys.argv[0],
|
|
124
144
|
]
|
|
145
|
+
|
|
125
146
|
cmd += self._top_level_options + [
|
|
126
147
|
"card",
|
|
127
148
|
"create",
|
|
@@ -155,6 +176,9 @@ class CardCreator:
|
|
|
155
176
|
if data_file is not None:
|
|
156
177
|
cmd += ["--data-file", data_file.name]
|
|
157
178
|
|
|
179
|
+
if save_metadata:
|
|
180
|
+
cmd += ["--save-metadata", json.dumps(metadata_dict)]
|
|
181
|
+
|
|
158
182
|
response, fail = self._run_command(
|
|
159
183
|
cmd,
|
|
160
184
|
card_uuid,
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
from collections import namedtuple
|
|
6
2
|
from io import BytesIO
|
|
7
3
|
import os
|
|
@@ -13,10 +9,10 @@ from metaflow.metaflow_config import (
|
|
|
13
9
|
CARD_S3ROOT,
|
|
14
10
|
CARD_LOCALROOT,
|
|
15
11
|
DATASTORE_LOCAL_DIR,
|
|
12
|
+
DATASTORE_SPIN_LOCAL_DIR,
|
|
16
13
|
CARD_SUFFIX,
|
|
17
14
|
CARD_AZUREROOT,
|
|
18
15
|
CARD_GSROOT,
|
|
19
|
-
SKIP_CARD_DUALWRITE,
|
|
20
16
|
)
|
|
21
17
|
import metaflow.metaflow_config as metaflow_config
|
|
22
18
|
|
|
@@ -63,25 +59,28 @@ class CardDatastore(object):
|
|
|
63
59
|
return CARD_AZUREROOT
|
|
64
60
|
elif storage_type == "gs":
|
|
65
61
|
return CARD_GSROOT
|
|
66
|
-
elif storage_type == "local":
|
|
62
|
+
elif storage_type == "local" or storage_type == "spin":
|
|
67
63
|
# Borrowing some of the logic from LocalStorage.get_storage_root
|
|
68
64
|
result = CARD_LOCALROOT
|
|
65
|
+
local_dir = (
|
|
66
|
+
DATASTORE_SPIN_LOCAL_DIR
|
|
67
|
+
if storage_type == "spin"
|
|
68
|
+
else DATASTORE_LOCAL_DIR
|
|
69
|
+
)
|
|
69
70
|
if result is None:
|
|
70
71
|
current_path = os.getcwd()
|
|
71
|
-
check_dir = os.path.join(current_path,
|
|
72
|
+
check_dir = os.path.join(current_path, local_dir)
|
|
72
73
|
check_dir = os.path.realpath(check_dir)
|
|
73
74
|
orig_path = check_dir
|
|
74
75
|
while not os.path.isdir(check_dir):
|
|
75
76
|
new_path = os.path.dirname(current_path)
|
|
76
77
|
if new_path == current_path:
|
|
77
|
-
|
|
78
|
+
# No longer making upward progress so we
|
|
79
|
+
# return the top level path
|
|
80
|
+
return os.path.join(orig_path, CARD_SUFFIX)
|
|
78
81
|
current_path = new_path
|
|
79
|
-
check_dir = os.path.join(
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
result = orig_path
|
|
83
|
-
|
|
84
|
-
return result
|
|
82
|
+
check_dir = os.path.join(current_path, local_dir)
|
|
83
|
+
return os.path.join(check_dir, CARD_SUFFIX)
|
|
85
84
|
else:
|
|
86
85
|
# Let's make it obvious we need to update this block for each new datastore backend...
|
|
87
86
|
raise NotImplementedError(
|
|
@@ -231,23 +230,6 @@ class CardDatastore(object):
|
|
|
231
230
|
|
|
232
231
|
def save_card(self, uuid, card_type, card_html, card_id=None, overwrite=True):
|
|
233
232
|
card_file_name = card_type
|
|
234
|
-
# TEMPORARY_WORKAROUND: FIXME (LATER) : Fix the duplication of below block in a few months.
|
|
235
|
-
# Check file blame to understand the age of this temporary workaround.
|
|
236
|
-
|
|
237
|
-
# This function will end up saving cards at two locations.
|
|
238
|
-
# Thereby doubling the number of cards. (Which is a temporary fix)
|
|
239
|
-
# Why do this ? :
|
|
240
|
-
# When cards were introduced there was an assumption made about task-ids being unique.
|
|
241
|
-
# This assumption was incorrect.
|
|
242
|
-
# Only the pathspec needs to be unique but there is no such guarantees about task-ids.
|
|
243
|
-
# When task-ids are non-unique, card read would result in finding incorrect cards.
|
|
244
|
-
# This happens because cards were stored based on task-ids.
|
|
245
|
-
# If we immediately switch from storing based on task-ids to a step-name abstraction folder,
|
|
246
|
-
# then card reading will crash for many users.
|
|
247
|
-
# It would especially happen for users who are accessing cards created by a newer
|
|
248
|
-
# MF client from an older version of MF client.
|
|
249
|
-
# It will also easily end up breaking the metaflow-ui (which maybe using a client from an older version).
|
|
250
|
-
# Hence, we are writing cards to both paths so that we can introduce breaking changes later in the future.
|
|
251
233
|
card_path_with_steps = self.get_card_location(
|
|
252
234
|
self._get_card_write_path(),
|
|
253
235
|
card_file_name,
|
|
@@ -255,24 +237,10 @@ class CardDatastore(object):
|
|
|
255
237
|
card_id=card_id,
|
|
256
238
|
suffix=CardNameSuffix.CARD,
|
|
257
239
|
)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
263
|
-
else:
|
|
264
|
-
card_path_without_steps = self.get_card_location(
|
|
265
|
-
self._get_card_read_path(with_steps=False),
|
|
266
|
-
card_file_name,
|
|
267
|
-
uuid,
|
|
268
|
-
card_id=card_id,
|
|
269
|
-
suffix=CardNameSuffix.CARD,
|
|
270
|
-
)
|
|
271
|
-
for cp in [card_path_with_steps, card_path_without_steps]:
|
|
272
|
-
self._backend.save_bytes(
|
|
273
|
-
[(cp, BytesIO(bytes(card_html, "utf-8")))], overwrite=overwrite
|
|
274
|
-
)
|
|
275
|
-
|
|
240
|
+
self._backend.save_bytes(
|
|
241
|
+
[(card_path_with_steps, BytesIO(bytes(card_html, "utf-8")))],
|
|
242
|
+
overwrite=overwrite,
|
|
243
|
+
)
|
|
276
244
|
return self.info_from_path(card_path_with_steps, suffix=CardNameSuffix.CARD)
|
|
277
245
|
|
|
278
246
|
def _list_card_paths(self, card_type=None, card_hash=None, card_id=None):
|
|
@@ -283,6 +251,10 @@ class CardDatastore(object):
|
|
|
283
251
|
)
|
|
284
252
|
|
|
285
253
|
if len(card_paths_with_steps) == 0:
|
|
254
|
+
# The listing logic is reading the cards with steps and without steps
|
|
255
|
+
# because earlier versions of clients (ones that wrote cards before June 2022),
|
|
256
|
+
# would have written cards without steps. So as a fallback we will try to check for the
|
|
257
|
+
# cards without steps.
|
|
286
258
|
card_paths_without_steps = self._backend.list_content(
|
|
287
259
|
[self._get_card_read_path(with_steps=False)]
|
|
288
260
|
)
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import tempfile
|
|
5
|
+
from typing import Tuple, Dict
|
|
6
|
+
|
|
1
7
|
from metaflow.decorators import StepDecorator
|
|
8
|
+
from metaflow.metadata_provider import MetaDatum
|
|
2
9
|
from metaflow.metaflow_current import current
|
|
10
|
+
from metaflow.user_configs.config_options import ConfigInput
|
|
11
|
+
from metaflow.user_configs.config_parameters import dump_config_values
|
|
3
12
|
from metaflow.util import to_unicode
|
|
13
|
+
|
|
4
14
|
from .component_serializer import CardComponentCollector, get_card_class
|
|
5
15
|
from .card_creator import CardCreator
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# from metaflow import get_metadata
|
|
9
|
-
import re
|
|
10
|
-
|
|
11
16
|
from .exception import CARD_ID_PATTERN, TYPE_CHECK_REGEX
|
|
12
17
|
|
|
13
18
|
ASYNC_TIMEOUT = 30
|
|
@@ -19,6 +24,24 @@ def warning_message(message, logger=None, ts=False):
|
|
|
19
24
|
logger(msg, timestamp=ts, bad=True)
|
|
20
25
|
|
|
21
26
|
|
|
27
|
+
class MetadataStateManager(object):
|
|
28
|
+
def __init__(self, info_func):
|
|
29
|
+
self._info_func = info_func
|
|
30
|
+
self._metadata_registered = {}
|
|
31
|
+
|
|
32
|
+
def register_metadata(self, card_uuid) -> Tuple[bool, Dict]:
|
|
33
|
+
info = self._info_func()
|
|
34
|
+
# Check that metadata was not written yet. We only want to write once.
|
|
35
|
+
if (
|
|
36
|
+
info is None
|
|
37
|
+
or info.get(card_uuid) is None
|
|
38
|
+
or self._metadata_registered.get(card_uuid)
|
|
39
|
+
):
|
|
40
|
+
return False, {}
|
|
41
|
+
self._metadata_registered[card_uuid] = True
|
|
42
|
+
return True, info.get(card_uuid)
|
|
43
|
+
|
|
44
|
+
|
|
22
45
|
class CardDecorator(StepDecorator):
|
|
23
46
|
"""
|
|
24
47
|
Creates a human-readable report, a Metaflow Card, after this step completes.
|
|
@@ -52,11 +75,14 @@ class CardDecorator(StepDecorator):
|
|
|
52
75
|
The or one of the cards attached to this step.
|
|
53
76
|
"""
|
|
54
77
|
|
|
78
|
+
_GLOBAL_CARD_INFO = {}
|
|
79
|
+
|
|
55
80
|
name = "card"
|
|
56
81
|
defaults = {
|
|
57
82
|
"type": "default",
|
|
58
83
|
"options": {},
|
|
59
84
|
"scope": "task",
|
|
85
|
+
"rank": None, # Can be one of "high", "medium", "low". Can help derive ordering on the UI.
|
|
60
86
|
"timeout": 45,
|
|
61
87
|
"id": None,
|
|
62
88
|
"save_errors": True,
|
|
@@ -73,6 +99,12 @@ class CardDecorator(StepDecorator):
|
|
|
73
99
|
|
|
74
100
|
card_creator = None
|
|
75
101
|
|
|
102
|
+
_config_values = None
|
|
103
|
+
|
|
104
|
+
_config_file_name = None
|
|
105
|
+
|
|
106
|
+
task_finished_decos = 0
|
|
107
|
+
|
|
76
108
|
def __init__(self, *args, **kwargs):
|
|
77
109
|
super(CardDecorator, self).__init__(*args, **kwargs)
|
|
78
110
|
self._task_datastore = None
|
|
@@ -82,6 +114,7 @@ class CardDecorator(StepDecorator):
|
|
|
82
114
|
self._is_editable = False
|
|
83
115
|
self._card_uuid = None
|
|
84
116
|
self._user_set_card_id = None
|
|
117
|
+
self._metadata_registered = False
|
|
85
118
|
|
|
86
119
|
@classmethod
|
|
87
120
|
def _set_card_creator(cls, card_creator):
|
|
@@ -103,20 +136,61 @@ class CardDecorator(StepDecorator):
|
|
|
103
136
|
def _increment_step_counter(cls):
|
|
104
137
|
cls.step_counter += 1
|
|
105
138
|
|
|
139
|
+
@classmethod
|
|
140
|
+
def _increment_completed_counter(cls):
|
|
141
|
+
cls.task_finished_decos += 1
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def _set_config_values(cls, config_values):
|
|
145
|
+
cls._config_values = config_values
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def _set_config_file_name(cls, flow):
|
|
149
|
+
# Only create a config file from the very first card decorator.
|
|
150
|
+
if cls._config_values and not cls._config_file_name:
|
|
151
|
+
with tempfile.NamedTemporaryFile(
|
|
152
|
+
mode="w", encoding="utf-8", delete=False
|
|
153
|
+
) as config_file:
|
|
154
|
+
config_value = dump_config_values(flow)
|
|
155
|
+
json.dump(config_value, config_file)
|
|
156
|
+
cls._config_file_name = config_file.name
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def _register_card_info(cls, **kwargs):
|
|
160
|
+
if not kwargs.get("card_uuid"):
|
|
161
|
+
raise ValueError("card_uuid is required")
|
|
162
|
+
cls._GLOBAL_CARD_INFO[kwargs["card_uuid"]] = kwargs
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def all_cards_info(cls):
|
|
166
|
+
return cls._GLOBAL_CARD_INFO.copy()
|
|
167
|
+
|
|
106
168
|
def step_init(
|
|
107
169
|
self, flow, graph, step_name, decorators, environment, flow_datastore, logger
|
|
108
170
|
):
|
|
109
171
|
self._flow_datastore = flow_datastore
|
|
110
172
|
self._environment = environment
|
|
111
173
|
self._logger = logger
|
|
174
|
+
|
|
112
175
|
self.card_options = None
|
|
113
176
|
|
|
177
|
+
# We check for configuration options. We do this here before they are
|
|
178
|
+
# converted to properties.
|
|
179
|
+
self._set_config_values(
|
|
180
|
+
[
|
|
181
|
+
(config.name, ConfigInput.make_key_name(config.name))
|
|
182
|
+
for _, config in flow._get_parameters()
|
|
183
|
+
if config.IS_CONFIG_PARAMETER
|
|
184
|
+
]
|
|
185
|
+
)
|
|
186
|
+
|
|
114
187
|
self.card_options = self.attributes["options"]
|
|
115
188
|
|
|
116
189
|
evt_name = "step-init"
|
|
117
190
|
# `'%s-%s'%(evt_name,step_name)` ensures that we capture this once per @card per @step.
|
|
118
191
|
# Since there can be many steps checking if event is registered for `evt_name` will only make it check it once for all steps.
|
|
119
192
|
# Hence, we have `_is_event_registered('%s-%s'%(evt_name,step_name))`
|
|
193
|
+
self._is_runtime_card = False
|
|
120
194
|
evt = "%s-%s" % (evt_name, step_name)
|
|
121
195
|
if not self._is_event_registered(evt):
|
|
122
196
|
# We set the total count of decorators so that we can use it for
|
|
@@ -145,6 +219,19 @@ class CardDecorator(StepDecorator):
|
|
|
145
219
|
self._task_datastore = task_datastore
|
|
146
220
|
self._metadata = metadata
|
|
147
221
|
|
|
222
|
+
# If we have configs, we need to dump them to a file so we can re-use them
|
|
223
|
+
# when calling the card creation subprocess.
|
|
224
|
+
# Since a step can contain multiple card decorators, and all the card creation processes
|
|
225
|
+
# will reference the same config file (because of how the CardCreator is created (only single class instance)),
|
|
226
|
+
# we need to ensure that a single config file is being referenced for all card create commands.
|
|
227
|
+
# This config file will be removed when the last card decorator has finished creating its card.
|
|
228
|
+
self._set_config_file_name(flow)
|
|
229
|
+
# The MetadataStateManager is used to track the state of the metadata registration.
|
|
230
|
+
# It is there to ensure that we only register metadata for the card once. This is so that we
|
|
231
|
+
# avoid any un-necessary metadata writes because the create command can be called multiple times during the
|
|
232
|
+
# card creation process.
|
|
233
|
+
self._metadata_state_manager = MetadataStateManager(self.all_cards_info)
|
|
234
|
+
|
|
148
235
|
card_type = self.attributes["type"]
|
|
149
236
|
card_class = get_card_class(card_type)
|
|
150
237
|
|
|
@@ -178,7 +265,12 @@ class CardDecorator(StepDecorator):
|
|
|
178
265
|
# we need to ensure that `current.card` has `CardComponentCollector` instantiated only once.
|
|
179
266
|
if not self._is_event_registered("pre-step"):
|
|
180
267
|
self._register_event("pre-step")
|
|
181
|
-
self._set_card_creator(
|
|
268
|
+
self._set_card_creator(
|
|
269
|
+
CardCreator(
|
|
270
|
+
self._create_top_level_args(flow),
|
|
271
|
+
self._metadata_state_manager.register_metadata,
|
|
272
|
+
)
|
|
273
|
+
)
|
|
182
274
|
|
|
183
275
|
current._update_env(
|
|
184
276
|
{"card": CardComponentCollector(self._logger, self.card_creator)}
|
|
@@ -201,6 +293,18 @@ class CardDecorator(StepDecorator):
|
|
|
201
293
|
)
|
|
202
294
|
self._card_uuid = card_metadata["uuid"]
|
|
203
295
|
|
|
296
|
+
self._register_card_info(
|
|
297
|
+
card_uuid=self._card_uuid,
|
|
298
|
+
rank=self.attributes["rank"],
|
|
299
|
+
type=self.attributes["type"],
|
|
300
|
+
options=self.card_options,
|
|
301
|
+
is_editable=self._is_editable,
|
|
302
|
+
is_runtime_card=self._is_runtime_card,
|
|
303
|
+
refresh_interval=self.attributes["refresh_interval"],
|
|
304
|
+
customize=customize,
|
|
305
|
+
id=self._user_set_card_id,
|
|
306
|
+
)
|
|
307
|
+
|
|
204
308
|
# This means that we are calling `task_pre_step` on the last card decorator.
|
|
205
309
|
# We can now `finalize` method in the CardComponentCollector object.
|
|
206
310
|
# This will set up the `current.card` object for usage inside `@step` code.
|
|
@@ -222,6 +326,8 @@ class CardDecorator(StepDecorator):
|
|
|
222
326
|
self.card_creator.create(mode="render", final=True, **create_options)
|
|
223
327
|
self.card_creator.create(mode="refresh", final=True, **create_options)
|
|
224
328
|
|
|
329
|
+
self._cleanup(step_name)
|
|
330
|
+
|
|
225
331
|
@staticmethod
|
|
226
332
|
def _options(mapping):
|
|
227
333
|
for k, v in mapping.items():
|
|
@@ -231,9 +337,13 @@ class CardDecorator(StepDecorator):
|
|
|
231
337
|
for value in v:
|
|
232
338
|
yield "--%s" % k
|
|
233
339
|
if not isinstance(value, bool):
|
|
234
|
-
|
|
340
|
+
if isinstance(value, tuple):
|
|
341
|
+
for val in value:
|
|
342
|
+
yield to_unicode(val)
|
|
343
|
+
else:
|
|
344
|
+
yield to_unicode(value)
|
|
235
345
|
|
|
236
|
-
def _create_top_level_args(self):
|
|
346
|
+
def _create_top_level_args(self, flow):
|
|
237
347
|
top_level_options = {
|
|
238
348
|
"quiet": True,
|
|
239
349
|
"metadata": self._metadata.TYPE,
|
|
@@ -246,4 +356,18 @@ class CardDecorator(StepDecorator):
|
|
|
246
356
|
# We don't provide --with as all execution is taking place in
|
|
247
357
|
# the context of the main process
|
|
248
358
|
}
|
|
359
|
+
if self._config_values:
|
|
360
|
+
top_level_options["config-value"] = self._config_values
|
|
361
|
+
top_level_options["local-config-file"] = self._config_file_name
|
|
362
|
+
|
|
249
363
|
return list(self._options(top_level_options))
|
|
364
|
+
|
|
365
|
+
def _cleanup(self, step_name):
|
|
366
|
+
self._increment_completed_counter()
|
|
367
|
+
if self.task_finished_decos == self.total_decos_on_step[step_name]:
|
|
368
|
+
# Unlink the config file if it exists
|
|
369
|
+
if self._config_file_name:
|
|
370
|
+
try:
|
|
371
|
+
os.unlink(self._config_file_name)
|
|
372
|
+
except Exception as e:
|
|
373
|
+
pass
|