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,5 +1,3 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
import json
|
|
3
1
|
import os
|
|
4
2
|
import platform
|
|
5
3
|
import re
|
|
@@ -7,12 +5,9 @@ import sys
|
|
|
7
5
|
import tempfile
|
|
8
6
|
|
|
9
7
|
from metaflow.decorators import FlowDecorator, StepDecorator
|
|
10
|
-
from metaflow.
|
|
11
|
-
from metaflow.metadata import MetaDatum
|
|
8
|
+
from metaflow.metadata_provider import MetaDatum
|
|
12
9
|
from metaflow.metaflow_environment import InvalidEnvironmentException
|
|
13
|
-
from metaflow.
|
|
14
|
-
|
|
15
|
-
from ... import INFO_FILE
|
|
10
|
+
from metaflow.packaging_sys import ContentType
|
|
16
11
|
|
|
17
12
|
|
|
18
13
|
class CondaStepDecorator(StepDecorator):
|
|
@@ -45,19 +40,36 @@ class CondaStepDecorator(StepDecorator):
|
|
|
45
40
|
"python": None,
|
|
46
41
|
"disabled": None,
|
|
47
42
|
}
|
|
43
|
+
|
|
44
|
+
_metaflow_home = None
|
|
45
|
+
_addl_env_vars = None
|
|
46
|
+
|
|
48
47
|
# To define conda channels for the whole solve, users can specify
|
|
49
48
|
# CONDA_CHANNELS in their environment. For pinning specific packages to specific
|
|
50
49
|
# conda channels, users can specify channel::package as the package name.
|
|
51
50
|
|
|
52
|
-
def __init__(self, attributes=None, statically_defined=False):
|
|
53
|
-
|
|
51
|
+
def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
|
|
52
|
+
self._attributes_with_user_values = (
|
|
53
|
+
set(attributes.keys()) if attributes is not None else set()
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
super(CondaStepDecorator, self).__init__(
|
|
57
|
+
attributes, statically_defined, inserted_by
|
|
58
|
+
)
|
|
54
59
|
|
|
60
|
+
def init(self):
|
|
55
61
|
# Support legacy 'libraries=' attribute for the decorator.
|
|
56
62
|
self.attributes["packages"] = {
|
|
57
63
|
**self.attributes["libraries"],
|
|
58
64
|
**self.attributes["packages"],
|
|
59
65
|
}
|
|
60
|
-
|
|
66
|
+
# Keep because otherwise make_decorator_spec will fail
|
|
67
|
+
self.attributes["libraries"] = {}
|
|
68
|
+
if self.attributes["packages"]:
|
|
69
|
+
self._attributes_with_user_values.add("packages")
|
|
70
|
+
|
|
71
|
+
def is_attribute_user_defined(self, name):
|
|
72
|
+
return name in self._attributes_with_user_values
|
|
61
73
|
|
|
62
74
|
def step_init(self, flow, graph, step, decos, environment, flow_datastore, logger):
|
|
63
75
|
# The init_environment hook for Environment creates the relevant virtual
|
|
@@ -71,11 +83,16 @@ class CondaStepDecorator(StepDecorator):
|
|
|
71
83
|
|
|
72
84
|
# Support flow-level decorator.
|
|
73
85
|
if "conda_base" in self.flow._flow_decorators:
|
|
74
|
-
|
|
86
|
+
conda_base = self.flow._flow_decorators["conda_base"][0]
|
|
87
|
+
super_attributes = conda_base.attributes
|
|
75
88
|
self.attributes["packages"] = {
|
|
76
89
|
**super_attributes["packages"],
|
|
77
90
|
**self.attributes["packages"],
|
|
78
91
|
}
|
|
92
|
+
self._attributes_with_user_values.update(
|
|
93
|
+
conda_base._attributes_with_user_values
|
|
94
|
+
)
|
|
95
|
+
|
|
79
96
|
self.attributes["python"] = (
|
|
80
97
|
self.attributes["python"] or super_attributes["python"]
|
|
81
98
|
)
|
|
@@ -100,6 +117,10 @@ class CondaStepDecorator(StepDecorator):
|
|
|
100
117
|
# --environment=pypi to --environment=conda
|
|
101
118
|
_supported_virtual_envs.extend(["pypi"])
|
|
102
119
|
|
|
120
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
|
121
|
+
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
|
122
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
|
123
|
+
|
|
103
124
|
# The --environment= requirement ensures that valid virtual environments are
|
|
104
125
|
# created for every step to execute it, greatly simplifying the @conda
|
|
105
126
|
# implementation.
|
|
@@ -131,67 +152,17 @@ class CondaStepDecorator(StepDecorator):
|
|
|
131
152
|
def runtime_init(self, flow, graph, package, run_id):
|
|
132
153
|
if self.disabled:
|
|
133
154
|
return
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
os.symlink(
|
|
145
|
-
info, os.path.join(self.metaflow_dir.name, os.path.basename(INFO_FILE))
|
|
155
|
+
# We need to make all the code package available to the user code in
|
|
156
|
+
# a temporary directory which will be added to the PYTHONPATH.
|
|
157
|
+
if self.__class__._metaflow_home is None:
|
|
158
|
+
# Do this ONCE per flow
|
|
159
|
+
self.__class__._metaflow_home = tempfile.TemporaryDirectory(dir="/tmp")
|
|
160
|
+
package.extract_into(
|
|
161
|
+
self.__class__._metaflow_home.name, ContentType.ALL_CONTENT
|
|
162
|
+
)
|
|
163
|
+
self.__class__._addl_env_vars = package.get_post_extract_env_vars(
|
|
164
|
+
package.package_metadata, self.__class__._metaflow_home.name
|
|
146
165
|
)
|
|
147
|
-
else:
|
|
148
|
-
# If there is no info file, we will actually create one in this new
|
|
149
|
-
# place because we won't be able to properly resolve the EXT_PKG extensions
|
|
150
|
-
# the same way as outside conda (looking at distributions, etc.). In a
|
|
151
|
-
# Conda environment, as shown below (where we set self.addl_paths), all
|
|
152
|
-
# EXT_PKG extensions are PYTHONPATH extensions. Instead of re-resolving,
|
|
153
|
-
# we use the resolved information that is written out to the INFO file.
|
|
154
|
-
with open(
|
|
155
|
-
os.path.join(self.metaflow_dir.name, os.path.basename(INFO_FILE)),
|
|
156
|
-
mode="wt",
|
|
157
|
-
encoding="utf-8",
|
|
158
|
-
) as f:
|
|
159
|
-
f.write(
|
|
160
|
-
json.dumps(
|
|
161
|
-
self.environment.get_environment_info(include_ext_info=True)
|
|
162
|
-
)
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
# Support metaflow extensions.
|
|
166
|
-
self.addl_paths = None
|
|
167
|
-
try:
|
|
168
|
-
m = importlib.import_module(EXT_PKG)
|
|
169
|
-
except ImportError:
|
|
170
|
-
# No additional check needed because if we are here, we already checked
|
|
171
|
-
# for other issues when loading at the toplevel.
|
|
172
|
-
pass
|
|
173
|
-
else:
|
|
174
|
-
custom_paths = list(set(m.__path__))
|
|
175
|
-
# For some reason, at times, unique paths appear multiple times. We
|
|
176
|
-
# simplify to avoid un-necessary links.
|
|
177
|
-
|
|
178
|
-
if len(custom_paths) == 1:
|
|
179
|
-
# Regular package; we take a quick shortcut here.
|
|
180
|
-
os.symlink(
|
|
181
|
-
custom_paths[0],
|
|
182
|
-
os.path.join(self.metaflow_dir.name, EXT_PKG),
|
|
183
|
-
)
|
|
184
|
-
else:
|
|
185
|
-
# This is a namespace package, we therefore create a bunch of
|
|
186
|
-
# directories so that we can symlink in those separately, and we will
|
|
187
|
-
# add those paths to the PYTHONPATH for the interpreter. Note that we
|
|
188
|
-
# don't symlink to the parent of the package because that could end up
|
|
189
|
-
# including more stuff we don't want
|
|
190
|
-
self.addl_paths = []
|
|
191
|
-
for p in custom_paths:
|
|
192
|
-
temp_dir = tempfile.mkdtemp(dir=self.metaflow_dir.name)
|
|
193
|
-
os.symlink(p, os.path.join(temp_dir, EXT_PKG))
|
|
194
|
-
self.addl_paths.append(temp_dir)
|
|
195
166
|
|
|
196
167
|
# # Also install any environment escape overrides directly here to enable
|
|
197
168
|
# # the escape to work even in non metaflow-created subprocesses
|
|
@@ -206,7 +177,8 @@ class CondaStepDecorator(StepDecorator):
|
|
|
206
177
|
self.interpreter = (
|
|
207
178
|
self.environment.interpreter(self.step)
|
|
208
179
|
if not any(
|
|
209
|
-
decorator.name
|
|
180
|
+
decorator.name
|
|
181
|
+
in ["batch", "kubernetes", "nvidia", "snowpark", "slurm", "nvct"]
|
|
210
182
|
for decorator in next(
|
|
211
183
|
step for step in self.flow if step.name == self.step
|
|
212
184
|
).decorators
|
|
@@ -269,11 +241,17 @@ class CondaStepDecorator(StepDecorator):
|
|
|
269
241
|
if self.disabled:
|
|
270
242
|
return
|
|
271
243
|
# Ensure local installation of Metaflow is visible to user code
|
|
272
|
-
python_path = self.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
244
|
+
python_path = self.__class__._metaflow_home.name
|
|
245
|
+
addl_env_vars = {}
|
|
246
|
+
if self.__class__._addl_env_vars:
|
|
247
|
+
for key, value in self.__class__._addl_env_vars.items():
|
|
248
|
+
if key.endswith(":"):
|
|
249
|
+
addl_env_vars[key[:-1]] = value
|
|
250
|
+
elif key == "PYTHONPATH":
|
|
251
|
+
addl_env_vars[key] = os.pathsep.join([value, python_path])
|
|
252
|
+
else:
|
|
253
|
+
addl_env_vars[key] = value
|
|
254
|
+
cli_args.env.update(addl_env_vars)
|
|
277
255
|
if self.interpreter:
|
|
278
256
|
# https://github.com/conda/conda/issues/7707
|
|
279
257
|
# Also ref - https://github.com/Netflix/metaflow/pull/178
|
|
@@ -284,7 +262,9 @@ class CondaStepDecorator(StepDecorator):
|
|
|
284
262
|
def runtime_finished(self, exception):
|
|
285
263
|
if self.disabled:
|
|
286
264
|
return
|
|
287
|
-
self.
|
|
265
|
+
if self.__class__._metaflow_home is not None:
|
|
266
|
+
self.__class__._metaflow_home.cleanup()
|
|
267
|
+
self.__class__._metaflow_home = None
|
|
288
268
|
|
|
289
269
|
|
|
290
270
|
class CondaFlowDecorator(FlowDecorator):
|
|
@@ -317,21 +297,38 @@ class CondaFlowDecorator(FlowDecorator):
|
|
|
317
297
|
"disabled": None,
|
|
318
298
|
}
|
|
319
299
|
|
|
320
|
-
def __init__(self, attributes=None, statically_defined=False):
|
|
321
|
-
|
|
300
|
+
def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
|
|
301
|
+
self._attributes_with_user_values = (
|
|
302
|
+
set(attributes.keys()) if attributes is not None else set()
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
super(CondaFlowDecorator, self).__init__(
|
|
306
|
+
attributes, statically_defined, inserted_by
|
|
307
|
+
)
|
|
322
308
|
|
|
309
|
+
def init(self):
|
|
323
310
|
# Support legacy 'libraries=' attribute for the decorator.
|
|
324
311
|
self.attributes["packages"] = {
|
|
325
312
|
**self.attributes["libraries"],
|
|
326
313
|
**self.attributes["packages"],
|
|
327
314
|
}
|
|
328
|
-
|
|
315
|
+
# Keep because otherwise make_decorator_spec will fail
|
|
316
|
+
self.attributes["libraries"] = {}
|
|
329
317
|
if self.attributes["python"]:
|
|
330
318
|
self.attributes["python"] = str(self.attributes["python"])
|
|
331
319
|
|
|
320
|
+
def is_attribute_user_defined(self, name):
|
|
321
|
+
return name in self._attributes_with_user_values
|
|
322
|
+
|
|
332
323
|
def flow_init(
|
|
333
324
|
self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
|
|
334
325
|
):
|
|
326
|
+
# NOTE: Important for extensions implementing custom virtual environments.
|
|
327
|
+
# Without this steps will not have an implicit conda step decorator on them unless the environment adds one in its decospecs.
|
|
328
|
+
from metaflow import decorators
|
|
329
|
+
|
|
330
|
+
decorators._attach_decorators(flow, ["conda"])
|
|
331
|
+
|
|
335
332
|
# @conda uses a conda environment to create a virtual environment.
|
|
336
333
|
# The conda environment can be created through micromamba.
|
|
337
334
|
_supported_virtual_envs = ["conda"]
|
|
@@ -340,6 +337,10 @@ class CondaFlowDecorator(FlowDecorator):
|
|
|
340
337
|
# --environment=pypi to --environment=conda
|
|
341
338
|
_supported_virtual_envs.extend(["pypi"])
|
|
342
339
|
|
|
340
|
+
# TODO: Hardcoded for now to support the fast bakery environment.
|
|
341
|
+
# We should introduce a more robust mechanism for appending supported environments, for example from within extensions.
|
|
342
|
+
_supported_virtual_envs.extend(["fast-bakery"])
|
|
343
|
+
|
|
343
344
|
# The --environment= requirement ensures that valid virtual environments are
|
|
344
345
|
# created for every step to execute it, greatly simplifying the @conda
|
|
345
346
|
# implementation.
|
|
@@ -5,21 +5,19 @@ import functools
|
|
|
5
5
|
import io
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
-
import sys
|
|
9
8
|
import tarfile
|
|
10
|
-
import
|
|
11
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
9
|
+
import threading
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
11
|
+
from functools import wraps
|
|
12
12
|
from hashlib import sha256
|
|
13
13
|
from io import BufferedIOBase, BytesIO
|
|
14
|
-
from itertools import chain
|
|
15
14
|
from urllib.parse import unquote, urlparse
|
|
16
15
|
|
|
17
|
-
import
|
|
18
|
-
|
|
16
|
+
from metaflow.debug import debug
|
|
19
17
|
from metaflow.exception import MetaflowException
|
|
20
18
|
from metaflow.metaflow_config import get_pinned_conda_libs
|
|
21
19
|
from metaflow.metaflow_environment import MetaflowEnvironment
|
|
22
|
-
from metaflow.
|
|
20
|
+
from metaflow.packaging_sys import ContentType
|
|
23
21
|
|
|
24
22
|
from . import MAGIC_FILE, _datastore_packageroot
|
|
25
23
|
from .utils import conda_platform
|
|
@@ -35,8 +33,10 @@ class CondaEnvironmentException(MetaflowException):
|
|
|
35
33
|
class CondaEnvironment(MetaflowEnvironment):
|
|
36
34
|
TYPE = "conda"
|
|
37
35
|
_filecache = None
|
|
36
|
+
_force_rebuild = False
|
|
38
37
|
|
|
39
38
|
def __init__(self, flow):
|
|
39
|
+
super().__init__(flow)
|
|
40
40
|
self.flow = flow
|
|
41
41
|
|
|
42
42
|
def set_local_root(self, local_root):
|
|
@@ -48,9 +48,8 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
48
48
|
# Apply conda decorator to manage the task execution lifecycle.
|
|
49
49
|
return ("conda",) + super().decospecs()
|
|
50
50
|
|
|
51
|
-
def validate_environment(self,
|
|
51
|
+
def validate_environment(self, logger, datastore_type):
|
|
52
52
|
self.datastore_type = datastore_type
|
|
53
|
-
self.echo = echo
|
|
54
53
|
|
|
55
54
|
# Avoiding circular imports.
|
|
56
55
|
from metaflow.plugins import DATASTORES
|
|
@@ -62,10 +61,23 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
62
61
|
from .micromamba import Micromamba
|
|
63
62
|
from .pip import Pip
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
print_lock = threading.Lock()
|
|
65
|
+
|
|
66
|
+
def make_thread_safe(func):
|
|
67
|
+
@wraps(func)
|
|
68
|
+
def wrapper(*args, **kwargs):
|
|
69
|
+
with print_lock:
|
|
70
|
+
return func(*args, **kwargs)
|
|
71
|
+
|
|
72
|
+
return wrapper
|
|
73
|
+
|
|
74
|
+
self.logger = make_thread_safe(logger)
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
# TODO: Wire up logging
|
|
77
|
+
micromamba = Micromamba(self.logger, self._force_rebuild)
|
|
78
|
+
self.solvers = {"conda": micromamba, "pypi": Pip(micromamba, self.logger)}
|
|
79
|
+
|
|
80
|
+
def init_environment(self, echo, only_steps=None):
|
|
69
81
|
# The implementation optimizes for latency to ensure as many operations can
|
|
70
82
|
# be turned into cheap no-ops as feasible. Otherwise, we focus on maintaining
|
|
71
83
|
# a balance between latency and maintainability of code without re-implementing
|
|
@@ -77,6 +89,8 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
77
89
|
def environments(type_):
|
|
78
90
|
seen = set()
|
|
79
91
|
for step in self.flow:
|
|
92
|
+
if only_steps and step.name not in only_steps:
|
|
93
|
+
continue
|
|
80
94
|
environment = self.get_environment(step)
|
|
81
95
|
if type_ in environment and environment["id_"] not in seen:
|
|
82
96
|
seen.add(environment["id_"])
|
|
@@ -96,7 +110,10 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
96
110
|
return (
|
|
97
111
|
id_,
|
|
98
112
|
(
|
|
99
|
-
|
|
113
|
+
(
|
|
114
|
+
not self._force_rebuild
|
|
115
|
+
and self.read_from_environment_manifest([id_, platform, type_])
|
|
116
|
+
)
|
|
100
117
|
or self.write_to_environment_manifest(
|
|
101
118
|
[id_, platform, type_],
|
|
102
119
|
self.solvers[type_].solve(id_, **environment),
|
|
@@ -107,6 +124,11 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
107
124
|
)
|
|
108
125
|
|
|
109
126
|
def cache(storage, results, type_):
|
|
127
|
+
debug.conda_exec(
|
|
128
|
+
"Caching packages for %s environments %s"
|
|
129
|
+
% (type_, [result[0] for result in results])
|
|
130
|
+
)
|
|
131
|
+
|
|
110
132
|
def _path(url, local_path):
|
|
111
133
|
# Special handling for VCS packages
|
|
112
134
|
if url.startswith("git+"):
|
|
@@ -137,7 +159,7 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
137
159
|
_meta = copy.deepcopy(local_packages)
|
|
138
160
|
for id_, packages, _, _ in results:
|
|
139
161
|
for package in packages:
|
|
140
|
-
if package.get("path"):
|
|
162
|
+
if package.get("path") and not self._force_rebuild:
|
|
141
163
|
# Cache only those packages that manifest is unaware of
|
|
142
164
|
local_packages.pop(package["url"], None)
|
|
143
165
|
else:
|
|
@@ -148,6 +170,9 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
148
170
|
(
|
|
149
171
|
package["path"],
|
|
150
172
|
# Lazily fetch package from the interweb if needed.
|
|
173
|
+
# TODO: Depending on the len_hint, the package might be downloaded from
|
|
174
|
+
# the interweb prematurely. save_bytes needs to be adjusted to handle
|
|
175
|
+
# this scenario.
|
|
151
176
|
LazyOpen(
|
|
152
177
|
package["local_path"],
|
|
153
178
|
"rb",
|
|
@@ -156,41 +181,131 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
156
181
|
)
|
|
157
182
|
for url, package in local_packages.items()
|
|
158
183
|
]
|
|
184
|
+
debug.conda_exec(
|
|
185
|
+
"Caching %s new packages to the datastore for %s environment %s"
|
|
186
|
+
% (
|
|
187
|
+
len(list_of_path_and_filehandle),
|
|
188
|
+
type_,
|
|
189
|
+
[result[0] for result in results],
|
|
190
|
+
)
|
|
191
|
+
)
|
|
159
192
|
storage.save_bytes(
|
|
160
193
|
list_of_path_and_filehandle,
|
|
161
194
|
len_hint=len(list_of_path_and_filehandle),
|
|
195
|
+
overwrite=self._force_rebuild,
|
|
162
196
|
)
|
|
163
197
|
for id_, packages, _, platform in results:
|
|
164
198
|
if id_ in dirty:
|
|
165
199
|
self.write_to_environment_manifest([id_, platform, type_], packages)
|
|
166
200
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
201
|
+
debug.conda_exec("Finished caching packages.")
|
|
202
|
+
|
|
203
|
+
storage = None
|
|
204
|
+
if self.datastore_type not in ["local"]:
|
|
205
|
+
# Initialize storage for caching if using a remote datastore
|
|
206
|
+
storage = self.datastore(_datastore_packageroot(self.datastore, echo))
|
|
207
|
+
|
|
208
|
+
self.logger("Bootstrapping virtual environment(s) ...")
|
|
209
|
+
# Sequence of operations:
|
|
210
|
+
# 1. Start all conda solves in parallel
|
|
211
|
+
# 2. Download conda packages sequentially
|
|
212
|
+
# 3. Create and cache conda environments in parallel
|
|
213
|
+
# 4. Start PyPI solves in parallel after each conda environment is created
|
|
214
|
+
# 5. Download PyPI packages sequentially
|
|
215
|
+
# 6. Create and cache PyPI environments in parallel
|
|
216
|
+
with ThreadPoolExecutor() as executor:
|
|
217
|
+
# Start all conda solves in parallel
|
|
218
|
+
debug.conda_exec("Solving packages for Conda environments..")
|
|
219
|
+
conda_solve_futures = [
|
|
220
|
+
executor.submit(lambda x: solve(*x, "conda"), env)
|
|
221
|
+
for env in environments("conda")
|
|
222
|
+
]
|
|
223
|
+
conda_create_futures = []
|
|
224
|
+
|
|
225
|
+
pypi_envs = {env[0]: env for env in environments("pypi")}
|
|
226
|
+
pypi_solve_futures = []
|
|
227
|
+
pypi_create_futures = []
|
|
228
|
+
|
|
229
|
+
cache_futures = []
|
|
230
|
+
# Process conda results sequentially for downloads
|
|
231
|
+
for future in as_completed(conda_solve_futures):
|
|
232
|
+
result = future.result()
|
|
233
|
+
# Sequential conda download
|
|
234
|
+
debug.conda_exec(
|
|
235
|
+
"Downloading packages for Conda environment %s" % result[0]
|
|
173
236
|
)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
237
|
+
self.solvers["conda"].download(*result)
|
|
238
|
+
# Parallel conda create and cache
|
|
239
|
+
conda_create_future = executor.submit(
|
|
240
|
+
self.solvers["conda"].create, *result
|
|
178
241
|
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
242
|
+
if storage:
|
|
243
|
+
cache_futures.append(
|
|
244
|
+
executor.submit(cache, storage, [result], "conda")
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Queue PyPI solve to start after conda create
|
|
248
|
+
if result[0] in pypi_envs:
|
|
249
|
+
debug.conda_exec(
|
|
250
|
+
"Solving packages for PyPI environment %s" % result[0]
|
|
251
|
+
)
|
|
252
|
+
# solve pypi envs uniquely
|
|
253
|
+
pypi_env = pypi_envs.pop(result[0])
|
|
254
|
+
|
|
255
|
+
def pypi_solve(env):
|
|
256
|
+
conda_create_future.result() # Wait for conda create
|
|
257
|
+
return solve(*env, "pypi")
|
|
258
|
+
|
|
259
|
+
pypi_solve_futures.append(executor.submit(pypi_solve, pypi_env))
|
|
260
|
+
else:
|
|
261
|
+
# add conda create future to the generic list
|
|
262
|
+
conda_create_futures.append(conda_create_future)
|
|
263
|
+
|
|
264
|
+
# Process PyPI results sequentially for downloads
|
|
265
|
+
for solve_future in as_completed(pypi_solve_futures):
|
|
266
|
+
result = solve_future.result()
|
|
267
|
+
# Sequential PyPI download
|
|
268
|
+
debug.conda_exec(
|
|
269
|
+
"Downloading packages for PyPI environment %s" % result[0]
|
|
270
|
+
)
|
|
271
|
+
self.solvers["pypi"].download(*result)
|
|
272
|
+
# Parallel PyPI create and cache
|
|
273
|
+
pypi_create_futures.append(
|
|
274
|
+
executor.submit(self.solvers["pypi"].create, *result)
|
|
183
275
|
)
|
|
184
|
-
|
|
185
|
-
|
|
276
|
+
if storage:
|
|
277
|
+
cache_futures.append(
|
|
278
|
+
executor.submit(cache, storage, [result], "pypi")
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Raise exceptions for conda create
|
|
282
|
+
debug.conda_exec("Checking results for Conda create..")
|
|
283
|
+
for future in as_completed(conda_create_futures):
|
|
284
|
+
future.result()
|
|
285
|
+
|
|
286
|
+
# Raise exceptions for pypi create
|
|
287
|
+
debug.conda_exec("Checking results for PyPI create..")
|
|
288
|
+
for future in as_completed(pypi_create_futures):
|
|
289
|
+
future.result()
|
|
290
|
+
|
|
291
|
+
# Raise exceptions for caching
|
|
292
|
+
debug.conda_exec("Checking results for caching..")
|
|
293
|
+
for future in as_completed(cache_futures):
|
|
294
|
+
# check for result in order to raise any exceptions.
|
|
295
|
+
future.result()
|
|
296
|
+
|
|
297
|
+
self.logger("Virtual environment(s) bootstrapped!")
|
|
186
298
|
|
|
187
299
|
def executable(self, step_name, default=None):
|
|
188
|
-
step = next(step for step in self.flow if step.name == step_name)
|
|
300
|
+
step = next((step for step in self.flow if step.name == step_name), None)
|
|
301
|
+
if step is None:
|
|
302
|
+
# requesting internal steps e.g. _parameters
|
|
303
|
+
return super().executable(step_name, default)
|
|
189
304
|
id_ = self.get_environment(step).get("id_")
|
|
190
305
|
if id_:
|
|
191
306
|
# bootstrap.py is responsible for ensuring the validity of this executable.
|
|
192
307
|
# -s is important! Can otherwise leak packages to other environments.
|
|
193
|
-
return os.path.join("
|
|
308
|
+
return os.path.join("$MF_ARCH", id_, "bin/python -s")
|
|
194
309
|
else:
|
|
195
310
|
# for @conda/@pypi(disabled=True).
|
|
196
311
|
return super().executable(step_name, default)
|
|
@@ -220,9 +335,9 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
220
335
|
disabled = decorator.attributes["disabled"]
|
|
221
336
|
if not disabled or str(disabled).lower() == "false":
|
|
222
337
|
environment[decorator.name] = {
|
|
223
|
-
k: decorator.attributes[k]
|
|
338
|
+
k: copy.deepcopy(decorator.attributes[k])
|
|
224
339
|
for k in decorator.attributes
|
|
225
|
-
if k
|
|
340
|
+
if k not in ("disabled", "libraries")
|
|
226
341
|
}
|
|
227
342
|
else:
|
|
228
343
|
return {}
|
|
@@ -263,14 +378,21 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
263
378
|
# 5. All resolved packages (Conda or PyPI) are cached
|
|
264
379
|
# 6. PyPI packages are only installed for local platform
|
|
265
380
|
|
|
266
|
-
# Resolve `linux-64` Conda environments if @batch or @kubernetes are in play
|
|
267
381
|
target_platform = conda_platform()
|
|
268
382
|
for decorator in step.decorators:
|
|
269
|
-
#
|
|
270
|
-
#
|
|
271
|
-
if
|
|
272
|
-
|
|
273
|
-
|
|
383
|
+
# NOTE: Keep the list of supported decorator names for backward compatibility purposes.
|
|
384
|
+
# Older versions did not implement the 'support_conda_environment' attribute.
|
|
385
|
+
if getattr(
|
|
386
|
+
decorator, "supports_conda_environment", False
|
|
387
|
+
) or decorator.name in [
|
|
388
|
+
"batch",
|
|
389
|
+
"kubernetes",
|
|
390
|
+
"nvidia",
|
|
391
|
+
"snowpark",
|
|
392
|
+
"slurm",
|
|
393
|
+
"nvct",
|
|
394
|
+
]:
|
|
395
|
+
target_platform = getattr(decorator, "target_platform", "linux-64")
|
|
274
396
|
break
|
|
275
397
|
|
|
276
398
|
environment["conda"]["platforms"] = [target_platform]
|
|
@@ -288,19 +410,20 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
288
410
|
# PyPI registries, the usage of environment variable `GOOGLE_APPLICATION_CREDENTIALS`
|
|
289
411
|
# demands that `keyrings.google-artifactregistry-auth` has to be installed
|
|
290
412
|
# and available in the underlying python environment.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
413
|
+
|
|
414
|
+
# commenting this out per https://outerboundsco.slack.com/archives/C040K733FND/p1719262399355449
|
|
415
|
+
# this should be a temporary workaround. Need to find a better fix
|
|
416
|
+
# if os.getenv("GOOGLE_APPLICATION_CREDENTIALS"):
|
|
417
|
+
# environment["conda"]["packages"][
|
|
418
|
+
# "keyrings.google-artifactregistry-auth"
|
|
419
|
+
# ] = ">=1.1.1"
|
|
295
420
|
|
|
296
421
|
# Z combinator for a recursive lambda
|
|
297
422
|
deep_sort = (lambda f: f(f))(
|
|
298
423
|
lambda f: lambda obj: (
|
|
299
424
|
{k: f(f)(v) for k, v in sorted(obj.items())}
|
|
300
425
|
if isinstance(obj, dict)
|
|
301
|
-
else sorted([f(f)(e) for e in obj])
|
|
302
|
-
if isinstance(obj, list)
|
|
303
|
-
else obj
|
|
426
|
+
else sorted([f(f)(e) for e in obj]) if isinstance(obj, list) else obj
|
|
304
427
|
)
|
|
305
428
|
)
|
|
306
429
|
|
|
@@ -316,7 +439,7 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
316
439
|
**environment,
|
|
317
440
|
**{
|
|
318
441
|
"package_root": _datastore_packageroot(
|
|
319
|
-
self.datastore, self.
|
|
442
|
+
self.datastore, self.logger
|
|
320
443
|
)
|
|
321
444
|
},
|
|
322
445
|
}
|
|
@@ -356,7 +479,9 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
356
479
|
files = []
|
|
357
480
|
manifest = self.get_environment_manifest_path()
|
|
358
481
|
if os.path.exists(manifest):
|
|
359
|
-
files.append(
|
|
482
|
+
files.append(
|
|
483
|
+
(manifest, os.path.basename(manifest), ContentType.OTHER_CONTENT)
|
|
484
|
+
)
|
|
360
485
|
return files
|
|
361
486
|
|
|
362
487
|
def bootstrap_commands(self, step_name, datastore_type):
|
|
@@ -366,14 +491,18 @@ class CondaEnvironment(MetaflowEnvironment):
|
|
|
366
491
|
if id_:
|
|
367
492
|
return [
|
|
368
493
|
"echo 'Bootstrapping virtual environment...'",
|
|
494
|
+
"flush_mflogs",
|
|
369
495
|
# We have to prevent the tracing module from loading,
|
|
370
496
|
# as the bootstrapping process uses the internal S3 client which would fail to import tracing
|
|
371
497
|
# due to the required dependencies being bundled into the conda environment,
|
|
372
498
|
# which is yet to be initialized at this point.
|
|
373
|
-
'DISABLE_TRACING=True python -m metaflow.plugins.pypi.bootstrap "%s" %s "%s"
|
|
499
|
+
'DISABLE_TRACING=True python -m metaflow.plugins.pypi.bootstrap "%s" %s "%s"'
|
|
374
500
|
% (self.flow.name, id_, self.datastore_type),
|
|
375
501
|
"echo 'Environment bootstrapped.'",
|
|
376
|
-
"
|
|
502
|
+
"flush_mflogs",
|
|
503
|
+
# To avoid having to install micromamba in the PATH in micromamba.py, we add it to the PATH here.
|
|
504
|
+
"export PATH=$PATH:$(pwd)/micromamba/bin",
|
|
505
|
+
"export MF_ARCH=$(case $(uname)/$(uname -m) in Darwin/arm64)echo osx-arm64;;Darwin/*)echo osx-64;;Linux/aarch64)echo linux-aarch64;;*)echo linux-64;;esac)",
|
|
377
506
|
]
|
|
378
507
|
else:
|
|
379
508
|
# for @conda/@pypi(disabled=True).
|
|
@@ -434,6 +563,7 @@ class LazyOpen(BufferedIOBase):
|
|
|
434
563
|
self._file = None
|
|
435
564
|
self._buffer = None
|
|
436
565
|
self._position = 0
|
|
566
|
+
self.requests = None
|
|
437
567
|
|
|
438
568
|
def _ensure_file(self):
|
|
439
569
|
if not self._file:
|
|
@@ -450,8 +580,13 @@ class LazyOpen(BufferedIOBase):
|
|
|
450
580
|
raise ValueError("Both filename and url are missing")
|
|
451
581
|
|
|
452
582
|
def _download_to_buffer(self):
|
|
583
|
+
if self.requests is None:
|
|
584
|
+
# TODO: Remove dependency on requests
|
|
585
|
+
import requests
|
|
586
|
+
|
|
587
|
+
self.requests = requests
|
|
453
588
|
# TODO: Stream it in chunks?
|
|
454
|
-
response = requests.get(self.url, stream=True)
|
|
589
|
+
response = self.requests.get(self.url, stream=True)
|
|
455
590
|
response.raise_for_status()
|
|
456
591
|
return response.content
|
|
457
592
|
|