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,9 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import json
|
|
2
3
|
|
|
3
4
|
from metaflow import current
|
|
4
5
|
from metaflow.decorators import FlowDecorator
|
|
5
6
|
from metaflow.exception import MetaflowException
|
|
6
7
|
from metaflow.util import is_stringish
|
|
8
|
+
from metaflow.parameters import DeployTimeField, deploy_time_eval
|
|
7
9
|
|
|
8
10
|
# TODO: Support dynamic parameter mapping through a context object that exposes
|
|
9
11
|
# flow name and user name similar to parameter context
|
|
@@ -68,6 +70,128 @@ class TriggerDecorator(FlowDecorator):
|
|
|
68
70
|
"options": {},
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
def process_event(self, event):
|
|
74
|
+
"""
|
|
75
|
+
Process a single event and return a dictionary if static trigger and a function
|
|
76
|
+
if deploy-time trigger.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
event : Union[str, Dict[str, Any], Callable]
|
|
81
|
+
Event to process
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
Union[Dict[str, Union[str, Callable]], Callable]
|
|
86
|
+
Processed event
|
|
87
|
+
|
|
88
|
+
Raises
|
|
89
|
+
------
|
|
90
|
+
MetaflowException
|
|
91
|
+
If the event is not in the correct format
|
|
92
|
+
"""
|
|
93
|
+
if is_stringish(event):
|
|
94
|
+
return {"name": str(event)}
|
|
95
|
+
elif isinstance(event, dict):
|
|
96
|
+
if "name" not in event:
|
|
97
|
+
raise MetaflowException(
|
|
98
|
+
"The *event* attribute for *@trigger* is missing the *name* key."
|
|
99
|
+
)
|
|
100
|
+
if callable(event["name"]) and not isinstance(
|
|
101
|
+
event["name"], DeployTimeField
|
|
102
|
+
):
|
|
103
|
+
event["name"] = DeployTimeField(
|
|
104
|
+
"event_name",
|
|
105
|
+
str,
|
|
106
|
+
None,
|
|
107
|
+
event["name"],
|
|
108
|
+
False,
|
|
109
|
+
print_representation=str(event["name"]),
|
|
110
|
+
)
|
|
111
|
+
event["parameters"] = self.process_parameters(
|
|
112
|
+
event.get("parameters", {}), event["name"]
|
|
113
|
+
)
|
|
114
|
+
return event
|
|
115
|
+
elif callable(event) and not isinstance(event, DeployTimeField):
|
|
116
|
+
return DeployTimeField(
|
|
117
|
+
"event",
|
|
118
|
+
[str, dict],
|
|
119
|
+
None,
|
|
120
|
+
event,
|
|
121
|
+
False,
|
|
122
|
+
print_representation=str(event),
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
raise MetaflowException(
|
|
126
|
+
"Incorrect format for *event* attribute in *@trigger* decorator. "
|
|
127
|
+
"Supported formats are string and dictionary - \n"
|
|
128
|
+
"@trigger(event='foo') or @trigger(event={'name': 'foo', "
|
|
129
|
+
"'parameters': {'alpha': 'beta'}})"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def process_parameters(self, parameters, event_name):
|
|
133
|
+
"""
|
|
134
|
+
Process the parameters for an event and return a dictionary of parameter mappings if
|
|
135
|
+
parameters was statically defined or a function if deploy-time trigger.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
Parameters : Union[Dict[str, str], List[Union[str, Tuple[str, str]]], Callable]
|
|
140
|
+
Parameters to process
|
|
141
|
+
|
|
142
|
+
event_name : Union[str, callable]
|
|
143
|
+
Name of the event
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
Union[Dict[str, str], Callable]
|
|
148
|
+
Processed parameters
|
|
149
|
+
|
|
150
|
+
Raises
|
|
151
|
+
------
|
|
152
|
+
MetaflowException
|
|
153
|
+
If the parameters are not in the correct format
|
|
154
|
+
"""
|
|
155
|
+
new_param_values = {}
|
|
156
|
+
if isinstance(parameters, list):
|
|
157
|
+
for mapping in parameters:
|
|
158
|
+
if is_stringish(mapping):
|
|
159
|
+
# param_name
|
|
160
|
+
new_param_values[mapping] = mapping
|
|
161
|
+
elif isinstance(mapping, tuple) and len(mapping) == 2:
|
|
162
|
+
# (param_name, field_name)
|
|
163
|
+
param_name, field_name = mapping
|
|
164
|
+
if not is_stringish(param_name) or not is_stringish(field_name):
|
|
165
|
+
raise MetaflowException(
|
|
166
|
+
f"The *parameters* attribute for event {event_name} is invalid. "
|
|
167
|
+
"It should be a list/tuple of strings and lists/tuples of size 2."
|
|
168
|
+
)
|
|
169
|
+
new_param_values[param_name] = field_name
|
|
170
|
+
else:
|
|
171
|
+
raise MetaflowException(
|
|
172
|
+
"The *parameters* attribute for event is invalid. "
|
|
173
|
+
"It should be a list/tuple of strings and lists/tuples of size 2"
|
|
174
|
+
)
|
|
175
|
+
elif isinstance(parameters, dict):
|
|
176
|
+
for key, value in parameters.items():
|
|
177
|
+
if not is_stringish(key) or not is_stringish(value):
|
|
178
|
+
raise MetaflowException(
|
|
179
|
+
f"The *parameters* attribute for event {event_name} is invalid. "
|
|
180
|
+
"It should be a dictionary of string keys and string values."
|
|
181
|
+
)
|
|
182
|
+
new_param_values[key] = value
|
|
183
|
+
elif callable(parameters) and not isinstance(parameters, DeployTimeField):
|
|
184
|
+
# func
|
|
185
|
+
return DeployTimeField(
|
|
186
|
+
"parameters",
|
|
187
|
+
[list, dict, tuple],
|
|
188
|
+
None,
|
|
189
|
+
parameters,
|
|
190
|
+
False,
|
|
191
|
+
print_representation=str(parameters),
|
|
192
|
+
)
|
|
193
|
+
return new_param_values
|
|
194
|
+
|
|
71
195
|
def flow_init(
|
|
72
196
|
self,
|
|
73
197
|
flow_name,
|
|
@@ -86,41 +210,9 @@ class TriggerDecorator(FlowDecorator):
|
|
|
86
210
|
"attributes in *@trigger* decorator."
|
|
87
211
|
)
|
|
88
212
|
elif self.attributes["event"]:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# 'parameters': {'alpha': 'member_weight'}}
|
|
93
|
-
if is_stringish(self.attributes["event"]):
|
|
94
|
-
self.triggers.append({"name": str(self.attributes["event"])})
|
|
95
|
-
elif isinstance(self.attributes["event"], dict):
|
|
96
|
-
if "name" not in self.attributes["event"]:
|
|
97
|
-
raise MetaflowException(
|
|
98
|
-
"The *event* attribute for *@trigger* is missing the "
|
|
99
|
-
"*name* key."
|
|
100
|
-
)
|
|
101
|
-
param_value = self.attributes["event"].get("parameters", {})
|
|
102
|
-
if isinstance(param_value, (list, tuple)):
|
|
103
|
-
new_param_value = {}
|
|
104
|
-
for mapping in param_value:
|
|
105
|
-
if is_stringish(mapping):
|
|
106
|
-
new_param_value[mapping] = mapping
|
|
107
|
-
elif isinstance(mapping, (list, tuple)) and len(mapping) == 2:
|
|
108
|
-
new_param_value[mapping[0]] = mapping[1]
|
|
109
|
-
else:
|
|
110
|
-
raise MetaflowException(
|
|
111
|
-
"The *parameters* attribute for event '%s' is invalid. "
|
|
112
|
-
"It should be a list/tuple of strings and lists/tuples "
|
|
113
|
-
"of size 2" % self.attributes["event"]["name"]
|
|
114
|
-
)
|
|
115
|
-
self.attributes["event"]["parameters"] = new_param_value
|
|
116
|
-
self.triggers.append(self.attributes["event"])
|
|
117
|
-
else:
|
|
118
|
-
raise MetaflowException(
|
|
119
|
-
"Incorrect format for *event* attribute in *@trigger* decorator. "
|
|
120
|
-
"Supported formats are string and dictionary - \n"
|
|
121
|
-
"@trigger(event='foo') or @trigger(event={'name': 'foo', "
|
|
122
|
-
"'parameters': {'alpha': 'beta'}})"
|
|
123
|
-
)
|
|
213
|
+
event = self.attributes["event"]
|
|
214
|
+
processed_event = self.process_event(event)
|
|
215
|
+
self.triggers.append(processed_event)
|
|
124
216
|
elif self.attributes["events"]:
|
|
125
217
|
# events attribute supports the following formats -
|
|
126
218
|
# 1. events=[{'name': 'table.prod_db.members',
|
|
@@ -128,43 +220,22 @@ class TriggerDecorator(FlowDecorator):
|
|
|
128
220
|
# {'name': 'table.prod_db.metadata',
|
|
129
221
|
# 'parameters': {'beta': 'grade'}}]
|
|
130
222
|
if isinstance(self.attributes["events"], list):
|
|
223
|
+
# process every event in events
|
|
131
224
|
for event in self.attributes["events"]:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
elif (
|
|
147
|
-
isinstance(mapping, (list, tuple))
|
|
148
|
-
and len(mapping) == 2
|
|
149
|
-
):
|
|
150
|
-
new_param_value[mapping[0]] = mapping[1]
|
|
151
|
-
else:
|
|
152
|
-
raise MetaflowException(
|
|
153
|
-
"The *parameters* attribute for event '%s' is "
|
|
154
|
-
"invalid. It should be a list/tuple of strings "
|
|
155
|
-
"and lists/tuples of size 2" % event["name"]
|
|
156
|
-
)
|
|
157
|
-
event["parameters"] = new_param_value
|
|
158
|
-
self.triggers.append(event)
|
|
159
|
-
else:
|
|
160
|
-
raise MetaflowException(
|
|
161
|
-
"One or more events in *events* attribute in *@trigger* "
|
|
162
|
-
"decorator have an incorrect format. Supported format "
|
|
163
|
-
"is dictionary - \n"
|
|
164
|
-
"@trigger(events=[{'name': 'foo', 'parameters': {'alpha': "
|
|
165
|
-
"'beta'}}, {'name': 'bar', 'parameters': "
|
|
166
|
-
"{'gamma': 'kappa'}}])"
|
|
167
|
-
)
|
|
225
|
+
processed_event = self.process_event(event)
|
|
226
|
+
self.triggers.append(processed_event)
|
|
227
|
+
elif callable(self.attributes["events"]) and not isinstance(
|
|
228
|
+
self.attributes["events"], DeployTimeField
|
|
229
|
+
):
|
|
230
|
+
trig = DeployTimeField(
|
|
231
|
+
"events",
|
|
232
|
+
list,
|
|
233
|
+
None,
|
|
234
|
+
self.attributes["events"],
|
|
235
|
+
False,
|
|
236
|
+
print_representation=str(self.attributes["events"]),
|
|
237
|
+
)
|
|
238
|
+
self.triggers.append(trig)
|
|
168
239
|
else:
|
|
169
240
|
raise MetaflowException(
|
|
170
241
|
"Incorrect format for *events* attribute in *@trigger* decorator. "
|
|
@@ -178,7 +249,12 @@ class TriggerDecorator(FlowDecorator):
|
|
|
178
249
|
raise MetaflowException("No event(s) specified in *@trigger* decorator.")
|
|
179
250
|
|
|
180
251
|
# same event shouldn't occur more than once
|
|
181
|
-
names = [
|
|
252
|
+
names = [
|
|
253
|
+
x["name"]
|
|
254
|
+
for x in self.triggers
|
|
255
|
+
if not isinstance(x, DeployTimeField)
|
|
256
|
+
and not isinstance(x["name"], DeployTimeField)
|
|
257
|
+
]
|
|
182
258
|
if len(names) != len(set(names)):
|
|
183
259
|
raise MetaflowException(
|
|
184
260
|
"Duplicate event names defined in *@trigger* decorator."
|
|
@@ -188,6 +264,43 @@ class TriggerDecorator(FlowDecorator):
|
|
|
188
264
|
|
|
189
265
|
# TODO: Handle scenario for local testing using --trigger.
|
|
190
266
|
|
|
267
|
+
def format_deploytime_value(self):
|
|
268
|
+
new_triggers = []
|
|
269
|
+
|
|
270
|
+
# First pass to evaluate DeployTimeFields
|
|
271
|
+
for trigger in self.triggers:
|
|
272
|
+
# Case where trigger is a function that returns a list of events
|
|
273
|
+
# Need to do this bc we need to iterate over list later
|
|
274
|
+
if isinstance(trigger, DeployTimeField):
|
|
275
|
+
evaluated_trigger = deploy_time_eval(trigger)
|
|
276
|
+
if isinstance(evaluated_trigger, list):
|
|
277
|
+
for event in evaluated_trigger:
|
|
278
|
+
new_triggers.append(self.process_event(event))
|
|
279
|
+
else:
|
|
280
|
+
new_triggers.append(self.process_event(evaluated_trigger))
|
|
281
|
+
else:
|
|
282
|
+
new_triggers.append(trigger)
|
|
283
|
+
|
|
284
|
+
# Second pass to evaluate names
|
|
285
|
+
for trigger in new_triggers:
|
|
286
|
+
name = trigger.get("name")
|
|
287
|
+
if isinstance(name, DeployTimeField):
|
|
288
|
+
trigger["name"] = deploy_time_eval(name)
|
|
289
|
+
if not is_stringish(trigger["name"]):
|
|
290
|
+
raise MetaflowException(
|
|
291
|
+
f"The *name* attribute for event {trigger} is not a valid string"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# third pass to evaluate parameters
|
|
295
|
+
for trigger in new_triggers:
|
|
296
|
+
parameters = trigger.get("parameters", {})
|
|
297
|
+
if isinstance(parameters, DeployTimeField):
|
|
298
|
+
parameters_eval = deploy_time_eval(parameters)
|
|
299
|
+
parameters = self.process_parameters(parameters_eval, trigger["name"])
|
|
300
|
+
trigger["parameters"] = parameters
|
|
301
|
+
|
|
302
|
+
self.triggers = new_triggers
|
|
303
|
+
|
|
191
304
|
|
|
192
305
|
class TriggerOnFinishDecorator(FlowDecorator):
|
|
193
306
|
"""
|
|
@@ -246,11 +359,7 @@ class TriggerOnFinishDecorator(FlowDecorator):
|
|
|
246
359
|
"""
|
|
247
360
|
|
|
248
361
|
name = "trigger_on_finish"
|
|
249
|
-
|
|
250
|
-
"flow": None, # flow_name or project_flow_name
|
|
251
|
-
"flows": [], # flow_names or project_flow_names
|
|
252
|
-
"options": {},
|
|
253
|
-
}
|
|
362
|
+
|
|
254
363
|
options = {
|
|
255
364
|
"trigger": dict(
|
|
256
365
|
multiple=True,
|
|
@@ -258,6 +367,14 @@ class TriggerOnFinishDecorator(FlowDecorator):
|
|
|
258
367
|
help="Specify run pathspec for testing @trigger_on_finish locally.",
|
|
259
368
|
),
|
|
260
369
|
}
|
|
370
|
+
defaults = {
|
|
371
|
+
"flow": None, # flow_name or project_flow_name
|
|
372
|
+
"flows": [], # flow_names or project_flow_names
|
|
373
|
+
"options": {},
|
|
374
|
+
# Re-enable if you want to support TL options directly in the decorator like
|
|
375
|
+
# for @project decorator
|
|
376
|
+
# **{k: v["default"] for k, v in options.items()},
|
|
377
|
+
}
|
|
261
378
|
|
|
262
379
|
def flow_init(
|
|
263
380
|
self,
|
|
@@ -278,97 +395,32 @@ class TriggerOnFinishDecorator(FlowDecorator):
|
|
|
278
395
|
)
|
|
279
396
|
elif self.attributes["flow"]:
|
|
280
397
|
# flow supports the format @trigger_on_finish(flow='FooFlow')
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
398
|
+
flow = self.attributes["flow"]
|
|
399
|
+
if callable(flow) and not isinstance(
|
|
400
|
+
self.attributes["flow"], DeployTimeField
|
|
401
|
+
):
|
|
402
|
+
trig = DeployTimeField(
|
|
403
|
+
"fq_name",
|
|
404
|
+
[str, dict],
|
|
405
|
+
None,
|
|
406
|
+
flow,
|
|
407
|
+
False,
|
|
408
|
+
print_representation=str(flow),
|
|
286
409
|
)
|
|
287
|
-
|
|
288
|
-
if "name" not in self.attributes["flow"]:
|
|
289
|
-
raise MetaflowException(
|
|
290
|
-
"The *flow* attribute for *@trigger_on_finish* is missing the "
|
|
291
|
-
"*name* key."
|
|
292
|
-
)
|
|
293
|
-
flow_name = self.attributes["flow"]["name"]
|
|
294
|
-
|
|
295
|
-
if not is_stringish(flow_name) or "." in flow_name:
|
|
296
|
-
raise MetaflowException(
|
|
297
|
-
"The *name* attribute of the *flow* is not a valid string"
|
|
298
|
-
)
|
|
299
|
-
result = {"fq_name": flow_name}
|
|
300
|
-
if "project" in self.attributes["flow"]:
|
|
301
|
-
if is_stringish(self.attributes["flow"]["project"]):
|
|
302
|
-
result["project"] = self.attributes["flow"]["project"]
|
|
303
|
-
else:
|
|
304
|
-
raise MetaflowException(
|
|
305
|
-
"The *project* attribute of the *flow* is not a string"
|
|
306
|
-
)
|
|
307
|
-
if "project_branch" in self.attributes["flow"]:
|
|
308
|
-
if is_stringish(self.attributes["flow"]["project_branch"]):
|
|
309
|
-
result["branch"] = self.attributes["flow"]["project_branch"]
|
|
310
|
-
else:
|
|
311
|
-
raise MetaflowException(
|
|
312
|
-
"The *project_branch* attribute of the *flow* is not a string"
|
|
313
|
-
)
|
|
314
|
-
self.triggers.append(result)
|
|
410
|
+
self.triggers.append(trig)
|
|
315
411
|
else:
|
|
316
|
-
|
|
317
|
-
"Incorrect type for *flow* attribute in *@trigger_on_finish* "
|
|
318
|
-
" decorator. Supported type is string or Dict[str, str] - \n"
|
|
319
|
-
"@trigger_on_finish(flow='FooFlow') or "
|
|
320
|
-
"@trigger_on_finish(flow={'name':'FooFlow', 'project_branch': 'branch'})"
|
|
321
|
-
)
|
|
412
|
+
self.triggers.extend(self._parse_static_triggers([flow]))
|
|
322
413
|
elif self.attributes["flows"]:
|
|
323
414
|
# flows attribute supports the following formats -
|
|
324
415
|
# 1. flows=['FooFlow', 'BarFlow']
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
elif isinstance(flow, dict):
|
|
334
|
-
if "name" not in flow:
|
|
335
|
-
raise MetaflowException(
|
|
336
|
-
"One or more flows in the *flows* attribute for "
|
|
337
|
-
"*@trigger_on_finish* is missing the "
|
|
338
|
-
"*name* key."
|
|
339
|
-
)
|
|
340
|
-
flow_name = flow["name"]
|
|
341
|
-
|
|
342
|
-
if not is_stringish(flow_name) or "." in flow_name:
|
|
343
|
-
raise MetaflowException(
|
|
344
|
-
"The *name* attribute '%s' is not a valid string"
|
|
345
|
-
% str(flow_name)
|
|
346
|
-
)
|
|
347
|
-
result = {"fq_name": flow_name}
|
|
348
|
-
if "project" in flow:
|
|
349
|
-
if is_stringish(flow["project"]):
|
|
350
|
-
result["project"] = flow["project"]
|
|
351
|
-
else:
|
|
352
|
-
raise MetaflowException(
|
|
353
|
-
"The *project* attribute of the *flow* '%s' is not "
|
|
354
|
-
"a string" % flow_name
|
|
355
|
-
)
|
|
356
|
-
if "project_branch" in flow:
|
|
357
|
-
if is_stringish(flow["project_branch"]):
|
|
358
|
-
result["branch"] = flow["project_branch"]
|
|
359
|
-
else:
|
|
360
|
-
raise MetaflowException(
|
|
361
|
-
"The *project_branch* attribute of the *flow* %s "
|
|
362
|
-
"is not a string" % flow_name
|
|
363
|
-
)
|
|
364
|
-
self.triggers.append(result)
|
|
365
|
-
else:
|
|
366
|
-
raise MetaflowException(
|
|
367
|
-
"One or more flows in *flows* attribute in "
|
|
368
|
-
"*@trigger_on_finish* decorator have an incorrect type. "
|
|
369
|
-
"Supported type is string or Dict[str, str]- \n"
|
|
370
|
-
"@trigger_on_finish(flows=['FooFlow', 'BarFlow']"
|
|
371
|
-
)
|
|
416
|
+
flows = self.attributes["flows"]
|
|
417
|
+
if callable(flows) and not isinstance(flows, DeployTimeField):
|
|
418
|
+
trig = DeployTimeField(
|
|
419
|
+
"flows", list, None, flows, False, print_representation=str(flows)
|
|
420
|
+
)
|
|
421
|
+
self.triggers.append(trig)
|
|
422
|
+
elif isinstance(flows, list):
|
|
423
|
+
self.triggers.extend(self._parse_static_triggers(flows))
|
|
372
424
|
else:
|
|
373
425
|
raise MetaflowException(
|
|
374
426
|
"Incorrect type for *flows* attribute in *@trigger_on_finish* "
|
|
@@ -383,37 +435,50 @@ class TriggerOnFinishDecorator(FlowDecorator):
|
|
|
383
435
|
|
|
384
436
|
# Make triggers @project aware
|
|
385
437
|
for trigger in self.triggers:
|
|
386
|
-
if trigger
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
elif trigger["fq_name"].count(".") >= 2:
|
|
390
|
-
# fully qualified name is of the format - project.branch.flow_name
|
|
391
|
-
trigger["project"], tail = trigger["fq_name"].split(".", maxsplit=1)
|
|
392
|
-
trigger["branch"], trigger["flow"] = tail.rsplit(".", maxsplit=1)
|
|
393
|
-
else:
|
|
394
|
-
raise MetaflowException(
|
|
395
|
-
"Incorrect format for *flow* in *@trigger_on_finish* "
|
|
396
|
-
"decorator. Specify either just the *flow_name* or a fully "
|
|
397
|
-
"qualified name like *project_name.branch_name.flow_name*."
|
|
398
|
-
)
|
|
399
|
-
# TODO: Also sanity check project and branch names
|
|
400
|
-
if not re.match(r"^[A-Za-z0-9_]+$", trigger["flow"]):
|
|
401
|
-
raise MetaflowException(
|
|
402
|
-
"Invalid flow name *%s* in *@trigger_on_finish* "
|
|
403
|
-
"decorator. Only alphanumeric characters and "
|
|
404
|
-
"underscores(_) are allowed." % trigger["flow"]
|
|
405
|
-
)
|
|
438
|
+
if isinstance(trigger, DeployTimeField):
|
|
439
|
+
continue
|
|
440
|
+
self._parse_fq_name(trigger)
|
|
406
441
|
|
|
407
442
|
self.options = self.attributes["options"]
|
|
408
443
|
|
|
409
444
|
# Handle scenario for local testing using --trigger.
|
|
445
|
+
|
|
446
|
+
# Re-enable this code if you want to support passing trigger directly in the
|
|
447
|
+
# decorator in a way similar to how production and branch are passed in the
|
|
448
|
+
# project decorator.
|
|
449
|
+
|
|
450
|
+
# # This is overkill since default is None for all options but adding this code
|
|
451
|
+
# # to make it safe if other non None-default options are added in the future.
|
|
452
|
+
# for op in options:
|
|
453
|
+
# if (
|
|
454
|
+
# op in self._user_defined_attributes
|
|
455
|
+
# and options[op] != self.defaults[op]
|
|
456
|
+
# and self.attributes[op] != options[op]
|
|
457
|
+
# ):
|
|
458
|
+
# # Exception if:
|
|
459
|
+
# # - the user provides a value in the attributes field
|
|
460
|
+
# # - AND the user provided a value in the command line (non default)
|
|
461
|
+
# # - AND the values are different
|
|
462
|
+
# # Note that this won't raise an error if the user provided the default
|
|
463
|
+
# # value in the command line and provided one in attribute but although
|
|
464
|
+
# # slightly inconsistent, it is not incorrect.
|
|
465
|
+
# raise MetaflowException(
|
|
466
|
+
# "You cannot pass %s as both a command-line argument and an attribute "
|
|
467
|
+
# "of the @trigger_on_finish decorator." % op
|
|
468
|
+
# )
|
|
469
|
+
|
|
470
|
+
# if "trigger" in self._user_defined_attributes:
|
|
471
|
+
# trigger_option = self.attributes["trigger"]
|
|
472
|
+
# else:
|
|
473
|
+
trigger_option = options["trigger"]
|
|
474
|
+
|
|
410
475
|
self._option_values = options
|
|
411
|
-
if
|
|
476
|
+
if trigger_option:
|
|
412
477
|
from metaflow import Run
|
|
413
478
|
from metaflow.events import Trigger
|
|
414
479
|
|
|
415
480
|
run_objs = []
|
|
416
|
-
for run_pathspec in
|
|
481
|
+
for run_pathspec in trigger_option:
|
|
417
482
|
if len(run_pathspec.split("/")) != 2:
|
|
418
483
|
raise MetaflowException(
|
|
419
484
|
"Incorrect format for run pathspec for *--trigger*. "
|
|
@@ -427,5 +492,98 @@ class TriggerOnFinishDecorator(FlowDecorator):
|
|
|
427
492
|
run_objs.append(run_obj)
|
|
428
493
|
current._update_env({"trigger": Trigger.from_runs(run_objs)})
|
|
429
494
|
|
|
495
|
+
@staticmethod
|
|
496
|
+
def _parse_static_triggers(flows):
|
|
497
|
+
results = []
|
|
498
|
+
for flow in flows:
|
|
499
|
+
if is_stringish(flow):
|
|
500
|
+
results.append(
|
|
501
|
+
{
|
|
502
|
+
"fq_name": flow,
|
|
503
|
+
}
|
|
504
|
+
)
|
|
505
|
+
elif isinstance(flow, dict):
|
|
506
|
+
if "name" not in flow:
|
|
507
|
+
if len(flows) > 1:
|
|
508
|
+
raise MetaflowException(
|
|
509
|
+
"One or more flows in the *flows* attribute for "
|
|
510
|
+
"*@trigger_on_finish* is missing the "
|
|
511
|
+
"*name* key."
|
|
512
|
+
)
|
|
513
|
+
raise MetaflowException(
|
|
514
|
+
"The *flow* attribute for *@trigger_on_finish* is missing the "
|
|
515
|
+
"*name* key."
|
|
516
|
+
)
|
|
517
|
+
flow_name = flow["name"]
|
|
518
|
+
|
|
519
|
+
if not is_stringish(flow_name) or "." in flow_name:
|
|
520
|
+
raise MetaflowException(
|
|
521
|
+
f"The *name* attribute of the *flow* {flow_name} is not a valid string"
|
|
522
|
+
)
|
|
523
|
+
result = {"fq_name": flow_name}
|
|
524
|
+
if "project" in flow:
|
|
525
|
+
if is_stringish(flow["project"]):
|
|
526
|
+
result["project"] = flow["project"]
|
|
527
|
+
else:
|
|
528
|
+
raise MetaflowException(
|
|
529
|
+
f"The *project* attribute of the *flow* {flow_name} is not a string"
|
|
530
|
+
)
|
|
531
|
+
if "project_branch" in flow:
|
|
532
|
+
if is_stringish(flow["project_branch"]):
|
|
533
|
+
result["branch"] = flow["project_branch"]
|
|
534
|
+
else:
|
|
535
|
+
raise MetaflowException(
|
|
536
|
+
f"The *project_branch* attribute of the *flow* {flow_name} is not a string"
|
|
537
|
+
)
|
|
538
|
+
results.append(result)
|
|
539
|
+
else:
|
|
540
|
+
if len(flows) > 1:
|
|
541
|
+
raise MetaflowException(
|
|
542
|
+
"One or more flows in the *flows* attribute for "
|
|
543
|
+
"*@trigger_on_finish* decorator have an incorrect type. "
|
|
544
|
+
"Supported type is string or Dict[str, str]- \n"
|
|
545
|
+
"@trigger_on_finish(flows=['FooFlow', 'BarFlow']"
|
|
546
|
+
)
|
|
547
|
+
raise MetaflowException(
|
|
548
|
+
"Incorrect type for *flow* attribute in *@trigger_on_finish* "
|
|
549
|
+
" decorator. Supported type is string or Dict[str, str] - \n"
|
|
550
|
+
"@trigger_on_finish(flow='FooFlow') or "
|
|
551
|
+
"@trigger_on_finish(flow={'name':'FooFlow', 'project_branch': 'branch'})"
|
|
552
|
+
)
|
|
553
|
+
return results
|
|
554
|
+
|
|
555
|
+
def _parse_fq_name(self, trigger):
|
|
556
|
+
if trigger["fq_name"].count(".") == 0:
|
|
557
|
+
# fully qualified name is just the flow name
|
|
558
|
+
trigger["flow"] = trigger["fq_name"]
|
|
559
|
+
elif trigger["fq_name"].count(".") >= 2:
|
|
560
|
+
# fully qualified name is of the format - project.branch.flow_name
|
|
561
|
+
trigger["project"], tail = trigger["fq_name"].split(".", maxsplit=1)
|
|
562
|
+
trigger["branch"], trigger["flow"] = tail.rsplit(".", maxsplit=1)
|
|
563
|
+
else:
|
|
564
|
+
raise MetaflowException(
|
|
565
|
+
"Incorrect format for *flow* in *@trigger_on_finish* "
|
|
566
|
+
"decorator. Specify either just the *flow_name* or a fully "
|
|
567
|
+
"qualified name like *project_name.branch_name.flow_name*."
|
|
568
|
+
)
|
|
569
|
+
if not re.match(r"^[A-Za-z0-9_]+$", trigger["flow"]):
|
|
570
|
+
raise MetaflowException(
|
|
571
|
+
"Invalid flow name *%s* in *@trigger_on_finish* "
|
|
572
|
+
"decorator. Only alphanumeric characters and "
|
|
573
|
+
"underscores(_) are allowed." % trigger["flow"]
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
def format_deploytime_value(self):
|
|
577
|
+
if len(self.triggers) == 1 and isinstance(self.triggers[0], DeployTimeField):
|
|
578
|
+
deploy_value = deploy_time_eval(self.triggers[0])
|
|
579
|
+
if isinstance(deploy_value, list):
|
|
580
|
+
self.triggers = deploy_value
|
|
581
|
+
else:
|
|
582
|
+
self.triggers = [deploy_value]
|
|
583
|
+
triggers = self._parse_static_triggers(self.triggers)
|
|
584
|
+
for trigger in triggers:
|
|
585
|
+
self._parse_fq_name(trigger)
|
|
586
|
+
self.triggers = triggers
|
|
587
|
+
|
|
430
588
|
def get_top_level_options(self):
|
|
431
589
|
return list(self._option_values.items())
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from metaflow.decorators import FlowDecorator
|
|
2
|
+
from metaflow.exception import MetaflowException
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ExitHookDecorator(FlowDecorator):
|
|
6
|
+
name = "exit_hook"
|
|
7
|
+
allow_multiple = True
|
|
8
|
+
|
|
9
|
+
defaults = {
|
|
10
|
+
"on_success": [],
|
|
11
|
+
"on_error": [],
|
|
12
|
+
"options": {},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def flow_init(
|
|
16
|
+
self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
|
|
17
|
+
):
|
|
18
|
+
on_success = self.attributes["on_success"]
|
|
19
|
+
on_error = self.attributes["on_error"]
|
|
20
|
+
|
|
21
|
+
if not on_success and not on_error:
|
|
22
|
+
raise MetaflowException(
|
|
23
|
+
"Choose at least one of the options on_success/on_error"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
self.success_hooks = []
|
|
27
|
+
self.error_hooks = []
|
|
28
|
+
for success_fn in on_success:
|
|
29
|
+
if isinstance(success_fn, str):
|
|
30
|
+
self.success_hooks.append(success_fn)
|
|
31
|
+
elif callable(success_fn):
|
|
32
|
+
self.success_hooks.append(success_fn.__name__)
|
|
33
|
+
else:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"Exit hooks inside 'on_success' must be a function or a string referring to the function"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for error_fn in on_error:
|
|
39
|
+
if isinstance(error_fn, str):
|
|
40
|
+
self.error_hooks.append(error_fn)
|
|
41
|
+
elif callable(error_fn):
|
|
42
|
+
self.error_hooks.append(error_fn.__name__)
|
|
43
|
+
else:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"Exit hooks inside 'on_error' must be a function or a string referring to the function"
|
|
46
|
+
)
|