ob-metaflow 2.15.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/__init__.py +10 -3
- metaflow/_vendor/imghdr/__init__.py +186 -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 +4 -0
- metaflow/cli.py +125 -21
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +204 -40
- metaflow/cli_components/step_cmd.py +160 -4
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +198 -130
- metaflow/client/filecache.py +59 -32
- metaflow/cmd/code/__init__.py +2 -1
- metaflow/cmd/develop/stub_generator.py +49 -18
- metaflow/cmd/develop/stubs.py +9 -27
- metaflow/cmd/make_wrapper.py +30 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +40 -9
- metaflow/datastore/datastore_set.py +10 -1
- metaflow/datastore/flow_datastore.py +124 -4
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +92 -6
- metaflow/debug.py +5 -0
- metaflow/decorators.py +331 -82
- metaflow/extension_support/__init__.py +414 -356
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +322 -82
- metaflow/graph.py +178 -15
- metaflow/includefile.py +25 -3
- metaflow/lint.py +94 -3
- metaflow/meta_files.py +13 -0
- metaflow/metadata_provider/metadata.py +13 -2
- metaflow/metaflow_config.py +66 -4
- metaflow/metaflow_environment.py +91 -25
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +16 -1
- 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 +6 -2
- metaflow/plugins/__init__.py +6 -0
- metaflow/plugins/airflow/airflow.py +11 -1
- metaflow/plugins/airflow/airflow_cli.py +16 -5
- metaflow/plugins/argo/argo_client.py +42 -20
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +1023 -344
- metaflow/plugins/argo/argo_workflows_cli.py +396 -94
- metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
- metaflow/plugins/argo/capture_error.py +5 -2
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +6 -0
- metaflow/plugins/aws/aws_utils.py +33 -1
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +24 -3
- metaflow/plugins/aws/batch/batch_decorator.py +57 -6
- metaflow/plugins/aws/step_functions/step_functions.py +28 -3
- metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +58 -6
- metaflow/plugins/cards/card_modules/basic.py +38 -9
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +592 -3
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
- 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 +56 -41
- metaflow/plugins/cards/card_modules/test_cards.py +22 -6
- metaflow/plugins/cards/component_serializer.py +1 -8
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/s3/s3.py +49 -17
- metaflow/plugins/datatools/s3/s3op.py +113 -66
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/events_decorator.py +127 -121
- 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/kubernetes/kubernetes.py +12 -1
- metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
- metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
- metaflow/plugins/metadata_providers/local.py +76 -82
- metaflow/plugins/metadata_providers/service.py +13 -9
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +11 -2
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/pypi/bootstrap.py +7 -1
- metaflow/plugins/pypi/conda_decorator.py +41 -82
- metaflow/plugins/pypi/conda_environment.py +14 -6
- metaflow/plugins/pypi/micromamba.py +9 -1
- metaflow/plugins/pypi/pip.py +41 -5
- metaflow/plugins/pypi/pypi_decorator.py +4 -4
- metaflow/plugins/pypi/utils.py +22 -0
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +14 -178
- 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/test_unbounded_foreach_decorator.py +2 -2
- metaflow/plugins/timeout_decorator.py +0 -1
- metaflow/plugins/uv/bootstrap.py +29 -1
- metaflow/plugins/uv/uv_environment.py +5 -3
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +79 -26
- metaflow/runner/deployer.py +208 -6
- metaflow/runner/deployer_impl.py +32 -12
- metaflow/runner/metaflow_runner.py +266 -33
- metaflow/runner/subprocess_manager.py +21 -1
- metaflow/runner/utils.py +27 -16
- metaflow/runtime.py +660 -66
- metaflow/task.py +255 -26
- metaflow/user_configs/config_options.py +33 -21
- metaflow/user_configs/config_parameters.py +220 -58
- 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 +197 -7
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
- {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- 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/_vendor/v3_5/zipp.py +0 -329
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
metaflow/decorators.py
CHANGED
|
@@ -1,37 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
import importlib
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import Any, Callable, Dict, List, NewType, Tuple, TypeVar, Union, overload
|
|
6
7
|
|
|
7
|
-
from .flowspec import FlowSpec
|
|
8
|
+
from .flowspec import FlowSpec, FlowStateItems
|
|
8
9
|
from .exception import (
|
|
9
10
|
MetaflowInternalError,
|
|
10
11
|
MetaflowException,
|
|
11
12
|
InvalidDecoratorAttribute,
|
|
12
13
|
)
|
|
13
14
|
|
|
15
|
+
from .debug import debug
|
|
14
16
|
from .parameters import current_flow
|
|
15
|
-
from .user_configs.config_decorators import CustomStepDecorator
|
|
16
17
|
from .user_configs.config_parameters import (
|
|
17
18
|
UNPACK_KEY,
|
|
18
19
|
resolve_delayed_evaluator,
|
|
19
20
|
unpack_delayed_evaluator,
|
|
20
21
|
)
|
|
21
|
-
|
|
22
|
+
from .user_decorators.mutable_flow import MutableFlow
|
|
23
|
+
from .user_decorators.mutable_step import MutableStep
|
|
24
|
+
from .user_decorators.user_flow_decorator import FlowMutator, FlowMutatorMeta
|
|
25
|
+
from .user_decorators.user_step_decorator import (
|
|
26
|
+
StepMutator,
|
|
27
|
+
UserStepDecoratorBase,
|
|
28
|
+
UserStepDecoratorMeta,
|
|
29
|
+
)
|
|
30
|
+
from .metaflow_config import SPIN_ALLOWED_DECORATORS
|
|
22
31
|
from metaflow._vendor import click
|
|
23
32
|
|
|
24
|
-
try:
|
|
25
|
-
unicode
|
|
26
|
-
except NameError:
|
|
27
|
-
unicode = str
|
|
28
|
-
basestring = str
|
|
29
|
-
|
|
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
33
|
|
|
36
34
|
class BadStepDecoratorException(MetaflowException):
|
|
37
35
|
headline = "Syntax error"
|
|
@@ -42,7 +40,7 @@ class BadStepDecoratorException(MetaflowException):
|
|
|
42
40
|
"not declared as a @step. Make sure you apply this decorator "
|
|
43
41
|
"on a function which has @step on the line just before the "
|
|
44
42
|
"function name and @{deco} is above @step.".format(
|
|
45
|
-
deco=deco, func=func
|
|
43
|
+
deco=deco, func=getattr(func, "__name__", str(func))
|
|
46
44
|
)
|
|
47
45
|
)
|
|
48
46
|
super(BadStepDecoratorException, self).__init__(msg)
|
|
@@ -63,11 +61,14 @@ class UnknownStepDecoratorException(MetaflowException):
|
|
|
63
61
|
headline = "Unknown step decorator"
|
|
64
62
|
|
|
65
63
|
def __init__(self, deconame):
|
|
66
|
-
from .plugins import STEP_DECORATORS
|
|
67
|
-
|
|
68
64
|
decos = ", ".join(
|
|
69
|
-
|
|
65
|
+
[
|
|
66
|
+
x
|
|
67
|
+
for x in UserStepDecoratorMeta.all_decorators().keys()
|
|
68
|
+
if not x.endswith("_internal")
|
|
69
|
+
]
|
|
70
70
|
)
|
|
71
|
+
|
|
71
72
|
msg = (
|
|
72
73
|
"Unknown step decorator *{deconame}*. The following decorators are "
|
|
73
74
|
"supported: *{decos}*".format(deconame=deconame, decos=decos)
|
|
@@ -92,9 +93,7 @@ class UnknownFlowDecoratorException(MetaflowException):
|
|
|
92
93
|
headline = "Unknown flow decorator"
|
|
93
94
|
|
|
94
95
|
def __init__(self, deconame):
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
decos = ", ".join(t.name for t in FLOW_DECORATORS)
|
|
96
|
+
decos = ", ".join(FlowMutatorMeta.all_decorators().keys())
|
|
98
97
|
msg = (
|
|
99
98
|
"Unknown flow decorator *{deconame}*. The following decorators are "
|
|
100
99
|
"supported: *{decos}*".format(deconame=deconame, decos=decos)
|
|
@@ -123,9 +122,10 @@ class Decorator(object):
|
|
|
123
122
|
# `allow_multiple` allows setting many decorators of the same type to a step/flow.
|
|
124
123
|
allow_multiple = False
|
|
125
124
|
|
|
126
|
-
def __init__(self, attributes=None, statically_defined=False):
|
|
125
|
+
def __init__(self, attributes=None, statically_defined=False, inserted_by=None):
|
|
127
126
|
self.attributes = self.defaults.copy()
|
|
128
127
|
self.statically_defined = statically_defined
|
|
128
|
+
self.inserted_by = inserted_by
|
|
129
129
|
self._user_defined_attributes = set()
|
|
130
130
|
self._ran_init = False
|
|
131
131
|
|
|
@@ -143,7 +143,9 @@ class Decorator(object):
|
|
|
143
143
|
Initializes the decorator. In general, any operation you would do in __init__
|
|
144
144
|
should be done here.
|
|
145
145
|
"""
|
|
146
|
+
pass
|
|
146
147
|
|
|
148
|
+
def external_init(self):
|
|
147
149
|
# In some cases (specifically when using remove_decorator), we may need to call
|
|
148
150
|
# init multiple times. Short-circuit re-evaluating.
|
|
149
151
|
if self._ran_init:
|
|
@@ -152,14 +154,16 @@ class Decorator(object):
|
|
|
152
154
|
# Note that by design, later values override previous ones.
|
|
153
155
|
self.attributes, new_user_attributes = unpack_delayed_evaluator(self.attributes)
|
|
154
156
|
self._user_defined_attributes.update(new_user_attributes)
|
|
155
|
-
self.attributes = resolve_delayed_evaluator(self.attributes)
|
|
157
|
+
self.attributes = resolve_delayed_evaluator(self.attributes, to_dict=True)
|
|
156
158
|
|
|
159
|
+
if "init" in self.__class__.__dict__:
|
|
160
|
+
self.init()
|
|
157
161
|
self._ran_init = True
|
|
158
162
|
|
|
159
163
|
@classmethod
|
|
160
|
-
def
|
|
164
|
+
def extract_args_kwargs_from_decorator_spec(cls, deco_spec):
|
|
161
165
|
if len(deco_spec) == 0:
|
|
162
|
-
return
|
|
166
|
+
return [], {}
|
|
163
167
|
|
|
164
168
|
attrs = {}
|
|
165
169
|
# TODO: Do we really want to allow spaces in the names of attributes?!?
|
|
@@ -179,9 +183,20 @@ class Decorator(object):
|
|
|
179
183
|
val_parsed = val.strip()
|
|
180
184
|
|
|
181
185
|
attrs[name.strip()] = val_parsed
|
|
182
|
-
|
|
186
|
+
|
|
187
|
+
return [], attrs
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def parse_decorator_spec(cls, deco_spec):
|
|
191
|
+
if len(deco_spec) == 0:
|
|
192
|
+
return cls()
|
|
193
|
+
|
|
194
|
+
_, kwargs = cls.extract_args_kwargs_from_decorator_spec(deco_spec)
|
|
195
|
+
return cls(attributes=kwargs)
|
|
183
196
|
|
|
184
197
|
def make_decorator_spec(self):
|
|
198
|
+
# Make sure all attributes are evaluated
|
|
199
|
+
self.external_init()
|
|
185
200
|
attrs = {k: v for k, v in self.attributes.items() if v is not None}
|
|
186
201
|
if attrs:
|
|
187
202
|
attr_list = []
|
|
@@ -189,7 +204,7 @@ class Decorator(object):
|
|
|
189
204
|
# escaping but for more complex types (typically dictionaries or lists),
|
|
190
205
|
# we dump using JSON.
|
|
191
206
|
for k, v in attrs.items():
|
|
192
|
-
if isinstance(v, (int, float,
|
|
207
|
+
if isinstance(v, (int, float, str)):
|
|
193
208
|
attr_list.append("%s=%s" % (k, str(v)))
|
|
194
209
|
else:
|
|
195
210
|
attr_list.append("%s=%s" % (k, json.dumps(v).replace('"', '\\"')))
|
|
@@ -199,8 +214,21 @@ class Decorator(object):
|
|
|
199
214
|
else:
|
|
200
215
|
return self.name
|
|
201
216
|
|
|
217
|
+
def get_args_kwargs(self) -> Tuple[List[Any], Dict[str, Any]]:
|
|
218
|
+
"""
|
|
219
|
+
Get the arguments and keyword arguments of the decorator.
|
|
220
|
+
|
|
221
|
+
Returns
|
|
222
|
+
-------
|
|
223
|
+
Tuple[List[Any], Dict[str, Any]]
|
|
224
|
+
A tuple containing a list of arguments and a dictionary of keyword arguments.
|
|
225
|
+
"""
|
|
226
|
+
return [], dict(self.attributes)
|
|
227
|
+
|
|
202
228
|
def __str__(self):
|
|
203
229
|
mode = "static" if self.statically_defined else "dynamic"
|
|
230
|
+
if self.inserted_by:
|
|
231
|
+
mode += " (inserted by %s)" % " from ".join(self.inserted_by)
|
|
204
232
|
attrs = " ".join("%s=%s" % x for x in self.attributes.items())
|
|
205
233
|
if attrs:
|
|
206
234
|
attrs = " " + attrs
|
|
@@ -266,7 +294,11 @@ def add_decorator_options(cmd):
|
|
|
266
294
|
|
|
267
295
|
|
|
268
296
|
def flow_decorators(flow_cls):
|
|
269
|
-
return [
|
|
297
|
+
return [
|
|
298
|
+
d
|
|
299
|
+
for deco_list in flow_cls._flow_state[FlowStateItems.FLOW_DECORATORS].values()
|
|
300
|
+
for d in deco_list
|
|
301
|
+
]
|
|
270
302
|
|
|
271
303
|
|
|
272
304
|
class StepDecorator(Decorator):
|
|
@@ -315,15 +347,36 @@ class StepDecorator(Decorator):
|
|
|
315
347
|
|
|
316
348
|
def add_to_package(self):
|
|
317
349
|
"""
|
|
318
|
-
Called to add custom
|
|
350
|
+
Called to add custom files needed for this environment. This hook will be
|
|
319
351
|
called in the `MetaflowPackage` class where metaflow compiles the code package
|
|
320
|
-
tarball. This hook
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
352
|
+
tarball. This hook can return one of two things (the first is for backwards
|
|
353
|
+
compatibility -- move to the second):
|
|
354
|
+
- a generator yielding a tuple of `(file_path, arcname)` to add files to
|
|
355
|
+
the code package. `file_path` is the path to the file on the local filesystem
|
|
356
|
+
and `arcname` is the path relative to the packaged code.
|
|
357
|
+
- a generator yielding a tuple of `(content, arcname, type)` where:
|
|
358
|
+
- type is one of
|
|
359
|
+
ContentType.{USER_CONTENT, CODE_CONTENT, MODULE_CONTENT, OTHER_CONTENT}
|
|
360
|
+
- for USER_CONTENT:
|
|
361
|
+
- the file will be included relative to the directory containing the
|
|
362
|
+
user's flow file.
|
|
363
|
+
- content: path to the file to include
|
|
364
|
+
- arcname: path relative to the directory containing the user's flow file
|
|
365
|
+
- for CODE_CONTENT:
|
|
366
|
+
- the file will be included relative to the code directory in the package.
|
|
367
|
+
This will be the directory containing `metaflow`.
|
|
368
|
+
- content: path to the file to include
|
|
369
|
+
- arcname: path relative to the code directory in the package
|
|
370
|
+
- for MODULE_CONTENT:
|
|
371
|
+
- the module will be added to the code package as a python module. It will
|
|
372
|
+
be accessible as usual (import <module_name>)
|
|
373
|
+
- content: name of the module
|
|
374
|
+
- arcname: None (ignored)
|
|
375
|
+
- for OTHER_CONTENT:
|
|
376
|
+
- the file will be included relative to any other configuration/metadata
|
|
377
|
+
files for the flow
|
|
378
|
+
- content: path to the file to include
|
|
379
|
+
- arcname: path relative to the config directory in the package
|
|
327
380
|
"""
|
|
328
381
|
return []
|
|
329
382
|
|
|
@@ -443,12 +496,20 @@ def _base_flow_decorator(decofunc, *args, **kwargs):
|
|
|
443
496
|
cls = args[0]
|
|
444
497
|
if isinstance(cls, type) and issubclass(cls, FlowSpec):
|
|
445
498
|
# flow decorators add attributes in the class dictionary,
|
|
446
|
-
#
|
|
447
|
-
|
|
499
|
+
# cls._flow_state[FlowStateItems.FLOW_DECORATORS]. This is of type `{key:[decos]}`
|
|
500
|
+
self_flow_decos = cls._flow_state.self_data[FlowStateItems.FLOW_DECORATORS]
|
|
501
|
+
inherited_flow_decos = cls._flow_state.inherited_data.get(
|
|
502
|
+
FlowStateItems.FLOW_DECORATORS, {}
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
if (
|
|
506
|
+
decofunc.name in self_flow_decos
|
|
507
|
+
or decofunc.name in inherited_flow_decos
|
|
508
|
+
) and not decofunc.allow_multiple:
|
|
448
509
|
raise DuplicateFlowDecoratorException(decofunc.name)
|
|
449
510
|
else:
|
|
450
511
|
deco_instance = decofunc(attributes=kwargs, statically_defined=True)
|
|
451
|
-
|
|
512
|
+
self_flow_decos.setdefault(decofunc.name, []).append(deco_instance)
|
|
452
513
|
else:
|
|
453
514
|
raise BadFlowDecoratorException(decofunc.name)
|
|
454
515
|
return cls
|
|
@@ -472,7 +533,7 @@ def _base_step_decorator(decotype, *args, **kwargs):
|
|
|
472
533
|
# No keyword arguments specified for the decorator, e.g. @foobar.
|
|
473
534
|
# The first argument is the function to be decorated.
|
|
474
535
|
func = args[0]
|
|
475
|
-
if isinstance(func,
|
|
536
|
+
if isinstance(func, (StepMutator, UserStepDecoratorBase)):
|
|
476
537
|
func = func._my_step
|
|
477
538
|
if not hasattr(func, "is_step"):
|
|
478
539
|
raise BadStepDecoratorException(decotype.name, func)
|
|
@@ -498,9 +559,10 @@ def _base_step_decorator(decotype, *args, **kwargs):
|
|
|
498
559
|
|
|
499
560
|
|
|
500
561
|
_all_step_decos = None
|
|
562
|
+
_all_flow_decos = None
|
|
501
563
|
|
|
502
564
|
|
|
503
|
-
def
|
|
565
|
+
def get_all_step_decos():
|
|
504
566
|
global _all_step_decos
|
|
505
567
|
if _all_step_decos is None:
|
|
506
568
|
from .plugins import STEP_DECORATORS
|
|
@@ -509,6 +571,67 @@ def _get_all_step_decos():
|
|
|
509
571
|
return _all_step_decos
|
|
510
572
|
|
|
511
573
|
|
|
574
|
+
def get_all_flow_decos():
|
|
575
|
+
global _all_flow_decos
|
|
576
|
+
if _all_flow_decos is None:
|
|
577
|
+
from .plugins import FLOW_DECORATORS
|
|
578
|
+
|
|
579
|
+
_all_flow_decos = {decotype.name: decotype for decotype in FLOW_DECORATORS}
|
|
580
|
+
return _all_flow_decos
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def extract_step_decorator_from_decospec(decospec: str):
|
|
584
|
+
splits = decospec.split(":", 1)
|
|
585
|
+
deconame = splits[0]
|
|
586
|
+
|
|
587
|
+
# Check if it is a user-defined decorator or metaflow decorator
|
|
588
|
+
deco_cls = UserStepDecoratorMeta.get_decorator_by_name(deconame)
|
|
589
|
+
if deco_cls is not None:
|
|
590
|
+
return (
|
|
591
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
592
|
+
len(splits) > 1,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
# Check if this is a decorator we can import
|
|
596
|
+
if "." in deconame:
|
|
597
|
+
# We consider this to be a import path to a user decorator so
|
|
598
|
+
# something like "my_package.my_decorator"
|
|
599
|
+
module_name, class_name = deconame.rsplit(".", 1)
|
|
600
|
+
try:
|
|
601
|
+
module = importlib.import_module(module_name)
|
|
602
|
+
except ImportError as e:
|
|
603
|
+
raise MetaflowException(
|
|
604
|
+
"Could not import user decorator %s" % deconame
|
|
605
|
+
) from e
|
|
606
|
+
deco_cls = getattr(module, class_name, None)
|
|
607
|
+
if (
|
|
608
|
+
deco_cls is None
|
|
609
|
+
or not isinstance(deco_cls, type)
|
|
610
|
+
or not issubclass(deco_cls, UserStepDecoratorBase)
|
|
611
|
+
):
|
|
612
|
+
raise UnknownStepDecoratorException(deconame)
|
|
613
|
+
return (
|
|
614
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
615
|
+
len(splits) > 1,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
raise UnknownStepDecoratorException(deconame)
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def extract_flow_decorator_from_decospec(decospec: str):
|
|
622
|
+
splits = decospec.split(":", 1)
|
|
623
|
+
deconame = splits[0]
|
|
624
|
+
# Check if it is a user-defined decorator or metaflow decorator
|
|
625
|
+
deco_cls = FlowMutatorMeta.get_decorator_by_name(deconame)
|
|
626
|
+
if deco_cls is not None:
|
|
627
|
+
return (
|
|
628
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
629
|
+
len(splits) > 1,
|
|
630
|
+
)
|
|
631
|
+
else:
|
|
632
|
+
raise UnknownFlowDecoratorException(deconame)
|
|
633
|
+
|
|
634
|
+
|
|
512
635
|
def _attach_decorators(flow, decospecs):
|
|
513
636
|
"""
|
|
514
637
|
Attach decorators to all steps during runtime. This has the same
|
|
@@ -532,49 +655,95 @@ def _attach_decorators_to_step(step, decospecs):
|
|
|
532
655
|
effect as if you defined the decorators statically in the source for
|
|
533
656
|
the step.
|
|
534
657
|
"""
|
|
658
|
+
for decospec in decospecs:
|
|
659
|
+
step_deco, _ = extract_step_decorator_from_decospec(decospec)
|
|
660
|
+
if isinstance(step_deco, StepDecorator):
|
|
661
|
+
# Check multiple
|
|
662
|
+
if (
|
|
663
|
+
step_deco.name not in [deco.name for deco in step.decorators]
|
|
664
|
+
or step_deco.allow_multiple
|
|
665
|
+
):
|
|
666
|
+
step.decorators.append(step_deco)
|
|
667
|
+
# Else it is ignored -- this is a non-static decorator
|
|
535
668
|
|
|
536
|
-
|
|
669
|
+
else:
|
|
670
|
+
step_deco.add_or_raise(step, False, 1, None)
|
|
537
671
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
672
|
+
|
|
673
|
+
def _should_skip_decorator_for_spin(
|
|
674
|
+
deco, is_spin, skip_decorators, logger, decorator_type="decorator"
|
|
675
|
+
):
|
|
676
|
+
"""
|
|
677
|
+
Determine if a decorator should be skipped for spin steps.
|
|
678
|
+
|
|
679
|
+
Parameters:
|
|
680
|
+
-----------
|
|
681
|
+
deco : Decorator
|
|
682
|
+
The decorator instance to check
|
|
683
|
+
is_spin : bool
|
|
684
|
+
Whether this is a spin step
|
|
685
|
+
skip_decorators : bool
|
|
686
|
+
Whether to skip all decorators
|
|
687
|
+
logger : callable
|
|
688
|
+
Logger function for warnings
|
|
689
|
+
decorator_type : str
|
|
690
|
+
Type of decorator ("Flow decorator" or "Step decorator") for logging
|
|
691
|
+
|
|
692
|
+
Returns:
|
|
693
|
+
--------
|
|
694
|
+
bool
|
|
695
|
+
True if the decorator should be skipped, False otherwise
|
|
696
|
+
"""
|
|
697
|
+
if not is_spin:
|
|
698
|
+
return False
|
|
699
|
+
|
|
700
|
+
# Skip all decorator hooks if skip_decorators is True
|
|
701
|
+
if skip_decorators:
|
|
702
|
+
return True
|
|
703
|
+
|
|
704
|
+
# Run decorator hooks for spin steps only if they are in the whitelist
|
|
705
|
+
if deco.name not in SPIN_ALLOWED_DECORATORS:
|
|
706
|
+
logger(
|
|
707
|
+
f"[Warning] Ignoring {decorator_type} '{deco.name}' as it is not supported in spin steps.",
|
|
708
|
+
system_msg=True,
|
|
709
|
+
timestamp=False,
|
|
710
|
+
bad=True,
|
|
711
|
+
)
|
|
712
|
+
return True
|
|
713
|
+
|
|
714
|
+
return False
|
|
556
715
|
|
|
557
716
|
|
|
558
717
|
def _init(flow, only_non_static=False):
|
|
559
|
-
|
|
718
|
+
flow_decos = flow._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
719
|
+
for decorators in flow_decos.values():
|
|
560
720
|
for deco in decorators:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
deco.init()
|
|
564
|
-
_inited_decorators.add(deco)
|
|
721
|
+
deco.external_init()
|
|
722
|
+
|
|
565
723
|
for flowstep in flow:
|
|
566
724
|
for deco in flowstep.decorators:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
deco.
|
|
570
|
-
|
|
725
|
+
deco.external_init()
|
|
726
|
+
for deco in flowstep.config_decorators or []:
|
|
727
|
+
deco.external_init()
|
|
728
|
+
for deco in flowstep.wrappers or []:
|
|
729
|
+
deco.external_init()
|
|
571
730
|
|
|
572
731
|
|
|
573
732
|
def _init_flow_decorators(
|
|
574
|
-
flow,
|
|
733
|
+
flow,
|
|
734
|
+
graph,
|
|
735
|
+
environment,
|
|
736
|
+
flow_datastore,
|
|
737
|
+
metadata,
|
|
738
|
+
logger,
|
|
739
|
+
echo,
|
|
740
|
+
deco_options,
|
|
741
|
+
is_spin=False,
|
|
742
|
+
skip_decorators=False,
|
|
575
743
|
):
|
|
576
744
|
# Since all flow decorators are stored as `{key:[deco]}` we iterate through each of them.
|
|
577
|
-
|
|
745
|
+
flow_decos = flow._flow_state[FlowStateItems.FLOW_DECORATORS]
|
|
746
|
+
for decorators in flow_decos.values():
|
|
578
747
|
# First resolve the `options` for the flow decorator.
|
|
579
748
|
# Options are passed from cli.
|
|
580
749
|
# For example `@project` can take a `--name` / `--branch` from the cli as options.
|
|
@@ -600,6 +769,10 @@ def _init_flow_decorators(
|
|
|
600
769
|
for option, option_info in deco.options.items()
|
|
601
770
|
}
|
|
602
771
|
for deco in decorators:
|
|
772
|
+
if _should_skip_decorator_for_spin(
|
|
773
|
+
deco, is_spin, skip_decorators, logger, "Flow decorator"
|
|
774
|
+
):
|
|
775
|
+
continue
|
|
603
776
|
deco.flow_init(
|
|
604
777
|
flow,
|
|
605
778
|
graph,
|
|
@@ -612,9 +785,89 @@ def _init_flow_decorators(
|
|
|
612
785
|
)
|
|
613
786
|
|
|
614
787
|
|
|
615
|
-
def _init_step_decorators(
|
|
788
|
+
def _init_step_decorators(
|
|
789
|
+
flow,
|
|
790
|
+
graph,
|
|
791
|
+
environment,
|
|
792
|
+
flow_datastore,
|
|
793
|
+
logger,
|
|
794
|
+
is_spin=False,
|
|
795
|
+
skip_decorators=False,
|
|
796
|
+
):
|
|
797
|
+
# NOTE: We don't need the graph but keeping it for backwards compatibility with
|
|
798
|
+
# extensions that use it directly. We will remove it at some point.
|
|
799
|
+
|
|
800
|
+
# We call the mutate method for both the flow and step mutators.
|
|
801
|
+
cls = flow.__class__
|
|
802
|
+
# Run all the decorators. We first run the flow-level decorators
|
|
803
|
+
# and then the step level ones to maintain a consistent order with how
|
|
804
|
+
# other decorators are run.
|
|
805
|
+
|
|
806
|
+
for deco in cls._flow_state[FlowStateItems.FLOW_MUTATORS]:
|
|
807
|
+
if isinstance(deco, FlowMutator):
|
|
808
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
|
809
|
+
mutable_flow = MutableFlow(
|
|
810
|
+
cls,
|
|
811
|
+
pre_mutate=False,
|
|
812
|
+
statically_defined=deco.statically_defined,
|
|
813
|
+
inserted_by=inserted_by_value,
|
|
814
|
+
)
|
|
815
|
+
# Sanity check to make sure we are applying the decorator to the right
|
|
816
|
+
# class
|
|
817
|
+
if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
|
|
818
|
+
raise MetaflowInternalError(
|
|
819
|
+
"FlowMutator registered on the wrong flow -- "
|
|
820
|
+
"expected %s but got %s" % (deco._flow_cls.__name__, cls.__name__)
|
|
821
|
+
)
|
|
822
|
+
debug.userconf_exec(
|
|
823
|
+
"Evaluating flow level decorator %s (mutate)" % deco.__class__.__name__
|
|
824
|
+
)
|
|
825
|
+
deco.mutate(mutable_flow)
|
|
826
|
+
# We reset cached_parameters on the very off chance that the user added
|
|
827
|
+
# more configurations based on the configuration
|
|
828
|
+
if cls._flow_state[FlowStateItems.CACHED_PARAMETERS] is not None:
|
|
829
|
+
cls._flow_state[FlowStateItems.CACHED_PARAMETERS] = None
|
|
830
|
+
else:
|
|
831
|
+
raise MetaflowInternalError(
|
|
832
|
+
"A non FlowMutator found in flow custom decorators"
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
for step in cls._steps:
|
|
836
|
+
for deco in step.config_decorators:
|
|
837
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
|
838
|
+
|
|
839
|
+
if isinstance(deco, StepMutator):
|
|
840
|
+
debug.userconf_exec(
|
|
841
|
+
"Evaluating step level decorator %s for %s (mutate)"
|
|
842
|
+
% (deco.__class__.__name__, step.name)
|
|
843
|
+
)
|
|
844
|
+
deco.mutate(
|
|
845
|
+
MutableStep(
|
|
846
|
+
cls,
|
|
847
|
+
step,
|
|
848
|
+
pre_mutate=False,
|
|
849
|
+
statically_defined=deco.statically_defined,
|
|
850
|
+
inserted_by=inserted_by_value,
|
|
851
|
+
)
|
|
852
|
+
)
|
|
853
|
+
else:
|
|
854
|
+
raise MetaflowInternalError(
|
|
855
|
+
"A non StepMutator found in step custom decorators"
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
if step.config_decorators:
|
|
859
|
+
# We remove all mention of the custom step decorator
|
|
860
|
+
setattr(cls, step.name, step)
|
|
861
|
+
|
|
862
|
+
cls._init_graph()
|
|
863
|
+
graph = flow._graph
|
|
864
|
+
|
|
616
865
|
for step in flow:
|
|
617
866
|
for deco in step.decorators:
|
|
867
|
+
if _should_skip_decorator_for_spin(
|
|
868
|
+
deco, is_spin, skip_decorators, logger, "Step decorator"
|
|
869
|
+
):
|
|
870
|
+
continue
|
|
618
871
|
deco.step_init(
|
|
619
872
|
flow,
|
|
620
873
|
graph,
|
|
@@ -638,7 +891,7 @@ StepFlag = NewType("StepFlag", bool)
|
|
|
638
891
|
|
|
639
892
|
@overload
|
|
640
893
|
def step(
|
|
641
|
-
f: Callable[[FlowSpecDerived], None]
|
|
894
|
+
f: Callable[[FlowSpecDerived], None],
|
|
642
895
|
) -> Callable[[FlowSpecDerived, StepFlag], None]: ...
|
|
643
896
|
|
|
644
897
|
|
|
@@ -649,7 +902,7 @@ def step(
|
|
|
649
902
|
|
|
650
903
|
|
|
651
904
|
def step(
|
|
652
|
-
f: Union[Callable[[FlowSpecDerived], None], Callable[[FlowSpecDerived, Any], None]]
|
|
905
|
+
f: Union[Callable[[FlowSpecDerived], None], Callable[[FlowSpecDerived, Any], None]],
|
|
653
906
|
):
|
|
654
907
|
"""
|
|
655
908
|
Marks a method in a FlowSpec as a Metaflow Step. Note that this
|
|
@@ -685,12 +938,8 @@ def step(
|
|
|
685
938
|
f.is_step = True
|
|
686
939
|
f.decorators = []
|
|
687
940
|
f.config_decorators = []
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
f.name = f.__name__
|
|
691
|
-
except:
|
|
692
|
-
# python 2
|
|
693
|
-
f.name = f.__func__.func_name
|
|
941
|
+
f.wrappers = []
|
|
942
|
+
f.name = f.__name__
|
|
694
943
|
return f
|
|
695
944
|
|
|
696
945
|
|