ob-metaflow 2.11.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metaflow/R.py +10 -7
- metaflow/__init__.py +40 -25
- metaflow/_vendor/imghdr/__init__.py +186 -0
- metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/typeguard/__init__.py +48 -0
- metaflow/_vendor/typeguard/_checkers.py +1070 -0
- metaflow/_vendor/typeguard/_config.py +108 -0
- metaflow/_vendor/typeguard/_decorators.py +233 -0
- metaflow/_vendor/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/typeguard/_functions.py +308 -0
- metaflow/_vendor/typeguard/_importhook.py +213 -0
- metaflow/_vendor/typeguard/_memo.py +48 -0
- metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
- metaflow/_vendor/typeguard/_suppression.py +86 -0
- metaflow/_vendor/typeguard/_transformer.py +1229 -0
- metaflow/_vendor/typeguard/_union_transformer.py +55 -0
- metaflow/_vendor/typeguard/_utils.py +173 -0
- metaflow/_vendor/typeguard/py.typed +0 -0
- metaflow/_vendor/typing_extensions.py +3641 -0
- metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
- metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
- metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
- metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
- metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
- metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
- metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
- metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
- metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
- metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
- metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
- metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
- metaflow/_vendor/yaml/__init__.py +427 -0
- metaflow/_vendor/yaml/composer.py +139 -0
- metaflow/_vendor/yaml/constructor.py +748 -0
- metaflow/_vendor/yaml/cyaml.py +101 -0
- metaflow/_vendor/yaml/dumper.py +62 -0
- metaflow/_vendor/yaml/emitter.py +1137 -0
- metaflow/_vendor/yaml/error.py +75 -0
- metaflow/_vendor/yaml/events.py +86 -0
- metaflow/_vendor/yaml/loader.py +63 -0
- metaflow/_vendor/yaml/nodes.py +49 -0
- metaflow/_vendor/yaml/parser.py +589 -0
- metaflow/_vendor/yaml/reader.py +185 -0
- metaflow/_vendor/yaml/representer.py +389 -0
- metaflow/_vendor/yaml/resolver.py +227 -0
- metaflow/_vendor/yaml/scanner.py +1435 -0
- metaflow/_vendor/yaml/serializer.py +111 -0
- metaflow/_vendor/yaml/tokens.py +104 -0
- metaflow/cards.py +5 -0
- metaflow/cli.py +331 -785
- metaflow/cli_args.py +17 -0
- metaflow/cli_components/__init__.py +0 -0
- metaflow/cli_components/dump_cmd.py +96 -0
- metaflow/cli_components/init_cmd.py +52 -0
- metaflow/cli_components/run_cmds.py +546 -0
- metaflow/cli_components/step_cmd.py +334 -0
- metaflow/cli_components/utils.py +140 -0
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +467 -73
- metaflow/client/filecache.py +75 -35
- metaflow/clone_util.py +7 -1
- metaflow/cmd/code/__init__.py +231 -0
- metaflow/cmd/develop/stub_generator.py +756 -288
- metaflow/cmd/develop/stubs.py +12 -28
- metaflow/cmd/main_cli.py +6 -4
- metaflow/cmd/make_wrapper.py +78 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +41 -10
- metaflow/datastore/datastore_set.py +11 -2
- metaflow/datastore/flow_datastore.py +156 -10
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +154 -39
- metaflow/debug.py +5 -0
- metaflow/decorators.py +404 -78
- metaflow/exception.py +8 -2
- metaflow/extension_support/__init__.py +527 -376
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/extension_support/plugins.py +49 -31
- metaflow/flowspec.py +482 -33
- metaflow/graph.py +210 -42
- metaflow/includefile.py +84 -40
- metaflow/lint.py +141 -22
- metaflow/meta_files.py +13 -0
- metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
- metaflow/{metadata → metadata_provider}/metadata.py +86 -1
- metaflow/metaflow_config.py +175 -28
- metaflow/metaflow_config_funcs.py +51 -3
- metaflow/metaflow_current.py +4 -10
- metaflow/metaflow_environment.py +139 -53
- metaflow/metaflow_git.py +115 -0
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +150 -66
- metaflow/mflog/__init__.py +4 -3
- metaflow/mflog/save_logs.py +2 -2
- metaflow/multicore_utils.py +31 -14
- metaflow/package/__init__.py +673 -0
- metaflow/packaging_sys/__init__.py +880 -0
- metaflow/packaging_sys/backend.py +128 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +99 -0
- metaflow/packaging_sys/utils.py +54 -0
- metaflow/packaging_sys/v1.py +527 -0
- metaflow/parameters.py +149 -28
- metaflow/plugins/__init__.py +74 -5
- metaflow/plugins/airflow/airflow.py +40 -25
- metaflow/plugins/airflow/airflow_cli.py +22 -5
- metaflow/plugins/airflow/airflow_decorator.py +1 -1
- metaflow/plugins/airflow/airflow_utils.py +5 -3
- metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
- metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
- metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
- metaflow/plugins/argo/argo_client.py +78 -33
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +2410 -527
- metaflow/plugins/argo/argo_workflows_cli.py +571 -121
- metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
- metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
- metaflow/plugins/argo/capture_error.py +73 -0
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/jobset_input_paths.py +15 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +10 -3
- metaflow/plugins/aws/aws_utils.py +55 -2
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +33 -10
- metaflow/plugins/aws/batch/batch_client.py +4 -3
- metaflow/plugins/aws/batch/batch_decorator.py +102 -35
- metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
- metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
- metaflow/plugins/aws/step_functions/production_token.py +1 -1
- metaflow/plugins/aws/step_functions/step_functions.py +65 -8
- metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
- metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
- metaflow/plugins/azure/azure_exceptions.py +1 -1
- metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
- metaflow/plugins/azure/azure_tail.py +1 -1
- metaflow/plugins/azure/includefile_support.py +2 -0
- metaflow/plugins/cards/card_cli.py +66 -30
- metaflow/plugins/cards/card_creator.py +25 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +132 -8
- metaflow/plugins/cards/card_modules/basic.py +112 -17
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/card.py +16 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +665 -28
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
- metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
- metaflow/plugins/cards/card_modules/main.css +1 -0
- metaflow/plugins/cards/card_modules/main.js +68 -49
- metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
- metaflow/plugins/cards/card_modules/test_cards.py +26 -12
- metaflow/plugins/cards/card_server.py +39 -14
- metaflow/plugins/cards/component_serializer.py +2 -9
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/azure_storage.py +10 -1
- metaflow/plugins/datastores/gs_storage.py +6 -2
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/local.py +2 -0
- metaflow/plugins/datatools/s3/s3.py +126 -75
- metaflow/plugins/datatools/s3/s3op.py +254 -121
- metaflow/plugins/env_escape/__init__.py +3 -3
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/env_escape/server.py +7 -0
- metaflow/plugins/env_escape/stub.py +24 -5
- metaflow/plugins/events_decorator.py +343 -185
- metaflow/plugins/exit_hook/__init__.py +0 -0
- metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
- metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
- metaflow/plugins/gcp/__init__.py +1 -1
- metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
- metaflow/plugins/gcp/gs_tail.py +10 -6
- metaflow/plugins/gcp/includefile_support.py +3 -0
- metaflow/plugins/kubernetes/kube_utils.py +108 -0
- metaflow/plugins/kubernetes/kubernetes.py +411 -130
- metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
- metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
- metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
- metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
- metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
- metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
- metaflow/plugins/logs_cli.py +359 -0
- metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
- metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +128 -11
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/project_decorator.py +51 -5
- metaflow/plugins/pypi/bootstrap.py +357 -105
- metaflow/plugins/pypi/conda_decorator.py +82 -81
- metaflow/plugins/pypi/conda_environment.py +187 -52
- metaflow/plugins/pypi/micromamba.py +157 -47
- metaflow/plugins/pypi/parsers.py +268 -0
- metaflow/plugins/pypi/pip.py +88 -13
- metaflow/plugins/pypi/pypi_decorator.py +37 -1
- metaflow/plugins/pypi/utils.py +48 -2
- metaflow/plugins/resources_decorator.py +2 -2
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +26 -181
- metaflow/plugins/secrets/secrets_func.py +49 -0
- metaflow/plugins/secrets/secrets_spec.py +101 -0
- metaflow/plugins/secrets/utils.py +74 -0
- metaflow/plugins/tag_cli.py +4 -7
- metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
- metaflow/plugins/timeout_decorator.py +3 -3
- metaflow/plugins/uv/__init__.py +0 -0
- metaflow/plugins/uv/bootstrap.py +128 -0
- metaflow/plugins/uv/uv_environment.py +72 -0
- metaflow/procpoll.py +1 -1
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/__init__.py +0 -0
- metaflow/runner/click_api.py +717 -0
- metaflow/runner/deployer.py +470 -0
- metaflow/runner/deployer_impl.py +201 -0
- metaflow/runner/metaflow_runner.py +714 -0
- metaflow/runner/nbdeploy.py +132 -0
- metaflow/runner/nbrun.py +225 -0
- metaflow/runner/subprocess_manager.py +650 -0
- metaflow/runner/utils.py +335 -0
- metaflow/runtime.py +1078 -260
- metaflow/sidecar/sidecar_worker.py +1 -1
- metaflow/system/__init__.py +5 -0
- metaflow/system/system_logger.py +85 -0
- metaflow/system/system_monitor.py +108 -0
- metaflow/system/system_utils.py +19 -0
- metaflow/task.py +521 -225
- metaflow/tracing/__init__.py +7 -7
- metaflow/tracing/span_exporter.py +31 -38
- metaflow/tracing/tracing_modules.py +38 -43
- metaflow/tuple_util.py +27 -0
- metaflow/user_configs/__init__.py +0 -0
- metaflow/user_configs/config_options.py +563 -0
- metaflow/user_configs/config_parameters.py +598 -0
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +512 -0
- metaflow/user_decorators/mutable_step.py +424 -0
- metaflow/user_decorators/user_flow_decorator.py +264 -0
- metaflow/user_decorators/user_step_decorator.py +749 -0
- metaflow/util.py +243 -27
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
- ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
- ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
- metaflow/_vendor/v3_5/__init__.py +0 -1
- metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
- metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
- metaflow/package.py +0 -188
- ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
- ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
- /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
- /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
- /metaflow/{metadata → metadata_provider}/util.py +0 -0
- /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from typing import Dict, Optional, List, Tuple
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class _TrieNode:
|
|
5
|
+
def __init__(
|
|
6
|
+
self, parent: Optional["_TrieNode"] = None, component: Optional[str] = None
|
|
7
|
+
):
|
|
8
|
+
self.parent = parent
|
|
9
|
+
self.component = component
|
|
10
|
+
self.children = {} # type: Dict[str, "_TrieNode"]
|
|
11
|
+
self.total_children = 0
|
|
12
|
+
self.value = None
|
|
13
|
+
self.end_value = None
|
|
14
|
+
|
|
15
|
+
def traverse(self, value: type) -> Optional["_TrieNode"]:
|
|
16
|
+
if self.total_children == 0:
|
|
17
|
+
self.end_value = value
|
|
18
|
+
else:
|
|
19
|
+
self.end_value = None
|
|
20
|
+
self.total_children += 1
|
|
21
|
+
|
|
22
|
+
def remove_child(self, child_name: str) -> bool:
|
|
23
|
+
if child_name in self.children:
|
|
24
|
+
del self.children[child_name]
|
|
25
|
+
self.total_children -= 1
|
|
26
|
+
return True
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ClassPath_Trie:
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self.root = _TrieNode(None, None)
|
|
33
|
+
self.inited = False
|
|
34
|
+
self._value_to_node = {} # type: Dict[type, _TrieNode]
|
|
35
|
+
|
|
36
|
+
def init(self, initial_nodes: Optional[List[Tuple[str, type]]] = None):
|
|
37
|
+
# We need to do this so we can delay import of STEP_DECORATORS
|
|
38
|
+
self.inited = True
|
|
39
|
+
for classpath_name, value in initial_nodes or []:
|
|
40
|
+
self.insert(classpath_name, value)
|
|
41
|
+
|
|
42
|
+
def insert(self, classpath_name: str, value: type):
|
|
43
|
+
node = self.root
|
|
44
|
+
components = reversed(classpath_name.split("."))
|
|
45
|
+
for c in components:
|
|
46
|
+
node = node.children.setdefault(c, _TrieNode(node, c))
|
|
47
|
+
node.traverse(value)
|
|
48
|
+
node.total_children -= (
|
|
49
|
+
1 # We do not count the last node as having itself as a child
|
|
50
|
+
)
|
|
51
|
+
node.value = value
|
|
52
|
+
self._value_to_node[value] = node
|
|
53
|
+
|
|
54
|
+
def search(self, classpath_name: str) -> Optional[type]:
|
|
55
|
+
node = self.root
|
|
56
|
+
components = reversed(classpath_name.split("."))
|
|
57
|
+
for c in components:
|
|
58
|
+
if c not in node.children:
|
|
59
|
+
return None
|
|
60
|
+
node = node.children[c]
|
|
61
|
+
return node.value
|
|
62
|
+
|
|
63
|
+
def remove(self, classpath_name: str):
|
|
64
|
+
components = list(reversed(classpath_name.split(".")))
|
|
65
|
+
|
|
66
|
+
def _remove(node: _TrieNode, components, depth):
|
|
67
|
+
if depth == len(components):
|
|
68
|
+
if node.value is not None:
|
|
69
|
+
del self._value_to_node[node.value]
|
|
70
|
+
node.value = None
|
|
71
|
+
return len(node.children) == 0
|
|
72
|
+
return False
|
|
73
|
+
c = components[depth]
|
|
74
|
+
if c not in node.children:
|
|
75
|
+
return False
|
|
76
|
+
did_delete_child = _remove(node.children[c], components, depth + 1)
|
|
77
|
+
if did_delete_child:
|
|
78
|
+
node.remove_child(c)
|
|
79
|
+
if node.total_children == 1:
|
|
80
|
+
# If we have one total child left, we have at least one
|
|
81
|
+
# child and that one has an end_value
|
|
82
|
+
for child in node.children.values():
|
|
83
|
+
assert (
|
|
84
|
+
child.end_value
|
|
85
|
+
), "Node with one child must have an end_value"
|
|
86
|
+
node.end_value = child.end_value
|
|
87
|
+
return node.total_children == 0
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
_remove(self.root, components, 0)
|
|
91
|
+
|
|
92
|
+
def unique_prefix_value(self, classpath_name: str) -> Optional[type]:
|
|
93
|
+
node = self.root
|
|
94
|
+
components = reversed(classpath_name.split("."))
|
|
95
|
+
for c in components:
|
|
96
|
+
if c not in node.children:
|
|
97
|
+
return None
|
|
98
|
+
node = node.children[c]
|
|
99
|
+
# If we reach here, it means the classpath_name is a prefix.
|
|
100
|
+
# We check if it has only one path forward (end_value will be non None)
|
|
101
|
+
# If value is not None, we also consider this to be a unique "prefix"
|
|
102
|
+
# This happens since this trie is also filled with metaflow default decorators
|
|
103
|
+
return node.end_value or node.value
|
|
104
|
+
|
|
105
|
+
def unique_prefix_for_type(self, value: type) -> Optional[str]:
|
|
106
|
+
node = self._value_to_node.get(value, None)
|
|
107
|
+
if node is None:
|
|
108
|
+
return None
|
|
109
|
+
components = []
|
|
110
|
+
while node:
|
|
111
|
+
if node.end_value == value:
|
|
112
|
+
components = []
|
|
113
|
+
if node.component is not None:
|
|
114
|
+
components.append(node.component)
|
|
115
|
+
node = node.parent
|
|
116
|
+
return ".".join(components)
|
|
117
|
+
|
|
118
|
+
def get_unique_prefixes(self) -> Dict[str, type]:
|
|
119
|
+
"""
|
|
120
|
+
Get all unique prefixes in the trie.
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
List[str]
|
|
125
|
+
A list of unique prefixes.
|
|
126
|
+
"""
|
|
127
|
+
to_return = {}
|
|
128
|
+
|
|
129
|
+
def _collect(node, current_prefix):
|
|
130
|
+
if node.end_value is not None:
|
|
131
|
+
to_return[current_prefix] = node.end_value
|
|
132
|
+
# We stop there and don't look further since we found the unique prefix
|
|
133
|
+
return
|
|
134
|
+
if node.value is not None:
|
|
135
|
+
to_return[current_prefix] = node.value
|
|
136
|
+
# We continue to look for more unique prefixes
|
|
137
|
+
for child_name, child_node in node.children.items():
|
|
138
|
+
_collect(
|
|
139
|
+
child_node,
|
|
140
|
+
f"{current_prefix}.{child_name}" if current_prefix else child_name,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
_collect(self.root, "")
|
|
144
|
+
return {".".join(reversed(k.split("."))): v for k, v in to_return.items()}
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
from typing import Any, Dict, Generator, List, Optional, Tuple, TYPE_CHECKING, Union
|
|
3
|
+
|
|
4
|
+
from metaflow.debug import debug
|
|
5
|
+
from metaflow.exception import MetaflowException
|
|
6
|
+
from metaflow.user_configs.config_parameters import ConfigValue
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import metaflow.flowspec
|
|
10
|
+
import metaflow.parameters
|
|
11
|
+
import metaflow.user_decorators.mutable_step
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MutableFlow:
|
|
15
|
+
IGNORE = 1
|
|
16
|
+
ERROR = 2
|
|
17
|
+
OVERRIDE = 3
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
flow_spec: "metaflow.flowspec.FlowSpec",
|
|
22
|
+
pre_mutate: bool = False,
|
|
23
|
+
statically_defined: bool = False,
|
|
24
|
+
inserted_by: Optional[str] = None,
|
|
25
|
+
):
|
|
26
|
+
self._flow_cls = flow_spec
|
|
27
|
+
self._pre_mutate = pre_mutate
|
|
28
|
+
self._statically_defined = statically_defined
|
|
29
|
+
self._inserted_by = inserted_by
|
|
30
|
+
if self._inserted_by is None:
|
|
31
|
+
# This is an error because MutableSteps should only be created by
|
|
32
|
+
# StepMutators or FlowMutators. We need to catch it now because otherwise
|
|
33
|
+
# we may put stuff on the command line (with --with) that would get added
|
|
34
|
+
# twice and weird behavior may ensue.
|
|
35
|
+
raise MetaflowException(
|
|
36
|
+
"MutableFlow should only be created by StepMutators or FlowMutators. "
|
|
37
|
+
"This is an internal error."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def decorator_specs(
|
|
42
|
+
self,
|
|
43
|
+
) -> Generator[Tuple[str, str, List[Any], Dict[str, Any]], None, None]:
|
|
44
|
+
"""
|
|
45
|
+
Iterate over all the decorator specifications of this flow. Note that the same
|
|
46
|
+
type of decorator may be present multiple times and no order is guaranteed.
|
|
47
|
+
|
|
48
|
+
The returned tuple contains:
|
|
49
|
+
- The decorator's name (shortest possible)
|
|
50
|
+
- The decorator's fully qualified name (in the case of Metaflow decorators, this
|
|
51
|
+
will indicate which extension the decorator comes from)
|
|
52
|
+
- A list of positional arguments
|
|
53
|
+
- A dictionary of keyword arguments
|
|
54
|
+
|
|
55
|
+
You can use the decorator specification to remove a decorator from the flow
|
|
56
|
+
for example.
|
|
57
|
+
|
|
58
|
+
Yields
|
|
59
|
+
------
|
|
60
|
+
str, str, List[Any], Dict[str, Any]
|
|
61
|
+
A tuple containing the decorator name, it's fully qualified name,
|
|
62
|
+
a list of positional arguments, and a dictionary of keyword arguments.
|
|
63
|
+
"""
|
|
64
|
+
from metaflow.flowspec import FlowStateItems
|
|
65
|
+
|
|
66
|
+
flow_decos = self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
67
|
+
for decos in flow_decos.values():
|
|
68
|
+
for deco in decos:
|
|
69
|
+
# 3.7 does not support yield foo, *bar syntax so we
|
|
70
|
+
# work around
|
|
71
|
+
|
|
72
|
+
r = [
|
|
73
|
+
deco.name,
|
|
74
|
+
"%s.%s"
|
|
75
|
+
% (
|
|
76
|
+
deco.__class__.__module__,
|
|
77
|
+
deco.__class__.__name__,
|
|
78
|
+
),
|
|
79
|
+
]
|
|
80
|
+
r.extend(deco.get_args_kwargs())
|
|
81
|
+
yield tuple(r)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def configs(self) -> Generator[Tuple[str, ConfigValue], None, None]:
|
|
85
|
+
"""
|
|
86
|
+
Iterate over all user configurations in this flow
|
|
87
|
+
|
|
88
|
+
Use this to parameterize your flow based on configuration. As an example, the
|
|
89
|
+
`pre_mutate`/`mutate` methods can add decorators to steps in the flow that
|
|
90
|
+
depend on values in the configuration.
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
class MyDecorator(FlowMutator):
|
|
94
|
+
def mutate(flow: MutableFlow):
|
|
95
|
+
val = next(flow.configs)[1].steps.start.cpu
|
|
96
|
+
flow.start.add_decorator(environment, vars={'mycpu': val})
|
|
97
|
+
return flow
|
|
98
|
+
|
|
99
|
+
@MyDecorator()
|
|
100
|
+
class TestFlow(FlowSpec):
|
|
101
|
+
config = Config('myconfig.json')
|
|
102
|
+
|
|
103
|
+
@step
|
|
104
|
+
def start(self):
|
|
105
|
+
pass
|
|
106
|
+
```
|
|
107
|
+
can be used to add an environment decorator to the `start` step.
|
|
108
|
+
|
|
109
|
+
Yields
|
|
110
|
+
------
|
|
111
|
+
Tuple[str, ConfigValue]
|
|
112
|
+
Iterates over the configurations of the flow
|
|
113
|
+
"""
|
|
114
|
+
from metaflow.flowspec import FlowStateItems
|
|
115
|
+
|
|
116
|
+
# When configs are parsed, they are loaded in _flow_state[FlowStateItems.CONFIGS]
|
|
117
|
+
for name, value in self._flow_cls._flow_state[FlowStateItems.CONFIGS].items():
|
|
118
|
+
r = name, ConfigValue(value) if value is not None else None
|
|
119
|
+
debug.userconf_exec("Mutable flow yielding config: %s" % str(r))
|
|
120
|
+
yield r
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def parameters(
|
|
124
|
+
self,
|
|
125
|
+
) -> Generator[Tuple[str, "metaflow.parameters.Parameter"], None, None]:
|
|
126
|
+
"""
|
|
127
|
+
Iterate over all the parameters in this flow.
|
|
128
|
+
|
|
129
|
+
Yields
|
|
130
|
+
------
|
|
131
|
+
Tuple[str, Parameter]
|
|
132
|
+
Name of the parameter and parameter in the flow
|
|
133
|
+
"""
|
|
134
|
+
for var, param in self._flow_cls._get_parameters():
|
|
135
|
+
if param.IS_CONFIG_PARAMETER:
|
|
136
|
+
continue
|
|
137
|
+
debug.userconf_exec(
|
|
138
|
+
"Mutable flow yielding parameter: %s" % str((var, param))
|
|
139
|
+
)
|
|
140
|
+
yield var, param
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def steps(
|
|
144
|
+
self,
|
|
145
|
+
) -> Generator[
|
|
146
|
+
Tuple[str, "metaflow.user_decorators.mutable_step.MutableStep"], None, None
|
|
147
|
+
]:
|
|
148
|
+
"""
|
|
149
|
+
Iterate over all the steps in this flow. The order of the steps
|
|
150
|
+
returned is not guaranteed.
|
|
151
|
+
|
|
152
|
+
Yields
|
|
153
|
+
------
|
|
154
|
+
Tuple[str, MutableStep]
|
|
155
|
+
A tuple with the step name and the step proxy
|
|
156
|
+
"""
|
|
157
|
+
from .mutable_step import MutableStep
|
|
158
|
+
|
|
159
|
+
for var in dir(self._flow_cls):
|
|
160
|
+
potential_step = getattr(self._flow_cls, var)
|
|
161
|
+
if callable(potential_step) and hasattr(potential_step, "is_step"):
|
|
162
|
+
debug.userconf_exec("Mutable flow yielding step: %s" % var)
|
|
163
|
+
yield var, MutableStep(
|
|
164
|
+
self._flow_cls,
|
|
165
|
+
potential_step,
|
|
166
|
+
pre_mutate=self._pre_mutate,
|
|
167
|
+
statically_defined=self._statically_defined,
|
|
168
|
+
inserted_by=self._inserted_by,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def add_parameter(
|
|
172
|
+
self, name: str, value: "metaflow.parameters.Parameter", overwrite: bool = False
|
|
173
|
+
) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Add a parameter to the flow. You can only add parameters in the `pre_mutate`
|
|
176
|
+
method.
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
name : str
|
|
181
|
+
Name of the parameter
|
|
182
|
+
value : Parameter
|
|
183
|
+
Parameter to add to the flow
|
|
184
|
+
overwrite : bool, default False
|
|
185
|
+
If True, overwrite the parameter if it already exists
|
|
186
|
+
"""
|
|
187
|
+
if not self._pre_mutate:
|
|
188
|
+
raise MetaflowException(
|
|
189
|
+
"Adding parameter '%s' from %s is only allowed in the `pre_mutate` "
|
|
190
|
+
"method and not the `mutate` method" % (name, self._inserted_by)
|
|
191
|
+
)
|
|
192
|
+
from metaflow.parameters import Parameter
|
|
193
|
+
|
|
194
|
+
if hasattr(self._flow_cls, name) and not overwrite:
|
|
195
|
+
raise MetaflowException(
|
|
196
|
+
"Flow '%s' already has a class member '%s' -- "
|
|
197
|
+
"set overwrite=True in add_parameter to overwrite it."
|
|
198
|
+
% (self._flow_cls.__name__, name)
|
|
199
|
+
)
|
|
200
|
+
if not isinstance(value, Parameter) or value.IS_CONFIG_PARAMETER:
|
|
201
|
+
raise MetaflowException(
|
|
202
|
+
"Only a Parameter or an IncludeFile can be added using `add_parameter`"
|
|
203
|
+
"; got %s" % type(value)
|
|
204
|
+
)
|
|
205
|
+
debug.userconf_exec("Mutable flow adding parameter %s to flow" % name)
|
|
206
|
+
setattr(self._flow_cls, name, value)
|
|
207
|
+
|
|
208
|
+
def remove_parameter(self, parameter_name: str) -> bool:
|
|
209
|
+
"""
|
|
210
|
+
Remove a parameter from the flow.
|
|
211
|
+
|
|
212
|
+
The name given should match the name of the parameter (can be different
|
|
213
|
+
from the name of the parameter in the flow. You can not remove config parameters.
|
|
214
|
+
You can only remove parameters in the `pre_mutate` method.
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
parameter_name : str
|
|
219
|
+
Name of the parameter
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
bool
|
|
224
|
+
Returns True if the parameter was removed
|
|
225
|
+
"""
|
|
226
|
+
if not self._pre_mutate:
|
|
227
|
+
raise MetaflowException(
|
|
228
|
+
"Removing parameter '%s' from %s is only allowed in the `pre_mutate` "
|
|
229
|
+
"method and not the `mutate` method"
|
|
230
|
+
% (parameter_name, " from ".join(self._inserted_by))
|
|
231
|
+
)
|
|
232
|
+
from metaflow.flowspec import FlowStateItems
|
|
233
|
+
|
|
234
|
+
for var, param in self._flow_cls._get_parameters():
|
|
235
|
+
if param.IS_CONFIG_PARAMETER:
|
|
236
|
+
continue
|
|
237
|
+
if param.name == parameter_name:
|
|
238
|
+
delattr(self._flow_cls, var)
|
|
239
|
+
debug.userconf_exec(
|
|
240
|
+
"Mutable flow removing parameter %s from flow" % var
|
|
241
|
+
)
|
|
242
|
+
# Reset so that we don't list it again
|
|
243
|
+
self._flow_cls._flow_state.pop(FlowStateItems.CACHED_PARAMETERS, None)
|
|
244
|
+
return True
|
|
245
|
+
debug.userconf_exec(
|
|
246
|
+
"Mutable flow failed to remove parameter %s from flow" % parameter_name
|
|
247
|
+
)
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
def add_decorator(
|
|
251
|
+
self,
|
|
252
|
+
deco_type: Union[partial, str],
|
|
253
|
+
deco_args: Optional[List[Any]] = None,
|
|
254
|
+
deco_kwargs: Optional[Dict[str, Any]] = None,
|
|
255
|
+
duplicates: int = IGNORE,
|
|
256
|
+
) -> None:
|
|
257
|
+
"""
|
|
258
|
+
Add a Metaflow flow-decorator to a flow. You can only add decorators in the
|
|
259
|
+
`pre_mutate` method.
|
|
260
|
+
|
|
261
|
+
You can either add the decorator itself or its decorator specification for it
|
|
262
|
+
(the same you would get back from decorator_specs). You can also mix and match
|
|
263
|
+
but you cannot provide arguments both through the string and the
|
|
264
|
+
deco_args/deco_kwargs.
|
|
265
|
+
|
|
266
|
+
As an example:
|
|
267
|
+
```
|
|
268
|
+
from metaflow import project
|
|
269
|
+
|
|
270
|
+
...
|
|
271
|
+
my_flow.add_decorator(project, deco_kwargs={"name":"my_project"})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
is equivalent to:
|
|
275
|
+
```
|
|
276
|
+
my_flow.add_decorator("project:name=my_project")
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Note in the later case, there is no need to import the flow decorator.
|
|
280
|
+
|
|
281
|
+
The latter syntax is useful to, for example, allow decorators to be stored as
|
|
282
|
+
strings in a configuration file.
|
|
283
|
+
|
|
284
|
+
In terms of precedence for decorators:
|
|
285
|
+
- if a decorator can be applied multiple times all decorators
|
|
286
|
+
added are kept (this is rare for flow-decorators).
|
|
287
|
+
- if `duplicates` is set to `MutableFlow.IGNORE`, then the decorator
|
|
288
|
+
being added is ignored (in other words, the existing decorator has precedence).
|
|
289
|
+
- if `duplicates` is set to `MutableFlow.OVERRIDE`, then the *existing*
|
|
290
|
+
decorator is removed and this newly added one replaces it (in other
|
|
291
|
+
words, the newly added decorator has precedence).
|
|
292
|
+
- if `duplicates` is set to `MutableFlow.ERROR`, then an error is raised but only
|
|
293
|
+
if the newly added decorator is *static* (ie: defined directly in the code).
|
|
294
|
+
If not, it is ignored.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
deco_type : Union[partial, str]
|
|
299
|
+
The decorator class to add to this flow. If using a string, you cannot specify
|
|
300
|
+
additional arguments as all argument will be parsed from the decorator
|
|
301
|
+
specification.
|
|
302
|
+
deco_args : List[Any], optional, default None
|
|
303
|
+
Positional arguments to pass to the decorator.
|
|
304
|
+
deco_kwargs : Dict[str, Any], optional, default None
|
|
305
|
+
Keyword arguments to pass to the decorator.
|
|
306
|
+
duplicates : int, default MutableFlow.IGNORE
|
|
307
|
+
Instruction on how to handle duplicates. It can be one of:
|
|
308
|
+
- `MutableFlow.IGNORE`: Ignore the decorator if it already exists.
|
|
309
|
+
- `MutableFlow.ERROR`: Raise an error if the decorator already exists.
|
|
310
|
+
- `MutableFlow.OVERRIDE`: Remove the existing decorator and add this one.
|
|
311
|
+
|
|
312
|
+
"""
|
|
313
|
+
if not self._pre_mutate:
|
|
314
|
+
raise MetaflowException(
|
|
315
|
+
"Adding flow-decorator '%s' from %s is only allowed in the `pre_mutate` "
|
|
316
|
+
"method and not the `mutate` method"
|
|
317
|
+
% (
|
|
318
|
+
deco_type if isinstance(deco_type, str) else deco_type.name,
|
|
319
|
+
self._inserted_by,
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
# Prevent circular import
|
|
323
|
+
from metaflow.decorators import (
|
|
324
|
+
DuplicateFlowDecoratorException,
|
|
325
|
+
FlowDecorator,
|
|
326
|
+
extract_flow_decorator_from_decospec,
|
|
327
|
+
)
|
|
328
|
+
from metaflow.flowspec import FlowStateItems
|
|
329
|
+
|
|
330
|
+
deco_args = deco_args or []
|
|
331
|
+
deco_kwargs = deco_kwargs or {}
|
|
332
|
+
|
|
333
|
+
def _add_flow_decorator(flow_deco):
|
|
334
|
+
# NOTE: Here we operate not on self_data or inherited_data because mutators
|
|
335
|
+
# are processed on the end flow anyways (they can come from any of the base
|
|
336
|
+
# flow classes but they only execute on the flow actually being run). This makes
|
|
337
|
+
# it easier particularly for the case of OVERRIDE where we need to override
|
|
338
|
+
# a decorator that could be in either of the inherited or self dictionaries.
|
|
339
|
+
if deco_args:
|
|
340
|
+
raise MetaflowException(
|
|
341
|
+
"Flow decorators do not take additional positional arguments"
|
|
342
|
+
)
|
|
343
|
+
# Update kwargs:
|
|
344
|
+
flow_deco.attributes.update(deco_kwargs)
|
|
345
|
+
|
|
346
|
+
# Check duplicates
|
|
347
|
+
def _do_add():
|
|
348
|
+
flow_deco.statically_defined = self._statically_defined
|
|
349
|
+
flow_deco.inserted_by = self._inserted_by
|
|
350
|
+
flow_decos = self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
351
|
+
|
|
352
|
+
flow_decos.setdefault(flow_deco.name, []).append(flow_deco)
|
|
353
|
+
debug.userconf_exec(
|
|
354
|
+
"Mutable flow adding flow decorator '%s'" % deco_type
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
# self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS] is a dictionary of form :
|
|
358
|
+
# <deco_name> : [deco_instance, deco_instance, ...]
|
|
359
|
+
flow_decos = self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
360
|
+
existing_deco = [d for d in flow_decos if d == flow_deco.name]
|
|
361
|
+
|
|
362
|
+
if flow_deco.allow_multiple or not existing_deco:
|
|
363
|
+
_do_add()
|
|
364
|
+
elif duplicates == MutableFlow.IGNORE:
|
|
365
|
+
# If we ignore, we do not add the decorator
|
|
366
|
+
debug.userconf_exec(
|
|
367
|
+
"Mutable flow ignoring flow decorator '%s'"
|
|
368
|
+
"(already exists and duplicates are ignored)" % flow_deco.name
|
|
369
|
+
)
|
|
370
|
+
elif duplicates == MutableFlow.OVERRIDE:
|
|
371
|
+
# If we override, we remove the existing decorator and add this one
|
|
372
|
+
debug.userconf_exec(
|
|
373
|
+
"Mutable flow overriding flow decorator '%s' "
|
|
374
|
+
"(removing existing decorator and adding new one)" % flow_deco.name
|
|
375
|
+
)
|
|
376
|
+
flow_decos = self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
377
|
+
self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS] = {
|
|
378
|
+
d: flow_decos[d] for d in flow_decos if d != flow_deco.name
|
|
379
|
+
}
|
|
380
|
+
_do_add()
|
|
381
|
+
elif duplicates == MutableFlow.ERROR:
|
|
382
|
+
# If we error, we raise an exception
|
|
383
|
+
if self._statically_defined:
|
|
384
|
+
raise DuplicateFlowDecoratorException(flow_deco.name)
|
|
385
|
+
else:
|
|
386
|
+
debug.userconf_exec(
|
|
387
|
+
"Mutable flow ignoring flow decorator '%s' "
|
|
388
|
+
"(already exists and non statically defined)" % flow_deco.name
|
|
389
|
+
)
|
|
390
|
+
else:
|
|
391
|
+
raise ValueError("Invalid duplicates value: %s" % duplicates)
|
|
392
|
+
|
|
393
|
+
# If deco_type is a string, we want to parse it to a decospec
|
|
394
|
+
if isinstance(deco_type, str):
|
|
395
|
+
flow_deco, has_args_kwargs = extract_flow_decorator_from_decospec(deco_type)
|
|
396
|
+
if (deco_args or deco_kwargs) and has_args_kwargs:
|
|
397
|
+
raise MetaflowException(
|
|
398
|
+
"Cannot specify additional arguments when adding a flow decorator "
|
|
399
|
+
"using a decospec that already contains arguments"
|
|
400
|
+
)
|
|
401
|
+
_add_flow_decorator(flow_deco)
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
# Validate deco_type
|
|
405
|
+
if (
|
|
406
|
+
not isinstance(deco_type, partial)
|
|
407
|
+
or len(deco_type.args) != 1
|
|
408
|
+
or not issubclass(deco_type.args[0], FlowDecorator)
|
|
409
|
+
):
|
|
410
|
+
raise TypeError("add_decorator takes a FlowDecorator")
|
|
411
|
+
|
|
412
|
+
deco_type = deco_type.args[0]
|
|
413
|
+
_add_flow_decorator(
|
|
414
|
+
deco_type(
|
|
415
|
+
attributes=deco_kwargs,
|
|
416
|
+
statically_defined=self._statically_defined,
|
|
417
|
+
inserted_by=self._inserted_by,
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
def remove_decorator(
|
|
422
|
+
self,
|
|
423
|
+
deco_name: str,
|
|
424
|
+
deco_args: Optional[List[Any]] = None,
|
|
425
|
+
deco_kwargs: Optional[Dict[str, Any]] = None,
|
|
426
|
+
) -> bool:
|
|
427
|
+
"""
|
|
428
|
+
Remove a flow-level decorator. To remove a decorator, you can pass the decorator
|
|
429
|
+
specification (obtained from `decorator_specs` for example).
|
|
430
|
+
Note that if multiple decorators share the same decorator specification
|
|
431
|
+
(very rare), they will all be removed.
|
|
432
|
+
|
|
433
|
+
You can only remove decorators in the `pre_mutate` method.
|
|
434
|
+
|
|
435
|
+
Parameters
|
|
436
|
+
----------
|
|
437
|
+
deco_name : str
|
|
438
|
+
Decorator specification of the decorator to remove. If nothing else is
|
|
439
|
+
specified, all decorators matching that name will be removed.
|
|
440
|
+
deco_args : List[Any], optional, default None
|
|
441
|
+
Positional arguments to match the decorator specification.
|
|
442
|
+
deco_kwargs : Dict[str, Any], optional, default None
|
|
443
|
+
Keyword arguments to match the decorator specification.
|
|
444
|
+
|
|
445
|
+
Returns
|
|
446
|
+
-------
|
|
447
|
+
bool
|
|
448
|
+
Returns True if a decorator was removed.
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
# Prevent circular import
|
|
452
|
+
from metaflow.flowspec import FlowStateItems
|
|
453
|
+
|
|
454
|
+
if not self._pre_mutate:
|
|
455
|
+
raise MetaflowException(
|
|
456
|
+
"Removing flow-decorator '%s' from %s is only allowed in the `pre_mutate` "
|
|
457
|
+
"method and not the `mutate` method" % (deco_name, self._inserted_by)
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
do_all = deco_args is None and deco_kwargs is None
|
|
461
|
+
did_remove = False
|
|
462
|
+
flow_decos = self._flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
463
|
+
|
|
464
|
+
if do_all and deco_name in flow_decos:
|
|
465
|
+
del flow_decos[deco_name]
|
|
466
|
+
return True
|
|
467
|
+
old_deco_list = flow_decos.get(deco_name)
|
|
468
|
+
if not old_deco_list:
|
|
469
|
+
debug.userconf_exec(
|
|
470
|
+
"Mutable flow failed to remove decorator '%s' from flow (non present)"
|
|
471
|
+
% deco_name
|
|
472
|
+
)
|
|
473
|
+
return False
|
|
474
|
+
new_deco_list = []
|
|
475
|
+
for deco in old_deco_list:
|
|
476
|
+
if deco.get_args_kwargs() == (deco_args or [], deco_kwargs or {}):
|
|
477
|
+
did_remove = True
|
|
478
|
+
else:
|
|
479
|
+
new_deco_list.append(deco)
|
|
480
|
+
debug.userconf_exec(
|
|
481
|
+
"Mutable flow removed %d decorators from flow"
|
|
482
|
+
% (len(old_deco_list) - len(new_deco_list))
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
if new_deco_list:
|
|
486
|
+
flow_decos[deco_name] = new_deco_list
|
|
487
|
+
else:
|
|
488
|
+
del flow_decos[deco_name]
|
|
489
|
+
return did_remove
|
|
490
|
+
|
|
491
|
+
def __getattr__(self, name):
|
|
492
|
+
# We allow direct access to the steps, configs and parameters but nothing else
|
|
493
|
+
from metaflow.parameters import Parameter
|
|
494
|
+
|
|
495
|
+
from .mutable_step import MutableStep
|
|
496
|
+
|
|
497
|
+
attr = getattr(self._flow_cls, name)
|
|
498
|
+
if attr:
|
|
499
|
+
# Steps
|
|
500
|
+
if callable(attr) and hasattr(attr, "is_step"):
|
|
501
|
+
return MutableStep(
|
|
502
|
+
self._flow_cls,
|
|
503
|
+
attr,
|
|
504
|
+
pre_mutate=self._pre_mutate,
|
|
505
|
+
statically_defined=self._statically_defined,
|
|
506
|
+
inserted_by=self._inserted_by,
|
|
507
|
+
)
|
|
508
|
+
if name[0] == "_" or name in self._flow_cls._NON_PARAMETERS:
|
|
509
|
+
raise AttributeError(self, name)
|
|
510
|
+
if isinstance(attr, (Parameter, ConfigValue)):
|
|
511
|
+
return attr
|
|
512
|
+
raise AttributeError(self, name)
|