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
|
@@ -7,6 +7,7 @@ from hashlib import sha1
|
|
|
7
7
|
from metaflow import current, decorators
|
|
8
8
|
from metaflow._vendor import click
|
|
9
9
|
from metaflow.exception import MetaflowException, MetaflowInternalError
|
|
10
|
+
from metaflow.metaflow_config import FEAT_ALWAYS_UPLOAD_CODE_PACKAGE
|
|
10
11
|
from metaflow.package import MetaflowPackage
|
|
11
12
|
from metaflow.plugins.aws.step_functions.production_token import (
|
|
12
13
|
load_token,
|
|
@@ -283,24 +284,35 @@ def make_flow(
|
|
|
283
284
|
):
|
|
284
285
|
# Attach @kubernetes.
|
|
285
286
|
decorators._attach_decorators(obj.flow, [KubernetesDecorator.name])
|
|
287
|
+
decorators._init(obj.flow)
|
|
286
288
|
|
|
287
289
|
decorators._init_step_decorators(
|
|
288
290
|
obj.flow, obj.graph, obj.environment, obj.flow_datastore, obj.logger
|
|
289
291
|
)
|
|
290
|
-
|
|
292
|
+
obj.graph = obj.flow._graph
|
|
291
293
|
# Save the code package in the flow datastore so that both user code and
|
|
292
294
|
# metaflow package can be retrieved during workflow execution.
|
|
293
295
|
obj.package = MetaflowPackage(
|
|
294
|
-
obj.flow,
|
|
296
|
+
obj.flow,
|
|
297
|
+
obj.environment,
|
|
298
|
+
obj.echo,
|
|
299
|
+
suffixes=obj.package_suffixes,
|
|
300
|
+
flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
|
|
295
301
|
)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
302
|
+
# This blocks until the package is created
|
|
303
|
+
if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE:
|
|
304
|
+
package_url = obj.package.package_url()
|
|
305
|
+
package_sha = obj.package.package_sha()
|
|
306
|
+
else:
|
|
307
|
+
package_url, package_sha = obj.flow_datastore.save_data(
|
|
308
|
+
[obj.package.blob], len_hint=1
|
|
309
|
+
)[0]
|
|
299
310
|
|
|
300
311
|
return Airflow(
|
|
301
312
|
dag_name,
|
|
302
313
|
obj.graph,
|
|
303
314
|
obj.flow,
|
|
315
|
+
obj.package.package_metadata,
|
|
304
316
|
package_sha,
|
|
305
317
|
package_url,
|
|
306
318
|
obj.metadata,
|
|
@@ -389,6 +401,11 @@ def _validate_workflow(flow, graph, flow_datastore, metadata, workflow_timeout):
|
|
|
389
401
|
"Step *%s* is marked for execution on AWS Batch with Airflow which isn't currently supported."
|
|
390
402
|
% node.name
|
|
391
403
|
)
|
|
404
|
+
if any([d.name == "slurm" for d in node.decorators]):
|
|
405
|
+
raise NotSupportedException(
|
|
406
|
+
"Step *%s* is marked for execution on Slurm with Airflow which isn't currently supported."
|
|
407
|
+
% node.name
|
|
408
|
+
)
|
|
392
409
|
SUPPORTED_DATASTORES = ("azure", "s3", "gs")
|
|
393
410
|
if flow_datastore.TYPE not in SUPPORTED_DATASTORES:
|
|
394
411
|
raise AirflowException(
|
|
@@ -567,9 +567,11 @@ class AirflowTask(object):
|
|
|
567
567
|
return cls(
|
|
568
568
|
task_dict["name"],
|
|
569
569
|
is_mapper_node=is_mapper_node,
|
|
570
|
-
operator_type=
|
|
571
|
-
|
|
572
|
-
|
|
570
|
+
operator_type=(
|
|
571
|
+
task_dict["operator_type"]
|
|
572
|
+
if "operator_type" in task_dict
|
|
573
|
+
else "kubernetes"
|
|
574
|
+
),
|
|
573
575
|
flow_name=flow_name,
|
|
574
576
|
flow_contains_foreach=flow_contains_foreach,
|
|
575
577
|
).set_operator_args(**op_args)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
from metaflow.decorators import FlowDecorator
|
|
2
|
+
from metaflow.decorators import FlowDecorator, flow_decorators
|
|
3
3
|
from ..exception import AirflowException
|
|
4
4
|
from ..airflow_utils import AirflowTask, id_creator, TASK_ID_HASH_LEN
|
|
5
5
|
|
|
@@ -49,7 +49,7 @@ class AirflowSensorDecorator(FlowDecorator):
|
|
|
49
49
|
operator_type=self.operator_type,
|
|
50
50
|
).set_operator_args(**{k: v for k, v in task_args.items() if v is not None})
|
|
51
51
|
|
|
52
|
-
def validate(self):
|
|
52
|
+
def validate(self, flow):
|
|
53
53
|
"""
|
|
54
54
|
Validate if the arguments for the sensor are correct.
|
|
55
55
|
"""
|
|
@@ -58,7 +58,7 @@ class AirflowSensorDecorator(FlowDecorator):
|
|
|
58
58
|
if self.attributes["name"] is None:
|
|
59
59
|
deco_index = [
|
|
60
60
|
d._id
|
|
61
|
-
for d in
|
|
61
|
+
for d in flow_decorators(flow)
|
|
62
62
|
if issubclass(d.__class__, AirflowSensorDecorator)
|
|
63
63
|
].index(self._id)
|
|
64
64
|
self._airflow_task_name = "%s-%s" % (
|
|
@@ -71,4 +71,4 @@ class AirflowSensorDecorator(FlowDecorator):
|
|
|
71
71
|
def flow_init(
|
|
72
72
|
self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
|
|
73
73
|
):
|
|
74
|
-
self.validate()
|
|
74
|
+
self.validate(flow)
|
|
@@ -85,7 +85,7 @@ class ExternalTaskSensorDecorator(AirflowSensorDecorator):
|
|
|
85
85
|
)
|
|
86
86
|
return task_args
|
|
87
87
|
|
|
88
|
-
def validate(self):
|
|
88
|
+
def validate(self, flow):
|
|
89
89
|
if self.attributes["external_dag_id"] is None:
|
|
90
90
|
raise AirflowException(
|
|
91
91
|
"`%s` argument of `@%s`cannot be `None`."
|
|
@@ -131,4 +131,4 @@ class ExternalTaskSensorDecorator(AirflowSensorDecorator):
|
|
|
131
131
|
self.name,
|
|
132
132
|
)
|
|
133
133
|
)
|
|
134
|
-
super().validate()
|
|
134
|
+
super().validate(flow)
|
|
@@ -58,9 +58,9 @@ class S3KeySensorDecorator(AirflowSensorDecorator):
|
|
|
58
58
|
# `verify` is a airflow variable.
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
def validate(self):
|
|
61
|
+
def validate(self, flow):
|
|
62
62
|
if self.attributes["bucket_key"] is None:
|
|
63
63
|
raise AirflowException(
|
|
64
64
|
"`bucket_key` for `@%s`cannot be empty." % (self.name)
|
|
65
65
|
)
|
|
66
|
-
super().validate()
|
|
66
|
+
super().validate(flow)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
2
|
|
|
3
|
+
from metaflow.metaflow_config import ARGO_EVENTS_SENSOR_NAMESPACE
|
|
5
4
|
from metaflow.exception import MetaflowException
|
|
6
5
|
from metaflow.plugins.kubernetes.kubernetes_client import KubernetesClient
|
|
7
6
|
|
|
@@ -10,6 +9,14 @@ class ArgoClientException(MetaflowException):
|
|
|
10
9
|
headline = "Argo Client error"
|
|
11
10
|
|
|
12
11
|
|
|
12
|
+
class ArgoResourceNotFound(MetaflowException):
|
|
13
|
+
headline = "Resource not found"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ArgoNotPermitted(MetaflowException):
|
|
17
|
+
headline = "Operation not permitted"
|
|
18
|
+
|
|
19
|
+
|
|
13
20
|
class ArgoClient(object):
|
|
14
21
|
def __init__(self, namespace=None):
|
|
15
22
|
self._client = KubernetesClient()
|
|
@@ -52,21 +59,38 @@ class ArgoClient(object):
|
|
|
52
59
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
53
60
|
)
|
|
54
61
|
|
|
55
|
-
def get_workflow_templates(self):
|
|
62
|
+
def get_workflow_templates(self, page_size=100):
|
|
56
63
|
client = self._client.get()
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
continue_token = None
|
|
65
|
+
|
|
66
|
+
while True:
|
|
67
|
+
try:
|
|
68
|
+
params = {"limit": page_size}
|
|
69
|
+
if continue_token:
|
|
70
|
+
params["_continue"] = continue_token
|
|
71
|
+
|
|
72
|
+
response = client.CustomObjectsApi().list_namespaced_custom_object(
|
|
73
|
+
group=self._group,
|
|
74
|
+
version=self._version,
|
|
75
|
+
namespace=self._namespace,
|
|
76
|
+
plural="workflowtemplates",
|
|
77
|
+
**params,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
for item in response.get("items", []):
|
|
81
|
+
yield item
|
|
82
|
+
|
|
83
|
+
metadata = response.get("metadata", {})
|
|
84
|
+
continue_token = metadata.get("continue")
|
|
85
|
+
|
|
86
|
+
if not continue_token:
|
|
87
|
+
break
|
|
88
|
+
except client.rest.ApiException as e:
|
|
89
|
+
if e.status == 404:
|
|
90
|
+
return None
|
|
91
|
+
raise ArgoClientException(
|
|
92
|
+
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
93
|
+
)
|
|
70
94
|
|
|
71
95
|
def register_workflow_template(self, name, workflow_template):
|
|
72
96
|
# Unfortunately, Kubernetes client does not handle optimistic
|
|
@@ -140,9 +164,7 @@ class ArgoClient(object):
|
|
|
140
164
|
if e.status == 404:
|
|
141
165
|
return None
|
|
142
166
|
else:
|
|
143
|
-
raise
|
|
144
|
-
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
145
|
-
)
|
|
167
|
+
raise wrap_api_error(e)
|
|
146
168
|
|
|
147
169
|
def delete_workflow_template(self, name):
|
|
148
170
|
"""
|
|
@@ -164,9 +186,7 @@ class ArgoClient(object):
|
|
|
164
186
|
if e.status == 404:
|
|
165
187
|
return None
|
|
166
188
|
else:
|
|
167
|
-
raise
|
|
168
|
-
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
169
|
-
)
|
|
189
|
+
raise wrap_api_error(e)
|
|
170
190
|
|
|
171
191
|
def terminate_workflow(self, name):
|
|
172
192
|
client = self._client.get()
|
|
@@ -254,12 +274,19 @@ class ArgoClient(object):
|
|
|
254
274
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
255
275
|
)
|
|
256
276
|
|
|
257
|
-
def trigger_workflow_template(self, name, parameters={}):
|
|
277
|
+
def trigger_workflow_template(self, name, usertype, username, parameters={}):
|
|
258
278
|
client = self._client.get()
|
|
259
279
|
body = {
|
|
260
280
|
"apiVersion": "argoproj.io/v1alpha1",
|
|
261
281
|
"kind": "Workflow",
|
|
262
|
-
"metadata": {
|
|
282
|
+
"metadata": {
|
|
283
|
+
"generateName": name + "-",
|
|
284
|
+
"annotations": {
|
|
285
|
+
"metaflow/triggered_by_user": json.dumps(
|
|
286
|
+
{"type": usertype, "name": username}
|
|
287
|
+
)
|
|
288
|
+
},
|
|
289
|
+
},
|
|
263
290
|
"spec": {
|
|
264
291
|
"workflowTemplateRef": {"name": name},
|
|
265
292
|
"arguments": {
|
|
@@ -295,7 +322,10 @@ class ArgoClient(object):
|
|
|
295
322
|
"suspend": schedule is None,
|
|
296
323
|
"schedule": schedule,
|
|
297
324
|
"timezone": timezone,
|
|
325
|
+
"failedJobsHistoryLimit": 10000, # default is unfortunately 1
|
|
326
|
+
"successfulJobsHistoryLimit": 10000, # default is unfortunately 3
|
|
298
327
|
"workflowSpec": {"workflowTemplateRef": {"name": name}},
|
|
328
|
+
"startingDeadlineSeconds": 3540, # configuring this to 59 minutes so a failed trigger of cron workflow can succeed at most 59 mins after scheduled execution
|
|
299
329
|
},
|
|
300
330
|
}
|
|
301
331
|
try:
|
|
@@ -349,12 +379,15 @@ class ArgoClient(object):
|
|
|
349
379
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
350
380
|
)
|
|
351
381
|
|
|
352
|
-
def register_sensor(
|
|
382
|
+
def register_sensor(
|
|
383
|
+
self, name, sensor=None, sensor_namespace=ARGO_EVENTS_SENSOR_NAMESPACE
|
|
384
|
+
):
|
|
353
385
|
if sensor is None:
|
|
354
386
|
sensor = {}
|
|
355
387
|
# Unfortunately, Kubernetes client does not handle optimistic
|
|
356
388
|
# concurrency control by itself unlike kubectl
|
|
357
389
|
client = self._client.get()
|
|
390
|
+
|
|
358
391
|
if not sensor:
|
|
359
392
|
sensor["metadata"] = {}
|
|
360
393
|
|
|
@@ -364,7 +397,7 @@ class ArgoClient(object):
|
|
|
364
397
|
] = client.CustomObjectsApi().get_namespaced_custom_object(
|
|
365
398
|
group=self._group,
|
|
366
399
|
version=self._version,
|
|
367
|
-
namespace=
|
|
400
|
+
namespace=sensor_namespace,
|
|
368
401
|
plural="sensors",
|
|
369
402
|
name=name,
|
|
370
403
|
)[
|
|
@@ -379,7 +412,7 @@ class ArgoClient(object):
|
|
|
379
412
|
return client.CustomObjectsApi().create_namespaced_custom_object(
|
|
380
413
|
group=self._group,
|
|
381
414
|
version=self._version,
|
|
382
|
-
namespace=
|
|
415
|
+
namespace=sensor_namespace,
|
|
383
416
|
plural="sensors",
|
|
384
417
|
body=sensor,
|
|
385
418
|
)
|
|
@@ -397,7 +430,7 @@ class ArgoClient(object):
|
|
|
397
430
|
return client.CustomObjectsApi().replace_namespaced_custom_object(
|
|
398
431
|
group=self._group,
|
|
399
432
|
version=self._version,
|
|
400
|
-
namespace=
|
|
433
|
+
namespace=sensor_namespace,
|
|
401
434
|
plural="sensors",
|
|
402
435
|
body=sensor,
|
|
403
436
|
name=name,
|
|
@@ -407,7 +440,7 @@ class ArgoClient(object):
|
|
|
407
440
|
json.loads(e.body)["message"] if e.body is not None else e.reason
|
|
408
441
|
)
|
|
409
442
|
|
|
410
|
-
def delete_sensor(self, name):
|
|
443
|
+
def delete_sensor(self, name, sensor_namespace):
|
|
411
444
|
"""
|
|
412
445
|
Issues an API call for deleting a sensor
|
|
413
446
|
|
|
@@ -419,13 +452,25 @@ class ArgoClient(object):
|
|
|
419
452
|
return client.CustomObjectsApi().delete_namespaced_custom_object(
|
|
420
453
|
group=self._group,
|
|
421
454
|
version=self._version,
|
|
422
|
-
namespace=
|
|
455
|
+
namespace=sensor_namespace,
|
|
423
456
|
plural="sensors",
|
|
424
457
|
name=name,
|
|
425
458
|
)
|
|
426
459
|
except client.rest.ApiException as e:
|
|
427
460
|
if e.status == 404:
|
|
428
461
|
return None
|
|
429
|
-
raise
|
|
430
|
-
|
|
431
|
-
|
|
462
|
+
raise wrap_api_error(e)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def wrap_api_error(error):
|
|
466
|
+
message = (
|
|
467
|
+
json.loads(error.body)["message"] if error.body is not None else error.reason
|
|
468
|
+
)
|
|
469
|
+
# catch all
|
|
470
|
+
ex = ArgoClientException(message)
|
|
471
|
+
if error.status == 404:
|
|
472
|
+
# usually handled outside this function as most cases want to return None instead.
|
|
473
|
+
ex = ArgoResourceNotFound(message)
|
|
474
|
+
if error.status == 403:
|
|
475
|
+
ex = ArgoNotPermitted(message)
|
|
476
|
+
return ex
|
|
@@ -11,6 +11,7 @@ from metaflow.metaflow_config import (
|
|
|
11
11
|
ARGO_EVENTS_WEBHOOK_AUTH,
|
|
12
12
|
ARGO_EVENTS_WEBHOOK_URL,
|
|
13
13
|
SERVICE_HEADERS,
|
|
14
|
+
SERVICE_RETRY_COUNT,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
@@ -127,12 +128,11 @@ class ArgoEvent(object):
|
|
|
127
128
|
headers={"Content-Type": "application/json", **headers},
|
|
128
129
|
data=json.dumps(data).encode("utf-8"),
|
|
129
130
|
)
|
|
130
|
-
retries = 3
|
|
131
|
-
backoff_factor = 2
|
|
132
131
|
|
|
133
|
-
for i in range(
|
|
132
|
+
for i in range(SERVICE_RETRY_COUNT):
|
|
134
133
|
try:
|
|
135
|
-
|
|
134
|
+
# we do not want to wait indefinitely for a response on the event broadcast, as this will keep the task running.
|
|
135
|
+
urllib.request.urlopen(request, timeout=60)
|
|
136
136
|
print(
|
|
137
137
|
"Argo Event (%s) published." % self._name, file=sys.stderr
|
|
138
138
|
)
|
|
@@ -141,10 +141,10 @@ class ArgoEvent(object):
|
|
|
141
141
|
# TODO: Retry retryable HTTP error codes
|
|
142
142
|
raise e
|
|
143
143
|
except urllib.error.URLError as e:
|
|
144
|
-
if i ==
|
|
144
|
+
if i == SERVICE_RETRY_COUNT - 1:
|
|
145
145
|
raise e
|
|
146
146
|
else:
|
|
147
|
-
time.sleep(
|
|
147
|
+
time.sleep(2**i)
|
|
148
148
|
except Exception as e:
|
|
149
149
|
msg = "Unable to publish Argo Event (%s): %s" % (self._name, e)
|
|
150
150
|
if ignore_errors:
|