ob-metaflow 2.15.18.1__py2.py3-none-any.whl → 2.16.0.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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/__init__.py +7 -1
- metaflow/_vendor/imghdr/__init__.py +180 -0
- metaflow/cli.py +16 -1
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +6 -2
- metaflow/client/core.py +22 -30
- metaflow/cmd/develop/stub_generator.py +19 -2
- metaflow/datastore/task_datastore.py +0 -1
- metaflow/debug.py +5 -0
- metaflow/decorators.py +230 -70
- metaflow/extension_support/__init__.py +15 -8
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +80 -53
- 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 +476 -0
- metaflow/plugins/__init__.py +3 -0
- metaflow/plugins/airflow/airflow.py +11 -1
- metaflow/plugins/airflow/airflow_cli.py +15 -4
- metaflow/plugins/argo/argo_workflows.py +346 -301
- metaflow/plugins/argo/argo_workflows_cli.py +16 -4
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/aws/aws_utils.py +1 -1
- 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 +10 -1
- metaflow/plugins/aws/step_functions/step_functions_cli.py +15 -4
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_decorator.py +57 -6
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +5 -2
- metaflow/plugins/cards/card_modules/test_cards.py +16 -0
- metaflow/plugins/cards/metadata.py +22 -0
- 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 +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/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +9 -173
- 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 +11 -0
- metaflow/plugins/uv/uv_environment.py +4 -2
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +5 -4
- metaflow/runner/metaflow_runner.py +16 -1
- metaflow/runner/subprocess_manager.py +14 -2
- metaflow/runtime.py +82 -11
- metaflow/task.py +91 -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 +263 -0
- metaflow/user_decorators/user_step_decorator.py +712 -0
- metaflow/util.py +4 -1
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/Tiltfile +27 -2
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/METADATA +2 -2
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/RECORD +90 -70
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/Makefile +0 -0
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.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, _FlowState
|
|
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
|
)
|
|
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
|
+
)
|
|
21
30
|
|
|
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"
|
|
@@ -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:
|
|
@@ -154,12 +156,14 @@ class Decorator(object):
|
|
|
154
156
|
self._user_defined_attributes.update(new_user_attributes)
|
|
155
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
|
|
@@ -315,15 +343,36 @@ class StepDecorator(Decorator):
|
|
|
315
343
|
|
|
316
344
|
def add_to_package(self):
|
|
317
345
|
"""
|
|
318
|
-
Called to add custom
|
|
346
|
+
Called to add custom files needed for this environment. This hook will be
|
|
319
347
|
called in the `MetaflowPackage` class where metaflow compiles the code package
|
|
320
|
-
tarball. This hook
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
348
|
+
tarball. This hook can return one of two things (the first is for backwards
|
|
349
|
+
compatibility -- move to the second):
|
|
350
|
+
- a generator yielding a tuple of `(file_path, arcname)` to add files to
|
|
351
|
+
the code package. `file_path` is the path to the file on the local filesystem
|
|
352
|
+
and `arcname` is the path relative to the packaged code.
|
|
353
|
+
- a generator yielding a tuple of `(content, arcname, type)` where:
|
|
354
|
+
- type is one of
|
|
355
|
+
ContentType.{USER_CONTENT, CODE_CONTENT, MODULE_CONTENT, OTHER_CONTENT}
|
|
356
|
+
- for USER_CONTENT:
|
|
357
|
+
- the file will be included relative to the directory containing the
|
|
358
|
+
user's flow file.
|
|
359
|
+
- content: path to the file to include
|
|
360
|
+
- arcname: path relative to the directory containing the user's flow file
|
|
361
|
+
- for CODE_CONTENT:
|
|
362
|
+
- the file will be included relative to the code directory in the package.
|
|
363
|
+
This will be the directory containing `metaflow`.
|
|
364
|
+
- content: path to the file to include
|
|
365
|
+
- arcname: path relative to the code directory in the package
|
|
366
|
+
- for MODULE_CONTENT:
|
|
367
|
+
- the module will be added to the code package as a python module. It will
|
|
368
|
+
be accessible as usual (import <module_name>)
|
|
369
|
+
- content: name of the module
|
|
370
|
+
- arcname: None (ignored)
|
|
371
|
+
- for OTHER_CONTENT:
|
|
372
|
+
- the file will be included relative to any other configuration/metadata
|
|
373
|
+
files for the flow
|
|
374
|
+
- content: path to the file to include
|
|
375
|
+
- arcname: path relative to the config directory in the package
|
|
327
376
|
"""
|
|
328
377
|
return []
|
|
329
378
|
|
|
@@ -472,7 +521,7 @@ def _base_step_decorator(decotype, *args, **kwargs):
|
|
|
472
521
|
# No keyword arguments specified for the decorator, e.g. @foobar.
|
|
473
522
|
# The first argument is the function to be decorated.
|
|
474
523
|
func = args[0]
|
|
475
|
-
if isinstance(func,
|
|
524
|
+
if isinstance(func, StepMutator):
|
|
476
525
|
func = func._my_step
|
|
477
526
|
if not hasattr(func, "is_step"):
|
|
478
527
|
raise BadStepDecoratorException(decotype.name, func)
|
|
@@ -498,9 +547,10 @@ def _base_step_decorator(decotype, *args, **kwargs):
|
|
|
498
547
|
|
|
499
548
|
|
|
500
549
|
_all_step_decos = None
|
|
550
|
+
_all_flow_decos = None
|
|
501
551
|
|
|
502
552
|
|
|
503
|
-
def
|
|
553
|
+
def get_all_step_decos():
|
|
504
554
|
global _all_step_decos
|
|
505
555
|
if _all_step_decos is None:
|
|
506
556
|
from .plugins import STEP_DECORATORS
|
|
@@ -509,6 +559,67 @@ def _get_all_step_decos():
|
|
|
509
559
|
return _all_step_decos
|
|
510
560
|
|
|
511
561
|
|
|
562
|
+
def get_all_flow_decos():
|
|
563
|
+
global _all_flow_decos
|
|
564
|
+
if _all_flow_decos is None:
|
|
565
|
+
from .plugins import FLOW_DECORATORS
|
|
566
|
+
|
|
567
|
+
_all_flow_decos = {decotype.name: decotype for decotype in FLOW_DECORATORS}
|
|
568
|
+
return _all_flow_decos
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def extract_step_decorator_from_decospec(decospec: str):
|
|
572
|
+
splits = decospec.split(":", 1)
|
|
573
|
+
deconame = splits[0]
|
|
574
|
+
|
|
575
|
+
# Check if it is a user-defined decorator or metaflow decorator
|
|
576
|
+
deco_cls = UserStepDecoratorMeta.get_decorator_by_name(deconame)
|
|
577
|
+
if deco_cls is not None:
|
|
578
|
+
return (
|
|
579
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
580
|
+
len(splits) > 1,
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Check if this is a decorator we can import
|
|
584
|
+
if "." in deconame:
|
|
585
|
+
# We consider this to be a import path to a user decorator so
|
|
586
|
+
# something like "my_package.my_decorator"
|
|
587
|
+
module_name, class_name = deconame.rsplit(".", 1)
|
|
588
|
+
try:
|
|
589
|
+
module = importlib.import_module(module_name)
|
|
590
|
+
except ImportError as e:
|
|
591
|
+
raise MetaflowException(
|
|
592
|
+
"Could not import user decorator %s" % deconame
|
|
593
|
+
) from e
|
|
594
|
+
deco_cls = getattr(module, class_name, None)
|
|
595
|
+
if (
|
|
596
|
+
deco_cls is None
|
|
597
|
+
or not isinstance(deco_cls, type)
|
|
598
|
+
or not issubclass(deco_cls, UserStepDecoratorBase)
|
|
599
|
+
):
|
|
600
|
+
raise UnknownStepDecoratorException(deconame)
|
|
601
|
+
return (
|
|
602
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
603
|
+
len(splits) > 1,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
raise UnknownStepDecoratorException(deconame)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def extract_flow_decorator_from_decospec(decospec: str):
|
|
610
|
+
splits = decospec.split(":", 1)
|
|
611
|
+
deconame = splits[0]
|
|
612
|
+
# Check if it is a user-defined decorator or metaflow decorator
|
|
613
|
+
deco_cls = FlowMutatorMeta.get_decorator_by_name(deconame)
|
|
614
|
+
if deco_cls is not None:
|
|
615
|
+
return (
|
|
616
|
+
deco_cls.parse_decorator_spec(splits[1] if len(splits) > 1 else ""),
|
|
617
|
+
len(splits) > 1,
|
|
618
|
+
)
|
|
619
|
+
else:
|
|
620
|
+
raise UnknownFlowDecoratorException(deconame)
|
|
621
|
+
|
|
622
|
+
|
|
512
623
|
def _attach_decorators(flow, decospecs):
|
|
513
624
|
"""
|
|
514
625
|
Attach decorators to all steps during runtime. This has the same
|
|
@@ -532,42 +643,33 @@ def _attach_decorators_to_step(step, decospecs):
|
|
|
532
643
|
effect as if you defined the decorators statically in the source for
|
|
533
644
|
the step.
|
|
534
645
|
"""
|
|
535
|
-
|
|
536
|
-
decos = _get_all_step_decos()
|
|
537
|
-
|
|
538
646
|
for decospec in decospecs:
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
# then add the decorator to the step
|
|
552
|
-
deco = decos[deconame]._parse_decorator_spec(
|
|
553
|
-
splits[1] if len(splits) > 1 else ""
|
|
554
|
-
)
|
|
555
|
-
step.decorators.append(deco)
|
|
647
|
+
step_deco, _ = extract_step_decorator_from_decospec(decospec)
|
|
648
|
+
if isinstance(step_deco, StepDecorator):
|
|
649
|
+
# Check multiple
|
|
650
|
+
if (
|
|
651
|
+
step_deco.name not in [deco.name for deco in step.decorators]
|
|
652
|
+
or step_deco.allow_multiple
|
|
653
|
+
):
|
|
654
|
+
step.decorators.append(step_deco)
|
|
655
|
+
# Else it is ignored -- this is a non-static decorator
|
|
656
|
+
|
|
657
|
+
else:
|
|
658
|
+
step_deco.add_or_raise(step, False, 1, None)
|
|
556
659
|
|
|
557
660
|
|
|
558
661
|
def _init(flow, only_non_static=False):
|
|
559
662
|
for decorators in flow._flow_decorators.values():
|
|
560
663
|
for deco in decorators:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
deco.init()
|
|
564
|
-
_inited_decorators.add(deco)
|
|
664
|
+
deco.external_init()
|
|
665
|
+
|
|
565
666
|
for flowstep in flow:
|
|
566
667
|
for deco in flowstep.decorators:
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
deco.
|
|
570
|
-
|
|
668
|
+
deco.external_init()
|
|
669
|
+
for deco in flowstep.config_decorators or []:
|
|
670
|
+
deco.external_init()
|
|
671
|
+
for deco in flowstep.wrappers or []:
|
|
672
|
+
deco.external_init()
|
|
571
673
|
|
|
572
674
|
|
|
573
675
|
def _init_flow_decorators(
|
|
@@ -613,6 +715,68 @@ def _init_flow_decorators(
|
|
|
613
715
|
|
|
614
716
|
|
|
615
717
|
def _init_step_decorators(flow, graph, environment, flow_datastore, logger):
|
|
718
|
+
# We call the mutate method for both the flow and step mutators.
|
|
719
|
+
cls = flow.__class__
|
|
720
|
+
# Run all the decorators. We first run the flow-level decorators
|
|
721
|
+
# and then the step level ones to maintain a consistent order with how
|
|
722
|
+
# other decorators are run.
|
|
723
|
+
|
|
724
|
+
for deco in cls._flow_state.get(_FlowState.CONFIG_DECORATORS, []):
|
|
725
|
+
if isinstance(deco, FlowMutator):
|
|
726
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
|
727
|
+
mutable_flow = MutableFlow(
|
|
728
|
+
cls,
|
|
729
|
+
pre_mutate=False,
|
|
730
|
+
statically_defined=deco.statically_defined,
|
|
731
|
+
inserted_by=inserted_by_value,
|
|
732
|
+
)
|
|
733
|
+
# Sanity check to make sure we are applying the decorator to the right
|
|
734
|
+
# class
|
|
735
|
+
if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
|
|
736
|
+
raise MetaflowInternalError(
|
|
737
|
+
"FlowMutator registered on the wrong flow -- "
|
|
738
|
+
"expected %s but got %s" % (deco._flow_cls.__name__, cls.__name__)
|
|
739
|
+
)
|
|
740
|
+
debug.userconf_exec(
|
|
741
|
+
"Evaluating flow level decorator %s (post)" % deco.__class__.__name__
|
|
742
|
+
)
|
|
743
|
+
deco.mutate(mutable_flow)
|
|
744
|
+
# We reset cached_parameters on the very off chance that the user added
|
|
745
|
+
# more configurations based on the configuration
|
|
746
|
+
if _FlowState.CACHED_PARAMETERS in cls._flow_state:
|
|
747
|
+
del cls._flow_state[_FlowState.CACHED_PARAMETERS]
|
|
748
|
+
else:
|
|
749
|
+
raise MetaflowInternalError(
|
|
750
|
+
"A non FlowMutator found in flow custom decorators"
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
for step in cls._steps:
|
|
754
|
+
for deco in step.config_decorators:
|
|
755
|
+
inserted_by_value = [deco.decorator_name] + (deco.inserted_by or [])
|
|
756
|
+
|
|
757
|
+
if isinstance(deco, StepMutator):
|
|
758
|
+
debug.userconf_exec(
|
|
759
|
+
"Evaluating step level decorator %s (post) for %s"
|
|
760
|
+
% (deco.__class__.__name__, step.name)
|
|
761
|
+
)
|
|
762
|
+
deco.mutate(
|
|
763
|
+
MutableStep(
|
|
764
|
+
cls,
|
|
765
|
+
step,
|
|
766
|
+
pre_mutate=False,
|
|
767
|
+
statically_defined=deco.statically_defined,
|
|
768
|
+
inserted_by=inserted_by_value,
|
|
769
|
+
)
|
|
770
|
+
)
|
|
771
|
+
else:
|
|
772
|
+
raise MetaflowInternalError(
|
|
773
|
+
"A non StepMutator found in step custom decorators"
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
if step.config_decorators:
|
|
777
|
+
# We remove all mention of the custom step decorator
|
|
778
|
+
setattr(cls, step.name, step)
|
|
779
|
+
|
|
616
780
|
for step in flow:
|
|
617
781
|
for deco in step.decorators:
|
|
618
782
|
deco.step_init(
|
|
@@ -685,12 +849,8 @@ def step(
|
|
|
685
849
|
f.is_step = True
|
|
686
850
|
f.decorators = []
|
|
687
851
|
f.config_decorators = []
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
f.name = f.__name__
|
|
691
|
-
except:
|
|
692
|
-
# python 2
|
|
693
|
-
f.name = f.__func__.func_name
|
|
852
|
+
f.wrappers = []
|
|
853
|
+
f.name = f.__name__
|
|
694
854
|
return f
|
|
695
855
|
|
|
696
856
|
|
|
@@ -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.
|