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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import bz2
|
|
2
|
+
import concurrent.futures
|
|
2
3
|
import io
|
|
3
4
|
import json
|
|
4
5
|
import os
|
|
@@ -6,133 +7,384 @@ import shutil
|
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
8
9
|
import tarfile
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
import time
|
|
11
|
+
import platform
|
|
12
|
+
from urllib.error import URLError
|
|
13
|
+
from urllib.request import urlopen
|
|
14
|
+
from metaflow.metaflow_config import DATASTORE_LOCAL_DIR, CONDA_USE_FAST_INIT
|
|
15
|
+
from metaflow.packaging_sys import MetaflowCodeContent, ContentType
|
|
11
16
|
from metaflow.plugins import DATASTORES
|
|
17
|
+
from metaflow.plugins.pypi.utils import MICROMAMBA_MIRROR_URL, MICROMAMBA_URL
|
|
12
18
|
from metaflow.util import which
|
|
19
|
+
from urllib.request import Request
|
|
20
|
+
import warnings
|
|
13
21
|
|
|
14
22
|
from . import MAGIC_FILE, _datastore_packageroot
|
|
15
23
|
|
|
24
|
+
FAST_INIT_BIN_URL = "https://fast-flow-init.outerbounds.sh/{platform}/latest"
|
|
25
|
+
|
|
16
26
|
# Bootstraps a valid conda virtual environment composed of conda and pypi packages
|
|
17
27
|
|
|
28
|
+
|
|
29
|
+
def timer(func):
|
|
30
|
+
def wrapper(*args, **kwargs):
|
|
31
|
+
start_time = time.time()
|
|
32
|
+
result = func(*args, **kwargs)
|
|
33
|
+
duration = time.time() - start_time
|
|
34
|
+
# print(f"Time taken for {func.__name__}: {duration:.2f} seconds")
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
return wrapper
|
|
38
|
+
|
|
39
|
+
|
|
18
40
|
if __name__ == "__main__":
|
|
19
|
-
if len(sys.argv) != 5:
|
|
20
|
-
print("Usage: bootstrap.py <flow_name> <id> <datastore_type> <architecture>")
|
|
21
|
-
sys.exit(1)
|
|
22
|
-
_, flow_name, id_, datastore_type, architecture = sys.argv
|
|
23
|
-
|
|
24
|
-
# TODO: Detect architecture on the fly when dealing with arm architectures.
|
|
25
|
-
# ARCH=$(uname -m)
|
|
26
|
-
# OS=$(uname)
|
|
27
|
-
|
|
28
|
-
# if [[ "$OS" == "Linux" ]]; then
|
|
29
|
-
# PLATFORM="linux"
|
|
30
|
-
# if [[ "$ARCH" == "aarch64" ]]; then
|
|
31
|
-
# ARCH="aarch64";
|
|
32
|
-
# elif [[ $ARCH == "ppc64le" ]]; then
|
|
33
|
-
# ARCH="ppc64le";
|
|
34
|
-
# else
|
|
35
|
-
# ARCH="64";
|
|
36
|
-
# fi
|
|
37
|
-
# fi
|
|
38
|
-
|
|
39
|
-
# if [[ "$OS" == "Darwin" ]]; then
|
|
40
|
-
# PLATFORM="osx";
|
|
41
|
-
# if [[ "$ARCH" == "arm64" ]]; then
|
|
42
|
-
# ARCH="arm64";
|
|
43
|
-
# else
|
|
44
|
-
# ARCH="64"
|
|
45
|
-
# fi
|
|
46
|
-
# fi
|
|
47
|
-
|
|
48
|
-
prefix = os.path.join(os.getcwd(), architecture, id_)
|
|
49
|
-
pkgs_dir = os.path.join(os.getcwd(), ".pkgs")
|
|
50
|
-
manifest_dir = os.path.join(os.getcwd(), DATASTORE_LOCAL_DIR, flow_name)
|
|
51
|
-
|
|
52
|
-
datastores = [d for d in DATASTORES if d.TYPE == datastore_type]
|
|
53
|
-
if not datastores:
|
|
54
|
-
print(f"No datastore found for type: {datastore_type}")
|
|
55
|
-
sys.exit(1)
|
|
56
41
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
def run_cmd(cmd, stdin_str=None):
|
|
43
|
+
result = subprocess.run(
|
|
44
|
+
cmd,
|
|
45
|
+
shell=True,
|
|
46
|
+
input=stdin_str,
|
|
47
|
+
stdout=subprocess.PIPE,
|
|
48
|
+
stderr=subprocess.PIPE,
|
|
49
|
+
text=True,
|
|
50
|
+
)
|
|
51
|
+
if result.returncode != 0:
|
|
52
|
+
print(f"Bootstrap failed while executing: {cmd}")
|
|
53
|
+
print("Stdout:", result.stdout)
|
|
54
|
+
print("Stderr:", result.stderr)
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
@timer
|
|
58
|
+
def install_fast_initializer(architecture):
|
|
59
|
+
import gzip
|
|
60
|
+
|
|
61
|
+
fast_initializer_path = os.path.join(
|
|
62
|
+
os.getcwd(), "fast-initializer", "bin", "fast-initializer"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if which("fast-initializer"):
|
|
66
|
+
return which("fast-initializer")
|
|
67
|
+
if os.path.exists(fast_initializer_path):
|
|
68
|
+
os.environ["PATH"] += os.pathsep + os.path.dirname(fast_initializer_path)
|
|
69
|
+
return fast_initializer_path
|
|
70
|
+
|
|
71
|
+
url = FAST_INIT_BIN_URL.format(platform=architecture)
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
shutil.move(
|
|
64
|
-
os.path.join(os.getcwd(), MAGIC_FILE),
|
|
65
|
-
os.path.join(manifest_dir, MAGIC_FILE),
|
|
66
|
-
)
|
|
73
|
+
# Prepare directory once
|
|
74
|
+
os.makedirs(os.path.dirname(fast_initializer_path), exist_ok=True)
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
# Download and decompress in one go
|
|
77
|
+
def _download_and_extract(url):
|
|
78
|
+
headers = {
|
|
79
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
80
|
+
"Connection": "keep-alive",
|
|
81
|
+
"User-Agent": "python-urllib",
|
|
82
|
+
}
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
max_retries = 3
|
|
85
|
+
for attempt in range(max_retries):
|
|
86
|
+
try:
|
|
87
|
+
req = Request(url, headers=headers)
|
|
88
|
+
with urlopen(req) as response:
|
|
89
|
+
with gzip.GzipFile(fileobj=response) as gz:
|
|
90
|
+
with open(fast_initializer_path, "wb") as f:
|
|
91
|
+
f.write(gz.read())
|
|
92
|
+
break
|
|
93
|
+
except (URLError, IOError) as e:
|
|
94
|
+
if attempt == max_retries - 1:
|
|
95
|
+
raise Exception(
|
|
96
|
+
f"Failed to download fast-initializer after {max_retries} attempts: {e}"
|
|
97
|
+
)
|
|
98
|
+
time.sleep(2**attempt)
|
|
99
|
+
|
|
100
|
+
_download_and_extract(url)
|
|
101
|
+
|
|
102
|
+
# Set executable permission
|
|
103
|
+
os.chmod(fast_initializer_path, 0o755)
|
|
104
|
+
|
|
105
|
+
# Update PATH only once at the end
|
|
106
|
+
os.environ["PATH"] += os.pathsep + os.path.dirname(fast_initializer_path)
|
|
107
|
+
return fast_initializer_path
|
|
108
|
+
|
|
109
|
+
@timer
|
|
110
|
+
def install_micromamba(architecture):
|
|
111
|
+
micromamba_dir = os.path.join(os.getcwd(), "micromamba")
|
|
112
|
+
micromamba_path = os.path.join(micromamba_dir, "bin", "micromamba")
|
|
113
|
+
|
|
114
|
+
if which("micromamba"):
|
|
115
|
+
return which("micromamba")
|
|
116
|
+
if os.path.exists(micromamba_path):
|
|
117
|
+
os.environ["PATH"] += os.pathsep + os.path.dirname(micromamba_path)
|
|
118
|
+
return micromamba_path
|
|
119
|
+
|
|
120
|
+
# Download and extract in one go
|
|
121
|
+
url = MICROMAMBA_URL.format(platform=architecture, version="2.0.4")
|
|
122
|
+
mirror_url = MICROMAMBA_MIRROR_URL.format(
|
|
123
|
+
platform=architecture, version="2.0.4"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Prepare directory once
|
|
127
|
+
os.makedirs(os.path.dirname(micromamba_path), exist_ok=True)
|
|
128
|
+
|
|
129
|
+
# Download and decompress in one go
|
|
130
|
+
def _download_and_extract(url):
|
|
131
|
+
headers = {
|
|
132
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
133
|
+
"Connection": "keep-alive",
|
|
134
|
+
"User-Agent": "python-urllib",
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
max_retries = 3
|
|
138
|
+
for attempt in range(max_retries):
|
|
139
|
+
try:
|
|
140
|
+
req = Request(url, headers=headers)
|
|
141
|
+
|
|
142
|
+
with urlopen(req) as response:
|
|
143
|
+
decompressor = bz2.BZ2Decompressor()
|
|
144
|
+
with warnings.catch_warnings():
|
|
145
|
+
warnings.filterwarnings(
|
|
146
|
+
"ignore", category=DeprecationWarning
|
|
147
|
+
)
|
|
148
|
+
with tarfile.open(
|
|
149
|
+
fileobj=io.BytesIO(
|
|
150
|
+
decompressor.decompress(response.read())
|
|
151
|
+
),
|
|
152
|
+
mode="r:",
|
|
153
|
+
) as tar:
|
|
154
|
+
member = tar.getmember("bin/micromamba")
|
|
155
|
+
tar.extract(member, micromamba_dir)
|
|
156
|
+
break
|
|
157
|
+
except (URLError, IOError) as e:
|
|
158
|
+
if attempt == max_retries - 1:
|
|
159
|
+
raise Exception(
|
|
160
|
+
f"Failed to download micromamba after {max_retries} attempts: {e}"
|
|
161
|
+
)
|
|
162
|
+
time.sleep(2**attempt)
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
# first try from mirror
|
|
166
|
+
_download_and_extract(mirror_url)
|
|
167
|
+
except Exception:
|
|
168
|
+
# download from mirror failed, try official source before failing.
|
|
169
|
+
_download_and_extract(url)
|
|
170
|
+
|
|
171
|
+
# Set executable permission
|
|
172
|
+
os.chmod(micromamba_path, 0o755)
|
|
173
|
+
|
|
174
|
+
# Update PATH only once at the end
|
|
175
|
+
os.environ["PATH"] += os.pathsep + os.path.dirname(micromamba_path)
|
|
176
|
+
return micromamba_path
|
|
177
|
+
|
|
178
|
+
@timer
|
|
179
|
+
def download_conda_packages(storage, packages, dest_dir):
|
|
180
|
+
def process_conda_package(args):
|
|
75
181
|
# Ensure that conda packages go into architecture specific folders.
|
|
76
182
|
# The path looks like REPO/CHANNEL/CONDA_SUBDIR/PACKAGE. We trick
|
|
77
183
|
# Micromamba into believing that all packages are coming from a local
|
|
78
184
|
# channel - the only hurdle is ensuring that packages are organised
|
|
79
185
|
# properly.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
dest = os.path.join(conda_pkgs_dir, "/".join(key.split("/")[-2:]))
|
|
186
|
+
key, tmpfile, dest_dir = args
|
|
187
|
+
dest = os.path.join(dest_dir, "/".join(key.split("/")[-2:]))
|
|
83
188
|
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
84
189
|
shutil.move(tmpfile, dest)
|
|
85
190
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
191
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
192
|
+
with storage.load_bytes([package["path"] for package in packages]) as results:
|
|
193
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
194
|
+
executor.map(
|
|
195
|
+
process_conda_package,
|
|
196
|
+
[(key, tmpfile, dest_dir) for key, tmpfile, _ in results],
|
|
197
|
+
)
|
|
198
|
+
# for key, tmpfile, _ in results:
|
|
199
|
+
|
|
200
|
+
# # TODO: consider RAM disk
|
|
201
|
+
# dest = os.path.join(dest_dir, "/".join(key.split("/")[-2:]))
|
|
202
|
+
# os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
203
|
+
# shutil.move(tmpfile, dest)
|
|
204
|
+
return dest_dir
|
|
205
|
+
|
|
206
|
+
@timer
|
|
207
|
+
def download_pypi_packages(storage, packages, dest_dir):
|
|
208
|
+
def process_pypi_package(args):
|
|
209
|
+
key, tmpfile, dest_dir = args
|
|
210
|
+
dest = os.path.join(dest_dir, os.path.basename(key))
|
|
211
|
+
shutil.move(tmpfile, dest)
|
|
212
|
+
|
|
213
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
214
|
+
with storage.load_bytes([package["path"] for package in packages]) as results:
|
|
215
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
216
|
+
executor.map(
|
|
217
|
+
process_pypi_package,
|
|
218
|
+
[(key, tmpfile, dest_dir) for key, tmpfile, _ in results],
|
|
219
|
+
)
|
|
220
|
+
# for key, tmpfile, _ in results:
|
|
221
|
+
# dest = os.path.join(dest_dir, os.path.basename(key))
|
|
222
|
+
# shutil.move(tmpfile, dest)
|
|
223
|
+
return dest_dir
|
|
224
|
+
|
|
225
|
+
@timer
|
|
226
|
+
def create_conda_environment(prefix, conda_pkgs_dir):
|
|
227
|
+
cmd = f'''set -e;
|
|
228
|
+
tmpfile=$(mktemp);
|
|
229
|
+
echo "@EXPLICIT" > "$tmpfile";
|
|
230
|
+
ls -d {conda_pkgs_dir}/*/* >> "$tmpfile";
|
|
94
231
|
export PATH=$PATH:$(pwd)/micromamba;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
232
|
+
export CONDA_PKGS_DIRS=$(pwd)/micromamba/pkgs;
|
|
233
|
+
export MAMBA_NO_LOW_SPEED_LIMIT=1;
|
|
234
|
+
export MAMBA_USE_INDEX_CACHE=1;
|
|
235
|
+
export MAMBA_NO_PROGRESS_BARS=1;
|
|
236
|
+
export CONDA_FETCH_THREADS=1;
|
|
237
|
+
micromamba create --yes --offline --no-deps \
|
|
238
|
+
--safety-checks=disabled --no-extra-safety-checks \
|
|
239
|
+
--prefix {prefix} --file "$tmpfile" \
|
|
240
|
+
--no-pyc --no-rc --always-copy;
|
|
241
|
+
rm "$tmpfile"'''
|
|
242
|
+
run_cmd(cmd)
|
|
243
|
+
|
|
244
|
+
@timer
|
|
245
|
+
def install_pypi_packages(prefix, pypi_pkgs_dir):
|
|
246
|
+
cmd = f"""set -e;
|
|
247
|
+
export PATH=$PATH:$(pwd)/micromamba;
|
|
248
|
+
export CONDA_PKGS_DIRS=$(pwd)/micromamba/pkgs;
|
|
249
|
+
micromamba run --prefix {prefix} python -m pip --disable-pip-version-check \
|
|
250
|
+
install --root-user-action=ignore --no-compile --no-index \
|
|
251
|
+
--no-cache-dir --no-deps --prefer-binary \
|
|
252
|
+
--find-links={pypi_pkgs_dir} --no-user \
|
|
253
|
+
--no-warn-script-location --no-input \
|
|
254
|
+
{pypi_pkgs_dir}/*.whl
|
|
255
|
+
"""
|
|
256
|
+
run_cmd(cmd)
|
|
257
|
+
|
|
258
|
+
@timer
|
|
259
|
+
def setup_environment(
|
|
260
|
+
architecture, storage, env, prefix, conda_pkgs_dir, pypi_pkgs_dir
|
|
261
|
+
):
|
|
262
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
|
|
263
|
+
# install micromamba, download conda and pypi packages in parallel
|
|
264
|
+
futures = {
|
|
265
|
+
"micromamba": executor.submit(install_micromamba, architecture),
|
|
266
|
+
"conda_pkgs": executor.submit(
|
|
267
|
+
download_conda_packages, storage, env["conda"], conda_pkgs_dir
|
|
268
|
+
),
|
|
269
|
+
}
|
|
270
|
+
if "pypi" in env:
|
|
271
|
+
futures["pypi_pkgs"] = executor.submit(
|
|
272
|
+
download_pypi_packages, storage, env["pypi"], pypi_pkgs_dir
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# create conda environment after micromamba is installed and conda packages are downloaded
|
|
276
|
+
done, _ = concurrent.futures.wait(
|
|
277
|
+
[futures["micromamba"], futures["conda_pkgs"]],
|
|
278
|
+
return_when=concurrent.futures.ALL_COMPLETED,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
for future in done:
|
|
282
|
+
future.result()
|
|
283
|
+
|
|
284
|
+
# start conda environment creation
|
|
285
|
+
futures["conda_env"] = executor.submit(
|
|
286
|
+
create_conda_environment, prefix, conda_pkgs_dir
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if "pypi" in env:
|
|
290
|
+
# install pypi packages after conda environment is created and pypi packages are downloaded
|
|
291
|
+
done, _ = concurrent.futures.wait(
|
|
292
|
+
[futures["conda_env"], futures["pypi_pkgs"]],
|
|
293
|
+
return_when=concurrent.futures.ALL_COMPLETED,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
for future in done:
|
|
297
|
+
future.result()
|
|
298
|
+
|
|
299
|
+
# install pypi packages
|
|
300
|
+
futures["pypi_install"] = executor.submit(
|
|
301
|
+
install_pypi_packages, prefix, pypi_pkgs_dir
|
|
302
|
+
)
|
|
303
|
+
# wait for pypi packages to be installed
|
|
304
|
+
futures["pypi_install"].result()
|
|
305
|
+
else:
|
|
306
|
+
# wait for conda environment to be created
|
|
307
|
+
futures["conda_env"].result()
|
|
308
|
+
|
|
309
|
+
@timer
|
|
310
|
+
def fast_setup_environment(architecture, storage, env, prefix, pkgs_dir):
|
|
311
|
+
install_fast_initializer(architecture)
|
|
312
|
+
|
|
313
|
+
# Get package urls
|
|
314
|
+
conda_pkgs = env["conda"]
|
|
315
|
+
pypi_pkgs = env.get("pypi", [])
|
|
316
|
+
conda_pkg_urls = [package["path"] for package in conda_pkgs]
|
|
317
|
+
pypi_pkg_urls = [package["path"] for package in pypi_pkgs]
|
|
318
|
+
|
|
319
|
+
# Create string with package URLs
|
|
320
|
+
all_package_urls = ""
|
|
321
|
+
for url in conda_pkg_urls:
|
|
322
|
+
all_package_urls += f"{storage.datastore_root}/{url}\n"
|
|
323
|
+
all_package_urls += "---\n"
|
|
324
|
+
for url in pypi_pkg_urls:
|
|
325
|
+
all_package_urls += f"{storage.datastore_root}/{url}\n"
|
|
326
|
+
|
|
327
|
+
# Initialize environment
|
|
328
|
+
# NOTE: For the time being the fast-initializer only works for the S3 datastore implementation
|
|
329
|
+
cmd = f"fast-initializer --prefix {prefix} --packages-dir {pkgs_dir}"
|
|
330
|
+
run_cmd(cmd, all_package_urls)
|
|
331
|
+
|
|
332
|
+
if len(sys.argv) != 4:
|
|
333
|
+
print("Usage: bootstrap.py <flow_name> <id> <datastore_type>")
|
|
334
|
+
sys.exit(1)
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
_, flow_name, id_, datastore_type = sys.argv
|
|
338
|
+
|
|
339
|
+
system = platform.system().lower()
|
|
340
|
+
arch_machine = platform.machine().lower()
|
|
341
|
+
|
|
342
|
+
if system == "darwin" and arch_machine == "arm64":
|
|
343
|
+
architecture = "osx-arm64"
|
|
344
|
+
elif system == "darwin":
|
|
345
|
+
architecture = "osx-64"
|
|
346
|
+
elif system == "linux" and arch_machine == "aarch64":
|
|
347
|
+
architecture = "linux-aarch64"
|
|
348
|
+
else:
|
|
349
|
+
# default fallback
|
|
350
|
+
architecture = "linux-64"
|
|
351
|
+
|
|
352
|
+
prefix = os.path.join(os.getcwd(), architecture, id_)
|
|
353
|
+
pkgs_dir = os.path.join(os.getcwd(), ".pkgs")
|
|
354
|
+
conda_pkgs_dir = os.path.join(pkgs_dir, "conda")
|
|
112
355
|
pypi_pkgs_dir = os.path.join(pkgs_dir, "pypi")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
cmds.extend(
|
|
123
|
-
[
|
|
124
|
-
f"""set -e;
|
|
125
|
-
export PATH=$PATH:$(pwd)/micromamba;
|
|
126
|
-
micromamba run --prefix {prefix} pip --disable-pip-version-check install --root-user-action=ignore --no-compile {pypi_pkgs_dir}/*.whl"""
|
|
127
|
-
]
|
|
356
|
+
manifest_dir = os.path.join(os.getcwd(), DATASTORE_LOCAL_DIR, flow_name)
|
|
357
|
+
|
|
358
|
+
datastores = [d for d in DATASTORES if d.TYPE == datastore_type]
|
|
359
|
+
if not datastores:
|
|
360
|
+
print(f"No datastore found for type: {datastore_type}")
|
|
361
|
+
sys.exit(1)
|
|
362
|
+
|
|
363
|
+
storage = datastores[0](
|
|
364
|
+
_datastore_packageroot(datastores[0], lambda *args, **kwargs: None)
|
|
128
365
|
)
|
|
129
366
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
367
|
+
# Move MAGIC_FILE inside local datastore.
|
|
368
|
+
os.makedirs(manifest_dir, exist_ok=True)
|
|
369
|
+
path_to_manifest = MetaflowCodeContent.get_filename(
|
|
370
|
+
MAGIC_FILE, ContentType.OTHER_CONTENT
|
|
133
371
|
)
|
|
134
|
-
if
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
372
|
+
if path_to_manifest is None:
|
|
373
|
+
raise RuntimeError(f"Cannot find {MAGIC_FILE} in the package")
|
|
374
|
+
shutil.move(
|
|
375
|
+
path_to_manifest,
|
|
376
|
+
os.path.join(manifest_dir, MAGIC_FILE),
|
|
377
|
+
)
|
|
378
|
+
with open(os.path.join(manifest_dir, MAGIC_FILE)) as f:
|
|
379
|
+
env = json.load(f)[id_][architecture]
|
|
380
|
+
|
|
381
|
+
if CONDA_USE_FAST_INIT:
|
|
382
|
+
fast_setup_environment(architecture, storage, env, prefix, pkgs_dir)
|
|
383
|
+
else:
|
|
384
|
+
setup_environment(
|
|
385
|
+
architecture, storage, env, prefix, conda_pkgs_dir, pypi_pkgs_dir
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
except Exception as e:
|
|
389
|
+
print(f"Error: {str(e)}", file=sys.stderr)
|
|
390
|
+
sys.exit(1)
|