ob-metaflow 2.9.10.1__py2.py3-none-any.whl → 2.10.2.6__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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/_vendor/packaging/__init__.py +15 -0
- metaflow/_vendor/packaging/_elffile.py +108 -0
- metaflow/_vendor/packaging/_manylinux.py +238 -0
- metaflow/_vendor/packaging/_musllinux.py +80 -0
- metaflow/_vendor/packaging/_parser.py +328 -0
- metaflow/_vendor/packaging/_structures.py +61 -0
- metaflow/_vendor/packaging/_tokenizer.py +188 -0
- metaflow/_vendor/packaging/markers.py +245 -0
- metaflow/_vendor/packaging/requirements.py +95 -0
- metaflow/_vendor/packaging/specifiers.py +1005 -0
- metaflow/_vendor/packaging/tags.py +546 -0
- metaflow/_vendor/packaging/utils.py +141 -0
- metaflow/_vendor/packaging/version.py +563 -0
- metaflow/_vendor/v3_7/__init__.py +1 -0
- metaflow/_vendor/v3_7/zipp.py +329 -0
- metaflow/metaflow_config.py +2 -1
- metaflow/metaflow_environment.py +3 -1
- metaflow/mflog/mflog.py +7 -1
- metaflow/multicore_utils.py +12 -2
- metaflow/plugins/__init__.py +8 -3
- metaflow/plugins/airflow/airflow.py +13 -0
- metaflow/plugins/argo/argo_client.py +16 -0
- metaflow/plugins/argo/argo_events.py +7 -1
- metaflow/plugins/argo/argo_workflows.py +62 -0
- metaflow/plugins/argo/argo_workflows_cli.py +15 -0
- metaflow/plugins/aws/batch/batch.py +10 -0
- metaflow/plugins/aws/batch/batch_cli.py +1 -2
- metaflow/plugins/aws/batch/batch_decorator.py +2 -9
- metaflow/plugins/datatools/s3/s3.py +4 -0
- metaflow/plugins/env_escape/client.py +24 -3
- metaflow/plugins/env_escape/stub.py +2 -8
- metaflow/plugins/kubernetes/kubernetes.py +13 -0
- metaflow/plugins/kubernetes/kubernetes_cli.py +1 -2
- metaflow/plugins/kubernetes/kubernetes_decorator.py +9 -2
- metaflow/plugins/pypi/__init__.py +29 -0
- metaflow/plugins/pypi/bootstrap.py +131 -0
- metaflow/plugins/pypi/conda_decorator.py +335 -0
- metaflow/plugins/pypi/conda_environment.py +414 -0
- metaflow/plugins/pypi/micromamba.py +294 -0
- metaflow/plugins/pypi/pip.py +205 -0
- metaflow/plugins/pypi/pypi_decorator.py +130 -0
- metaflow/plugins/pypi/pypi_environment.py +7 -0
- metaflow/plugins/pypi/utils.py +75 -0
- metaflow/task.py +0 -3
- metaflow/vendor.py +1 -0
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/METADATA +1 -1
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/RECORD +51 -33
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/WHEEL +1 -1
- metaflow/plugins/conda/__init__.py +0 -90
- metaflow/plugins/conda/batch_bootstrap.py +0 -104
- metaflow/plugins/conda/conda.py +0 -247
- metaflow/plugins/conda/conda_environment.py +0 -136
- metaflow/plugins/conda/conda_flow_decorator.py +0 -35
- metaflow/plugins/conda/conda_step_decorator.py +0 -416
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/LICENSE +0 -0
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.9.10.1.dist-info → ob_metaflow-2.10.2.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
from metaflow.decorators import FlowDecorator, StepDecorator
|
|
9
|
+
from metaflow.extension_support import EXT_PKG
|
|
10
|
+
from metaflow.metaflow_environment import InvalidEnvironmentException
|
|
11
|
+
from metaflow.util import get_metaflow_root
|
|
12
|
+
|
|
13
|
+
from ... import INFO_FILE
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CondaStepDecorator(StepDecorator):
|
|
17
|
+
"""
|
|
18
|
+
Specifies the Conda environment for the step.
|
|
19
|
+
|
|
20
|
+
Information in this decorator will augment any
|
|
21
|
+
attributes set in the `@conda_base` flow-level decorator. Hence,
|
|
22
|
+
you can use `@conda_base` to set packages required by all
|
|
23
|
+
steps and use `@conda` to specify step-specific overrides.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
packages : Dict[str, str], default: {}
|
|
28
|
+
Packages to use for this step. The key is the name of the package
|
|
29
|
+
and the value is the version to use.
|
|
30
|
+
libraries : Dict[str, str], default: {}
|
|
31
|
+
Supported for backward compatibility. When used with packages, packages will take precedence.
|
|
32
|
+
python : str, optional
|
|
33
|
+
Version of Python to use, e.g. '3.7.4'. A default value of None implies
|
|
34
|
+
that the version used will correspond to the version of the Python interpreter used to start the run.
|
|
35
|
+
disabled : bool, default: False
|
|
36
|
+
If set to True, disables @conda.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
name = "conda"
|
|
40
|
+
defaults = {
|
|
41
|
+
"packages": {},
|
|
42
|
+
"libraries": {}, # Deprecated! Use packages going forward
|
|
43
|
+
"python": None,
|
|
44
|
+
"disabled": None,
|
|
45
|
+
}
|
|
46
|
+
# To define conda channels for the whole solve, users can specify
|
|
47
|
+
# CONDA_CHANNELS in their environment. For pinning specific packages to specific
|
|
48
|
+
# conda channels, users can specify channel::package as the package name.
|
|
49
|
+
|
|
50
|
+
def __init__(self, attributes=None, statically_defined=False):
|
|
51
|
+
super(CondaStepDecorator, self).__init__(attributes, statically_defined)
|
|
52
|
+
|
|
53
|
+
# Support legacy 'libraries=' attribute for the decorator.
|
|
54
|
+
self.attributes["packages"] = {
|
|
55
|
+
**self.attributes["libraries"],
|
|
56
|
+
**self.attributes["packages"],
|
|
57
|
+
}
|
|
58
|
+
del self.attributes["libraries"]
|
|
59
|
+
|
|
60
|
+
def step_init(self, flow, graph, step, decos, environment, flow_datastore, logger):
|
|
61
|
+
# The init_environment hook for Environment creates the relevant virtual
|
|
62
|
+
# environments. The step_init hook sets up the relevant state for that hook to
|
|
63
|
+
# do it's magic.
|
|
64
|
+
|
|
65
|
+
self.flow = flow
|
|
66
|
+
self.step = step
|
|
67
|
+
self.environment = environment
|
|
68
|
+
self.datastore = flow_datastore
|
|
69
|
+
|
|
70
|
+
# Support flow-level decorator.
|
|
71
|
+
if "conda_base" in self.flow._flow_decorators:
|
|
72
|
+
super_attributes = self.flow._flow_decorators["conda_base"][0].attributes
|
|
73
|
+
self.attributes["packages"] = {
|
|
74
|
+
**super_attributes["packages"],
|
|
75
|
+
**self.attributes["packages"],
|
|
76
|
+
}
|
|
77
|
+
self.attributes["python"] = (
|
|
78
|
+
self.attributes["python"] or super_attributes["python"]
|
|
79
|
+
)
|
|
80
|
+
self.attributes["disabled"] = (
|
|
81
|
+
self.attributes["disabled"]
|
|
82
|
+
if self.attributes["disabled"] is not None
|
|
83
|
+
else super_attributes["disabled"]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Set default for `disabled` argument.
|
|
87
|
+
if not self.attributes["disabled"]:
|
|
88
|
+
self.attributes["disabled"] = False
|
|
89
|
+
# Set Python interpreter to user's Python if necessary.
|
|
90
|
+
if not self.attributes["python"]:
|
|
91
|
+
self.attributes["python"] = platform.python_version() # CPython!
|
|
92
|
+
|
|
93
|
+
# @conda uses a conda environment to create a virtual environment.
|
|
94
|
+
# The conda environment can be created through micromamba.
|
|
95
|
+
_supported_virtual_envs = ["conda"]
|
|
96
|
+
|
|
97
|
+
# To placate people who don't want to see a shred of conda in UX, we symlink
|
|
98
|
+
# --environment=pypi to --environment=conda
|
|
99
|
+
_supported_virtual_envs.extend(["pypi"])
|
|
100
|
+
|
|
101
|
+
# The --environment= requirement ensures that valid virtual environments are
|
|
102
|
+
# created for every step to execute it, greatly simplifying the @conda
|
|
103
|
+
# implementation.
|
|
104
|
+
if environment.TYPE not in _supported_virtual_envs:
|
|
105
|
+
raise InvalidEnvironmentException(
|
|
106
|
+
"@%s decorator requires %s"
|
|
107
|
+
% (
|
|
108
|
+
self.name,
|
|
109
|
+
" or ".join(
|
|
110
|
+
["--environment=%s" % env for env in _supported_virtual_envs]
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# At this point, the list of 32 bit instance types is shrinking quite rapidly.
|
|
116
|
+
# We can worry about supporting them when there is a need.
|
|
117
|
+
|
|
118
|
+
# TODO: This code snippet can be done away with by altering the constructor of
|
|
119
|
+
# MetaflowEnvironment. A good first-task exercise.
|
|
120
|
+
# Avoid circular import
|
|
121
|
+
from metaflow.plugins.datastores.local_storage import LocalStorage
|
|
122
|
+
|
|
123
|
+
environment.set_local_root(LocalStorage.get_datastore_root_from_config(logger))
|
|
124
|
+
|
|
125
|
+
self.disabled = not self.environment.get_environment(
|
|
126
|
+
next(step for step in self.flow if step.name == self.step)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def runtime_init(self, flow, graph, package, run_id):
|
|
130
|
+
if self.disabled:
|
|
131
|
+
return
|
|
132
|
+
# Create a symlink to metaflow installed outside the virtual environment.
|
|
133
|
+
self.metaflow_dir = tempfile.TemporaryDirectory(dir="/tmp")
|
|
134
|
+
os.symlink(
|
|
135
|
+
os.path.join(get_metaflow_root(), "metaflow"),
|
|
136
|
+
os.path.join(self.metaflow_dir.name, "metaflow"),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
info = os.path.join(get_metaflow_root(), os.path.basename(INFO_FILE))
|
|
140
|
+
# Symlink the INFO file as well to properly propagate down the Metaflow version
|
|
141
|
+
if os.path.isfile(info):
|
|
142
|
+
os.symlink(
|
|
143
|
+
info, os.path.join(self.metaflow_dir.name, os.path.basename(INFO_FILE))
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
# If there is no info file, we will actually create one in this new
|
|
147
|
+
# place because we won't be able to properly resolve the EXT_PKG extensions
|
|
148
|
+
# the same way as outside conda (looking at distributions, etc.). In a
|
|
149
|
+
# Conda environment, as shown below (where we set self.addl_paths), all
|
|
150
|
+
# EXT_PKG extensions are PYTHONPATH extensions. Instead of re-resolving,
|
|
151
|
+
# we use the resolved information that is written out to the INFO file.
|
|
152
|
+
with open(
|
|
153
|
+
os.path.join(self.metaflow_dir.name, os.path.basename(INFO_FILE)),
|
|
154
|
+
mode="wt",
|
|
155
|
+
encoding="utf-8",
|
|
156
|
+
) as f:
|
|
157
|
+
f.write(
|
|
158
|
+
json.dumps(
|
|
159
|
+
self.environment.get_environment_info(include_ext_info=True)
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Support metaflow extensions.
|
|
164
|
+
self.addl_paths = None
|
|
165
|
+
try:
|
|
166
|
+
m = importlib.import_module(EXT_PKG)
|
|
167
|
+
except ImportError:
|
|
168
|
+
# No additional check needed because if we are here, we already checked
|
|
169
|
+
# for other issues when loading at the toplevel.
|
|
170
|
+
pass
|
|
171
|
+
else:
|
|
172
|
+
custom_paths = list(set(m.__path__))
|
|
173
|
+
# For some reason, at times, unique paths appear multiple times. We
|
|
174
|
+
# simplify to avoid un-necessary links.
|
|
175
|
+
|
|
176
|
+
if len(custom_paths) == 1:
|
|
177
|
+
# Regular package; we take a quick shortcut here.
|
|
178
|
+
os.symlink(
|
|
179
|
+
custom_paths[0],
|
|
180
|
+
os.path.join(self.metaflow_dir.name, EXT_PKG),
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
# This is a namespace package, we therefore create a bunch of
|
|
184
|
+
# directories so that we can symlink in those separately, and we will
|
|
185
|
+
# add those paths to the PYTHONPATH for the interpreter. Note that we
|
|
186
|
+
# don't symlink to the parent of the package because that could end up
|
|
187
|
+
# including more stuff we don't want
|
|
188
|
+
self.addl_paths = []
|
|
189
|
+
for p in custom_paths:
|
|
190
|
+
temp_dir = tempfile.mkdtemp(dir=self.metaflow_dir.name)
|
|
191
|
+
os.symlink(p, os.path.join(temp_dir, EXT_PKG))
|
|
192
|
+
self.addl_paths.append(temp_dir)
|
|
193
|
+
|
|
194
|
+
# # Also install any environment escape overrides directly here to enable
|
|
195
|
+
# # the escape to work even in non metaflow-created subprocesses
|
|
196
|
+
# from ..env_escape import generate_trampolines
|
|
197
|
+
# generate_trampolines(self.metaflow_dir.name)
|
|
198
|
+
|
|
199
|
+
def runtime_task_created(
|
|
200
|
+
self, task_datastore, task_id, split_index, input_paths, is_cloned, ubf_context
|
|
201
|
+
):
|
|
202
|
+
if self.disabled:
|
|
203
|
+
return
|
|
204
|
+
self.interpreter = (
|
|
205
|
+
self.environment.interpreter(self.step)
|
|
206
|
+
if not any(
|
|
207
|
+
decorator.name in ["batch", "kubernetes"]
|
|
208
|
+
for decorator in next(
|
|
209
|
+
step for step in self.flow if step.name == self.step
|
|
210
|
+
).decorators
|
|
211
|
+
)
|
|
212
|
+
else None
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def task_pre_step(
|
|
216
|
+
self,
|
|
217
|
+
step_name,
|
|
218
|
+
task_datastore,
|
|
219
|
+
meta,
|
|
220
|
+
run_id,
|
|
221
|
+
task_id,
|
|
222
|
+
flow,
|
|
223
|
+
graph,
|
|
224
|
+
retry_count,
|
|
225
|
+
max_retries,
|
|
226
|
+
ubf_context,
|
|
227
|
+
inputs,
|
|
228
|
+
):
|
|
229
|
+
if self.disabled:
|
|
230
|
+
return
|
|
231
|
+
# Add Python interpreter's parent to the path to ensure that any non-pythonic
|
|
232
|
+
# dependencies in the virtual environment are visible to the user code.
|
|
233
|
+
# sys.executable points to the Python interpreter in the virtual environment
|
|
234
|
+
# since we are already inside the task context.
|
|
235
|
+
os.environ["PATH"] = os.pathsep.join(
|
|
236
|
+
filter(
|
|
237
|
+
None,
|
|
238
|
+
(
|
|
239
|
+
os.path.dirname(os.path.realpath(sys.executable)),
|
|
240
|
+
os.environ.get("PATH"),
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
# TODO: Register metadata
|
|
245
|
+
|
|
246
|
+
def runtime_step_cli(
|
|
247
|
+
self, cli_args, retry_count, max_user_code_retries, ubf_context
|
|
248
|
+
):
|
|
249
|
+
if self.disabled:
|
|
250
|
+
return
|
|
251
|
+
# Ensure local installation of Metaflow is visible to user code
|
|
252
|
+
python_path = self.metaflow_dir.name
|
|
253
|
+
if self.addl_paths is not None:
|
|
254
|
+
addl_paths = os.pathsep.join(self.addl_paths)
|
|
255
|
+
python_path = os.pathsep.join([addl_paths, python_path])
|
|
256
|
+
cli_args.env["PYTHONPATH"] = python_path
|
|
257
|
+
if self.interpreter:
|
|
258
|
+
# https://github.com/conda/conda/issues/7707
|
|
259
|
+
# Also ref - https://github.com/Netflix/metaflow/pull/178
|
|
260
|
+
cli_args.env["PYTHONNOUSERSITE"] = "1"
|
|
261
|
+
# The executable is already in place for the user code to execute against
|
|
262
|
+
cli_args.entrypoint[0] = self.interpreter
|
|
263
|
+
|
|
264
|
+
def runtime_finished(self, exception):
|
|
265
|
+
if self.disabled:
|
|
266
|
+
return
|
|
267
|
+
self.metaflow_dir.cleanup()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class CondaFlowDecorator(FlowDecorator):
|
|
271
|
+
"""
|
|
272
|
+
Specifies the Conda environment for all steps of the flow.
|
|
273
|
+
|
|
274
|
+
Use `@conda_base` to set common libraries required by all
|
|
275
|
+
steps and use `@conda` to specify step-specific additions.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
packages : Dict[str, str], default: {}
|
|
280
|
+
Packages to use for this flow. The key is the name of the package
|
|
281
|
+
and the value is the version to use.
|
|
282
|
+
libraries : Dict[str, str], default: {}
|
|
283
|
+
Supported for backward compatibility. When used with packages, packages will take precedence.
|
|
284
|
+
python : str, optional
|
|
285
|
+
Version of Python to use, e.g. '3.7.4'. A default value of None implies
|
|
286
|
+
that the version used will correspond to the version of the Python interpreter used to start the run.
|
|
287
|
+
disabled : bool, default: False
|
|
288
|
+
If set to True, disables Conda.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
# TODO: Migrate conda_base keyword to conda for simplicity.
|
|
292
|
+
name = "conda_base"
|
|
293
|
+
defaults = {
|
|
294
|
+
"packages": {},
|
|
295
|
+
"libraries": {}, # Deprecated! Use packages going forward.
|
|
296
|
+
"python": None,
|
|
297
|
+
"disabled": None,
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
def __init__(self, attributes=None, statically_defined=False):
|
|
301
|
+
super(CondaFlowDecorator, self).__init__(attributes, statically_defined)
|
|
302
|
+
|
|
303
|
+
# Support legacy 'libraries=' attribute for the decorator.
|
|
304
|
+
self.attributes["packages"] = {
|
|
305
|
+
**self.attributes["libraries"],
|
|
306
|
+
**self.attributes["packages"],
|
|
307
|
+
}
|
|
308
|
+
del self.attributes["libraries"]
|
|
309
|
+
if self.attributes["python"]:
|
|
310
|
+
self.attributes["python"] = str(self.attributes["python"])
|
|
311
|
+
|
|
312
|
+
def flow_init(
|
|
313
|
+
self, flow, graph, environment, flow_datastore, metadata, logger, echo, options
|
|
314
|
+
):
|
|
315
|
+
# @conda uses a conda environment to create a virtual environment.
|
|
316
|
+
# The conda environment can be created through micromamba.
|
|
317
|
+
_supported_virtual_envs = ["conda"]
|
|
318
|
+
|
|
319
|
+
# To placate people who don't want to see a shred of conda in UX, we symlink
|
|
320
|
+
# --environment=pypi to --environment=conda
|
|
321
|
+
_supported_virtual_envs.extend(["pypi"])
|
|
322
|
+
|
|
323
|
+
# The --environment= requirement ensures that valid virtual environments are
|
|
324
|
+
# created for every step to execute it, greatly simplifying the @conda
|
|
325
|
+
# implementation.
|
|
326
|
+
if environment.TYPE not in _supported_virtual_envs:
|
|
327
|
+
raise InvalidEnvironmentException(
|
|
328
|
+
"@%s decorator requires %s"
|
|
329
|
+
% (
|
|
330
|
+
self.name,
|
|
331
|
+
" or ".join(
|
|
332
|
+
["--environment=%s" % env for env in _supported_virtual_envs]
|
|
333
|
+
),
|
|
334
|
+
)
|
|
335
|
+
)
|