metaflow 2.15.20__py2.py3-none-any.whl → 2.16.0__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.
Files changed (74) hide show
  1. metaflow/__init__.py +7 -1
  2. metaflow/cli.py +16 -1
  3. metaflow/cli_components/init_cmd.py +1 -0
  4. metaflow/cli_components/run_cmds.py +6 -2
  5. metaflow/client/core.py +22 -30
  6. metaflow/datastore/task_datastore.py +0 -1
  7. metaflow/debug.py +5 -0
  8. metaflow/decorators.py +230 -70
  9. metaflow/extension_support/__init__.py +15 -8
  10. metaflow/extension_support/_empty_file.py +2 -2
  11. metaflow/flowspec.py +80 -53
  12. metaflow/graph.py +24 -2
  13. metaflow/meta_files.py +13 -0
  14. metaflow/metadata_provider/metadata.py +7 -1
  15. metaflow/metaflow_config.py +5 -0
  16. metaflow/metaflow_environment.py +82 -25
  17. metaflow/metaflow_version.py +1 -1
  18. metaflow/package/__init__.py +664 -0
  19. metaflow/packaging_sys/__init__.py +870 -0
  20. metaflow/packaging_sys/backend.py +113 -0
  21. metaflow/packaging_sys/distribution_support.py +153 -0
  22. metaflow/packaging_sys/tar_backend.py +86 -0
  23. metaflow/packaging_sys/utils.py +91 -0
  24. metaflow/packaging_sys/v1.py +476 -0
  25. metaflow/plugins/airflow/airflow.py +5 -1
  26. metaflow/plugins/airflow/airflow_cli.py +15 -4
  27. metaflow/plugins/argo/argo_workflows.py +23 -17
  28. metaflow/plugins/argo/argo_workflows_cli.py +16 -4
  29. metaflow/plugins/aws/batch/batch.py +22 -3
  30. metaflow/plugins/aws/batch/batch_cli.py +3 -0
  31. metaflow/plugins/aws/batch/batch_decorator.py +13 -5
  32. metaflow/plugins/aws/step_functions/step_functions.py +4 -1
  33. metaflow/plugins/aws/step_functions/step_functions_cli.py +15 -4
  34. metaflow/plugins/cards/card_decorator.py +0 -5
  35. metaflow/plugins/kubernetes/kubernetes.py +8 -1
  36. metaflow/plugins/kubernetes/kubernetes_cli.py +3 -0
  37. metaflow/plugins/kubernetes/kubernetes_decorator.py +13 -5
  38. metaflow/plugins/package_cli.py +25 -23
  39. metaflow/plugins/parallel_decorator.py +4 -2
  40. metaflow/plugins/pypi/bootstrap.py +8 -2
  41. metaflow/plugins/pypi/conda_decorator.py +39 -82
  42. metaflow/plugins/pypi/conda_environment.py +6 -2
  43. metaflow/plugins/pypi/pypi_decorator.py +4 -4
  44. metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
  45. metaflow/plugins/timeout_decorator.py +0 -1
  46. metaflow/plugins/uv/bootstrap.py +11 -0
  47. metaflow/plugins/uv/uv_environment.py +4 -2
  48. metaflow/pylint_wrapper.py +5 -1
  49. metaflow/runner/click_api.py +5 -4
  50. metaflow/runner/subprocess_manager.py +14 -2
  51. metaflow/runtime.py +37 -11
  52. metaflow/task.py +91 -7
  53. metaflow/user_configs/config_options.py +13 -8
  54. metaflow/user_configs/config_parameters.py +0 -4
  55. metaflow/user_decorators/__init__.py +0 -0
  56. metaflow/user_decorators/common.py +144 -0
  57. metaflow/user_decorators/mutable_flow.py +499 -0
  58. metaflow/user_decorators/mutable_step.py +424 -0
  59. metaflow/user_decorators/user_flow_decorator.py +263 -0
  60. metaflow/user_decorators/user_step_decorator.py +712 -0
  61. metaflow/util.py +4 -1
  62. metaflow/version.py +1 -1
  63. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/METADATA +2 -2
  64. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/RECORD +71 -60
  65. metaflow/info_file.py +0 -25
  66. metaflow/package.py +0 -203
  67. metaflow/user_configs/config_decorators.py +0 -568
  68. {metaflow-2.15.20.data → metaflow-2.16.0.data}/data/share/metaflow/devtools/Makefile +0 -0
  69. {metaflow-2.15.20.data → metaflow-2.16.0.data}/data/share/metaflow/devtools/Tiltfile +0 -0
  70. {metaflow-2.15.20.data → metaflow-2.16.0.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
  71. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/WHEEL +0 -0
  72. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/entry_points.txt +0 -0
  73. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/licenses/LICENSE +0 -0
  74. {metaflow-2.15.20.dist-info → metaflow-2.16.0.dist-info}/top_level.txt +0 -0
@@ -1,568 +0,0 @@
1
- from functools import partial
2
- from typing import Any, Callable, Generator, Optional, TYPE_CHECKING, Tuple, Union
3
-
4
- from metaflow.debug import debug
5
- from metaflow.exception import MetaflowException
6
- from metaflow.user_configs.config_parameters import (
7
- ConfigValue,
8
- resolve_delayed_evaluator,
9
- )
10
-
11
- if TYPE_CHECKING:
12
- import metaflow.decorators
13
- import metaflow.flowspec
14
- import metaflow.parameters
15
-
16
-
17
- class MutableStep:
18
- """
19
- A MutableStep is a wrapper passed to the `CustomStepDecorator`'s `evaluate` method
20
- to allow the decorator to interact with the step and providing easy methods to
21
- modify the behavior of the step.
22
- """
23
-
24
- def __init__(
25
- self,
26
- flow_spec: "metaflow.flowspec.FlowSpec",
27
- step: Union[
28
- Callable[["metaflow.decorators.FlowSpecDerived"], None],
29
- Callable[["metaflow.decorators.FlowSpecDerived", Any], None],
30
- ],
31
- ):
32
- self._mutable_container = MutableFlow(flow_spec)
33
- self._my_step = step
34
-
35
- @property
36
- def flow(self) -> "MutableFlow":
37
- """
38
- The flow that contains this step
39
-
40
- Returns
41
- -------
42
- MutableFlow
43
- The flow that contains this step
44
- """
45
- return self._mutable_container
46
-
47
- @property
48
- def decorators(self) -> Generator["metaflow.decorators.StepDecorator", None, None]:
49
- """
50
- Iterate over all the decorators of this step. Note that the same type of decorator
51
- may be present multiple times and no order is guaranteed.
52
-
53
- Yields
54
- ------
55
- metaflow.decorators.StepDecorator
56
- A decorator of the step
57
- """
58
- for deco in self._my_step.decorators:
59
- yield deco
60
-
61
- def add_decorator(self, deco_type: partial, **kwargs) -> None:
62
- """
63
- Add a Metaflow decorator to a step.
64
-
65
- Parameters
66
- ----------
67
- deco_type : partial
68
- The decorator class to add to this step
69
- """
70
- # Prevent circular import
71
- from metaflow.decorators import DuplicateStepDecoratorException, StepDecorator
72
-
73
- # Validate deco_type
74
- if (
75
- not isinstance(deco_type, partial)
76
- or len(deco_type.args) != 1
77
- or not issubclass(deco_type.args[0], StepDecorator)
78
- ):
79
- raise TypeError("add_decorator takes a StepDecorator")
80
-
81
- deco_type = deco_type.args[0]
82
- if (
83
- deco_type.name in [deco.name for deco in self._my_step.decorators]
84
- and not deco_type.allow_multiple
85
- ):
86
- raise DuplicateStepDecoratorException(deco_type.name, self._my_step)
87
-
88
- debug.userconf_exec(
89
- "Mutable decorator adding step decorator %s to step %s"
90
- % (deco_type.name, self._my_step.name)
91
- )
92
- self._my_step.decorators.append(
93
- deco_type(attributes=kwargs, statically_defined=True)
94
- )
95
-
96
- def remove_decorator(self, deco_name: str, all: bool = True, **kwargs) -> bool:
97
- """
98
- Remove one or more Metaflow decorators from a step.
99
-
100
- Some decorators can be applied multiple times to a step. This method allows you
101
- to choose which decorator to remove or just remove all of them or one of them.
102
-
103
- Parameters
104
- ----------
105
- deco_name : str
106
- Name of the decorator to remove
107
- all : bool, default True
108
- If True, remove all instances of the decorator that match the filters
109
- passed using kwargs (or all the instances of the decorator if no filters are
110
- passed). If False, removes only the first found instance of the decorator.
111
-
112
- Returns
113
- -------
114
- bool
115
- Returns True if at least one decorator was removed.
116
- """
117
- new_deco_list = []
118
- did_remove = False
119
- for deco in self._my_step.decorators:
120
- if deco.name == deco_name:
121
- # Evaluate all the configuration values if any
122
- deco.init()
123
-
124
- # Check filters
125
- match_ok = True
126
- if kwargs:
127
- for k, v in kwargs.items():
128
- match_ok = k in deco.attributes and deco.attributes[k] == v
129
- if match_ok is False:
130
- break
131
- if match_ok:
132
- did_remove = True
133
- debug.userconf_exec(
134
- "Mutable decorator removing step decorator %s from step %s"
135
- % (deco.name, self._my_step.name)
136
- )
137
- else:
138
- new_deco_list.append(deco)
139
- else:
140
- new_deco_list.append(deco)
141
- if did_remove and not all:
142
- break
143
-
144
- self._my_step.decorators = new_deco_list
145
- return did_remove
146
-
147
-
148
- class MutableFlow:
149
- def __init__(self, flow_spec: "metaflow.flowspec.FlowSpec"):
150
- self._flow_cls = flow_spec
151
-
152
- @property
153
- def decorators(self) -> Generator["metaflow.decorators.FlowDecorator", None, None]:
154
- """
155
- Iterate over all the decorators of this flow. Note that the same type of decorator
156
- may be present multiple times and no order is guaranteed.
157
-
158
- Yields
159
- ------
160
- metaflow.decorators.FlowDecorator
161
- A decorator of the flow
162
- """
163
- for decos in self._flow_cls._flow_decorators.values():
164
- for deco in decos:
165
- yield deco
166
-
167
- @property
168
- def configs(self) -> Generator[Tuple[str, ConfigValue], None, None]:
169
- """
170
- Iterate over all user configurations in this flow
171
-
172
- Use this to parameterize your flow based on configuration. As an example, the
173
- `evaluate` method of your `CustomFlowDecorator` can use this to add an
174
- environment decorator.
175
- ```
176
- class MyDecorator(CustomFlowDecorator):
177
- def evaluate(flow: MutableFlow):
178
- val = next(flow.configs)[1].steps.start.cpu
179
- flow.start.add_decorator(environment, vars={'mycpu': val})
180
- return flow
181
-
182
- @MyDecorator()
183
- class TestFlow(FlowSpec):
184
- config = Config('myconfig.json')
185
-
186
- @step
187
- def start(self):
188
- pass
189
- ```
190
- can be used to add an environment decorator to the `start` step.
191
-
192
- Yields
193
- ------
194
- Tuple[str, ConfigValue]
195
- Iterates over the configurations of the flow
196
- """
197
- from metaflow.flowspec import _FlowState
198
-
199
- # When configs are parsed, they are loaded in _flow_state[_FlowState.CONFIGS]
200
- for name, value in self._flow_cls._flow_state.get(
201
- _FlowState.CONFIGS, {}
202
- ).items():
203
- yield name, ConfigValue(value) if value is not None else None
204
-
205
- @property
206
- def parameters(self) -> Generator[Tuple[str, Any], None, None]:
207
- for var, param in self._flow_cls._get_parameters():
208
- if param.IS_CONFIG_PARAMETER:
209
- continue
210
- yield var, param
211
-
212
- @property
213
- def steps(self) -> Generator[Tuple[str, MutableStep], None, None]:
214
- """
215
- Iterate over all the steps in this flow. The order of the steps
216
- returned is not guaranteed.
217
-
218
- Yields
219
- ------
220
- Tuple[str, MutableStep]
221
- A tuple with the step name and the step proxy
222
- """
223
- for var in dir(self._flow_cls):
224
- potential_step = getattr(self._flow_cls, var)
225
- if callable(potential_step) and hasattr(potential_step, "is_step"):
226
- yield var, MutableStep(self._flow_cls, potential_step)
227
-
228
- def add_parameter(
229
- self, name: str, value: "metaflow.parameters.Parameter", overwrite: bool = False
230
- ) -> None:
231
- from metaflow.parameters import Parameter
232
-
233
- if hasattr(self._flow_cls, name) and not overwrite:
234
- raise MetaflowException(
235
- "Flow '%s' already has a class member '%s' -- "
236
- "set overwrite=True in add_parameter to overwrite it."
237
- % (self._flow_cls.__name__, name)
238
- )
239
- if not isinstance(value, Parameter) or value.IS_CONFIG_PARAMETER:
240
- raise MetaflowException(
241
- "Only a Parameter or an IncludeFile can be added using `add_parameter`"
242
- "; got %s" % type(value)
243
- )
244
- debug.userconf_exec("Mutable flow decorator adding parameter %s to flow" % name)
245
- setattr(self._flow_cls, name, value)
246
-
247
- def remove_parameter(self, parameter_name: str) -> bool:
248
- """
249
- Remove a parameter from the flow.
250
-
251
- The name given should match the name of the parameter (can be different
252
- from the name of the parameter in the flow. You can not remove config parameters.
253
-
254
- Parameters
255
- ----------
256
- parameter_name : str
257
- Name of the parameter
258
-
259
- Returns
260
- -------
261
- bool
262
- Returns True if the parameter was removed
263
- """
264
- from metaflow.flowspec import _FlowState
265
-
266
- for var, param in self._flow_cls._get_parameters():
267
- if param.IS_CONFIG_PARAMETER:
268
- continue
269
- if param.name == parameter_name:
270
- delattr(self._flow_cls, var)
271
- debug.userconf_exec(
272
- "Mutable flow decorator removing parameter %s from flow" % var
273
- )
274
- # Reset so that we don't list it again
275
- del self._flow_cls._flow_state[_FlowState.CACHED_PARAMETERS]
276
- return True
277
- return False
278
-
279
- def add_decorator(self, deco_type: partial, **kwargs) -> None:
280
- """
281
- Add a Metaflow decorator to a flow.
282
-
283
- Parameters
284
- ----------
285
- deco_type : partial
286
- The decorator class to add to this flow
287
- """
288
- # Prevent circular import
289
- from metaflow.decorators import DuplicateFlowDecoratorException, FlowDecorator
290
-
291
- # Validate deco_type
292
- if (
293
- not isinstance(deco_type, partial)
294
- or len(deco_type.args) != 1
295
- or not issubclass(deco_type.args[0], FlowDecorator)
296
- ):
297
- raise TypeError("add_decorator takes a FlowDecorator")
298
-
299
- deco_type = deco_type.args[0]
300
- if (
301
- deco_type.name in self._flow_cls._flow_decorators
302
- and not deco_type.allow_multiple
303
- ):
304
- raise DuplicateFlowDecoratorException(deco_type.name)
305
-
306
- self._flow_cls._flow_decorators.setdefault(deco_type.name, []).append(
307
- deco_type(attributes=kwargs, statically_defined=True)
308
- )
309
- debug.userconf_exec(
310
- "Mutable flow decorator adding decorator %s to flow" % deco_type.name
311
- )
312
-
313
- def remove_decorator(self, deco_name: str, all: bool = True, **kwargs) -> bool:
314
- """
315
- Remove one or more Metaflow decorators from a flow.
316
-
317
- Some decorators can be applied multiple times to a flow. This method allows you
318
- to choose which decorator to remove or just remove all of them or one of them.
319
-
320
- Parameters
321
- ----------
322
- deco_name : str
323
- Name of the decorator to remove
324
- all : bool, default True
325
- If True, remove all instances of the decorator that match the filters
326
- passed using kwargs (or all the instances of the decorator if no filters are
327
- passed). If False, removes only the first found instance of the decorator.
328
-
329
- Returns
330
- -------
331
- bool
332
- Returns True if at least one decorator was removed.
333
- """
334
- new_deco_list = []
335
- old_deco_list = self._flow_cls._flow_decorators.get(deco_name)
336
- if old_deco_list is None:
337
- return False
338
-
339
- did_remove = False
340
- for deco in old_deco_list:
341
- # Evaluate all the configuration values if any
342
- deco.init()
343
-
344
- # Check filters
345
- match_ok = True
346
- if kwargs:
347
- for k, v in kwargs.items():
348
- match_ok = k in deco.attributes and deco.attributes[k] == v
349
- if match_ok is False:
350
- break
351
- if match_ok:
352
- did_remove = True
353
- debug.userconf_exec(
354
- "Mutable flow decorator removing decorator %s from flow" % deco.name
355
- )
356
- else:
357
- new_deco_list.append(deco)
358
- if did_remove and not all:
359
- break
360
-
361
- if new_deco_list:
362
- self._flow_cls._flow_decorators[deco_name] = new_deco_list
363
- else:
364
- del self._flow_cls._flow_decorators[deco_name]
365
- return did_remove
366
-
367
- def __getattr__(self, name):
368
- # We allow direct access to the steps, configs and parameters but nothing else
369
- from metaflow.parameters import Parameter
370
-
371
- attr = getattr(self._flow_cls, name)
372
- if attr:
373
- # Steps
374
- if callable(attr) and hasattr(attr, "is_step"):
375
- return MutableStep(self._flow_cls, attr)
376
- if name[0] == "_" or name in self._flow_cls._NON_PARAMETERS:
377
- raise AttributeError(self, name)
378
- if isinstance(attr, (Parameter, ConfigValue)):
379
- return attr
380
- raise AttributeError(self, name)
381
-
382
-
383
- class CustomFlowDecorator:
384
- def __init__(self, *args, **kwargs):
385
- from ..flowspec import FlowSpecMeta
386
-
387
- if args and isinstance(args[0], (CustomFlowDecorator, FlowSpecMeta)):
388
- # This means the decorator is bare like @MyDecorator
389
- # and the first argument is the FlowSpec or another decorator (they
390
- # can be stacked)
391
-
392
- # If we have a init function, we call it with no arguments -- this can
393
- # happen if the user defines a function with default parameters for example
394
- try:
395
- self.init()
396
- except NotImplementedError:
397
- pass
398
-
399
- # Now set the flow class we apply to
400
- if isinstance(args[0], FlowSpecMeta):
401
- self._set_flow_cls(args[0])
402
- else:
403
- self._set_flow_cls(args[0]._flow_cls)
404
- else:
405
- # The arguments are actually passed to the init function for this decorator
406
- self._args = args
407
- self._kwargs = kwargs
408
-
409
- def __call__(
410
- self, flow_spec: Optional["metaflow.flowspec.FlowSpecMeta"] = None
411
- ) -> "metaflow.flowspec.FlowSpecMeta":
412
- if flow_spec:
413
- # This is the case of a decorator @MyDecorator(foo=1, bar=2) and so
414
- # we already called __init__ and saved foo and bar in self._args and
415
- # self._kwargs and are now calling this on the flow itself.
416
-
417
- # You can use config values in the arguments to a CustomFlowDecorator
418
- # so we resolve those as well
419
- new_args = [resolve_delayed_evaluator(arg) for arg in self._args]
420
- new_kwargs = {
421
- k: resolve_delayed_evaluator(v) for k, v in self._kwargs.items()
422
- }
423
- self.init(*new_args, **new_kwargs)
424
- if hasattr(self, "_empty_init"):
425
- raise MetaflowException(
426
- "CustomFlowDecorator '%s' is used with arguments "
427
- "but does not implement init" % str(self.__class__)
428
- )
429
-
430
- return self._set_flow_cls(flow_spec)
431
- elif not self._flow_cls:
432
- # This means that somehow the initialization did not happen properly
433
- # so this may have been applied to a non flow
434
- raise MetaflowException(
435
- "A CustomFlowDecorator can only be applied to a FlowSpec"
436
- )
437
- # NOTA: This returns self._flow_cls() because the object in the case of
438
- # @FlowDecorator
439
- # class MyFlow(FlowSpec):
440
- # pass
441
- # the object is a FlowDecorator and when the main function calls it, we end up
442
- # here and need to actually call the FlowSpec. This is not the case when using
443
- # a decorator with arguments because in the line above, we will have returned a
444
- # FlowSpec object. Previous solution was to use __get__ but this does not seem
445
- # to work properly.
446
- return self._flow_cls()
447
-
448
- def _set_flow_cls(
449
- self, flow_spec: "metaflow.flowspec.FlowSpecMeta"
450
- ) -> "metaflow.flowspec.FlowSpecMeta":
451
- from ..flowspec import _FlowState
452
-
453
- flow_spec._flow_state.setdefault(_FlowState.CONFIG_DECORATORS, []).append(self)
454
- self._flow_cls = flow_spec
455
- return flow_spec
456
-
457
- def init(self, *args, **kwargs):
458
- """
459
- This method is intended to be optionally overridden if you need to
460
- have an initializer.
461
- """
462
- self._empty_init = True
463
-
464
- def evaluate(self, mutable_flow: MutableFlow) -> None:
465
- """
466
- Implement this method to act on the flow and modify it as needed.
467
-
468
- Parameters
469
- ----------
470
- mutable_flow : MutableFlow
471
- Flow
472
-
473
- Raises
474
- ------
475
- NotImplementedError
476
- _description_
477
- """
478
- raise NotImplementedError()
479
-
480
-
481
- class CustomStepDecorator:
482
- def __init__(self, *args, **kwargs):
483
- arg = None
484
- if args:
485
- if isinstance(args[0], CustomStepDecorator):
486
- arg = args[0]._my_step
487
- else:
488
- arg = args[0]
489
- if arg and callable(arg) and hasattr(arg, "is_step"):
490
- # This means the decorator is bare like @MyDecorator
491
- # and the first argument is the step
492
- self._set_my_step(arg)
493
- else:
494
- # The arguments are actually passed to the init function for this decorator
495
- self._args = args
496
- self._kwargs = kwargs
497
-
498
- def __get__(self, instance, owner):
499
- # Required so that we "present" as a step when the step decorator is
500
- # of the form
501
- # @MyStepDecorator
502
- # @step
503
- # def my_step(self):
504
- # pass
505
- #
506
- # In that case, if we don't have __get__, the object is a CustomStepDecorator
507
- # and not a step. Other parts of the code rely on steps having is_step. There are
508
- # other ways to solve this but this allowed for minimal changes going forward.
509
- return self()
510
-
511
- def __call__(
512
- self,
513
- step: Optional[
514
- Union[
515
- Callable[["metaflow.decorators.FlowSpecDerived"], None],
516
- Callable[["metaflow.decorators.FlowSpecDerived", Any], None],
517
- ]
518
- ] = None,
519
- ) -> Union[
520
- Callable[["metaflow.decorators.FlowSpecDerived"], None],
521
- Callable[["metaflow.decorators.FlowSpecDerived", Any], None],
522
- ]:
523
- if step:
524
- # You can use config values in the arguments to a CustomFlowDecorator
525
- # so we resolve those as well
526
- new_args = [resolve_delayed_evaluator(arg) for arg in self._args]
527
- new_kwargs = {
528
- k: resolve_delayed_evaluator(v) for k, v in self._kwargs.items()
529
- }
530
- self.init(*new_args, **new_kwargs)
531
- if hasattr(self, "_empty_init"):
532
- raise MetaflowException(
533
- "CustomStepDecorator '%s' is used with arguments "
534
- "but does not implement init" % str(self.__class__)
535
- )
536
- return self._set_my_step(step)
537
- elif not self._my_step:
538
- # This means that somehow the initialization did not happen properly
539
- # so this may have been applied to a non step
540
- raise MetaflowException(
541
- "A CustomStepDecorator can only be applied to a step function"
542
- )
543
- return self._my_step
544
-
545
- def _set_my_step(
546
- self,
547
- step: Union[
548
- Callable[["metaflow.decorators.FlowSpecDerived"], None],
549
- Callable[["metaflow.decorators.FlowSpecDerived", Any], None],
550
- ],
551
- ) -> Union[
552
- Callable[["metaflow.decorators.FlowSpecDerived"], None],
553
- Callable[["metaflow.decorators.FlowSpecDerived", Any], None],
554
- ]:
555
-
556
- self._my_step = step
557
- self._my_step.config_decorators.append(self)
558
- return self._my_step
559
-
560
- def init(self, *args, **kwargs):
561
- """
562
- This method is intended to be optionally overridden if you need to
563
- have an initializer.
564
- """
565
- self._empty_init = True
566
-
567
- def evaluate(self, mutable_step: MutableStep) -> None:
568
- raise NotImplementedError()