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
metaflow/lint.py
CHANGED
|
@@ -52,7 +52,7 @@ def check_reserved_words(graph):
|
|
|
52
52
|
msg = "Step name *%s* is a reserved word. Choose another name for the " "step."
|
|
53
53
|
for node in graph:
|
|
54
54
|
if node.name in RESERVED:
|
|
55
|
-
raise LintWarn(msg % node.name)
|
|
55
|
+
raise LintWarn(msg % node.name, node.func_lineno, node.source_file)
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
@linter.ensure_fundamentals
|
|
@@ -76,9 +76,9 @@ def check_that_end_is_end(graph):
|
|
|
76
76
|
node = graph["end"]
|
|
77
77
|
|
|
78
78
|
if node.has_tail_next or node.invalid_tail_next:
|
|
79
|
-
raise LintWarn(msg0, node.tail_next_lineno)
|
|
79
|
+
raise LintWarn(msg0, node.tail_next_lineno, node.source_file)
|
|
80
80
|
if node.num_args > 1:
|
|
81
|
-
raise LintWarn(msg1, node.tail_next_lineno)
|
|
81
|
+
raise LintWarn(msg1, node.tail_next_lineno, node.source_file)
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
@linter.ensure_fundamentals
|
|
@@ -90,7 +90,7 @@ def check_step_names(graph):
|
|
|
90
90
|
)
|
|
91
91
|
for node in graph:
|
|
92
92
|
if re.search("[^a-z0-9_]", node.name) or node.name[0] == "_":
|
|
93
|
-
raise LintWarn(msg.format(node), node.func_lineno)
|
|
93
|
+
raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
@linter.ensure_fundamentals
|
|
@@ -108,11 +108,11 @@ def check_num_args(graph):
|
|
|
108
108
|
msg2 = "Step *{0.name}* is missing the 'self' argument."
|
|
109
109
|
for node in graph:
|
|
110
110
|
if node.num_args > 2:
|
|
111
|
-
raise LintWarn(msg0.format(node), node.func_lineno)
|
|
111
|
+
raise LintWarn(msg0.format(node), node.func_lineno, node.source_file)
|
|
112
112
|
elif node.num_args == 2 and node.type != "join":
|
|
113
|
-
raise LintWarn(msg1.format(node), node.func_lineno)
|
|
113
|
+
raise LintWarn(msg1.format(node), node.func_lineno, node.source_file)
|
|
114
114
|
elif node.num_args == 0:
|
|
115
|
-
raise LintWarn(msg2.format(node), node.func_lineno)
|
|
115
|
+
raise LintWarn(msg2.format(node), node.func_lineno, node.source_file)
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
@linter.ensure_static_graph
|
|
@@ -125,7 +125,7 @@ def check_static_transitions(graph):
|
|
|
125
125
|
)
|
|
126
126
|
for node in graph:
|
|
127
127
|
if node.type != "end" and not node.has_tail_next:
|
|
128
|
-
raise LintWarn(msg.format(node), node.func_lineno)
|
|
128
|
+
raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
@linter.ensure_static_graph
|
|
@@ -134,11 +134,17 @@ def check_valid_transitions(graph):
|
|
|
134
134
|
msg = (
|
|
135
135
|
"Step *{0.name}* specifies an invalid self.next() transition. "
|
|
136
136
|
"Make sure the self.next() expression matches with one of the "
|
|
137
|
-
"supported transition types
|
|
137
|
+
"supported transition types:\n"
|
|
138
|
+
" • Linear: self.next(self.step_name)\n"
|
|
139
|
+
" • Fan-out: self.next(self.step1, self.step2, ...)\n"
|
|
140
|
+
" • Foreach: self.next(self.step, foreach='variable')\n"
|
|
141
|
+
" • Switch: self.next({{\"key\": self.step, ...}}, condition='variable')\n\n"
|
|
142
|
+
"For switch statements, keys must be string literals, numbers or config expressions "
|
|
143
|
+
"(self.config.key_name), not variables."
|
|
138
144
|
)
|
|
139
145
|
for node in graph:
|
|
140
146
|
if node.type != "end" and node.has_tail_next and node.invalid_tail_next:
|
|
141
|
-
raise LintWarn(msg.format(node), node.tail_next_lineno)
|
|
147
|
+
raise LintWarn(msg.format(node), node.tail_next_lineno, node.source_file)
|
|
142
148
|
|
|
143
149
|
|
|
144
150
|
@linter.ensure_static_graph
|
|
@@ -151,7 +157,11 @@ def check_unknown_transitions(graph):
|
|
|
151
157
|
for node in graph:
|
|
152
158
|
unknown = [n for n in node.out_funcs if n not in graph]
|
|
153
159
|
if unknown:
|
|
154
|
-
raise LintWarn(
|
|
160
|
+
raise LintWarn(
|
|
161
|
+
msg.format(node, step=unknown[0]),
|
|
162
|
+
node.tail_next_lineno,
|
|
163
|
+
node.source_file,
|
|
164
|
+
)
|
|
155
165
|
|
|
156
166
|
|
|
157
167
|
@linter.ensure_acyclicity
|
|
@@ -165,9 +175,13 @@ def check_for_acyclicity(graph):
|
|
|
165
175
|
|
|
166
176
|
def check_path(node, seen):
|
|
167
177
|
for n in node.out_funcs:
|
|
178
|
+
if node.type == "split-switch" and n == node.name:
|
|
179
|
+
continue
|
|
168
180
|
if n in seen:
|
|
169
181
|
path = "->".join(seen + [n])
|
|
170
|
-
raise LintWarn(
|
|
182
|
+
raise LintWarn(
|
|
183
|
+
msg.format(path), node.tail_next_lineno, node.source_file
|
|
184
|
+
)
|
|
171
185
|
else:
|
|
172
186
|
check_path(graph[n], seen + [n])
|
|
173
187
|
|
|
@@ -195,7 +209,7 @@ def check_for_orphans(graph):
|
|
|
195
209
|
orphans = nodeset - seen
|
|
196
210
|
if orphans:
|
|
197
211
|
orphan = graph[list(orphans)[0]]
|
|
198
|
-
raise LintWarn(msg.format(orphan), orphan.func_lineno)
|
|
212
|
+
raise LintWarn(msg.format(orphan), orphan.func_lineno, orphan.source_file)
|
|
199
213
|
|
|
200
214
|
|
|
201
215
|
@linter.ensure_static_graph
|
|
@@ -226,26 +240,48 @@ def check_split_join_balance(graph):
|
|
|
226
240
|
new_stack = split_stack
|
|
227
241
|
elif node.type in ("split", "foreach"):
|
|
228
242
|
new_stack = split_stack + [("split", node.out_funcs)]
|
|
243
|
+
elif node.type == "split-switch":
|
|
244
|
+
# For a switch, continue traversal down each path with the same stack
|
|
245
|
+
for n in node.out_funcs:
|
|
246
|
+
if node.type == "split-switch" and n == node.name:
|
|
247
|
+
continue
|
|
248
|
+
traverse(graph[n], split_stack)
|
|
249
|
+
return
|
|
229
250
|
elif node.type == "end":
|
|
251
|
+
new_stack = split_stack
|
|
230
252
|
if split_stack:
|
|
231
253
|
_, split_roots = split_stack.pop()
|
|
232
254
|
roots = ", ".join(split_roots)
|
|
233
|
-
raise LintWarn(
|
|
255
|
+
raise LintWarn(
|
|
256
|
+
msg0.format(roots=roots), node.func_lineno, node.source_file
|
|
257
|
+
)
|
|
234
258
|
elif node.type == "join":
|
|
259
|
+
new_stack = split_stack
|
|
235
260
|
if split_stack:
|
|
236
261
|
_, split_roots = split_stack[-1]
|
|
237
262
|
new_stack = split_stack[:-1]
|
|
238
|
-
|
|
239
|
-
|
|
263
|
+
|
|
264
|
+
# Resolve each incoming function to its root branch from the split.
|
|
265
|
+
resolved_branches = set(
|
|
266
|
+
graph[n].split_branches[-1] for n in node.in_funcs
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# compares the set of resolved branches against the expected branches
|
|
270
|
+
# from the split.
|
|
271
|
+
if len(resolved_branches) != len(
|
|
272
|
+
split_roots
|
|
273
|
+
) or resolved_branches ^ set(split_roots):
|
|
274
|
+
paths = ", ".join(resolved_branches)
|
|
240
275
|
roots = ", ".join(split_roots)
|
|
241
276
|
raise LintWarn(
|
|
242
277
|
msg1.format(
|
|
243
278
|
node, paths=paths, num_roots=len(split_roots), roots=roots
|
|
244
279
|
),
|
|
245
280
|
node.func_lineno,
|
|
281
|
+
node.source_file,
|
|
246
282
|
)
|
|
247
283
|
else:
|
|
248
|
-
raise LintWarn(msg2.format(node), node.func_lineno)
|
|
284
|
+
raise LintWarn(msg2.format(node), node.func_lineno, node.source_file)
|
|
249
285
|
|
|
250
286
|
# check that incoming steps come from the same lineage
|
|
251
287
|
# (no cross joins)
|
|
@@ -256,14 +292,56 @@ def check_split_join_balance(graph):
|
|
|
256
292
|
return tuple(graph[n].split_parents)
|
|
257
293
|
|
|
258
294
|
if not all_equal(map(parents, node.in_funcs)):
|
|
259
|
-
raise LintWarn(msg3.format(node), node.func_lineno)
|
|
295
|
+
raise LintWarn(msg3.format(node), node.func_lineno, node.source_file)
|
|
296
|
+
else:
|
|
297
|
+
new_stack = split_stack
|
|
260
298
|
|
|
261
299
|
for n in node.out_funcs:
|
|
300
|
+
if node.type == "split-switch" and n == node.name:
|
|
301
|
+
continue
|
|
262
302
|
traverse(graph[n], new_stack)
|
|
263
303
|
|
|
264
304
|
traverse(graph["start"], [])
|
|
265
305
|
|
|
266
306
|
|
|
307
|
+
@linter.ensure_static_graph
|
|
308
|
+
@linter.check
|
|
309
|
+
def check_switch_splits(graph):
|
|
310
|
+
"""Check conditional split constraints"""
|
|
311
|
+
msg0 = (
|
|
312
|
+
"Step *{0.name}* is a switch split but defines {num} transitions. "
|
|
313
|
+
"Switch splits must define at least 2 transitions."
|
|
314
|
+
)
|
|
315
|
+
msg1 = "Step *{0.name}* is a switch split but has no condition variable."
|
|
316
|
+
msg2 = "Step *{0.name}* is a switch split but has no switch cases defined."
|
|
317
|
+
|
|
318
|
+
for node in graph:
|
|
319
|
+
if node.type == "split-switch":
|
|
320
|
+
# Check at least 2 outputs
|
|
321
|
+
if len(node.out_funcs) < 2:
|
|
322
|
+
raise LintWarn(
|
|
323
|
+
msg0.format(node, num=len(node.out_funcs)),
|
|
324
|
+
node.func_lineno,
|
|
325
|
+
node.source_file,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Check condition exists
|
|
329
|
+
if not node.condition:
|
|
330
|
+
raise LintWarn(
|
|
331
|
+
msg1.format(node),
|
|
332
|
+
node.func_lineno,
|
|
333
|
+
node.source_file,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
# Check switch cases exist
|
|
337
|
+
if not node.switch_cases:
|
|
338
|
+
raise LintWarn(
|
|
339
|
+
msg2.format(node),
|
|
340
|
+
node.func_lineno,
|
|
341
|
+
node.source_file,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
|
|
267
345
|
@linter.ensure_static_graph
|
|
268
346
|
@linter.check
|
|
269
347
|
def check_empty_foreaches(graph):
|
|
@@ -276,7 +354,9 @@ def check_empty_foreaches(graph):
|
|
|
276
354
|
if node.type == "foreach":
|
|
277
355
|
joins = [n for n in node.out_funcs if graph[n].type == "join"]
|
|
278
356
|
if joins:
|
|
279
|
-
raise LintWarn(
|
|
357
|
+
raise LintWarn(
|
|
358
|
+
msg.format(node, join=joins[0]), node.func_lineno, node.source_file
|
|
359
|
+
)
|
|
280
360
|
|
|
281
361
|
|
|
282
362
|
@linter.ensure_static_graph
|
|
@@ -290,7 +370,22 @@ def check_parallel_step_after_next(graph):
|
|
|
290
370
|
if node.parallel_foreach and not all(
|
|
291
371
|
graph[out_node].parallel_step for out_node in node.out_funcs
|
|
292
372
|
):
|
|
293
|
-
raise LintWarn(msg.format(node))
|
|
373
|
+
raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@linter.ensure_static_graph
|
|
377
|
+
@linter.check
|
|
378
|
+
def check_join_followed_by_parallel_step(graph):
|
|
379
|
+
msg = (
|
|
380
|
+
"An @parallel step should be followed by a join step. Step *{0}* is called "
|
|
381
|
+
"after an @parallel step but is not a join step. Please add an extra `inputs` "
|
|
382
|
+
"argument to the step."
|
|
383
|
+
)
|
|
384
|
+
for node in graph:
|
|
385
|
+
if node.parallel_step and not graph[node.out_funcs[0]].type == "join":
|
|
386
|
+
raise LintWarn(
|
|
387
|
+
msg.format(node.out_funcs[0]), node.func_lineno, node.source_file
|
|
388
|
+
)
|
|
294
389
|
|
|
295
390
|
|
|
296
391
|
@linter.ensure_static_graph
|
|
@@ -305,7 +400,9 @@ def check_parallel_foreach_calls_parallel_step(graph):
|
|
|
305
400
|
for node2 in graph:
|
|
306
401
|
if node2.out_funcs and node.name in node2.out_funcs:
|
|
307
402
|
if not node2.parallel_foreach:
|
|
308
|
-
raise LintWarn(
|
|
403
|
+
raise LintWarn(
|
|
404
|
+
msg.format(node, node2), node.func_lineno, node.source_file
|
|
405
|
+
)
|
|
309
406
|
|
|
310
407
|
|
|
311
408
|
@linter.ensure_non_nested_foreach
|
|
@@ -318,4 +415,26 @@ def check_nested_foreach(graph):
|
|
|
318
415
|
for node in graph:
|
|
319
416
|
if node.type == "foreach":
|
|
320
417
|
if any(graph[p].type == "foreach" for p in node.split_parents):
|
|
321
|
-
raise LintWarn(msg.format(node))
|
|
418
|
+
raise LintWarn(msg.format(node), node.func_lineno, node.source_file)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@linter.ensure_static_graph
|
|
422
|
+
@linter.check
|
|
423
|
+
def check_ambiguous_joins(graph):
|
|
424
|
+
for node in graph:
|
|
425
|
+
if node.type == "join":
|
|
426
|
+
problematic_parents = [
|
|
427
|
+
p_name
|
|
428
|
+
for p_name in node.in_funcs
|
|
429
|
+
if graph[p_name].type == "split-switch"
|
|
430
|
+
]
|
|
431
|
+
if problematic_parents:
|
|
432
|
+
msg = (
|
|
433
|
+
"A conditional path cannot lead directly to a join step.\n"
|
|
434
|
+
"In your conditional step(s) {parents}, one or more of the possible paths transition directly to the join step {join_name}.\n"
|
|
435
|
+
"As a workaround, please introduce an intermediate, unconditional step on that specific path before joining."
|
|
436
|
+
).format(
|
|
437
|
+
parents=", ".join("*%s*" % p for p in problematic_parents),
|
|
438
|
+
join_name="*%s*" % node.name,
|
|
439
|
+
)
|
|
440
|
+
raise LintWarn(msg, node.func_lineno, node.source_file)
|
metaflow/meta_files.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
_UNINITIALIZED = object()
|
|
2
|
+
_info_file_content = _UNINITIALIZED
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_info_file():
|
|
6
|
+
# Prevent circular import
|
|
7
|
+
from .packaging_sys import MetaflowCodeContent
|
|
8
|
+
|
|
9
|
+
global _info_file_content
|
|
10
|
+
|
|
11
|
+
if id(_info_file_content) == id(_UNINITIALIZED):
|
|
12
|
+
_info_file_content = MetaflowCodeContent.get_info()
|
|
13
|
+
return _info_file_content
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import time
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
2
5
|
import requests
|
|
3
|
-
import json
|
|
4
6
|
|
|
5
|
-
from threading import Thread
|
|
6
|
-
from metaflow.sidecar import MessageTypes, Message
|
|
7
|
-
from metaflow.metaflow_config import SERVICE_HEADERS
|
|
8
7
|
from metaflow.exception import MetaflowException
|
|
8
|
+
from metaflow.metaflow_config import SERVICE_HEADERS
|
|
9
|
+
from metaflow.sidecar import Message, MessageTypes
|
|
9
10
|
|
|
10
11
|
HB_URL_KEY = "hb_url"
|
|
11
12
|
|
|
@@ -51,14 +52,29 @@ class MetadataHeartBeat(object):
|
|
|
51
52
|
time.sleep(frequency_secs)
|
|
52
53
|
retry_counter = 0
|
|
53
54
|
except HeartBeatException as e:
|
|
55
|
+
print(e)
|
|
54
56
|
retry_counter = retry_counter + 1
|
|
55
|
-
time.sleep(
|
|
57
|
+
time.sleep(1.5**retry_counter)
|
|
56
58
|
|
|
57
59
|
def _heartbeat(self):
|
|
58
60
|
if self.hb_url is not None:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
try:
|
|
62
|
+
response = requests.post(
|
|
63
|
+
url=self.hb_url, data="{}", headers=self.headers.copy()
|
|
64
|
+
)
|
|
65
|
+
except requests.exceptions.ConnectionError as e:
|
|
66
|
+
raise HeartBeatException(
|
|
67
|
+
"HeartBeat request (%s) failed" " (ConnectionError)" % (self.hb_url)
|
|
68
|
+
)
|
|
69
|
+
except requests.exceptions.Timeout as e:
|
|
70
|
+
raise HeartBeatException(
|
|
71
|
+
"HeartBeat request (%s) failed" " (Timeout)" % (self.hb_url)
|
|
72
|
+
)
|
|
73
|
+
except requests.exceptions.RequestException as e:
|
|
74
|
+
raise HeartBeatException(
|
|
75
|
+
"HeartBeat request (%s) failed"
|
|
76
|
+
" (RequestException) %s" % (self.hb_url, str(e))
|
|
77
|
+
)
|
|
62
78
|
# Unfortunately, response.json() returns a string that we need
|
|
63
79
|
# to cast to json; however when the request encounters an error
|
|
64
80
|
# the return type is a json blob :/
|
|
@@ -5,6 +5,7 @@ import time
|
|
|
5
5
|
from collections import namedtuple
|
|
6
6
|
from itertools import chain
|
|
7
7
|
|
|
8
|
+
from typing import List
|
|
8
9
|
from metaflow.exception import MetaflowInternalError, MetaflowTaggingError
|
|
9
10
|
from metaflow.tagging_util import validate_tag
|
|
10
11
|
from metaflow.util import get_username, resolve_identity_as_tuple, is_stringish
|
|
@@ -76,6 +77,12 @@ class ObjectOrder:
|
|
|
76
77
|
|
|
77
78
|
@with_metaclass(MetadataProviderMeta)
|
|
78
79
|
class MetadataProvider(object):
|
|
80
|
+
TYPE = None
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def metadata_str(cls):
|
|
84
|
+
return "%s@%s" % (cls.TYPE, cls.INFO)
|
|
85
|
+
|
|
79
86
|
@classmethod
|
|
80
87
|
def compute_info(cls, val):
|
|
81
88
|
"""
|
|
@@ -623,6 +630,25 @@ class MetadataProvider(object):
|
|
|
623
630
|
sys_info["r_version"] = env["r_version_code"]
|
|
624
631
|
return sys_info
|
|
625
632
|
|
|
633
|
+
def _get_git_info_as_dict(self):
|
|
634
|
+
git_info = {}
|
|
635
|
+
# NOTE: For flows executing remotely, we want to read from the INFO file of the code package that contains
|
|
636
|
+
# information on the original environment that deployed the flow.
|
|
637
|
+
# Otherwise git related info will be missing, as the repository is not part of the codepackage.
|
|
638
|
+
from metaflow.packaging_sys import MetaflowCodeContent
|
|
639
|
+
|
|
640
|
+
env = MetaflowCodeContent.get_info() or self._environment.get_environment_info()
|
|
641
|
+
for key in [
|
|
642
|
+
"repo_url",
|
|
643
|
+
"branch_name",
|
|
644
|
+
"commit_sha",
|
|
645
|
+
"has_uncommitted_changes",
|
|
646
|
+
]:
|
|
647
|
+
if key in env and env[key]:
|
|
648
|
+
git_info[key] = env[key]
|
|
649
|
+
|
|
650
|
+
return git_info
|
|
651
|
+
|
|
626
652
|
def _get_system_tags(self):
|
|
627
653
|
"""Convert system info dictionary into a list of system tags"""
|
|
628
654
|
return [
|
|
@@ -653,19 +679,78 @@ class MetadataProvider(object):
|
|
|
653
679
|
if code_sha:
|
|
654
680
|
code_url = os.environ.get("METAFLOW_CODE_URL")
|
|
655
681
|
code_ds = os.environ.get("METAFLOW_CODE_DS")
|
|
682
|
+
code_metadata = os.environ.get("METAFLOW_CODE_METADATA")
|
|
656
683
|
metadata.append(
|
|
657
684
|
MetaDatum(
|
|
658
685
|
field="code-package",
|
|
659
686
|
value=json.dumps(
|
|
660
|
-
{
|
|
687
|
+
{
|
|
688
|
+
"ds_type": code_ds,
|
|
689
|
+
"sha": code_sha,
|
|
690
|
+
"location": code_url,
|
|
691
|
+
"metadata": code_metadata,
|
|
692
|
+
}
|
|
661
693
|
),
|
|
662
694
|
type="code-package",
|
|
663
695
|
tags=["attempt_id:{0}".format(attempt)],
|
|
664
696
|
)
|
|
665
697
|
)
|
|
698
|
+
# Add script name as metadata
|
|
699
|
+
script_name = self._environment.get_environment_info()["script"]
|
|
700
|
+
metadata.append(
|
|
701
|
+
MetaDatum(
|
|
702
|
+
field="script-name",
|
|
703
|
+
value=script_name,
|
|
704
|
+
type="script-name",
|
|
705
|
+
tags=["attempt_id:{0}".format(attempt)],
|
|
706
|
+
)
|
|
707
|
+
)
|
|
708
|
+
# And add git metadata
|
|
709
|
+
git_info = self._get_git_info_as_dict()
|
|
710
|
+
if git_info:
|
|
711
|
+
metadata.append(
|
|
712
|
+
MetaDatum(
|
|
713
|
+
field="git-info",
|
|
714
|
+
value=json.dumps(git_info),
|
|
715
|
+
type="git-info",
|
|
716
|
+
tags=["attempt_id:{0}".format(attempt)],
|
|
717
|
+
)
|
|
718
|
+
)
|
|
666
719
|
if metadata:
|
|
667
720
|
self.register_metadata(run_id, step_name, task_id, metadata)
|
|
668
721
|
|
|
722
|
+
@classmethod
|
|
723
|
+
def filter_tasks_by_metadata(
|
|
724
|
+
cls,
|
|
725
|
+
flow_name: str,
|
|
726
|
+
run_id: str,
|
|
727
|
+
step_name: str,
|
|
728
|
+
field_name: str,
|
|
729
|
+
pattern: str,
|
|
730
|
+
) -> List[str]:
|
|
731
|
+
"""
|
|
732
|
+
Filter tasks by metadata field and pattern, returning task pathspecs that match criteria.
|
|
733
|
+
|
|
734
|
+
Parameters
|
|
735
|
+
----------
|
|
736
|
+
flow_name : str
|
|
737
|
+
Flow name, that the run belongs to.
|
|
738
|
+
run_id: str
|
|
739
|
+
Run id, together with flow_id, that identifies the specific Run whose tasks to query
|
|
740
|
+
step_name: str
|
|
741
|
+
Step name to query tasks from
|
|
742
|
+
field_name: str
|
|
743
|
+
Metadata field name to query
|
|
744
|
+
pattern: str
|
|
745
|
+
Pattern to match in metadata field value
|
|
746
|
+
|
|
747
|
+
Returns
|
|
748
|
+
-------
|
|
749
|
+
List[str]
|
|
750
|
+
List of task pathspecs that satisfy the query
|
|
751
|
+
"""
|
|
752
|
+
raise NotImplementedError()
|
|
753
|
+
|
|
669
754
|
@staticmethod
|
|
670
755
|
def _apply_filter(elts, filters):
|
|
671
756
|
if filters is None:
|