metaflow 2.15.21__py2.py3-none-any.whl → 2.16.1__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/__init__.py +7 -1
- metaflow/cli.py +19 -1
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +8 -2
- metaflow/client/core.py +22 -30
- metaflow/datastore/task_datastore.py +0 -1
- metaflow/debug.py +5 -0
- metaflow/decorators.py +236 -70
- metaflow/extension_support/__init__.py +15 -8
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +92 -60
- metaflow/graph.py +24 -2
- metaflow/meta_files.py +13 -0
- metaflow/metadata_provider/metadata.py +7 -1
- metaflow/metaflow_config.py +5 -0
- metaflow/metaflow_environment.py +82 -25
- metaflow/metaflow_version.py +1 -1
- metaflow/package/__init__.py +664 -0
- metaflow/packaging_sys/__init__.py +870 -0
- metaflow/packaging_sys/backend.py +113 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +86 -0
- metaflow/packaging_sys/utils.py +91 -0
- metaflow/packaging_sys/v1.py +480 -0
- metaflow/plugins/airflow/airflow.py +5 -1
- metaflow/plugins/airflow/airflow_cli.py +16 -5
- metaflow/plugins/argo/argo_workflows.py +15 -4
- metaflow/plugins/argo/argo_workflows_cli.py +17 -4
- metaflow/plugins/aws/batch/batch.py +22 -3
- metaflow/plugins/aws/batch/batch_cli.py +3 -0
- metaflow/plugins/aws/batch/batch_decorator.py +13 -5
- metaflow/plugins/aws/step_functions/step_functions.py +4 -1
- metaflow/plugins/aws/step_functions/step_functions_cli.py +16 -4
- metaflow/plugins/cards/card_decorator.py +0 -5
- metaflow/plugins/kubernetes/kubernetes.py +8 -1
- metaflow/plugins/kubernetes/kubernetes_cli.py +3 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +13 -5
- metaflow/plugins/package_cli.py +25 -23
- metaflow/plugins/parallel_decorator.py +4 -2
- metaflow/plugins/pypi/bootstrap.py +8 -2
- metaflow/plugins/pypi/conda_decorator.py +39 -82
- metaflow/plugins/pypi/conda_environment.py +6 -2
- metaflow/plugins/pypi/pypi_decorator.py +4 -4
- metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
- metaflow/plugins/timeout_decorator.py +0 -1
- metaflow/plugins/uv/bootstrap.py +12 -1
- metaflow/plugins/uv/uv_environment.py +4 -2
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +5 -4
- metaflow/runner/subprocess_manager.py +14 -2
- metaflow/runtime.py +37 -11
- metaflow/task.py +92 -7
- metaflow/user_configs/config_options.py +13 -8
- metaflow/user_configs/config_parameters.py +0 -4
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +499 -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 +712 -0
- metaflow/util.py +4 -1
- metaflow/version.py +1 -1
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/METADATA +2 -2
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/RECORD +71 -60
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {metaflow-2.15.21.data → metaflow-2.16.1.data}/data/share/metaflow/devtools/Makefile +0 -0
- {metaflow-2.15.21.data → metaflow-2.16.1.data}/data/share/metaflow/devtools/Tiltfile +0 -0
- {metaflow-2.15.21.data → metaflow-2.16.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/WHEEL +0 -0
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/entry_points.txt +0 -0
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/licenses/LICENSE +0 -0
- {metaflow-2.15.21.dist-info → metaflow-2.16.1.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ from importlib.abc import MetaPathFinder, Loader
|
|
12
12
|
from itertools import chain
|
13
13
|
from pathlib import Path
|
14
14
|
|
15
|
-
from metaflow.
|
15
|
+
from metaflow.meta_files import read_info_file
|
16
16
|
|
17
17
|
|
18
18
|
#
|
@@ -214,6 +214,10 @@ def package_mfext_all():
|
|
214
214
|
yield path_tuple
|
215
215
|
|
216
216
|
|
217
|
+
def package_mfext_all_descriptions():
|
218
|
+
return _all_packages
|
219
|
+
|
220
|
+
|
217
221
|
def load_globals(module, dst_globals, extra_indent=False):
|
218
222
|
if extra_indent:
|
219
223
|
extra_indent = " "
|
@@ -808,13 +812,16 @@ def _get_extension_packages(ignore_info_file=False, restrict_to_directories=None
|
|
808
812
|
" Extends '%s' with config '%s'"
|
809
813
|
% (_extension_points[idx], config_module)
|
810
814
|
)
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
815
|
+
if files_to_include:
|
816
|
+
mf_pkg_list.append(package_name)
|
817
|
+
mf_ext_packages[package_name] = {
|
818
|
+
"root_paths": [package_path],
|
819
|
+
"meta_module": meta_module,
|
820
|
+
"files": files_to_include,
|
821
|
+
"version": "_local_",
|
822
|
+
}
|
823
|
+
else:
|
824
|
+
_ext_debug("Skipping package as no files found (empty dir?)")
|
818
825
|
|
819
826
|
# Sanity check that we only have one package per configuration file.
|
820
827
|
# This prevents multiple packages from providing the same named configuration
|
@@ -1,2 +1,2 @@
|
|
1
|
-
# This file serves as a __init__.py for metaflow_extensions
|
2
|
-
# and needs to remain empty.
|
1
|
+
# This file serves as a __init__.py for metaflow_extensions or metaflow
|
2
|
+
# packages when they are packaged and needs to remain empty.
|
metaflow/flowspec.py
CHANGED
@@ -7,7 +7,7 @@ import reprlib
|
|
7
7
|
from enum import Enum
|
8
8
|
from itertools import islice
|
9
9
|
from types import FunctionType, MethodType
|
10
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
|
11
11
|
|
12
12
|
from . import cmd_with_io, parameters
|
13
13
|
from .debug import debug
|
@@ -23,13 +23,14 @@ from .extension_support import extension_info
|
|
23
23
|
|
24
24
|
from .graph import FlowGraph
|
25
25
|
from .unbounded_foreach import UnboundedForeachInput
|
26
|
-
from .user_configs.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
from .user_configs.config_parameters import ConfigValue
|
27
|
+
|
28
|
+
from .user_decorators.mutable_flow import MutableFlow
|
29
|
+
from .user_decorators.mutable_step import MutableStep
|
30
|
+
from .user_decorators.user_flow_decorator import FlowMutator
|
31
|
+
from .user_decorators.user_step_decorator import StepMutator
|
32
|
+
|
33
|
+
|
33
34
|
from .util import to_pod
|
34
35
|
from .metaflow_config import INCLUDE_FOREACH_STACK, MAXIMUM_FOREACH_VALUE_CHARS
|
35
36
|
|
@@ -77,8 +78,12 @@ class ParallelUBF(UnboundedForeachInput):
|
|
77
78
|
|
78
79
|
class _FlowState(Enum):
|
79
80
|
CONFIGS = 1
|
80
|
-
|
81
|
+
FLOW_MUTATORS = 2
|
81
82
|
CACHED_PARAMETERS = 3
|
83
|
+
SET_CONFIG_PARAMETERS = (
|
84
|
+
4 # These are Parameters that now have a ConfigValue (converted)
|
85
|
+
)
|
86
|
+
# but we need to remember them.
|
82
87
|
|
83
88
|
|
84
89
|
class FlowSpecMeta(type):
|
@@ -130,12 +135,18 @@ class FlowSpecMeta(type):
|
|
130
135
|
raise DuplicateFlowDecoratorException(deco_name)
|
131
136
|
cls._flow_decorators.setdefault(deco_name, []).extend(deco)
|
132
137
|
|
133
|
-
# Take care of configs and
|
134
|
-
base_configs = base._flow_state.get(_FlowState.
|
138
|
+
# Take care of configs and flow mutators
|
139
|
+
base_configs = base._flow_state.get(_FlowState.CONFIGS)
|
135
140
|
if base_configs:
|
136
|
-
cls._flow_state.setdefault(_FlowState.
|
141
|
+
cls._flow_state.setdefault(_FlowState.CONFIGS, {}).update(
|
137
142
|
base_configs
|
138
143
|
)
|
144
|
+
base_mutators = base._flow_state.get(_FlowState.FLOW_MUTATORS)
|
145
|
+
if base_mutators:
|
146
|
+
cls._flow_state.setdefault(_FlowState.FLOW_MUTATORS, []).extend(
|
147
|
+
base_mutators
|
148
|
+
)
|
149
|
+
|
139
150
|
cls._init_graph()
|
140
151
|
|
141
152
|
def _init_graph(cls):
|
@@ -232,30 +243,32 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
232
243
|
|
233
244
|
@classmethod
|
234
245
|
def _process_config_decorators(cls, config_options, process_configs=True):
|
235
|
-
|
236
246
|
if cls._configs_processed:
|
247
|
+
debug.userconf_exec("Mutating step/flow decorators already processed")
|
237
248
|
return None
|
238
249
|
cls._configs_processed = True
|
239
250
|
|
240
251
|
# Fast path for no user configurations
|
241
252
|
if not process_configs or (
|
242
|
-
not cls._flow_state.get(_FlowState.
|
253
|
+
not cls._flow_state.get(_FlowState.FLOW_MUTATORS)
|
243
254
|
and all(len(step.config_decorators) == 0 for step in cls._steps)
|
244
255
|
):
|
245
256
|
# Process parameters to allow them to also use config values easily
|
246
257
|
for var, param in cls._get_parameters():
|
247
|
-
if param.IS_CONFIG_PARAMETER:
|
258
|
+
if isinstance(param, ConfigValue) or param.IS_CONFIG_PARAMETER:
|
248
259
|
continue
|
249
260
|
param.init(not process_configs)
|
250
261
|
return None
|
251
262
|
|
252
263
|
debug.userconf_exec("Processing mutating step/flow decorators")
|
253
264
|
# We need to convert all the user configurations from DelayedEvaluationParameters
|
254
|
-
# to actual values so they can be used as is in the
|
265
|
+
# to actual values so they can be used as is in the mutators.
|
255
266
|
|
256
|
-
# We
|
257
|
-
#
|
258
|
-
|
267
|
+
# We, however, need to make sure _get_parameters still works properly so
|
268
|
+
# we store what was a config and has been set to a specific value.
|
269
|
+
# This is safe to do for now because all other uses of _get_parameters typically
|
270
|
+
# do not rely on the variable itself but just the parameter.
|
271
|
+
to_save_configs = []
|
259
272
|
cls._check_parameters(config_parameters=True)
|
260
273
|
for var, param in cls._get_parameters():
|
261
274
|
if not param.IS_CONFIG_PARAMETER:
|
@@ -267,71 +280,72 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
267
280
|
# We store the value as well so that in _set_constants, we don't try
|
268
281
|
# to recompute (no guarantee that it is stable)
|
269
282
|
param._store_value(val)
|
270
|
-
|
283
|
+
to_save_configs.append((var, param))
|
271
284
|
debug.userconf_exec("Setting config %s to %s" % (var, str(val)))
|
272
285
|
setattr(cls, var, val)
|
273
286
|
|
274
|
-
|
275
|
-
#
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
deco.evaluate(MutableStep(cls, step))
|
289
|
-
else:
|
290
|
-
raise MetaflowInternalError(
|
291
|
-
"A non CustomFlowDecorator found in step custom decorators"
|
292
|
-
)
|
293
|
-
if step.config_decorators:
|
294
|
-
# We remove all mention of the custom step decorator
|
295
|
-
setattr(cls, step.name, step)
|
296
|
-
|
297
|
-
mutable_flow = MutableFlow(cls)
|
298
|
-
for deco in cls._flow_state.get(_FlowState.CONFIG_DECORATORS, []):
|
299
|
-
if isinstance(deco, CustomFlowDecorator):
|
287
|
+
cls._flow_state[_FlowState.SET_CONFIG_PARAMETERS] = to_save_configs
|
288
|
+
# Run all the decorators. We first run the flow-level decorators
|
289
|
+
# and then the step level ones to maintain a consistent order with how
|
290
|
+
# other decorators are run.
|
291
|
+
|
292
|
+
for deco in cls._flow_state.get(_FlowState.FLOW_MUTATORS, []):
|
293
|
+
if isinstance(deco, FlowMutator):
|
294
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
295
|
+
mutable_flow = MutableFlow(
|
296
|
+
cls,
|
297
|
+
pre_mutate=True,
|
298
|
+
statically_defined=deco.statically_defined,
|
299
|
+
inserted_by=inserted_by_value,
|
300
|
+
)
|
300
301
|
# Sanity check to make sure we are applying the decorator to the right
|
301
302
|
# class
|
302
303
|
if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
|
303
304
|
raise MetaflowInternalError(
|
304
|
-
"
|
305
|
+
"FlowMutator registered on the wrong flow -- "
|
305
306
|
"expected %s but got %s"
|
306
307
|
% (deco._flow_cls.__name__, cls.__name__)
|
307
308
|
)
|
308
309
|
debug.userconf_exec(
|
309
310
|
"Evaluating flow level decorator %s" % deco.__class__.__name__
|
310
311
|
)
|
311
|
-
deco.
|
312
|
+
deco.pre_mutate(mutable_flow)
|
312
313
|
# We reset cached_parameters on the very off chance that the user added
|
313
314
|
# more configurations based on the configuration
|
314
315
|
if _FlowState.CACHED_PARAMETERS in cls._flow_state:
|
315
316
|
del cls._flow_state[_FlowState.CACHED_PARAMETERS]
|
316
317
|
else:
|
317
318
|
raise MetaflowInternalError(
|
318
|
-
"A non
|
319
|
+
"A non FlowMutator found in flow custom decorators"
|
319
320
|
)
|
320
321
|
|
322
|
+
for step in cls._steps:
|
323
|
+
for deco in step.config_decorators:
|
324
|
+
if isinstance(deco, StepMutator):
|
325
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
326
|
+
debug.userconf_exec(
|
327
|
+
"Evaluating step level decorator %s for %s"
|
328
|
+
% (deco.__class__.__name__, step.name)
|
329
|
+
)
|
330
|
+
deco.pre_mutate(
|
331
|
+
MutableStep(
|
332
|
+
cls,
|
333
|
+
step,
|
334
|
+
pre_mutate=True,
|
335
|
+
statically_defined=deco.statically_defined,
|
336
|
+
inserted_by=inserted_by_value,
|
337
|
+
)
|
338
|
+
)
|
339
|
+
else:
|
340
|
+
raise MetaflowInternalError(
|
341
|
+
"A non StepMutator found in step custom decorators"
|
342
|
+
)
|
343
|
+
|
321
344
|
# Process parameters to allow them to also use config values easily
|
322
345
|
for var, param in cls._get_parameters():
|
323
346
|
if param.IS_CONFIG_PARAMETER:
|
324
347
|
continue
|
325
348
|
param.init()
|
326
|
-
# Reset all configs that were already present in the class.
|
327
|
-
# TODO: This means that users can't override configs directly. Not sure if this
|
328
|
-
# is a pattern we want to support
|
329
|
-
for var, param in to_reset_configs:
|
330
|
-
setattr(cls, var, param)
|
331
|
-
|
332
|
-
# Reset cached parameters again since we added back the config parameters
|
333
|
-
if _FlowState.CACHED_PARAMETERS in cls._flow_state:
|
334
|
-
del cls._flow_state[_FlowState.CACHED_PARAMETERS]
|
335
349
|
|
336
350
|
# Set the current flow class we are in (the one we just created)
|
337
351
|
parameters.replace_flow_context(cls)
|
@@ -402,9 +416,19 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
402
416
|
"name": deco.name,
|
403
417
|
"attributes": to_pod(deco.attributes),
|
404
418
|
"statically_defined": deco.statically_defined,
|
419
|
+
"inserted_by": deco.inserted_by,
|
405
420
|
}
|
406
421
|
for deco in flow_decorators(self)
|
407
422
|
if not deco.name.startswith("_")
|
423
|
+
]
|
424
|
+
+ [
|
425
|
+
{
|
426
|
+
"name": deco.__class__.__name__,
|
427
|
+
"attributes": {},
|
428
|
+
"statically_defined": deco.statically_defined,
|
429
|
+
"inserted_by": deco.inserted_by,
|
430
|
+
}
|
431
|
+
for deco in self._flow_state.get(_FlowState.FLOW_MUTATORS, [])
|
408
432
|
],
|
409
433
|
"extensions": extension_info(),
|
410
434
|
}
|
@@ -413,11 +437,19 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
413
437
|
@classmethod
|
414
438
|
def _get_parameters(cls):
|
415
439
|
cached = cls._flow_state.get(_FlowState.CACHED_PARAMETERS)
|
440
|
+
returned = set()
|
416
441
|
if cached is not None:
|
442
|
+
for set_config in cls._flow_state.get(_FlowState.SET_CONFIG_PARAMETERS, []):
|
443
|
+
returned.add(set_config[0])
|
444
|
+
yield set_config[0], set_config[1]
|
417
445
|
for var in cached:
|
418
|
-
|
446
|
+
if var not in returned:
|
447
|
+
yield var, getattr(cls, var)
|
419
448
|
return
|
420
449
|
build_list = []
|
450
|
+
for set_config in cls._flow_state.get(_FlowState.SET_CONFIG_PARAMETERS, []):
|
451
|
+
returned.add(set_config[0])
|
452
|
+
yield set_config[0], set_config[1]
|
421
453
|
for var in dir(cls):
|
422
454
|
if var[0] == "_" or var in cls._NON_PARAMETERS:
|
423
455
|
continue
|
@@ -425,7 +457,7 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
425
457
|
val = getattr(cls, var)
|
426
458
|
except:
|
427
459
|
continue
|
428
|
-
if isinstance(val, Parameter):
|
460
|
+
if isinstance(val, Parameter) and var not in returned:
|
429
461
|
build_list.append(var)
|
430
462
|
yield var, val
|
431
463
|
cls._flow_state[_FlowState.CACHED_PARAMETERS] = build_list
|
metaflow/graph.py
CHANGED
@@ -2,6 +2,8 @@ import inspect
|
|
2
2
|
import ast
|
3
3
|
import re
|
4
4
|
|
5
|
+
from itertools import chain
|
6
|
+
|
5
7
|
|
6
8
|
from .util import to_pod
|
7
9
|
|
@@ -45,13 +47,17 @@ def deindent_docstring(doc):
|
|
45
47
|
|
46
48
|
|
47
49
|
class DAGNode(object):
|
48
|
-
def __init__(
|
50
|
+
def __init__(
|
51
|
+
self, func_ast, decos, wrappers, config_decorators, doc, source_file, lineno
|
52
|
+
):
|
49
53
|
self.name = func_ast.name
|
50
54
|
self.source_file = source_file
|
51
55
|
# lineno is the start line of decorators in source_file
|
52
56
|
# func_ast.lineno is lines from decorators start to def of function
|
53
57
|
self.func_lineno = lineno + func_ast.lineno - 1
|
54
58
|
self.decorators = decos
|
59
|
+
self.wrappers = wrappers
|
60
|
+
self.config_decorators = config_decorators
|
55
61
|
self.doc = deindent_docstring(doc)
|
56
62
|
self.parallel_step = any(getattr(deco, "IS_PARALLEL", False) for deco in decos)
|
57
63
|
|
@@ -181,7 +187,13 @@ class FlowGraph(object):
|
|
181
187
|
source_code = deindent_docstring("".join(source_lines))
|
182
188
|
function_ast = ast.parse(source_code).body[0]
|
183
189
|
node = DAGNode(
|
184
|
-
function_ast,
|
190
|
+
function_ast,
|
191
|
+
func.decorators,
|
192
|
+
func.wrappers,
|
193
|
+
func.config_decorators,
|
194
|
+
func.__doc__,
|
195
|
+
source_file,
|
196
|
+
lineno,
|
185
197
|
)
|
186
198
|
nodes[element] = node
|
187
199
|
return nodes
|
@@ -293,9 +305,19 @@ class FlowGraph(object):
|
|
293
305
|
"name": deco.name,
|
294
306
|
"attributes": to_pod(deco.attributes),
|
295
307
|
"statically_defined": deco.statically_defined,
|
308
|
+
"inserted_by": deco.inserted_by,
|
296
309
|
}
|
297
310
|
for deco in node.decorators
|
298
311
|
if not deco.name.startswith("_")
|
312
|
+
]
|
313
|
+
+ [
|
314
|
+
{
|
315
|
+
"name": deco.decorator_name,
|
316
|
+
"attributes": {"_args": deco._args, **deco._kwargs},
|
317
|
+
"statically_defined": deco.statically_defined,
|
318
|
+
"inserted_by": deco.inserted_by,
|
319
|
+
}
|
320
|
+
for deco in chain(node.wrappers, node.config_decorators)
|
299
321
|
],
|
300
322
|
"next": node.out_funcs,
|
301
323
|
}
|
metaflow/meta_files.py
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
_UNINITIALIZED = object()
|
2
|
+
_info_file_content = _UNINITIALIZED
|
3
|
+
|
4
|
+
|
5
|
+
def read_info_file():
|
6
|
+
# Prevent circular import
|
7
|
+
from .packaging_sys import MetaflowCodeContent
|
8
|
+
|
9
|
+
global _info_file_content
|
10
|
+
|
11
|
+
if id(_info_file_content) == id(_UNINITIALIZED):
|
12
|
+
_info_file_content = MetaflowCodeContent.get_info()
|
13
|
+
return _info_file_content
|
@@ -674,11 +674,17 @@ class MetadataProvider(object):
|
|
674
674
|
if code_sha:
|
675
675
|
code_url = os.environ.get("METAFLOW_CODE_URL")
|
676
676
|
code_ds = os.environ.get("METAFLOW_CODE_DS")
|
677
|
+
code_metadata = os.environ.get("METAFLOW_CODE_METADATA")
|
677
678
|
metadata.append(
|
678
679
|
MetaDatum(
|
679
680
|
field="code-package",
|
680
681
|
value=json.dumps(
|
681
|
-
{
|
682
|
+
{
|
683
|
+
"ds_type": code_ds,
|
684
|
+
"sha": code_sha,
|
685
|
+
"location": code_url,
|
686
|
+
"metadata": code_metadata,
|
687
|
+
}
|
682
688
|
),
|
683
689
|
type="code-package",
|
684
690
|
tags=["attempt_id:{0}".format(attempt)],
|
metaflow/metaflow_config.py
CHANGED
@@ -449,6 +449,10 @@ CONDA_USE_FAST_INIT = from_conf("CONDA_USE_FAST_INIT", False)
|
|
449
449
|
# Print out warning if escape hatch is not used for the target packages
|
450
450
|
ESCAPE_HATCH_WARNING = from_conf("ESCAPE_HATCH_WARNING", True)
|
451
451
|
|
452
|
+
###
|
453
|
+
# Features
|
454
|
+
###
|
455
|
+
FEAT_ALWAYS_UPLOAD_CODE_PACKAGE = from_conf("FEAT_ALWAYS_UPLOAD_CODE_PACKAGE", False)
|
452
456
|
###
|
453
457
|
# Debug configuration
|
454
458
|
###
|
@@ -460,6 +464,7 @@ DEBUG_OPTIONS = [
|
|
460
464
|
"stubgen",
|
461
465
|
"userconf",
|
462
466
|
"conda",
|
467
|
+
"package",
|
463
468
|
]
|
464
469
|
|
465
470
|
for typ in DEBUG_OPTIONS:
|
metaflow/metaflow_environment.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import json
|
1
2
|
import os
|
2
3
|
import platform
|
3
4
|
import sys
|
@@ -8,6 +9,8 @@ from . import metaflow_git
|
|
8
9
|
from metaflow.exception import MetaflowException
|
9
10
|
from metaflow.extension_support import dump_module_info
|
10
11
|
from metaflow.mflog import BASH_MFLOG, BASH_FLUSH_LOGS
|
12
|
+
from metaflow.package import MetaflowPackage
|
13
|
+
|
11
14
|
from . import R
|
12
15
|
|
13
16
|
|
@@ -49,8 +52,36 @@ class MetaflowEnvironment(object):
|
|
49
52
|
|
50
53
|
def add_to_package(self):
|
51
54
|
"""
|
52
|
-
|
53
|
-
|
55
|
+
Called to add custom files needed for this environment. This hook will be
|
56
|
+
called in the `MetaflowPackage` class where metaflow compiles the code package
|
57
|
+
tarball. This hook can return one of two things (the first is for backwards
|
58
|
+
compatibility -- move to the second):
|
59
|
+
- a generator yielding a tuple of `(file_path, arcname)` to add files to
|
60
|
+
the code package. `file_path` is the path to the file on the local filesystem
|
61
|
+
and `arcname` is the path relative to the packaged code.
|
62
|
+
- a generator yielding a tuple of `(content, arcname, type)` where:
|
63
|
+
- type is one of
|
64
|
+
ContentType.{USER_CONTENT, CODE_CONTENT, MODULE_CONTENT, OTHER_CONTENT}
|
65
|
+
- for USER_CONTENT:
|
66
|
+
- the file will be included relative to the directory containing the
|
67
|
+
user's flow file.
|
68
|
+
- content: path to the file to include
|
69
|
+
- arcname: path relative to the directory containing the user's flow file
|
70
|
+
- for CODE_CONTENT:
|
71
|
+
- the file will be included relative to the code directory in the package.
|
72
|
+
This will be the directory containing `metaflow`.
|
73
|
+
- content: path to the file to include
|
74
|
+
- arcname: path relative to the code directory in the package
|
75
|
+
- for MODULE_CONTENT:
|
76
|
+
- the module will be added to the code package as a python module. It will
|
77
|
+
be accessible as usual (import <module_name>)
|
78
|
+
- content: name of the module
|
79
|
+
- arcname: None (ignored)
|
80
|
+
- for OTHER_CONTENT:
|
81
|
+
- the file will be included relative to any other configuration/metadata
|
82
|
+
files for the flow
|
83
|
+
- content: path to the file to include
|
84
|
+
- arcname: path relative to the config directory in the package
|
54
85
|
"""
|
55
86
|
return []
|
56
87
|
|
@@ -157,29 +188,55 @@ class MetaflowEnvironment(object):
|
|
157
188
|
# skip pip installs if we know that packages might already be available
|
158
189
|
return "if [ -z $METAFLOW_SKIP_INSTALL_DEPENDENCIES ]; then {}; fi".format(cmd)
|
159
190
|
|
160
|
-
def get_package_commands(
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
191
|
+
def get_package_commands(
|
192
|
+
self, code_package_url, datastore_type, code_package_metadata=None
|
193
|
+
):
|
194
|
+
# HACK: We want to keep forward compatibility with compute layers so that
|
195
|
+
# they can still call get_package_commands and NOT pass any metadata. If
|
196
|
+
# there is no additional information, we *assume* that it is the default
|
197
|
+
# used.
|
198
|
+
if code_package_metadata is None:
|
199
|
+
code_package_metadata = json.dumps(
|
200
|
+
{
|
201
|
+
"version": 0,
|
202
|
+
"archive_format": "tgz",
|
203
|
+
"mfcontent_version": 1,
|
204
|
+
}
|
205
|
+
)
|
206
|
+
cmds = (
|
207
|
+
[
|
208
|
+
BASH_MFLOG,
|
209
|
+
BASH_FLUSH_LOGS,
|
210
|
+
"mflog 'Setting up task environment.'",
|
211
|
+
self._get_install_dependencies_cmd(datastore_type),
|
212
|
+
"mkdir metaflow",
|
213
|
+
"cd metaflow",
|
214
|
+
"mkdir .metaflow", # mute local datastore creation log
|
215
|
+
"i=0; while [ $i -le 5 ]; do "
|
216
|
+
"mflog 'Downloading code package...'; "
|
217
|
+
+ self._get_download_code_package_cmd(code_package_url, datastore_type)
|
218
|
+
+ " && mflog 'Code package downloaded.' && break; "
|
219
|
+
"sleep 10; i=$((i+1)); "
|
220
|
+
"done",
|
221
|
+
"if [ $i -gt 5 ]; then "
|
222
|
+
"mflog 'Failed to download code package from %s "
|
223
|
+
"after 6 tries. Exiting...' && exit 1; "
|
224
|
+
"fi" % code_package_url,
|
225
|
+
]
|
226
|
+
+ MetaflowPackage.get_extract_commands(
|
227
|
+
code_package_metadata, "job.tar", dest_dir="."
|
228
|
+
)
|
229
|
+
+ [
|
230
|
+
"export %s=%s:$(printenv %s)" % (k, v.replace('"', '\\"'), k)
|
231
|
+
for k, v in MetaflowPackage.get_post_extract_env_vars(
|
232
|
+
code_package_metadata, dest_dir="."
|
233
|
+
).items()
|
234
|
+
]
|
235
|
+
+ [
|
236
|
+
"mflog 'Task is starting.'",
|
237
|
+
"flush_mflogs",
|
238
|
+
]
|
239
|
+
)
|
183
240
|
return cmds
|
184
241
|
|
185
242
|
def get_environment_info(self, include_ext_info=False):
|
metaflow/metaflow_version.py
CHANGED
@@ -11,7 +11,7 @@ import subprocess
|
|
11
11
|
from os import path, name, environ, listdir
|
12
12
|
|
13
13
|
from metaflow.extension_support import update_package_info
|
14
|
-
from metaflow.
|
14
|
+
from metaflow.meta_files import read_info_file
|
15
15
|
|
16
16
|
|
17
17
|
# True/False correspond to the value `public`` in get_version
|