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/util.py
CHANGED
|
@@ -4,12 +4,13 @@ import sys
|
|
|
4
4
|
import tempfile
|
|
5
5
|
import zlib
|
|
6
6
|
import base64
|
|
7
|
+
import re
|
|
8
|
+
|
|
7
9
|
from functools import wraps
|
|
8
10
|
from io import BytesIO
|
|
9
11
|
from itertools import takewhile
|
|
10
|
-
import
|
|
12
|
+
from typing import Dict, Any, Tuple, Optional, List, Generator
|
|
11
13
|
|
|
12
|
-
from metaflow.exception import MetaflowUnknownUser, MetaflowInternalError
|
|
13
14
|
|
|
14
15
|
try:
|
|
15
16
|
# python2
|
|
@@ -51,21 +52,6 @@ except NameError:
|
|
|
51
52
|
from shlex import quote as _quote
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
from typing import NamedTuple
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def namedtuple_with_defaults(typename, field_descr, defaults=()):
|
|
58
|
-
T = NamedTuple(typename, field_descr)
|
|
59
|
-
T.__new__.__defaults__ = tuple(defaults)
|
|
60
|
-
|
|
61
|
-
# Adding the following to ensure the named tuple can be (un)pickled correctly.
|
|
62
|
-
import __main__
|
|
63
|
-
|
|
64
|
-
setattr(__main__, T.__name__, T)
|
|
65
|
-
T.__module__ = "__main__"
|
|
66
|
-
return T
|
|
67
|
-
|
|
68
|
-
|
|
69
55
|
class TempDir(object):
|
|
70
56
|
# Provide a temporary directory since Python 2.7 does not have it inbuilt
|
|
71
57
|
def __enter__(self):
|
|
@@ -177,6 +163,8 @@ def get_username():
|
|
|
177
163
|
|
|
178
164
|
|
|
179
165
|
def resolve_identity_as_tuple():
|
|
166
|
+
from metaflow.exception import MetaflowUnknownUser
|
|
167
|
+
|
|
180
168
|
prod_token = os.environ.get("METAFLOW_PRODUCTION_TOKEN")
|
|
181
169
|
if prod_token:
|
|
182
170
|
return "production", prod_token
|
|
@@ -192,6 +180,119 @@ def resolve_identity():
|
|
|
192
180
|
return "%s:%s" % (identity_type, identity_value)
|
|
193
181
|
|
|
194
182
|
|
|
183
|
+
def parse_spin_pathspec(pathspec: str, flow_name: str) -> Tuple:
|
|
184
|
+
"""
|
|
185
|
+
Parse various pathspec formats for the spin command.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
pathspec : str
|
|
190
|
+
The pathspec string in one of the following formats:
|
|
191
|
+
- step_name (e.g., 'start')
|
|
192
|
+
- run_id/step_name (e.g., '221165/start')
|
|
193
|
+
- run_id/step_name/task_id (e.g., '221165/start/1350987')
|
|
194
|
+
- flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')
|
|
195
|
+
- flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')
|
|
196
|
+
flow_name : str
|
|
197
|
+
The name of the current flow.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
Tuple
|
|
202
|
+
A tuple of (step_name, full_pathspec_or_none)
|
|
203
|
+
|
|
204
|
+
Raises
|
|
205
|
+
------
|
|
206
|
+
CommandException
|
|
207
|
+
If the pathspec format is invalid or flow name doesn't match.
|
|
208
|
+
"""
|
|
209
|
+
from .exception import CommandException
|
|
210
|
+
|
|
211
|
+
parts = pathspec.split("/")
|
|
212
|
+
|
|
213
|
+
if len(parts) == 1:
|
|
214
|
+
# Just step name: 'start'
|
|
215
|
+
step_name = parts[0]
|
|
216
|
+
parsed_pathspec = None
|
|
217
|
+
elif len(parts) == 2:
|
|
218
|
+
# run_id/step_name: '221165/start'
|
|
219
|
+
run_id, step_name = parts
|
|
220
|
+
parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
|
|
221
|
+
elif len(parts) == 3:
|
|
222
|
+
# Could be run_id/step_name/task_id or flow_name/run_id/step_name
|
|
223
|
+
if parts[0] == flow_name:
|
|
224
|
+
# flow_name/run_id/step_name
|
|
225
|
+
_, run_id, step_name = parts
|
|
226
|
+
parsed_pathspec = f"{flow_name}/{run_id}/{step_name}"
|
|
227
|
+
else:
|
|
228
|
+
# run_id/step_name/task_id
|
|
229
|
+
run_id, step_name, task_id = parts
|
|
230
|
+
parsed_pathspec = f"{flow_name}/{run_id}/{step_name}/{task_id}"
|
|
231
|
+
elif len(parts) == 4:
|
|
232
|
+
# flow_name/run_id/step_name/task_id
|
|
233
|
+
parsed_flow_name, run_id, step_name, task_id = parts
|
|
234
|
+
if parsed_flow_name != flow_name:
|
|
235
|
+
raise CommandException(
|
|
236
|
+
f"Flow name '{parsed_flow_name}' in pathspec does not match current flow '{flow_name}'."
|
|
237
|
+
)
|
|
238
|
+
parsed_pathspec = pathspec
|
|
239
|
+
else:
|
|
240
|
+
raise CommandException(
|
|
241
|
+
f"Invalid pathspec format: '{pathspec}'. \n"
|
|
242
|
+
"Expected formats:\n"
|
|
243
|
+
" - step_name (e.g., 'start')\n"
|
|
244
|
+
" - run_id/step_name (e.g., '221165/start')\n"
|
|
245
|
+
" - run_id/step_name/task_id (e.g., '221165/start/1350987')\n"
|
|
246
|
+
" - flow_name/run_id/step_name (e.g., 'ScalableFlow/221165/start')\n"
|
|
247
|
+
" - flow_name/run_id/step_name/task_id (e.g., 'ScalableFlow/221165/start/1350987')"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return step_name, parsed_pathspec
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def get_latest_task_pathspec(
|
|
254
|
+
flow_name: str, step_name: str, run_id: str = None
|
|
255
|
+
) -> "metaflow.Task":
|
|
256
|
+
"""
|
|
257
|
+
Returns a task pathspec from the latest run (or specified run) of the flow for the queried step.
|
|
258
|
+
If the queried step has several tasks, the task pathspec of the first task is returned.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
flow_name : str
|
|
263
|
+
The name of the flow.
|
|
264
|
+
step_name : str
|
|
265
|
+
The name of the step.
|
|
266
|
+
run_id : str, optional
|
|
267
|
+
The run ID to use. If None, uses the latest run.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
Task
|
|
272
|
+
A Metaflow Task instance containing the latest task for the queried step.
|
|
273
|
+
|
|
274
|
+
Raises
|
|
275
|
+
------
|
|
276
|
+
MetaflowNotFound
|
|
277
|
+
If no task or run is found for the queried step.
|
|
278
|
+
"""
|
|
279
|
+
from metaflow import Flow, Step
|
|
280
|
+
from metaflow.exception import MetaflowNotFound
|
|
281
|
+
|
|
282
|
+
if not run_id:
|
|
283
|
+
flow = Flow(flow_name)
|
|
284
|
+
run = flow.latest_run
|
|
285
|
+
if run is None:
|
|
286
|
+
raise MetaflowNotFound(f"No run found for flow {flow_name}")
|
|
287
|
+
run_id = run.id
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
task = Step(f"{flow_name}/{run_id}/{step_name}").task
|
|
291
|
+
return task
|
|
292
|
+
except:
|
|
293
|
+
raise MetaflowNotFound(f"No task found for step {step_name} in run {run_id}")
|
|
294
|
+
|
|
295
|
+
|
|
195
296
|
def get_latest_run_id(echo, flow_name):
|
|
196
297
|
from metaflow.plugins.datastores.local_storage import LocalStorage
|
|
197
298
|
|
|
@@ -251,6 +352,8 @@ def get_object_package_version(obj):
|
|
|
251
352
|
|
|
252
353
|
|
|
253
354
|
def compress_list(lst, separator=",", rangedelim=":", zlibmarker="!", zlibmin=500):
|
|
355
|
+
from metaflow.exception import MetaflowInternalError
|
|
356
|
+
|
|
254
357
|
bad_items = [x for x in lst if separator in x or rangedelim in x or zlibmarker in x]
|
|
255
358
|
if bad_items:
|
|
256
359
|
raise MetaflowInternalError(
|
|
@@ -311,6 +414,9 @@ def get_metaflow_root():
|
|
|
311
414
|
|
|
312
415
|
|
|
313
416
|
def dict_to_cli_options(params):
|
|
417
|
+
# Prevent circular imports
|
|
418
|
+
from .user_configs.config_options import ConfigInput
|
|
419
|
+
|
|
314
420
|
for k, v in params.items():
|
|
315
421
|
# Omit boolean options set to false or None, but preserve options with an empty
|
|
316
422
|
# string argument.
|
|
@@ -319,6 +425,20 @@ def dict_to_cli_options(params):
|
|
|
319
425
|
# keyword in Python, so we call it 'decospecs' in click args
|
|
320
426
|
if k == "decospecs":
|
|
321
427
|
k = "with"
|
|
428
|
+
if k in ("config", "config_value"):
|
|
429
|
+
# Special handling here since we gather them all in one option but actually
|
|
430
|
+
# need to send them one at a time using --config-value <name> kv.<name>
|
|
431
|
+
# Note it can be either config or config_value depending
|
|
432
|
+
# on click processing order.
|
|
433
|
+
for config_name in v.keys():
|
|
434
|
+
yield "--config-value"
|
|
435
|
+
yield to_unicode(config_name)
|
|
436
|
+
yield to_unicode(ConfigInput.make_key_name(config_name))
|
|
437
|
+
continue
|
|
438
|
+
if k == "local_config_file":
|
|
439
|
+
# Skip this value -- it should only be used locally and never when
|
|
440
|
+
# forming another command line
|
|
441
|
+
continue
|
|
322
442
|
k = k.replace("_", "-")
|
|
323
443
|
v = v if isinstance(v, (list, tuple, set)) else [v]
|
|
324
444
|
for value in v:
|
|
@@ -397,9 +517,9 @@ def to_camelcase(obj):
|
|
|
397
517
|
if isinstance(obj, dict):
|
|
398
518
|
res = obj.__class__()
|
|
399
519
|
for k in obj:
|
|
400
|
-
res[
|
|
401
|
-
|
|
402
|
-
|
|
520
|
+
res[re.sub(r"(?!^)_([a-zA-Z])", lambda x: x.group(1).upper(), k)] = (
|
|
521
|
+
to_camelcase(obj[k])
|
|
522
|
+
)
|
|
403
523
|
elif isinstance(obj, (list, set, tuple)):
|
|
404
524
|
res = obj.__class__(to_camelcase(v) for v in obj)
|
|
405
525
|
else:
|
|
@@ -416,9 +536,9 @@ def to_pascalcase(obj):
|
|
|
416
536
|
if isinstance(obj, dict):
|
|
417
537
|
res = obj.__class__()
|
|
418
538
|
for k in obj:
|
|
419
|
-
res[
|
|
420
|
-
|
|
421
|
-
|
|
539
|
+
res[re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), k, count=1)] = (
|
|
540
|
+
to_pascalcase(obj[k])
|
|
541
|
+
)
|
|
422
542
|
elif isinstance(obj, (list, set, tuple)):
|
|
423
543
|
res = obj.__class__(to_pascalcase(v) for v in obj)
|
|
424
544
|
else:
|
|
@@ -441,7 +561,103 @@ def tar_safe_extract(tar, path=".", members=None, *, numeric_owner=False):
|
|
|
441
561
|
tar.extractall(path, members, numeric_owner=numeric_owner)
|
|
442
562
|
|
|
443
563
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
564
|
+
def to_pod(value):
|
|
565
|
+
"""
|
|
566
|
+
Convert a python object to plain-old-data (POD) format.
|
|
567
|
+
|
|
568
|
+
Parameters
|
|
569
|
+
----------
|
|
570
|
+
value : Any
|
|
571
|
+
Value to convert to POD format. The value can be a string, number, list,
|
|
572
|
+
dictionary, or a nested structure of these types.
|
|
573
|
+
"""
|
|
574
|
+
# Prevent circular imports
|
|
575
|
+
from metaflow.parameters import DeployTimeField
|
|
576
|
+
|
|
577
|
+
if isinstance(value, (str, int, float)):
|
|
578
|
+
return value
|
|
579
|
+
if isinstance(value, dict):
|
|
580
|
+
return {to_pod(k): to_pod(v) for k, v in value.items()}
|
|
581
|
+
if isinstance(value, (list, set, tuple)):
|
|
582
|
+
return [to_pod(v) for v in value]
|
|
583
|
+
if isinstance(value, DeployTimeField):
|
|
584
|
+
return value.print_representation
|
|
585
|
+
return str(value)
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
from metaflow._vendor.packaging.version import parse as version_parse
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def read_artifacts_module(file_path: str) -> Dict[str, Any]:
|
|
592
|
+
"""
|
|
593
|
+
Read a Python module from the given file path and return its ARTIFACTS variable.
|
|
594
|
+
|
|
595
|
+
Parameters
|
|
596
|
+
----------
|
|
597
|
+
file_path : str
|
|
598
|
+
The path to the Python file containing the ARTIFACTS variable.
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
Dict[str, Any]
|
|
603
|
+
A dictionary containing the ARTIFACTS variable from the module.
|
|
604
|
+
|
|
605
|
+
Raises
|
|
606
|
+
-------
|
|
607
|
+
MetaflowInternalError
|
|
608
|
+
If the file cannot be read or does not contain the ARTIFACTS variable.
|
|
609
|
+
"""
|
|
610
|
+
import importlib.util
|
|
611
|
+
import os
|
|
612
|
+
|
|
613
|
+
try:
|
|
614
|
+
module_name = os.path.splitext(os.path.basename(file_path))[0]
|
|
615
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
616
|
+
module = importlib.util.module_from_spec(spec)
|
|
617
|
+
spec.loader.exec_module(module)
|
|
618
|
+
variables = vars(module)
|
|
619
|
+
if "ARTIFACTS" not in variables:
|
|
620
|
+
raise MetaflowInternalError(
|
|
621
|
+
f"Module {file_path} does not contain ARTIFACTS variable"
|
|
622
|
+
)
|
|
623
|
+
return variables.get("ARTIFACTS")
|
|
624
|
+
except Exception as e:
|
|
625
|
+
raise MetaflowInternalError(f"Error reading file {file_path}") from e
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# this is os.walk(follow_symlinks=True) with cycle detection
|
|
629
|
+
def walk_without_cycles(
|
|
630
|
+
top_root: str,
|
|
631
|
+
exclude_dirs: Optional[List[str]] = None,
|
|
632
|
+
) -> Generator[Tuple[str, List[str], List[str]], None, None]:
|
|
633
|
+
seen = set()
|
|
634
|
+
|
|
635
|
+
default_skip_dirs = ["__pycache__"]
|
|
636
|
+
|
|
637
|
+
def _recurse(root, skip_dirs):
|
|
638
|
+
for parent, dirs, files in os.walk(root):
|
|
639
|
+
dirs[:] = [d for d in dirs if d not in skip_dirs]
|
|
640
|
+
for d in dirs:
|
|
641
|
+
path = os.path.join(parent, d)
|
|
642
|
+
if os.path.islink(path):
|
|
643
|
+
# Breaking loops: never follow the same symlink twice
|
|
644
|
+
#
|
|
645
|
+
# NOTE: this also means that links to sibling links are
|
|
646
|
+
# not followed. In this case:
|
|
647
|
+
#
|
|
648
|
+
# x -> y
|
|
649
|
+
# y -> oo
|
|
650
|
+
# oo/real_file
|
|
651
|
+
#
|
|
652
|
+
# real_file is only included twice, not three times
|
|
653
|
+
reallink = os.path.realpath(path)
|
|
654
|
+
if reallink not in seen:
|
|
655
|
+
seen.add(reallink)
|
|
656
|
+
for x in _recurse(path, default_skip_dirs):
|
|
657
|
+
yield x
|
|
658
|
+
yield parent, dirs, files
|
|
659
|
+
|
|
660
|
+
skip_dirs = set(default_skip_dirs + (exclude_dirs or []))
|
|
661
|
+
for x in _recurse(top_root, skip_dirs):
|
|
662
|
+
skip_dirs = default_skip_dirs
|
|
663
|
+
yield x
|
metaflow/vendor.py
CHANGED
|
@@ -11,7 +11,6 @@ WHITELIST = {
|
|
|
11
11
|
"README.txt",
|
|
12
12
|
"__init__.py",
|
|
13
13
|
"vendor_any.txt",
|
|
14
|
-
"vendor_v3_5.txt",
|
|
15
14
|
"vendor_v3_6.txt",
|
|
16
15
|
"vendor_v3_7.txt",
|
|
17
16
|
"pip.LICENSE",
|
|
@@ -64,14 +63,29 @@ def find_vendored_libs(vendor_dir, whitelist, whitelist_dirs):
|
|
|
64
63
|
return vendored_libs, paths
|
|
65
64
|
|
|
66
65
|
|
|
67
|
-
def fetch_licenses(*
|
|
68
|
-
for
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
def fetch_licenses(*info_dirs, vendor_dir):
|
|
67
|
+
for dist_info in info_dirs:
|
|
68
|
+
metadata_file = dist_info / "METADATA"
|
|
69
|
+
if not metadata_file.exists():
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
project_name = None
|
|
73
|
+
for line in metadata_file.read_text("utf-8").splitlines():
|
|
74
|
+
if line.startswith("Name: "):
|
|
75
|
+
project_name = line.split("Name: ", 1)[1].strip()
|
|
76
|
+
break
|
|
77
|
+
if not project_name:
|
|
73
78
|
continue
|
|
74
79
|
|
|
80
|
+
for item in dist_info.iterdir():
|
|
81
|
+
if item.is_file() and re.search(r"(LICENSE|COPYING)", item.name, re.I):
|
|
82
|
+
shutil.copy(item, vendor_dir / f"{project_name}.LICENSE")
|
|
83
|
+
elif item.is_dir() and item.name.lower() == "licenses":
|
|
84
|
+
for license_file in item.iterdir():
|
|
85
|
+
if license_file.is_file():
|
|
86
|
+
dest_name = f"{project_name}.{license_file.name}"
|
|
87
|
+
shutil.copy(license_file, vendor_dir / dest_name)
|
|
88
|
+
|
|
75
89
|
|
|
76
90
|
def vendor(vendor_dir):
|
|
77
91
|
# remove everything
|
|
@@ -109,6 +123,8 @@ def vendor(vendor_dir):
|
|
|
109
123
|
"-r",
|
|
110
124
|
"_vendor/vendor_%s.txt" % subdir,
|
|
111
125
|
"--no-compile",
|
|
126
|
+
"--no-binary",
|
|
127
|
+
":all:",
|
|
112
128
|
]
|
|
113
129
|
)
|
|
114
130
|
|
metaflow/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
metaflow_version = "2.
|
|
1
|
+
metaflow_version = "2.19.7.1rc0"
|