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,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import inspect
|
|
3
|
+
import importlib
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main(flow_file, fn_name_or_path, run_pathspec):
|
|
8
|
+
hook_fn = None
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
module_path, function_name = fn_name_or_path.rsplit(".", 1)
|
|
12
|
+
module = importlib.import_module(module_path)
|
|
13
|
+
hook_fn = getattr(module, function_name)
|
|
14
|
+
except (ImportError, AttributeError, ValueError):
|
|
15
|
+
try:
|
|
16
|
+
module_name = os.path.splitext(os.path.basename(flow_file))[0]
|
|
17
|
+
spec = importlib.util.spec_from_file_location(module_name, flow_file)
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
spec.loader.exec_module(module)
|
|
20
|
+
hook_fn = getattr(module, fn_name_or_path)
|
|
21
|
+
except (AttributeError, IOError) as e:
|
|
22
|
+
print(
|
|
23
|
+
f"[exit_hook] Could not load function '{fn_name_or_path}' "
|
|
24
|
+
f"as an import path or from '{flow_file}': {e}"
|
|
25
|
+
)
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
argspec = inspect.getfullargspec(hook_fn)
|
|
29
|
+
|
|
30
|
+
# Check if fn expects a run object as an arg.
|
|
31
|
+
if "run" in argspec.args or argspec.varkw is not None:
|
|
32
|
+
from metaflow import Run
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
_run = Run(run_pathspec, _namespace_check=False)
|
|
36
|
+
except Exception as ex:
|
|
37
|
+
print(ex)
|
|
38
|
+
_run = None
|
|
39
|
+
|
|
40
|
+
hook_fn(run=_run)
|
|
41
|
+
else:
|
|
42
|
+
hook_fn()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
try:
|
|
47
|
+
flow_file, fn_name, run_pathspec = sys.argv[1:4]
|
|
48
|
+
except Exception:
|
|
49
|
+
print("Usage: exit_hook_script.py <flow_file> <function_name> <run_pathspec>")
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
main(flow_file, fn_name, run_pathspec)
|
metaflow/plugins/gcp/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .gs_storage_client_factory import get_credentials
|
|
1
|
+
from .gs_storage_client_factory import get_credentials
|
|
@@ -89,7 +89,9 @@ class GcpSecretManagerSecretsProvider(SecretsProvider):
|
|
|
89
89
|
|
|
90
90
|
# The latter two forms require METAFLOW_GCP_SECRET_MANAGER_PREFIX to be set.
|
|
91
91
|
|
|
92
|
-
match_full = re.match(
|
|
92
|
+
match_full = re.match(
|
|
93
|
+
r"^projects/\d+/secrets/([\w\-]+)(/versions/([\w\-]+))?$", secret_id
|
|
94
|
+
)
|
|
93
95
|
match_partial = re.match(r"^([\w\-]+)(/versions/[\w\-]+)?$", secret_id)
|
|
94
96
|
if match_full:
|
|
95
97
|
# Full path
|
|
@@ -105,20 +107,23 @@ class GcpSecretManagerSecretsProvider(SecretsProvider):
|
|
|
105
107
|
env_var_name = secret_id
|
|
106
108
|
if not GCP_SECRET_MANAGER_PREFIX:
|
|
107
109
|
raise ValueError(
|
|
108
|
-
|
|
110
|
+
"Cannot use simple secret_id without setting METAFLOW_GCP_SECRET_MANAGER_PREFIX. %s"
|
|
111
|
+
% GCP_SECRET_MANAGER_PREFIX
|
|
109
112
|
)
|
|
110
113
|
if match_partial.group(2):
|
|
111
114
|
# With version specified
|
|
112
|
-
full_secret_name =
|
|
115
|
+
full_secret_name = "%s%s" % (GCP_SECRET_MANAGER_PREFIX, secret_id)
|
|
113
116
|
env_var_name = match_partial.group(1)
|
|
114
117
|
else:
|
|
115
118
|
# No version specified, use latest
|
|
116
|
-
full_secret_name = (
|
|
117
|
-
|
|
119
|
+
full_secret_name = "%s%s/versions/latest" % (
|
|
120
|
+
GCP_SECRET_MANAGER_PREFIX,
|
|
121
|
+
secret_id,
|
|
118
122
|
)
|
|
119
123
|
else:
|
|
120
124
|
raise ValueError(
|
|
121
|
-
|
|
125
|
+
"Invalid secret_id: %s. Must be either a full path or a simple string."
|
|
126
|
+
% secret_id
|
|
122
127
|
)
|
|
123
128
|
|
|
124
129
|
result = {}
|
metaflow/plugins/gcp/gs_tail.py
CHANGED
|
@@ -11,15 +11,15 @@ from metaflow.plugins.gcp.gs_utils import parse_gs_full_path
|
|
|
11
11
|
class GSTail(object):
|
|
12
12
|
def __init__(self, blob_full_uri):
|
|
13
13
|
"""Location should be something like gs://<bucket_name>/blob"""
|
|
14
|
-
bucket_name, blob_name = parse_gs_full_path(blob_full_uri)
|
|
15
|
-
if not blob_name:
|
|
14
|
+
self.bucket_name, self.blob_name = parse_gs_full_path(blob_full_uri)
|
|
15
|
+
if not self.blob_name:
|
|
16
16
|
raise MetaflowException(
|
|
17
17
|
msg="Failed to parse blob_full_uri into gs://<bucket_name>/<blob_name> (got %s)"
|
|
18
18
|
% blob_full_uri
|
|
19
19
|
)
|
|
20
20
|
client = get_gs_storage_client()
|
|
21
|
-
bucket = client.bucket(bucket_name)
|
|
22
|
-
self._blob_client = bucket.blob(blob_name)
|
|
21
|
+
self.bucket = client.bucket(self.bucket_name)
|
|
22
|
+
self._blob_client = self.bucket.blob(self.blob_name)
|
|
23
23
|
self._pos = 0
|
|
24
24
|
self._tail = b""
|
|
25
25
|
|
|
@@ -46,7 +46,11 @@ class GSTail(object):
|
|
|
46
46
|
def _make_range_request(self):
|
|
47
47
|
try:
|
|
48
48
|
# Yes we read to the end... memory blow up is possible. We can improve by specifying length param
|
|
49
|
-
|
|
49
|
+
# NOTE: We must re-instantiate the whole client here due to a behavior with the GS library,
|
|
50
|
+
# otherwise download_as_bytes will simply return the same content for consecutive requests with the same attributes,
|
|
51
|
+
# even if the blob has grown in size.
|
|
52
|
+
blob_client = self.bucket.blob(self.blob_name)
|
|
53
|
+
return blob_client.download_as_bytes(start=self._pos)
|
|
50
54
|
except NotFound:
|
|
51
55
|
return None
|
|
52
56
|
except ClientError as e:
|
|
@@ -63,7 +67,7 @@ class GSTail(object):
|
|
|
63
67
|
if data is None:
|
|
64
68
|
return None
|
|
65
69
|
if data:
|
|
66
|
-
buf = BytesIO(data)
|
|
70
|
+
buf = BytesIO(self._tail + data)
|
|
67
71
|
self._pos += len(data)
|
|
68
72
|
self._tail = b""
|
|
69
73
|
return buf
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
from metaflow.exception import CommandException, MetaflowException
|
|
4
|
+
from metaflow.util import get_username, get_latest_run_id
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# avoid circular import by having the exception class contained here
|
|
8
|
+
class KubernetesException(MetaflowException):
|
|
9
|
+
headline = "Kubernetes error"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def parse_cli_options(flow_name, run_id, user, my_runs, echo):
|
|
13
|
+
if user and my_runs:
|
|
14
|
+
raise CommandException("--user and --my-runs are mutually exclusive.")
|
|
15
|
+
|
|
16
|
+
if run_id and my_runs:
|
|
17
|
+
raise CommandException("--run_id and --my-runs are mutually exclusive.")
|
|
18
|
+
|
|
19
|
+
if my_runs:
|
|
20
|
+
user = get_username()
|
|
21
|
+
|
|
22
|
+
latest_run = True
|
|
23
|
+
|
|
24
|
+
if user and not run_id:
|
|
25
|
+
latest_run = False
|
|
26
|
+
|
|
27
|
+
if not run_id and latest_run:
|
|
28
|
+
run_id = get_latest_run_id(echo, flow_name)
|
|
29
|
+
if run_id is None:
|
|
30
|
+
raise CommandException("A previous run id was not found. Specify --run-id.")
|
|
31
|
+
|
|
32
|
+
return flow_name, run_id, user
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def qos_requests_and_limits(qos: str, cpu: int, memory: int, storage: int):
|
|
36
|
+
"return resource requests and limits for the kubernetes pod based on the given QoS Class"
|
|
37
|
+
# case insensitive matching for QoS class
|
|
38
|
+
qos = qos.lower()
|
|
39
|
+
# Determine the requests and limits to define chosen QoS class
|
|
40
|
+
qos_limits = {}
|
|
41
|
+
qos_requests = {}
|
|
42
|
+
if qos == "guaranteed":
|
|
43
|
+
# Guaranteed - has both cpu/memory limits. requests not required, as these will be inferred.
|
|
44
|
+
qos_limits = {
|
|
45
|
+
"cpu": str(cpu),
|
|
46
|
+
"memory": "%sM" % str(memory),
|
|
47
|
+
"ephemeral-storage": "%sM" % str(storage),
|
|
48
|
+
}
|
|
49
|
+
# NOTE: Even though Kubernetes will produce matching requests for the specified limits, this happens late in the lifecycle.
|
|
50
|
+
# We specify them explicitly here to make some K8S tooling happy, in case they rely on .resources.requests being present at time of submitting the job.
|
|
51
|
+
qos_requests = qos_limits
|
|
52
|
+
else:
|
|
53
|
+
# Burstable - not Guaranteed, and has a memory/cpu limit or request
|
|
54
|
+
qos_requests = {
|
|
55
|
+
"cpu": str(cpu),
|
|
56
|
+
"memory": "%sM" % str(memory),
|
|
57
|
+
"ephemeral-storage": "%sM" % str(storage),
|
|
58
|
+
}
|
|
59
|
+
# TODO: Add support for BestEffort once there is a use case for it.
|
|
60
|
+
# BestEffort - no limit or requests for cpu/memory
|
|
61
|
+
return qos_requests, qos_limits
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def validate_kube_labels(
|
|
65
|
+
labels: Optional[Dict[str, Optional[str]]],
|
|
66
|
+
) -> bool:
|
|
67
|
+
"""Validate label values.
|
|
68
|
+
|
|
69
|
+
This validates the kubernetes label values. It does not validate the keys.
|
|
70
|
+
Ideally, keys should be static and also the validation rules for keys are
|
|
71
|
+
more complex than those for values. For full validation rules, see:
|
|
72
|
+
|
|
73
|
+
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#syntax-and-character-set
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def validate_label(s: Optional[str]):
|
|
77
|
+
regex_match = r"^(([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9])?$"
|
|
78
|
+
if not s:
|
|
79
|
+
# allow empty label
|
|
80
|
+
return True
|
|
81
|
+
if not re.search(regex_match, s):
|
|
82
|
+
raise KubernetesException(
|
|
83
|
+
'Invalid value: "%s"\n'
|
|
84
|
+
"A valid label must be an empty string or one that\n"
|
|
85
|
+
" - Consist of alphanumeric, '-', '_' or '.' characters\n"
|
|
86
|
+
" - Begins and ends with an alphanumeric character\n"
|
|
87
|
+
" - Is at most 63 characters" % s
|
|
88
|
+
)
|
|
89
|
+
return True
|
|
90
|
+
|
|
91
|
+
return all([validate_label(v) for v in labels.values()]) if labels else True
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def parse_kube_keyvalue_list(items: List[str], requires_both: bool = True):
|
|
95
|
+
try:
|
|
96
|
+
ret = {}
|
|
97
|
+
for item_str in items:
|
|
98
|
+
item = item_str.split("=", 1)
|
|
99
|
+
if requires_both:
|
|
100
|
+
item[1] # raise IndexError
|
|
101
|
+
if str(item[0]) in ret:
|
|
102
|
+
raise KubernetesException("Duplicate key found: %s" % str(item[0]))
|
|
103
|
+
ret[str(item[0])] = str(item[1]) if len(item) > 1 else None
|
|
104
|
+
return ret
|
|
105
|
+
except KubernetesException as e:
|
|
106
|
+
raise e
|
|
107
|
+
except (AttributeError, IndexError):
|
|
108
|
+
raise KubernetesException("Unable to parse kubernetes list: %s" % items)
|