ob-metaflow 2.12.36.2__py2.py3-none-any.whl → 2.12.36.3__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/__init__.py +0 -3
- metaflow/cli.py +697 -84
- metaflow/cli_args.py +0 -17
- metaflow/cmd/develop/stub_generator.py +2 -9
- metaflow/decorators.py +2 -63
- metaflow/extension_support/plugins.py +27 -41
- metaflow/flowspec.py +16 -156
- metaflow/includefile.py +22 -50
- metaflow/metaflow_config.py +1 -1
- metaflow/package.py +3 -17
- metaflow/parameters.py +23 -80
- metaflow/plugins/__init__.py +0 -4
- metaflow/plugins/airflow/airflow_cli.py +0 -1
- metaflow/plugins/argo/argo_workflows.py +1 -41
- metaflow/plugins/argo/argo_workflows_cli.py +0 -1
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +1 -5
- metaflow/plugins/aws/batch/batch_decorator.py +2 -2
- metaflow/plugins/aws/step_functions/step_functions.py +0 -32
- metaflow/plugins/aws/step_functions/step_functions_cli.py +0 -1
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +0 -3
- metaflow/plugins/datatools/s3/s3op.py +3 -3
- metaflow/plugins/kubernetes/kubernetes_cli.py +1 -1
- metaflow/plugins/kubernetes/kubernetes_decorator.py +2 -2
- metaflow/plugins/pypi/conda_decorator.py +10 -20
- metaflow/plugins/pypi/pypi_decorator.py +9 -11
- metaflow/plugins/timeout_decorator.py +2 -2
- metaflow/runner/click_api.py +19 -73
- metaflow/runner/deployer.py +1 -1
- metaflow/runner/deployer_impl.py +2 -2
- metaflow/runner/metaflow_runner.py +1 -4
- metaflow/runner/nbdeploy.py +0 -2
- metaflow/runner/nbrun.py +1 -1
- metaflow/runner/subprocess_manager.py +1 -3
- metaflow/runner/utils.py +20 -37
- metaflow/runtime.py +73 -111
- metaflow/sidecar/sidecar_worker.py +1 -1
- metaflow/util.py +0 -17
- metaflow/version.py +1 -1
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/METADATA +2 -3
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/RECORD +44 -54
- metaflow/cli_components/__init__.py +0 -0
- metaflow/cli_components/dump_cmd.py +0 -96
- metaflow/cli_components/init_cmd.py +0 -51
- metaflow/cli_components/run_cmds.py +0 -358
- metaflow/cli_components/step_cmd.py +0 -189
- metaflow/cli_components/utils.py +0 -140
- metaflow/user_configs/__init__.py +0 -0
- metaflow/user_configs/config_decorators.py +0 -563
- metaflow/user_configs/config_options.py +0 -495
- metaflow/user_configs/config_parameters.py +0 -386
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/LICENSE +0 -0
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.12.36.2.dist-info → ob_metaflow-2.12.36.3.dist-info}/top_level.txt +0 -0
metaflow/cli_args.py
CHANGED
|
@@ -12,14 +12,7 @@
|
|
|
12
12
|
# well as the converting of options in runtime.py. We should make it so that we
|
|
13
13
|
# can properly shlex things and un-shlex when using. Ideally this should all be
|
|
14
14
|
# done in one place.
|
|
15
|
-
#
|
|
16
|
-
# NOTE: There is an important between these two as well:
|
|
17
|
-
# - this one will include local_config_file whereas the other one WILL NOT.
|
|
18
|
-
# This is because this is used when constructing the parallel UBF command which
|
|
19
|
-
# executes locally and therefore needs the local_config_file but the other (remote)
|
|
20
|
-
# commands do not.
|
|
21
15
|
|
|
22
|
-
from .user_configs.config_options import ConfigInput
|
|
23
16
|
from .util import to_unicode
|
|
24
17
|
|
|
25
18
|
|
|
@@ -72,16 +65,6 @@ class CLIArgs(object):
|
|
|
72
65
|
# keyword in Python, so we call it 'decospecs' in click args
|
|
73
66
|
if k == "decospecs":
|
|
74
67
|
k = "with"
|
|
75
|
-
if k in ("config_file_options", "config_value_options"):
|
|
76
|
-
# Special handling here since we gather them all in one option but actually
|
|
77
|
-
# need to send them one at a time using --config-value <name> kv.<name>.
|
|
78
|
-
# Note it can be either config_file_options or config_value_options depending
|
|
79
|
-
# on click processing order.
|
|
80
|
-
for config_name in v.keys():
|
|
81
|
-
yield "--config-value"
|
|
82
|
-
yield to_unicode(config_name)
|
|
83
|
-
yield to_unicode(ConfigInput.make_key_name(config_name))
|
|
84
|
-
continue
|
|
85
68
|
k = k.replace("_", "-")
|
|
86
69
|
v = v if isinstance(v, (list, tuple, set)) else [v]
|
|
87
70
|
for value in v:
|
|
@@ -1238,32 +1238,25 @@ class StubGenerator:
|
|
|
1238
1238
|
buff.write(indentation + deco + "\n")
|
|
1239
1239
|
buff.write(indentation + "def " + name + "(")
|
|
1240
1240
|
kw_only_param = False
|
|
1241
|
-
has_var_args = False
|
|
1242
1241
|
for i, (par_name, parameter) in enumerate(my_sign.parameters.items()):
|
|
1243
1242
|
annotation = self._exploit_annotation(parameter.annotation)
|
|
1243
|
+
|
|
1244
1244
|
default = exploit_default(parameter.default)
|
|
1245
1245
|
|
|
1246
|
-
if
|
|
1247
|
-
kw_only_param
|
|
1248
|
-
and not has_var_args
|
|
1249
|
-
and parameter.kind != inspect.Parameter.KEYWORD_ONLY
|
|
1250
|
-
):
|
|
1246
|
+
if kw_only_param and parameter.kind != inspect.Parameter.KEYWORD_ONLY:
|
|
1251
1247
|
raise RuntimeError(
|
|
1252
1248
|
"In function '%s': cannot have a positional parameter after a "
|
|
1253
1249
|
"keyword only parameter" % name
|
|
1254
1250
|
)
|
|
1255
|
-
|
|
1256
1251
|
if (
|
|
1257
1252
|
parameter.kind == inspect.Parameter.KEYWORD_ONLY
|
|
1258
1253
|
and not kw_only_param
|
|
1259
|
-
and not has_var_args
|
|
1260
1254
|
):
|
|
1261
1255
|
kw_only_param = True
|
|
1262
1256
|
buff.write("*, ")
|
|
1263
1257
|
if parameter.kind == inspect.Parameter.VAR_KEYWORD:
|
|
1264
1258
|
par_name = "**%s" % par_name
|
|
1265
1259
|
elif parameter.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
1266
|
-
has_var_args = True
|
|
1267
1260
|
par_name = "*%s" % par_name
|
|
1268
1261
|
|
|
1269
1262
|
if default:
|
metaflow/decorators.py
CHANGED
|
@@ -12,12 +12,6 @@ from .exception import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from .parameters import current_flow
|
|
15
|
-
from .user_configs.config_decorators import CustomStepDecorator
|
|
16
|
-
from .user_configs.config_parameters import (
|
|
17
|
-
UNPACK_KEY,
|
|
18
|
-
resolve_delayed_evaluator,
|
|
19
|
-
unpack_delayed_evaluator,
|
|
20
|
-
)
|
|
21
15
|
|
|
22
16
|
from metaflow._vendor import click
|
|
23
17
|
|
|
@@ -27,11 +21,6 @@ except NameError:
|
|
|
27
21
|
unicode = str
|
|
28
22
|
basestring = str
|
|
29
23
|
|
|
30
|
-
# Contains the decorators on which _init was called. We want to ensure it is called
|
|
31
|
-
# only once on each decorator and, as the _init() function below can be called in
|
|
32
|
-
# several places, we need to track which decorator had their init function called
|
|
33
|
-
_inited_decorators = set()
|
|
34
|
-
|
|
35
24
|
|
|
36
25
|
class BadStepDecoratorException(MetaflowException):
|
|
37
26
|
headline = "Syntax error"
|
|
@@ -126,36 +115,14 @@ class Decorator(object):
|
|
|
126
115
|
def __init__(self, attributes=None, statically_defined=False):
|
|
127
116
|
self.attributes = self.defaults.copy()
|
|
128
117
|
self.statically_defined = statically_defined
|
|
129
|
-
self._user_defined_attributes = set()
|
|
130
|
-
self._ran_init = False
|
|
131
118
|
|
|
132
119
|
if attributes:
|
|
133
120
|
for k, v in attributes.items():
|
|
134
|
-
if k in self.defaults
|
|
121
|
+
if k in self.defaults:
|
|
135
122
|
self.attributes[k] = v
|
|
136
|
-
if not k.startswith(UNPACK_KEY):
|
|
137
|
-
self._user_defined_attributes.add(k)
|
|
138
123
|
else:
|
|
139
124
|
raise InvalidDecoratorAttribute(self.name, k, self.defaults)
|
|
140
125
|
|
|
141
|
-
def init(self):
|
|
142
|
-
"""
|
|
143
|
-
Initializes the decorator. In general, any operation you would do in __init__
|
|
144
|
-
should be done here.
|
|
145
|
-
"""
|
|
146
|
-
|
|
147
|
-
# In some cases (specifically when using remove_decorator), we may need to call
|
|
148
|
-
# init multiple times. Short-circuit re-evaluating.
|
|
149
|
-
if self._ran_init:
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
# Note that by design, later values override previous ones.
|
|
153
|
-
self.attributes = unpack_delayed_evaluator(self.attributes)
|
|
154
|
-
self._user_defined_attributes.update(self.attributes.keys())
|
|
155
|
-
self.attributes = resolve_delayed_evaluator(self.attributes)
|
|
156
|
-
|
|
157
|
-
self._ran_init = True
|
|
158
|
-
|
|
159
126
|
@classmethod
|
|
160
127
|
def _parse_decorator_spec(cls, deco_spec):
|
|
161
128
|
if len(deco_spec) == 0:
|
|
@@ -236,13 +203,10 @@ class FlowDecorator(Decorator):
|
|
|
236
203
|
|
|
237
204
|
# compare this to parameters.add_custom_parameters
|
|
238
205
|
def add_decorator_options(cmd):
|
|
206
|
+
seen = {}
|
|
239
207
|
flow_cls = getattr(current_flow, "flow_cls", None)
|
|
240
208
|
if flow_cls is None:
|
|
241
209
|
return cmd
|
|
242
|
-
|
|
243
|
-
seen = {}
|
|
244
|
-
existing_params = set(p.name.lower() for p in cmd.params)
|
|
245
|
-
# Add decorator options
|
|
246
210
|
for deco in flow_decorators(flow_cls):
|
|
247
211
|
for option, kwargs in deco.options.items():
|
|
248
212
|
if option in seen:
|
|
@@ -253,13 +217,7 @@ def add_decorator_options(cmd):
|
|
|
253
217
|
% (deco.name, option, seen[option])
|
|
254
218
|
)
|
|
255
219
|
raise MetaflowInternalError(msg)
|
|
256
|
-
elif deco.name.lower() in existing_params:
|
|
257
|
-
raise MetaflowInternalError(
|
|
258
|
-
"Flow decorator '%s' uses an option '%s' which is a reserved "
|
|
259
|
-
"keyword. Please use a different option name." % (deco.name, option)
|
|
260
|
-
)
|
|
261
220
|
else:
|
|
262
|
-
kwargs["envvar"] = "METAFLOW_FLOW_%s" % option.upper()
|
|
263
221
|
seen[option] = deco.name
|
|
264
222
|
cmd.params.insert(0, click.Option(("--" + option,), **kwargs))
|
|
265
223
|
return cmd
|
|
@@ -467,13 +425,10 @@ def _base_step_decorator(decotype, *args, **kwargs):
|
|
|
467
425
|
Decorator prototype for all step decorators. This function gets specialized
|
|
468
426
|
and imported for all decorators types by _import_plugin_decorators().
|
|
469
427
|
"""
|
|
470
|
-
|
|
471
428
|
if args:
|
|
472
429
|
# No keyword arguments specified for the decorator, e.g. @foobar.
|
|
473
430
|
# The first argument is the function to be decorated.
|
|
474
431
|
func = args[0]
|
|
475
|
-
if isinstance(func, CustomStepDecorator):
|
|
476
|
-
func = func._my_step
|
|
477
432
|
if not hasattr(func, "is_step"):
|
|
478
433
|
raise BadStepDecoratorException(decotype.name, func)
|
|
479
434
|
|
|
@@ -555,21 +510,6 @@ def _attach_decorators_to_step(step, decospecs):
|
|
|
555
510
|
step.decorators.append(deco)
|
|
556
511
|
|
|
557
512
|
|
|
558
|
-
def _init(flow, only_non_static=False):
|
|
559
|
-
for decorators in flow._flow_decorators.values():
|
|
560
|
-
for deco in decorators:
|
|
561
|
-
if deco in _inited_decorators:
|
|
562
|
-
continue
|
|
563
|
-
deco.init()
|
|
564
|
-
_inited_decorators.add(deco)
|
|
565
|
-
for flowstep in flow:
|
|
566
|
-
for deco in flowstep.decorators:
|
|
567
|
-
if deco in _inited_decorators:
|
|
568
|
-
continue
|
|
569
|
-
deco.init()
|
|
570
|
-
_inited_decorators.add(deco)
|
|
571
|
-
|
|
572
|
-
|
|
573
513
|
def _init_flow_decorators(
|
|
574
514
|
flow, graph, environment, flow_datastore, metadata, logger, echo, deco_options
|
|
575
515
|
):
|
|
@@ -680,7 +620,6 @@ def step(
|
|
|
680
620
|
"""
|
|
681
621
|
f.is_step = True
|
|
682
622
|
f.decorators = []
|
|
683
|
-
f.config_decorators = []
|
|
684
623
|
try:
|
|
685
624
|
# python 3
|
|
686
625
|
f.name = f.__name__
|
|
@@ -93,32 +93,7 @@ def merge_lists(base, overrides, attr):
|
|
|
93
93
|
base[:] = l[:]
|
|
94
94
|
|
|
95
95
|
|
|
96
|
-
def
|
|
97
|
-
path, cls_name = class_path.rsplit(".", 1)
|
|
98
|
-
try:
|
|
99
|
-
plugin_module = importlib.import_module(path)
|
|
100
|
-
except ImportError as e:
|
|
101
|
-
raise ValueError(
|
|
102
|
-
"Cannot locate %s plugin '%s' at '%s'" % (category, name, path)
|
|
103
|
-
) from e
|
|
104
|
-
cls = getattr(plugin_module, cls_name, None)
|
|
105
|
-
if cls is None:
|
|
106
|
-
raise ValueError(
|
|
107
|
-
"Cannot locate '%s' class for %s plugin at '%s'"
|
|
108
|
-
% (cls_name, category, path)
|
|
109
|
-
)
|
|
110
|
-
extracted_name = get_plugin_name(category, cls)
|
|
111
|
-
if extracted_name and extracted_name != name:
|
|
112
|
-
raise ValueError(
|
|
113
|
-
"Class '%s' at '%s' for %s plugin expected to be named '%s' but got '%s'"
|
|
114
|
-
% (cls_name, path, category, name, extracted_name)
|
|
115
|
-
)
|
|
116
|
-
globals()[cls_name] = cls
|
|
117
|
-
_ext_debug(" Added %s plugin '%s' from '%s'" % (category, name, class_path))
|
|
118
|
-
return cls
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def resolve_plugins(category, path_only=False):
|
|
96
|
+
def resolve_plugins(category):
|
|
122
97
|
# Called to return a list of classes that are the available plugins for 'category'
|
|
123
98
|
|
|
124
99
|
# The ENABLED_<category> variable is set in process_plugins
|
|
@@ -139,7 +114,7 @@ def resolve_plugins(category, path_only=False):
|
|
|
139
114
|
|
|
140
115
|
available_plugins = globals()[_dict_for_category(category)]
|
|
141
116
|
name_extractor = _plugin_categories[category]
|
|
142
|
-
if
|
|
117
|
+
if not name_extractor:
|
|
143
118
|
# If we have no name function, it means we just use the name in the dictionary
|
|
144
119
|
# and we return a dictionary. This is for sidecars mostly as they do not have
|
|
145
120
|
# a field that indicates their name
|
|
@@ -157,14 +132,32 @@ def resolve_plugins(category, path_only=False):
|
|
|
157
132
|
"Configuration requested %s plugin '%s' but no such plugin is available"
|
|
158
133
|
% (category, name)
|
|
159
134
|
)
|
|
160
|
-
|
|
161
|
-
|
|
135
|
+
path, cls_name = class_path.rsplit(".", 1)
|
|
136
|
+
try:
|
|
137
|
+
plugin_module = importlib.import_module(path)
|
|
138
|
+
except ImportError:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"Cannot locate %s plugin '%s' at '%s'" % (category, name, path)
|
|
141
|
+
)
|
|
142
|
+
cls = getattr(plugin_module, cls_name, None)
|
|
143
|
+
if cls is None:
|
|
144
|
+
raise ValueError(
|
|
145
|
+
"Cannot locate '%s' class for %s plugin at '%s'"
|
|
146
|
+
% (cls_name, category, path)
|
|
147
|
+
)
|
|
148
|
+
if name_extractor and name_extractor(cls) != name:
|
|
149
|
+
raise ValueError(
|
|
150
|
+
"Class '%s' at '%s' for %s plugin expected to be named '%s' but got '%s'"
|
|
151
|
+
% (cls_name, path, category, name, name_extractor(cls))
|
|
152
|
+
)
|
|
153
|
+
globals()[cls_name] = cls
|
|
154
|
+
if name_extractor is not None:
|
|
155
|
+
to_return.append(cls)
|
|
162
156
|
else:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
157
|
+
to_return[name] = cls
|
|
158
|
+
_ext_debug(
|
|
159
|
+
" Added %s plugin '%s' from '%s'" % (category, name, class_path)
|
|
160
|
+
)
|
|
168
161
|
return to_return
|
|
169
162
|
|
|
170
163
|
|
|
@@ -200,13 +193,6 @@ _plugin_categories = {
|
|
|
200
193
|
}
|
|
201
194
|
|
|
202
195
|
|
|
203
|
-
def get_plugin_name(category, plugin):
|
|
204
|
-
extractor = _plugin_categories[category]
|
|
205
|
-
if extractor:
|
|
206
|
-
return extractor(plugin)
|
|
207
|
-
return None
|
|
208
|
-
|
|
209
|
-
|
|
210
196
|
def _list_for_category(category):
|
|
211
197
|
# Convenience function to name the variable containing List[Tuple[str, str]] where
|
|
212
198
|
# each tuple contains:
|
metaflow/flowspec.py
CHANGED
|
@@ -4,18 +4,15 @@ import sys
|
|
|
4
4
|
import traceback
|
|
5
5
|
import reprlib
|
|
6
6
|
|
|
7
|
-
from enum import Enum
|
|
8
7
|
from itertools import islice
|
|
9
8
|
from types import FunctionType, MethodType
|
|
10
|
-
from typing import
|
|
9
|
+
from typing import Any, Callable, List, Optional, Tuple
|
|
11
10
|
|
|
12
11
|
from . import cmd_with_io, parameters
|
|
13
|
-
from .debug import debug
|
|
14
12
|
from .parameters import DelayedEvaluationParameter, Parameter
|
|
15
13
|
from .exception import (
|
|
16
14
|
MetaflowException,
|
|
17
15
|
MissingInMergeArtifactsException,
|
|
18
|
-
MetaflowInternalError,
|
|
19
16
|
UnhandledInMergeArtifactsException,
|
|
20
17
|
)
|
|
21
18
|
|
|
@@ -23,12 +20,6 @@ from .extension_support import extension_info
|
|
|
23
20
|
|
|
24
21
|
from .graph import FlowGraph
|
|
25
22
|
from .unbounded_foreach import UnboundedForeachInput
|
|
26
|
-
from .user_configs.config_decorators import (
|
|
27
|
-
CustomFlowDecorator,
|
|
28
|
-
CustomStepDecorator,
|
|
29
|
-
MutableFlow,
|
|
30
|
-
MutableStep,
|
|
31
|
-
)
|
|
32
23
|
from .util import to_pod
|
|
33
24
|
from .metaflow_config import INCLUDE_FOREACH_STACK, MAXIMUM_FOREACH_VALUE_CHARS
|
|
34
25
|
|
|
@@ -74,27 +65,14 @@ class ParallelUBF(UnboundedForeachInput):
|
|
|
74
65
|
return item or 0 # item is None for the control task, but it is also split 0
|
|
75
66
|
|
|
76
67
|
|
|
77
|
-
class _FlowState(Enum):
|
|
78
|
-
CONFIGS = 1
|
|
79
|
-
CONFIG_DECORATORS = 2
|
|
80
|
-
CACHED_PARAMETERS = 3
|
|
81
|
-
|
|
82
|
-
|
|
83
68
|
class FlowSpecMeta(type):
|
|
84
69
|
def __new__(cls, name, bases, dct):
|
|
85
70
|
f = super().__new__(cls, name, bases, dct)
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
# We should move _flow_decorators into this structure as well but keeping it
|
|
92
|
-
# out to limit the changes for now.
|
|
71
|
+
# This makes sure to give _flow_decorators to each
|
|
72
|
+
# child class (and not share it with the FlowSpec base
|
|
73
|
+
# class). This is important to not make a "global"
|
|
74
|
+
# _flow_decorators
|
|
93
75
|
f._flow_decorators = {}
|
|
94
|
-
|
|
95
|
-
# Keys are _FlowState enum values
|
|
96
|
-
f._flow_state = {}
|
|
97
|
-
|
|
98
76
|
return f
|
|
99
77
|
|
|
100
78
|
|
|
@@ -118,7 +96,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
118
96
|
"_cached_input",
|
|
119
97
|
"_graph",
|
|
120
98
|
"_flow_decorators",
|
|
121
|
-
"_flow_state",
|
|
122
99
|
"_steps",
|
|
123
100
|
"index",
|
|
124
101
|
"input",
|
|
@@ -171,11 +148,16 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
171
148
|
fname = fname[:-1]
|
|
172
149
|
return os.path.basename(fname)
|
|
173
150
|
|
|
174
|
-
def
|
|
151
|
+
def _set_constants(self, graph, kwargs):
|
|
152
|
+
from metaflow.decorators import (
|
|
153
|
+
flow_decorators,
|
|
154
|
+
) # To prevent circular dependency
|
|
155
|
+
|
|
156
|
+
# Persist values for parameters and other constants (class level variables)
|
|
157
|
+
# only once. This method is called before persist_constants is called to
|
|
158
|
+
# persist all values set using setattr
|
|
175
159
|
seen = set()
|
|
176
|
-
for
|
|
177
|
-
if param.IS_CONFIG_PARAMETER != config_parameters:
|
|
178
|
-
continue
|
|
160
|
+
for var, param in self._get_parameters():
|
|
179
161
|
norm = param.name.lower()
|
|
180
162
|
if norm in seen:
|
|
181
163
|
raise MetaflowException(
|
|
@@ -184,127 +166,13 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
184
166
|
"case-insensitive." % param.name
|
|
185
167
|
)
|
|
186
168
|
seen.add(norm)
|
|
187
|
-
|
|
188
|
-
def _process_config_decorators(self, config_options):
|
|
189
|
-
current_cls = self.__class__
|
|
190
|
-
|
|
191
|
-
# Fast path for no user configurations
|
|
192
|
-
if not self._flow_state.get(_FlowState.CONFIG_DECORATORS):
|
|
193
|
-
# Process parameters to allow them to also use config values easily
|
|
194
|
-
for var, param in self._get_parameters():
|
|
195
|
-
if param.IS_CONFIG_PARAMETER:
|
|
196
|
-
continue
|
|
197
|
-
param.init()
|
|
198
|
-
return self
|
|
199
|
-
|
|
200
|
-
debug.userconf_exec("Processing mutating step/flow decorators")
|
|
201
|
-
# We need to convert all the user configurations from DelayedEvaluationParameters
|
|
202
|
-
# to actual values so they can be used as is in the config decorators.
|
|
203
|
-
|
|
204
|
-
# We then reset them to be proper configs so they can be re-evaluated in
|
|
205
|
-
# _set_constants
|
|
206
|
-
to_reset_configs = []
|
|
207
|
-
self._check_parameters(config_parameters=True)
|
|
208
|
-
for var, param in self._get_parameters():
|
|
209
|
-
if not param.IS_CONFIG_PARAMETER:
|
|
210
|
-
continue
|
|
211
|
-
# Note that a config with no default and not required will be None
|
|
212
|
-
val = config_options.get(param.name.replace("-", "_").lower())
|
|
213
|
-
if isinstance(val, DelayedEvaluationParameter):
|
|
214
|
-
val = val()
|
|
215
|
-
# We store the value as well so that in _set_constants, we don't try
|
|
216
|
-
# to recompute (no guarantee that it is stable)
|
|
217
|
-
param._store_value(val)
|
|
218
|
-
to_reset_configs.append((var, param))
|
|
219
|
-
debug.userconf_exec("Setting config %s to %s" % (var, str(val)))
|
|
220
|
-
setattr(current_cls, var, val)
|
|
221
|
-
|
|
222
|
-
# Run all the decorators. Step decorators are directly in the step and
|
|
223
|
-
# we will run those first and *then* we run all the flow level decorators
|
|
224
|
-
for step in self._steps:
|
|
225
|
-
for deco in step.config_decorators:
|
|
226
|
-
if isinstance(deco, CustomStepDecorator):
|
|
227
|
-
debug.userconf_exec(
|
|
228
|
-
"Evaluating step level decorator %s for %s"
|
|
229
|
-
% (deco.__class__.__name__, step.name)
|
|
230
|
-
)
|
|
231
|
-
deco.evaluate(MutableStep(current_cls, step))
|
|
232
|
-
else:
|
|
233
|
-
raise MetaflowInternalError(
|
|
234
|
-
"A non CustomFlowDecorator found in step custom decorators"
|
|
235
|
-
)
|
|
236
|
-
if step.config_decorators:
|
|
237
|
-
# We remove all mention of the custom step decorator
|
|
238
|
-
setattr(current_cls, step.name, step)
|
|
239
|
-
|
|
240
|
-
mutable_flow = MutableFlow(current_cls)
|
|
241
|
-
for deco in self._flow_state[_FlowState.CONFIG_DECORATORS]:
|
|
242
|
-
if isinstance(deco, CustomFlowDecorator):
|
|
243
|
-
# Sanity check to make sure we are applying the decorator to the right
|
|
244
|
-
# class
|
|
245
|
-
if not deco._flow_cls == current_cls and not issubclass(
|
|
246
|
-
current_cls, deco._flow_cls
|
|
247
|
-
):
|
|
248
|
-
raise MetaflowInternalError(
|
|
249
|
-
"CustomFlowDecorator registered on the wrong flow -- "
|
|
250
|
-
"expected %s but got %s"
|
|
251
|
-
% (deco._flow_cls.__name__, current_cls.__name__)
|
|
252
|
-
)
|
|
253
|
-
debug.userconf_exec(
|
|
254
|
-
"Evaluating flow level decorator %s" % deco.__class__.__name__
|
|
255
|
-
)
|
|
256
|
-
deco.evaluate(mutable_flow)
|
|
257
|
-
# We reset cached_parameters on the very off chance that the user added
|
|
258
|
-
# more configurations based on the configuration
|
|
259
|
-
if _FlowState.CACHED_PARAMETERS in current_cls._flow_state:
|
|
260
|
-
del current_cls._flow_state[_FlowState.CACHED_PARAMETERS]
|
|
261
|
-
else:
|
|
262
|
-
raise MetaflowInternalError(
|
|
263
|
-
"A non CustomFlowDecorator found in flow custom decorators"
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
# Process parameters to allow them to also use config values easily
|
|
267
|
-
for var, param in self._get_parameters():
|
|
268
|
-
if param.IS_CONFIG_PARAMETER:
|
|
269
|
-
continue
|
|
270
|
-
param.init()
|
|
271
|
-
# Reset all configs that were already present in the class.
|
|
272
|
-
# TODO: This means that users can't override configs directly. Not sure if this
|
|
273
|
-
# is a pattern we want to support
|
|
274
|
-
for var, param in to_reset_configs:
|
|
275
|
-
setattr(current_cls, var, param)
|
|
276
|
-
|
|
277
|
-
# Reset cached parameters again since we added back the config parameters
|
|
278
|
-
if _FlowState.CACHED_PARAMETERS in current_cls._flow_state:
|
|
279
|
-
del current_cls._flow_state[_FlowState.CACHED_PARAMETERS]
|
|
280
|
-
|
|
281
|
-
# Set the current flow class we are in (the one we just created)
|
|
282
|
-
parameters.replace_flow_context(current_cls)
|
|
283
|
-
return current_cls(use_cli=False)
|
|
284
|
-
|
|
285
|
-
def _set_constants(self, graph, kwargs, config_options):
|
|
286
|
-
from metaflow.decorators import (
|
|
287
|
-
flow_decorators,
|
|
288
|
-
) # To prevent circular dependency
|
|
289
|
-
|
|
290
|
-
# Persist values for parameters and other constants (class level variables)
|
|
291
|
-
# only once. This method is called before persist_constants is called to
|
|
292
|
-
# persist all values set using setattr
|
|
293
|
-
self._check_parameters(config_parameters=False)
|
|
294
|
-
|
|
295
|
-
seen = set()
|
|
169
|
+
seen.clear()
|
|
296
170
|
self._success = True
|
|
297
171
|
|
|
298
172
|
parameters_info = []
|
|
299
173
|
for var, param in self._get_parameters():
|
|
300
174
|
seen.add(var)
|
|
301
|
-
|
|
302
|
-
# Use computed value if already evaluated, else get from config_options
|
|
303
|
-
val = param._computed_value or config_options.get(
|
|
304
|
-
param.name.replace("-", "_").lower()
|
|
305
|
-
)
|
|
306
|
-
else:
|
|
307
|
-
val = kwargs[param.name.replace("-", "_").lower()]
|
|
175
|
+
val = kwargs[param.name.replace("-", "_").lower()]
|
|
308
176
|
# Support for delayed evaluation of parameters.
|
|
309
177
|
if isinstance(val, DelayedEvaluationParameter):
|
|
310
178
|
val = val()
|
|
@@ -350,12 +218,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
350
218
|
|
|
351
219
|
@classmethod
|
|
352
220
|
def _get_parameters(cls):
|
|
353
|
-
cached = cls._flow_state.get(_FlowState.CACHED_PARAMETERS)
|
|
354
|
-
if cached is not None:
|
|
355
|
-
for var in cached:
|
|
356
|
-
yield var, getattr(cls, var)
|
|
357
|
-
return
|
|
358
|
-
build_list = []
|
|
359
221
|
for var in dir(cls):
|
|
360
222
|
if var[0] == "_" or var in cls._NON_PARAMETERS:
|
|
361
223
|
continue
|
|
@@ -364,9 +226,7 @@ class FlowSpec(metaclass=FlowSpecMeta):
|
|
|
364
226
|
except:
|
|
365
227
|
continue
|
|
366
228
|
if isinstance(val, Parameter):
|
|
367
|
-
build_list.append(var)
|
|
368
229
|
yield var, val
|
|
369
|
-
cls._flow_state[_FlowState.CACHED_PARAMETERS] = build_list
|
|
370
230
|
|
|
371
231
|
def _set_datastore(self, datastore):
|
|
372
232
|
self._datastore = datastore
|
metaflow/includefile.py
CHANGED
|
@@ -20,7 +20,6 @@ from .parameters import (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
from .plugins import DATACLIENTS
|
|
23
|
-
from .user_configs.config_parameters import ConfigValue
|
|
24
23
|
from .util import get_username
|
|
25
24
|
|
|
26
25
|
import functools
|
|
@@ -137,7 +136,6 @@ class FilePathClass(click.ParamType):
|
|
|
137
136
|
parameter_name=param.name,
|
|
138
137
|
logger=ctx.obj.echo,
|
|
139
138
|
ds_type=ctx.obj.datastore_impl.TYPE,
|
|
140
|
-
configs=None,
|
|
141
139
|
)
|
|
142
140
|
|
|
143
141
|
if len(value) > 0 and (value.startswith("{") or value.startswith('"{')):
|
|
@@ -245,63 +243,29 @@ class IncludeFile(Parameter):
|
|
|
245
243
|
default : Union[str, Callable[ParameterContext, str]]
|
|
246
244
|
Default path to a local file. A function
|
|
247
245
|
implies that the parameter corresponds to a *deploy-time parameter*.
|
|
248
|
-
is_text : bool,
|
|
246
|
+
is_text : bool, default True
|
|
249
247
|
Convert the file contents to a string using the provided `encoding`.
|
|
250
|
-
If False, the artifact is stored in `bytes`.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
is equivalent to "utf-8".
|
|
255
|
-
required : bool, optional, default None
|
|
248
|
+
If False, the artifact is stored in `bytes`.
|
|
249
|
+
encoding : str, optional, default 'utf-8'
|
|
250
|
+
Use this encoding to decode the file contexts if `is_text=True`.
|
|
251
|
+
required : bool, default False
|
|
256
252
|
Require that the user specified a value for the parameter.
|
|
257
|
-
`required=True` implies that the `default` is not used.
|
|
258
|
-
equivalent to False
|
|
253
|
+
`required=True` implies that the `default` is not used.
|
|
259
254
|
help : str, optional
|
|
260
255
|
Help text to show in `run --help`.
|
|
261
256
|
show_default : bool, default True
|
|
262
|
-
If True, show the default value in the help text.
|
|
263
|
-
to True.
|
|
257
|
+
If True, show the default value in the help text.
|
|
264
258
|
"""
|
|
265
259
|
|
|
266
260
|
def __init__(
|
|
267
261
|
self,
|
|
268
262
|
name: str,
|
|
269
|
-
required:
|
|
270
|
-
is_text:
|
|
271
|
-
encoding:
|
|
263
|
+
required: bool = False,
|
|
264
|
+
is_text: bool = True,
|
|
265
|
+
encoding: str = "utf-8",
|
|
272
266
|
help: Optional[str] = None,
|
|
273
267
|
**kwargs: Dict[str, str]
|
|
274
268
|
):
|
|
275
|
-
self._includefile_overrides = {}
|
|
276
|
-
if is_text is not None:
|
|
277
|
-
self._includefile_overrides["is_text"] = is_text
|
|
278
|
-
if encoding is not None:
|
|
279
|
-
self._includefile_overrides["encoding"] = encoding
|
|
280
|
-
# NOTA: Right now, there is an issue where these can't be overridden by config
|
|
281
|
-
# in all circumstances. Ignoring for now.
|
|
282
|
-
super(IncludeFile, self).__init__(
|
|
283
|
-
name,
|
|
284
|
-
required=required,
|
|
285
|
-
help=help,
|
|
286
|
-
type=FilePathClass(
|
|
287
|
-
self._includefile_overrides.get("is_text", True),
|
|
288
|
-
self._includefile_overrides.get("encoding", "utf-8"),
|
|
289
|
-
),
|
|
290
|
-
**kwargs,
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
def init(self):
|
|
294
|
-
super(IncludeFile, self).init()
|
|
295
|
-
|
|
296
|
-
# This will use the values set explicitly in the args if present, else will
|
|
297
|
-
# use and remove from kwargs else will use True/utf-8
|
|
298
|
-
is_text = self._includefile_overrides.get(
|
|
299
|
-
"is_text", self.kwargs.pop("is_text", True)
|
|
300
|
-
)
|
|
301
|
-
encoding = self._includefile_overrides.get(
|
|
302
|
-
"encoding", self.kwargs.pop("encoding", "utf-8")
|
|
303
|
-
)
|
|
304
|
-
|
|
305
269
|
# If a default is specified, it needs to be uploaded when the flow is deployed
|
|
306
270
|
# (for example when doing a `step-functions create`) so we make the default
|
|
307
271
|
# be a DeployTimeField. This means that it will be evaluated in two cases:
|
|
@@ -311,7 +275,7 @@ class IncludeFile(Parameter):
|
|
|
311
275
|
# In the first case, we will need to fully upload the file whereas in the
|
|
312
276
|
# second case, we can just return the string as the FilePath.convert method
|
|
313
277
|
# will take care of evaluating things.
|
|
314
|
-
v =
|
|
278
|
+
v = kwargs.get("default")
|
|
315
279
|
if v is not None:
|
|
316
280
|
# If the default is a callable, we have two DeployTimeField:
|
|
317
281
|
# - the callable nature of the default will require us to "call" the default
|
|
@@ -324,15 +288,23 @@ class IncludeFile(Parameter):
|
|
|
324
288
|
# (call the default)
|
|
325
289
|
if callable(v) and not isinstance(v, DeployTimeField):
|
|
326
290
|
# If default is a callable, make it a DeployTimeField (the inner one)
|
|
327
|
-
v = DeployTimeField(
|
|
328
|
-
|
|
329
|
-
|
|
291
|
+
v = DeployTimeField(name, str, "default", v, return_str=True)
|
|
292
|
+
kwargs["default"] = DeployTimeField(
|
|
293
|
+
name,
|
|
330
294
|
str,
|
|
331
295
|
"default",
|
|
332
296
|
IncludeFile._eval_default(is_text, encoding, v),
|
|
333
297
|
print_representation=v,
|
|
334
298
|
)
|
|
335
299
|
|
|
300
|
+
super(IncludeFile, self).__init__(
|
|
301
|
+
name,
|
|
302
|
+
required=required,
|
|
303
|
+
help=help,
|
|
304
|
+
type=FilePathClass(is_text, encoding),
|
|
305
|
+
**kwargs,
|
|
306
|
+
)
|
|
307
|
+
|
|
336
308
|
def load_parameter(self, v):
|
|
337
309
|
if v is None:
|
|
338
310
|
return v
|
metaflow/metaflow_config.py
CHANGED
|
@@ -441,7 +441,7 @@ ESCAPE_HATCH_WARNING = from_conf("ESCAPE_HATCH_WARNING", True)
|
|
|
441
441
|
###
|
|
442
442
|
# Debug configuration
|
|
443
443
|
###
|
|
444
|
-
DEBUG_OPTIONS = ["subcommand", "sidecar", "s3client", "tracing", "stubgen"
|
|
444
|
+
DEBUG_OPTIONS = ["subcommand", "sidecar", "s3client", "tracing", "stubgen"]
|
|
445
445
|
|
|
446
446
|
for typ in DEBUG_OPTIONS:
|
|
447
447
|
vars()["DEBUG_%s" % typ.upper()] = from_conf("DEBUG_%s" % typ.upper(), False)
|