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,101 @@
|
|
|
1
|
+
from metaflow.exception import MetaflowException
|
|
2
|
+
from metaflow.plugins.secrets.utils import get_default_secrets_backend_type
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SecretSpec:
|
|
6
|
+
def __init__(self, secrets_backend_type, secret_id, options={}, role=None):
|
|
7
|
+
self._secrets_backend_type = secrets_backend_type
|
|
8
|
+
self._secret_id = secret_id
|
|
9
|
+
self._options = options
|
|
10
|
+
self._role = role
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def secrets_backend_type(self):
|
|
14
|
+
return self._secrets_backend_type
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def secret_id(self):
|
|
18
|
+
return self._secret_id
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def options(self):
|
|
22
|
+
return self._options
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def role(self):
|
|
26
|
+
return self._role
|
|
27
|
+
|
|
28
|
+
def to_json(self):
|
|
29
|
+
"""Mainly used for testing... not the same as the input dict in secret_spec_from_dict()!"""
|
|
30
|
+
return {
|
|
31
|
+
"secrets_backend_type": self.secrets_backend_type,
|
|
32
|
+
"secret_id": self.secret_id,
|
|
33
|
+
"options": self.options,
|
|
34
|
+
"role": self.role,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
return "%s (%s)" % (self._secret_id, self._secrets_backend_type)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def secret_spec_from_str(secret_spec_str, role):
|
|
42
|
+
# "." may be used in secret_id one day (provider specific). HOWEVER, it provides the best UX for
|
|
43
|
+
# non-conflicting cases (i.e. for secret ids that don't contain "."). This is true for all AWS
|
|
44
|
+
# Secrets Manager secrets.
|
|
45
|
+
#
|
|
46
|
+
# So we skew heavily optimize for best upfront UX for the present (1/2023).
|
|
47
|
+
#
|
|
48
|
+
# If/when a certain secret backend supports "." secret names, we can figure out a solution at that time.
|
|
49
|
+
# At a minimum, dictionary style secret spec may be used with no code changes (see secret_spec_from_dict()).
|
|
50
|
+
# Other options could be:
|
|
51
|
+
# - accept and document that "." secret_ids don't work in Metaflow (across all possible providers)
|
|
52
|
+
# - add a Metaflow config variable that specifies the separator (default ".")
|
|
53
|
+
# - smarter spec parsing, that errors on secrets that look ambiguous. "aws-secrets-manager.XYZ" could mean:
|
|
54
|
+
# + secret_id "XYZ" in aws-secrets-manager backend, OR
|
|
55
|
+
# + secret_id "aws-secrets-manager.XYZ" default backend (if it is defined).
|
|
56
|
+
# + in this case, user can simply set "azure-key-vault.aws-secrets-manager.XYZ" instead!
|
|
57
|
+
parts = secret_spec_str.split(".", maxsplit=1)
|
|
58
|
+
if len(parts) == 1:
|
|
59
|
+
secrets_backend_type = get_default_secrets_backend_type()
|
|
60
|
+
secret_id = parts[0]
|
|
61
|
+
else:
|
|
62
|
+
secrets_backend_type = parts[0]
|
|
63
|
+
secret_id = parts[1]
|
|
64
|
+
return SecretSpec(
|
|
65
|
+
secrets_backend_type, secret_id=secret_id, options={}, role=role
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def secret_spec_from_dict(secret_spec_dict, role):
|
|
70
|
+
if "type" not in secret_spec_dict:
|
|
71
|
+
secrets_backend_type = get_default_secrets_backend_type()
|
|
72
|
+
else:
|
|
73
|
+
secrets_backend_type = secret_spec_dict["type"]
|
|
74
|
+
if not isinstance(secrets_backend_type, str):
|
|
75
|
+
raise MetaflowException(
|
|
76
|
+
"Bad @secrets specification - 'type' must be a string - found %s"
|
|
77
|
+
% type(secrets_backend_type)
|
|
78
|
+
)
|
|
79
|
+
secret_id = secret_spec_dict.get("id")
|
|
80
|
+
if not isinstance(secret_id, str):
|
|
81
|
+
raise MetaflowException(
|
|
82
|
+
"Bad @secrets specification - 'id' must be a string - found %s"
|
|
83
|
+
% type(secret_id)
|
|
84
|
+
)
|
|
85
|
+
options = secret_spec_dict.get("options", {})
|
|
86
|
+
if not isinstance(options, dict):
|
|
87
|
+
raise MetaflowException(
|
|
88
|
+
"Bad @secrets specification - 'option' must be a dict - found %s"
|
|
89
|
+
% type(options)
|
|
90
|
+
)
|
|
91
|
+
role_for_source = secret_spec_dict.get("role", None)
|
|
92
|
+
if role_for_source is not None:
|
|
93
|
+
if not isinstance(role_for_source, str):
|
|
94
|
+
raise MetaflowException(
|
|
95
|
+
"Bad @secrets specification - 'role' must be a str - found %s"
|
|
96
|
+
% type(role_for_source)
|
|
97
|
+
)
|
|
98
|
+
role = role_for_source
|
|
99
|
+
return SecretSpec(
|
|
100
|
+
secrets_backend_type, secret_id=secret_id, options=options, role=role
|
|
101
|
+
)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
from metaflow.exception import MetaflowException
|
|
4
|
+
|
|
5
|
+
DISALLOWED_SECRETS_ENV_VAR_PREFIXES = ["METAFLOW_"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_default_secrets_backend_type():
|
|
9
|
+
from metaflow.metaflow_config import DEFAULT_SECRETS_BACKEND_TYPE
|
|
10
|
+
|
|
11
|
+
if DEFAULT_SECRETS_BACKEND_TYPE is None:
|
|
12
|
+
raise MetaflowException(
|
|
13
|
+
"No default secrets backend type configured, but needed by @secrets. "
|
|
14
|
+
"Set METAFLOW_DEFAULT_SECRETS_BACKEND_TYPE."
|
|
15
|
+
)
|
|
16
|
+
return DEFAULT_SECRETS_BACKEND_TYPE
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_env_vars_across_secrets(all_secrets_env_vars):
|
|
20
|
+
vars_injected_by = {}
|
|
21
|
+
for secret_spec, env_vars in all_secrets_env_vars:
|
|
22
|
+
for k in env_vars:
|
|
23
|
+
if k in vars_injected_by:
|
|
24
|
+
raise MetaflowException(
|
|
25
|
+
"Secret '%s' will inject '%s' as env var, and it is also added by '%s'"
|
|
26
|
+
% (secret_spec, k, vars_injected_by[k])
|
|
27
|
+
)
|
|
28
|
+
vars_injected_by[k] = secret_spec
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_env_vars_vs_existing_env(all_secrets_env_vars):
|
|
32
|
+
for secret_spec, env_vars in all_secrets_env_vars:
|
|
33
|
+
for k in env_vars:
|
|
34
|
+
if k in os.environ:
|
|
35
|
+
raise MetaflowException(
|
|
36
|
+
"Secret '%s' will inject '%s' as env var, but it already exists in env"
|
|
37
|
+
% (secret_spec, k)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def validate_env_vars(env_vars):
|
|
42
|
+
for k, v in env_vars.items():
|
|
43
|
+
if not isinstance(k, str):
|
|
44
|
+
raise MetaflowException("Found non string key %s (%s)" % (str(k), type(k)))
|
|
45
|
+
if not isinstance(v, str):
|
|
46
|
+
raise MetaflowException(
|
|
47
|
+
"Found non string value %s (%s)" % (str(v), type(v))
|
|
48
|
+
)
|
|
49
|
+
if not re.fullmatch("[a-zA-Z_][a-zA-Z0-9_]*", k):
|
|
50
|
+
raise MetaflowException("Found invalid env var name '%s'." % k)
|
|
51
|
+
for disallowed_prefix in DISALLOWED_SECRETS_ENV_VAR_PREFIXES:
|
|
52
|
+
if k.startswith(disallowed_prefix):
|
|
53
|
+
raise MetaflowException(
|
|
54
|
+
"Found disallowed env var name '%s' (starts with '%s')."
|
|
55
|
+
% (k, disallowed_prefix)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_secrets_backend_provider(secrets_backend_type):
|
|
60
|
+
from metaflow.plugins import SECRETS_PROVIDERS
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
provider_cls = [
|
|
64
|
+
pc for pc in SECRETS_PROVIDERS if pc.TYPE == secrets_backend_type
|
|
65
|
+
][0]
|
|
66
|
+
return provider_cls()
|
|
67
|
+
except IndexError:
|
|
68
|
+
raise MetaflowException(
|
|
69
|
+
"Unknown secrets backend type %s (available types: %s)"
|
|
70
|
+
% (
|
|
71
|
+
secrets_backend_type,
|
|
72
|
+
", ".join(pc.TYPE for pc in SECRETS_PROVIDERS if pc.TYPE != "inline"),
|
|
73
|
+
)
|
|
74
|
+
)
|
metaflow/plugins/tag_cli.py
CHANGED
|
@@ -225,10 +225,7 @@ def _get_client_run_obj(obj, run_id, user_namespace):
|
|
|
225
225
|
|
|
226
226
|
|
|
227
227
|
def _set_current(obj):
|
|
228
|
-
current._set_env(
|
|
229
|
-
metadata_str="%s@%s"
|
|
230
|
-
% (obj.metadata.__class__.TYPE, obj.metadata.__class__.INFO)
|
|
231
|
-
)
|
|
228
|
+
current._set_env(metadata_str=obj.metadata.metadata_str())
|
|
232
229
|
|
|
233
230
|
|
|
234
231
|
@click.group()
|
|
@@ -507,9 +504,9 @@ def tag_list(
|
|
|
507
504
|
|
|
508
505
|
if not group_by_run and not group_by_tag:
|
|
509
506
|
# We list all the runs that match to print them out if needed.
|
|
510
|
-
system_tags_by_some_grouping[
|
|
511
|
-
",
|
|
512
|
-
|
|
507
|
+
system_tags_by_some_grouping[",".join(pathspecs)] = (
|
|
508
|
+
system_tags_by_some_grouping.get("_", set())
|
|
509
|
+
)
|
|
513
510
|
all_tags_by_some_grouping[",".join(pathspecs)] = all_tags_by_some_grouping.get(
|
|
514
511
|
"_", set()
|
|
515
512
|
)
|
|
@@ -8,8 +8,14 @@ import sys
|
|
|
8
8
|
from metaflow.cli_args import cli_args
|
|
9
9
|
from metaflow.decorators import StepDecorator
|
|
10
10
|
from metaflow.exception import MetaflowException
|
|
11
|
-
from metaflow.unbounded_foreach import
|
|
11
|
+
from metaflow.unbounded_foreach import (
|
|
12
|
+
UnboundedForeachInput,
|
|
13
|
+
UBF_CONTROL,
|
|
14
|
+
UBF_TASK,
|
|
15
|
+
CONTROL_TASK_TAG,
|
|
16
|
+
)
|
|
12
17
|
from metaflow.util import to_unicode
|
|
18
|
+
from metaflow.metadata_provider import MetaDatum
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
class InternalTestUnboundedForeachInput(UnboundedForeachInput):
|
|
@@ -50,9 +56,9 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
|
|
|
50
56
|
name = "unbounded_test_foreach_internal"
|
|
51
57
|
results_dict = {}
|
|
52
58
|
|
|
53
|
-
def __init__(self, attributes=None, statically_defined=False):
|
|
59
|
+
def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
|
|
54
60
|
super(InternalTestUnboundedForeachDecorator, self).__init__(
|
|
55
|
-
attributes, statically_defined
|
|
61
|
+
attributes, statically_defined, inserted_by
|
|
56
62
|
)
|
|
57
63
|
|
|
58
64
|
def step_init(
|
|
@@ -60,13 +66,42 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
|
|
|
60
66
|
):
|
|
61
67
|
self.environment = environment
|
|
62
68
|
|
|
69
|
+
def task_pre_step(
|
|
70
|
+
self,
|
|
71
|
+
step_name,
|
|
72
|
+
task_datastore,
|
|
73
|
+
metadata,
|
|
74
|
+
run_id,
|
|
75
|
+
task_id,
|
|
76
|
+
flow,
|
|
77
|
+
graph,
|
|
78
|
+
retry_count,
|
|
79
|
+
max_user_code_retries,
|
|
80
|
+
ubf_context,
|
|
81
|
+
inputs,
|
|
82
|
+
):
|
|
83
|
+
if ubf_context == UBF_CONTROL:
|
|
84
|
+
metadata.register_metadata(
|
|
85
|
+
run_id,
|
|
86
|
+
step_name,
|
|
87
|
+
task_id,
|
|
88
|
+
[
|
|
89
|
+
MetaDatum(
|
|
90
|
+
field="internal_task_type",
|
|
91
|
+
value=CONTROL_TASK_TAG,
|
|
92
|
+
type="internal_task_type",
|
|
93
|
+
tags=["attempt_id:{0}".format(0)],
|
|
94
|
+
)
|
|
95
|
+
],
|
|
96
|
+
)
|
|
97
|
+
self.input_paths = [obj.pathspec for obj in inputs]
|
|
98
|
+
|
|
63
99
|
def control_task_step_func(self, flow, graph, retry_count):
|
|
64
100
|
from metaflow import current
|
|
65
101
|
|
|
66
102
|
run_id = current.run_id
|
|
67
103
|
step_name = current.step_name
|
|
68
104
|
control_task_id = current.task_id
|
|
69
|
-
(_, split_step_name, split_task_id) = control_task_id.split("-")[1:]
|
|
70
105
|
# If we are running inside Conda, we use the base executable FIRST;
|
|
71
106
|
# the conda environment will then be used when runtime_step_cli is
|
|
72
107
|
# called. This is so that it can properly set up all the metaflow
|
|
@@ -94,10 +129,10 @@ class InternalTestUnboundedForeachDecorator(StepDecorator):
|
|
|
94
129
|
mapper_tasks = []
|
|
95
130
|
|
|
96
131
|
for i in range(foreach_num_splits):
|
|
97
|
-
task_id = "%s-%d" % (control_task_id
|
|
132
|
+
task_id = "%s-%d" % (control_task_id, i)
|
|
98
133
|
pathspec = "%s/%s/%s" % (run_id, step_name, task_id)
|
|
99
134
|
mapper_tasks.append(to_unicode(pathspec))
|
|
100
|
-
input_paths = "
|
|
135
|
+
input_paths = ",".join(self.input_paths)
|
|
101
136
|
|
|
102
137
|
# Override specific `step` kwargs.
|
|
103
138
|
kwargs = cli_args.step_kwargs
|
|
@@ -4,6 +4,7 @@ import traceback
|
|
|
4
4
|
from metaflow.exception import MetaflowException
|
|
5
5
|
from metaflow.decorators import StepDecorator
|
|
6
6
|
from metaflow.unbounded_foreach import UBF_CONTROL
|
|
7
|
+
from metaflow.metaflow_config import DEFAULT_RUNTIME_LIMIT
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class TimeoutException(MetaflowException):
|
|
@@ -36,8 +37,7 @@ class TimeoutDecorator(StepDecorator):
|
|
|
36
37
|
name = "timeout"
|
|
37
38
|
defaults = {"seconds": 0, "minutes": 0, "hours": 0}
|
|
38
39
|
|
|
39
|
-
def
|
|
40
|
-
super(TimeoutDecorator, self).__init__(*args, **kwargs)
|
|
40
|
+
def init(self):
|
|
41
41
|
# Initialize secs in __init__ so other decorators could safely use this
|
|
42
42
|
# value without worrying about decorator order.
|
|
43
43
|
# Convert values in attributes to type:int since they can be type:str
|
|
@@ -99,7 +99,7 @@ class TimeoutDecorator(StepDecorator):
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def get_run_time_limit_for_task(step_decos):
|
|
102
|
-
run_time_limit =
|
|
102
|
+
run_time_limit = DEFAULT_RUNTIME_LIMIT
|
|
103
103
|
for deco in step_decos:
|
|
104
104
|
if isinstance(deco, TimeoutDecorator):
|
|
105
105
|
run_time_limit = deco.secs
|
|
File without changes
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from metaflow.util import which
|
|
8
|
+
from metaflow.meta_files import read_info_file
|
|
9
|
+
from metaflow.metaflow_config import get_pinned_conda_libs
|
|
10
|
+
from metaflow.packaging_sys import MetaflowCodeContent, ContentType
|
|
11
|
+
from urllib.request import Request, urlopen
|
|
12
|
+
from urllib.error import URLError
|
|
13
|
+
|
|
14
|
+
# TODO: support version/platform/architecture selection.
|
|
15
|
+
UV_URL = "https://github.com/astral-sh/uv/releases/download/0.6.11/uv-x86_64-unknown-linux-gnu.tar.gz"
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
|
|
19
|
+
def run_cmd(cmd, stdin_str=None):
|
|
20
|
+
result = subprocess.run(
|
|
21
|
+
cmd,
|
|
22
|
+
shell=True,
|
|
23
|
+
input=stdin_str,
|
|
24
|
+
stdout=subprocess.PIPE,
|
|
25
|
+
stderr=subprocess.PIPE,
|
|
26
|
+
text=True,
|
|
27
|
+
)
|
|
28
|
+
if result.returncode != 0:
|
|
29
|
+
print(f"Bootstrap failed while executing: {cmd}")
|
|
30
|
+
print("Stdout:", result.stdout)
|
|
31
|
+
print("Stderr:", result.stderr)
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
def install_uv():
|
|
35
|
+
import tarfile
|
|
36
|
+
|
|
37
|
+
uv_install_path = os.path.join(os.getcwd(), "uv_install")
|
|
38
|
+
if which("uv"):
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
print("Installing uv...")
|
|
42
|
+
|
|
43
|
+
# Prepare directory once
|
|
44
|
+
os.makedirs(uv_install_path, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
# Download and decompress in one go
|
|
47
|
+
headers = {
|
|
48
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
49
|
+
"Connection": "keep-alive",
|
|
50
|
+
"User-Agent": "python-urllib",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def _tar_filter(member: tarfile.TarInfo, path):
|
|
54
|
+
if os.path.basename(member.name) != "uv":
|
|
55
|
+
return None # skip
|
|
56
|
+
member.path = os.path.basename(member.path)
|
|
57
|
+
return member
|
|
58
|
+
|
|
59
|
+
max_retries = 3
|
|
60
|
+
for attempt in range(max_retries):
|
|
61
|
+
try:
|
|
62
|
+
req = Request(UV_URL, headers=headers)
|
|
63
|
+
with urlopen(req) as response:
|
|
64
|
+
with tarfile.open(fileobj=response, mode="r:gz") as tar:
|
|
65
|
+
tar.extractall(uv_install_path, filter=_tar_filter)
|
|
66
|
+
break
|
|
67
|
+
except (URLError, IOError) as e:
|
|
68
|
+
if attempt == max_retries - 1:
|
|
69
|
+
raise Exception(
|
|
70
|
+
f"Failed to download UV after {max_retries} attempts: {e}"
|
|
71
|
+
)
|
|
72
|
+
time.sleep(2**attempt)
|
|
73
|
+
|
|
74
|
+
# Update PATH only once at the end
|
|
75
|
+
os.environ["PATH"] += os.pathsep + uv_install_path
|
|
76
|
+
|
|
77
|
+
def get_dependencies(datastore_type):
|
|
78
|
+
# return required dependencies for Metaflow that must be added to the UV environment.
|
|
79
|
+
pinned = get_pinned_conda_libs(None, datastore_type)
|
|
80
|
+
|
|
81
|
+
# return only dependency names instead of pinned versions
|
|
82
|
+
return pinned.keys()
|
|
83
|
+
|
|
84
|
+
def skip_metaflow_dependencies():
|
|
85
|
+
skip_pkgs = ["metaflow", "ob-metaflow"]
|
|
86
|
+
info = read_info_file()
|
|
87
|
+
if info is not None:
|
|
88
|
+
try:
|
|
89
|
+
skip_pkgs.extend([ext_name for ext_name in info["ext_info"][0].keys()])
|
|
90
|
+
except Exception:
|
|
91
|
+
print(
|
|
92
|
+
"Failed to read INFO. Metaflow-related packages might get installed during runtime."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return skip_pkgs
|
|
96
|
+
|
|
97
|
+
def sync_uv_project(datastore_type):
|
|
98
|
+
# Move the files to the current directory so uv can find them.
|
|
99
|
+
for filename in ["uv.lock", "pyproject.toml"]:
|
|
100
|
+
path_to_file = MetaflowCodeContent.get_filename(
|
|
101
|
+
filename, ContentType.OTHER_CONTENT
|
|
102
|
+
)
|
|
103
|
+
if path_to_file is None:
|
|
104
|
+
raise RuntimeError(f"Could not find {filename} in the package.")
|
|
105
|
+
shutil.move(path_to_file, os.path.join(os.getcwd(), filename))
|
|
106
|
+
|
|
107
|
+
print("Syncing uv project...")
|
|
108
|
+
dependencies = " ".join(get_dependencies(datastore_type))
|
|
109
|
+
skip_pkgs = " ".join(
|
|
110
|
+
[f"--no-install-package {dep}" for dep in skip_metaflow_dependencies()]
|
|
111
|
+
)
|
|
112
|
+
cmd = f"""set -e;
|
|
113
|
+
uv sync --frozen {skip_pkgs};
|
|
114
|
+
uv pip install {dependencies} --strict
|
|
115
|
+
"""
|
|
116
|
+
run_cmd(cmd)
|
|
117
|
+
|
|
118
|
+
if len(sys.argv) != 2:
|
|
119
|
+
print("Usage: bootstrap.py <datastore_type>")
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
datastore_type = sys.argv[1]
|
|
124
|
+
install_uv()
|
|
125
|
+
sync_uv_project(datastore_type)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
print(f"Error: {str(e)}", file=sys.stderr)
|
|
128
|
+
sys.exit(1)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from metaflow.exception import MetaflowException
|
|
4
|
+
from metaflow.metaflow_environment import MetaflowEnvironment
|
|
5
|
+
from metaflow.packaging_sys import ContentType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UVException(MetaflowException):
|
|
9
|
+
headline = "uv error"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UVEnvironment(MetaflowEnvironment):
|
|
13
|
+
TYPE = "uv"
|
|
14
|
+
|
|
15
|
+
def __init__(self, flow):
|
|
16
|
+
super().__init__(flow)
|
|
17
|
+
self.flow = flow
|
|
18
|
+
|
|
19
|
+
def validate_environment(self, logger, datastore_type):
|
|
20
|
+
self.datastore_type = datastore_type
|
|
21
|
+
self.logger = logger
|
|
22
|
+
|
|
23
|
+
def init_environment(self, echo, only_steps=None):
|
|
24
|
+
self.logger("Bootstrapping uv...")
|
|
25
|
+
|
|
26
|
+
def executable(self, step_name, default=None):
|
|
27
|
+
return "uv run --no-sync python"
|
|
28
|
+
|
|
29
|
+
def add_to_package(self):
|
|
30
|
+
# NOTE: We treat uv.lock and pyproject.toml as regular project assets and ship these along user code as part of the code package
|
|
31
|
+
# These are the minimal required files to reproduce the UV environment on the remote platform.
|
|
32
|
+
def _find(filename):
|
|
33
|
+
current_dir = os.getcwd()
|
|
34
|
+
while True:
|
|
35
|
+
file_path = os.path.join(current_dir, filename)
|
|
36
|
+
if os.path.isfile(file_path):
|
|
37
|
+
return file_path
|
|
38
|
+
parent_dir = os.path.dirname(current_dir)
|
|
39
|
+
if parent_dir == current_dir: # Reached root
|
|
40
|
+
raise UVException(
|
|
41
|
+
f"Could not find {filename} in current directory or any parent directory"
|
|
42
|
+
)
|
|
43
|
+
current_dir = parent_dir
|
|
44
|
+
|
|
45
|
+
pyproject_path = _find("pyproject.toml")
|
|
46
|
+
uv_lock_path = _find("uv.lock")
|
|
47
|
+
files = [
|
|
48
|
+
(uv_lock_path, "uv.lock", ContentType.OTHER_CONTENT),
|
|
49
|
+
(pyproject_path, "pyproject.toml", ContentType.OTHER_CONTENT),
|
|
50
|
+
]
|
|
51
|
+
return files
|
|
52
|
+
|
|
53
|
+
def pylint_config(self):
|
|
54
|
+
config = super().pylint_config()
|
|
55
|
+
# Disable (import-error) in pylint
|
|
56
|
+
config.append("--disable=F0401")
|
|
57
|
+
return config
|
|
58
|
+
|
|
59
|
+
def bootstrap_commands(self, step_name, datastore_type):
|
|
60
|
+
return [
|
|
61
|
+
"echo 'Bootstrapping uv project...'",
|
|
62
|
+
"flush_mflogs",
|
|
63
|
+
# We have to prevent the tracing module from loading, as the bootstrapping process
|
|
64
|
+
# uses the internal S3 client which would fail to import tracing due to the required
|
|
65
|
+
# dependencies being bundled into the conda environment, which is yet to be
|
|
66
|
+
# initialized at this point.
|
|
67
|
+
'DISABLE_TRACING=True python -m metaflow.plugins.uv.bootstrap "%s"'
|
|
68
|
+
% datastore_type,
|
|
69
|
+
"echo 'uv project bootstrapped.'",
|
|
70
|
+
"flush_mflogs",
|
|
71
|
+
"export PATH=$PATH:$(pwd)/uv_install",
|
|
72
|
+
]
|
metaflow/procpoll.py
CHANGED
metaflow/pylint_wrapper.py
CHANGED
|
@@ -28,7 +28,11 @@ class PyLint(object):
|
|
|
28
28
|
return self._run is not None
|
|
29
29
|
|
|
30
30
|
def run(self, logger=None, warnings=False, pylint_config=[]):
|
|
31
|
-
args = [
|
|
31
|
+
args = [
|
|
32
|
+
self._fname,
|
|
33
|
+
"--signature-mutators",
|
|
34
|
+
"metaflow.user_decorators.user_step_decorator.user_step_decorator",
|
|
35
|
+
]
|
|
32
36
|
if not warnings:
|
|
33
37
|
args.append("--errors-only")
|
|
34
38
|
if pylint_config:
|
|
File without changes
|