ob-metaflow 2.12.36.3__py2.py3-none-any.whl → 2.13.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.

Files changed (65) hide show
  1. metaflow/__init__.py +3 -0
  2. metaflow/cli.py +180 -718
  3. metaflow/cli_args.py +17 -0
  4. metaflow/cli_components/__init__.py +0 -0
  5. metaflow/cli_components/dump_cmd.py +96 -0
  6. metaflow/cli_components/init_cmd.py +51 -0
  7. metaflow/cli_components/run_cmds.py +360 -0
  8. metaflow/cli_components/step_cmd.py +189 -0
  9. metaflow/cli_components/utils.py +140 -0
  10. metaflow/cmd/develop/stub_generator.py +9 -2
  11. metaflow/datastore/flow_datastore.py +2 -2
  12. metaflow/decorators.py +63 -2
  13. metaflow/exception.py +8 -2
  14. metaflow/extension_support/plugins.py +41 -27
  15. metaflow/flowspec.py +175 -23
  16. metaflow/graph.py +28 -27
  17. metaflow/includefile.py +50 -22
  18. metaflow/lint.py +35 -20
  19. metaflow/metaflow_config.py +6 -1
  20. metaflow/package.py +17 -3
  21. metaflow/parameters.py +87 -23
  22. metaflow/plugins/__init__.py +4 -0
  23. metaflow/plugins/airflow/airflow_cli.py +1 -0
  24. metaflow/plugins/argo/argo_workflows.py +41 -1
  25. metaflow/plugins/argo/argo_workflows_cli.py +1 -0
  26. metaflow/plugins/argo/argo_workflows_deployer_objects.py +47 -1
  27. metaflow/plugins/aws/batch/batch_decorator.py +2 -2
  28. metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
  29. metaflow/plugins/aws/step_functions/step_functions.py +32 -0
  30. metaflow/plugins/aws/step_functions/step_functions_cli.py +1 -0
  31. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +3 -0
  32. metaflow/plugins/cards/card_creator.py +1 -0
  33. metaflow/plugins/cards/card_decorator.py +46 -8
  34. metaflow/plugins/datatools/s3/s3op.py +3 -3
  35. metaflow/plugins/kubernetes/kubernetes_cli.py +1 -1
  36. metaflow/plugins/kubernetes/kubernetes_decorator.py +2 -2
  37. metaflow/plugins/pypi/bootstrap.py +196 -61
  38. metaflow/plugins/pypi/conda_decorator.py +20 -10
  39. metaflow/plugins/pypi/conda_environment.py +76 -21
  40. metaflow/plugins/pypi/micromamba.py +42 -15
  41. metaflow/plugins/pypi/pip.py +8 -3
  42. metaflow/plugins/pypi/pypi_decorator.py +11 -9
  43. metaflow/plugins/timeout_decorator.py +2 -2
  44. metaflow/runner/click_api.py +240 -50
  45. metaflow/runner/deployer.py +1 -1
  46. metaflow/runner/deployer_impl.py +8 -3
  47. metaflow/runner/metaflow_runner.py +10 -2
  48. metaflow/runner/nbdeploy.py +2 -0
  49. metaflow/runner/nbrun.py +1 -1
  50. metaflow/runner/subprocess_manager.py +3 -1
  51. metaflow/runner/utils.py +41 -19
  52. metaflow/runtime.py +111 -73
  53. metaflow/sidecar/sidecar_worker.py +1 -1
  54. metaflow/user_configs/__init__.py +0 -0
  55. metaflow/user_configs/config_decorators.py +563 -0
  56. metaflow/user_configs/config_options.py +548 -0
  57. metaflow/user_configs/config_parameters.py +405 -0
  58. metaflow/util.py +17 -0
  59. metaflow/version.py +1 -1
  60. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/METADATA +3 -2
  61. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/RECORD +65 -55
  62. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/LICENSE +0 -0
  63. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/WHEEL +0 -0
  64. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/entry_points.txt +0 -0
  65. {ob_metaflow-2.12.36.3.dist-info → ob_metaflow-2.13.0.1.dist-info}/top_level.txt +0 -0
metaflow/flowspec.py CHANGED
@@ -4,15 +4,18 @@ import sys
4
4
  import traceback
5
5
  import reprlib
6
6
 
7
+ from enum import Enum
7
8
  from itertools import islice
8
9
  from types import FunctionType, MethodType
9
- from typing import Any, Callable, List, Optional, Tuple
10
+ from typing import TYPE_CHECKING, Any, Callable, Generator, List, Optional, Tuple
10
11
 
11
12
  from . import cmd_with_io, parameters
13
+ from .debug import debug
12
14
  from .parameters import DelayedEvaluationParameter, Parameter
13
15
  from .exception import (
14
16
  MetaflowException,
15
17
  MissingInMergeArtifactsException,
18
+ MetaflowInternalError,
16
19
  UnhandledInMergeArtifactsException,
17
20
  )
18
21
 
@@ -20,6 +23,13 @@ from .extension_support import extension_info
20
23
 
21
24
  from .graph import FlowGraph
22
25
  from .unbounded_foreach import UnboundedForeachInput
26
+ from .user_configs.config_decorators import (
27
+ ConfigValue,
28
+ CustomFlowDecorator,
29
+ CustomStepDecorator,
30
+ MutableFlow,
31
+ MutableStep,
32
+ )
23
33
  from .util import to_pod
24
34
  from .metaflow_config import INCLUDE_FOREACH_STACK, MAXIMUM_FOREACH_VALUE_CHARS
25
35
 
@@ -65,15 +75,36 @@ class ParallelUBF(UnboundedForeachInput):
65
75
  return item or 0 # item is None for the control task, but it is also split 0
66
76
 
67
77
 
78
+ class _FlowState(Enum):
79
+ CONFIGS = 1
80
+ CONFIG_DECORATORS = 2
81
+ CACHED_PARAMETERS = 3
82
+
83
+
68
84
  class FlowSpecMeta(type):
69
- def __new__(cls, name, bases, dct):
70
- f = super().__new__(cls, name, bases, dct)
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
75
- f._flow_decorators = {}
76
- return f
85
+ def __init__(cls, name, bases, attrs):
86
+ super().__init__(name, bases, attrs)
87
+ if name == "FlowSpec":
88
+ return
89
+ # We store some state in the flow class itself. This is primarily used to
90
+ # attach global state to a flow. It is *not* an actual global because of
91
+ # Runner/NBRunner. This is also created here in the meta class to avoid it being
92
+ # shared between different children classes.
93
+
94
+ # We should move _flow_decorators into this structure as well but keeping it
95
+ # out to limit the changes for now.
96
+ cls._flow_decorators = {}
97
+
98
+ # Keys are _FlowState enum values
99
+ cls._flow_state = {}
100
+
101
+ cls._init_attrs()
102
+
103
+ def _init_attrs(cls):
104
+ # Graph and steps are specific to the class -- store here so we can access
105
+ # in class method _process_config_decorators
106
+ cls._graph = FlowGraph(cls)
107
+ cls._steps = [getattr(cls, node.name) for node in cls._graph]
77
108
 
78
109
 
79
110
  class FlowSpec(metaclass=FlowSpecMeta):
@@ -96,6 +127,7 @@ class FlowSpec(metaclass=FlowSpecMeta):
96
127
  "_cached_input",
97
128
  "_graph",
98
129
  "_flow_decorators",
130
+ "_flow_state",
99
131
  "_steps",
100
132
  "index",
101
133
  "input",
@@ -122,9 +154,6 @@ class FlowSpec(metaclass=FlowSpecMeta):
122
154
  self._transition = None
123
155
  self._cached_input = {}
124
156
 
125
- self._graph = FlowGraph(self.__class__)
126
- self._steps = [getattr(self, node.name) for node in self._graph]
127
-
128
157
  if use_cli:
129
158
  with parameters.flow_context(self.__class__) as _:
130
159
  from . import cli
@@ -148,16 +177,12 @@ class FlowSpec(metaclass=FlowSpecMeta):
148
177
  fname = fname[:-1]
149
178
  return os.path.basename(fname)
150
179
 
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
180
+ @classmethod
181
+ def _check_parameters(cls, config_parameters=False):
159
182
  seen = set()
160
- for var, param in self._get_parameters():
183
+ for _, param in cls._get_parameters():
184
+ if param.IS_CONFIG_PARAMETER != config_parameters:
185
+ continue
161
186
  norm = param.name.lower()
162
187
  if norm in seen:
163
188
  raise MetaflowException(
@@ -166,17 +191,136 @@ class FlowSpec(metaclass=FlowSpecMeta):
166
191
  "case-insensitive." % param.name
167
192
  )
168
193
  seen.add(norm)
169
- seen.clear()
194
+
195
+ @classmethod
196
+ def _process_config_decorators(cls, config_options, ignore_errors=False):
197
+
198
+ # Fast path for no user configurations
199
+ if not cls._flow_state.get(_FlowState.CONFIG_DECORATORS):
200
+ # Process parameters to allow them to also use config values easily
201
+ for var, param in cls._get_parameters():
202
+ if param.IS_CONFIG_PARAMETER:
203
+ continue
204
+ param.init(ignore_errors)
205
+ return None
206
+
207
+ debug.userconf_exec("Processing mutating step/flow decorators")
208
+ # We need to convert all the user configurations from DelayedEvaluationParameters
209
+ # to actual values so they can be used as is in the config decorators.
210
+
211
+ # We then reset them to be proper configs so they can be re-evaluated in
212
+ # _set_constants
213
+ to_reset_configs = []
214
+ cls._check_parameters(config_parameters=True)
215
+ for var, param in cls._get_parameters():
216
+ if not param.IS_CONFIG_PARAMETER:
217
+ continue
218
+ # Note that a config with no default and not required will be None
219
+ val = config_options.get(param.name.replace("-", "_").lower())
220
+ if isinstance(val, DelayedEvaluationParameter):
221
+ val = val()
222
+ # We store the value as well so that in _set_constants, we don't try
223
+ # to recompute (no guarantee that it is stable)
224
+ param._store_value(val)
225
+ to_reset_configs.append((var, param))
226
+ debug.userconf_exec("Setting config %s to %s" % (var, str(val)))
227
+ setattr(cls, var, val)
228
+
229
+ # Run all the decorators. Step decorators are directly in the step and
230
+ # we will run those first and *then* we run all the flow level decorators
231
+ for step in cls._steps:
232
+ for deco in step.config_decorators:
233
+ if isinstance(deco, CustomStepDecorator):
234
+ debug.userconf_exec(
235
+ "Evaluating step level decorator %s for %s"
236
+ % (deco.__class__.__name__, step.name)
237
+ )
238
+ deco.evaluate(MutableStep(cls, step))
239
+ else:
240
+ raise MetaflowInternalError(
241
+ "A non CustomFlowDecorator found in step custom decorators"
242
+ )
243
+ if step.config_decorators:
244
+ # We remove all mention of the custom step decorator
245
+ setattr(cls, step.name, step)
246
+
247
+ mutable_flow = MutableFlow(cls)
248
+ for deco in cls._flow_state[_FlowState.CONFIG_DECORATORS]:
249
+ if isinstance(deco, CustomFlowDecorator):
250
+ # Sanity check to make sure we are applying the decorator to the right
251
+ # class
252
+ if not deco._flow_cls == cls and not issubclass(cls, deco._flow_cls):
253
+ raise MetaflowInternalError(
254
+ "CustomFlowDecorator registered on the wrong flow -- "
255
+ "expected %s but got %s"
256
+ % (deco._flow_cls.__name__, cls.__name__)
257
+ )
258
+ debug.userconf_exec(
259
+ "Evaluating flow level decorator %s" % deco.__class__.__name__
260
+ )
261
+ deco.evaluate(mutable_flow)
262
+ # We reset cached_parameters on the very off chance that the user added
263
+ # more configurations based on the configuration
264
+ if _FlowState.CACHED_PARAMETERS in cls._flow_state:
265
+ del cls._flow_state[_FlowState.CACHED_PARAMETERS]
266
+ else:
267
+ raise MetaflowInternalError(
268
+ "A non CustomFlowDecorator found in flow custom decorators"
269
+ )
270
+
271
+ # Process parameters to allow them to also use config values easily
272
+ for var, param in cls._get_parameters():
273
+ if param.IS_CONFIG_PARAMETER:
274
+ continue
275
+ param.init()
276
+ # Reset all configs that were already present in the class.
277
+ # TODO: This means that users can't override configs directly. Not sure if this
278
+ # is a pattern we want to support
279
+ for var, param in to_reset_configs:
280
+ setattr(cls, var, param)
281
+
282
+ # Reset cached parameters again since we added back the config parameters
283
+ if _FlowState.CACHED_PARAMETERS in cls._flow_state:
284
+ del cls._flow_state[_FlowState.CACHED_PARAMETERS]
285
+
286
+ # Set the current flow class we are in (the one we just created)
287
+ parameters.replace_flow_context(cls)
288
+
289
+ # Re-calculate class level attributes after modifying the class
290
+ cls._init_attrs()
291
+ return cls
292
+
293
+ def _set_constants(self, graph, kwargs, config_options):
294
+ from metaflow.decorators import (
295
+ flow_decorators,
296
+ ) # To prevent circular dependency
297
+
298
+ # Persist values for parameters and other constants (class level variables)
299
+ # only once. This method is called before persist_constants is called to
300
+ # persist all values set using setattr
301
+ self._check_parameters(config_parameters=False)
302
+
303
+ seen = set()
170
304
  self._success = True
171
305
 
172
306
  parameters_info = []
173
307
  for var, param in self._get_parameters():
174
308
  seen.add(var)
175
- val = kwargs[param.name.replace("-", "_").lower()]
309
+ if param.IS_CONFIG_PARAMETER:
310
+ # Use computed value if already evaluated, else get from config_options
311
+ val = param._computed_value or config_options.get(
312
+ param.name.replace("-", "_").lower()
313
+ )
314
+ else:
315
+ val = kwargs[param.name.replace("-", "_").lower()]
176
316
  # Support for delayed evaluation of parameters.
177
317
  if isinstance(val, DelayedEvaluationParameter):
178
318
  val = val()
179
319
  val = val.split(param.separator) if val and param.separator else val
320
+ if isinstance(val, ConfigValue):
321
+ # We store config values as dict so they are accessible with older
322
+ # metaflow clients. It also makes it easier to access.
323
+ val = val.to_dict()
180
324
  setattr(self, var, val)
181
325
  parameters_info.append({"name": var, "type": param.__class__.__name__})
182
326
 
@@ -218,6 +362,12 @@ class FlowSpec(metaclass=FlowSpecMeta):
218
362
 
219
363
  @classmethod
220
364
  def _get_parameters(cls):
365
+ cached = cls._flow_state.get(_FlowState.CACHED_PARAMETERS)
366
+ if cached is not None:
367
+ for var in cached:
368
+ yield var, getattr(cls, var)
369
+ return
370
+ build_list = []
221
371
  for var in dir(cls):
222
372
  if var[0] == "_" or var in cls._NON_PARAMETERS:
223
373
  continue
@@ -226,7 +376,9 @@ class FlowSpec(metaclass=FlowSpecMeta):
226
376
  except:
227
377
  continue
228
378
  if isinstance(val, Parameter):
379
+ build_list.append(var)
229
380
  yield var, val
381
+ cls._flow_state[_FlowState.CACHED_PARAMETERS] = build_list
230
382
 
231
383
  def _set_datastore(self, datastore):
232
384
  self._datastore = datastore
metaflow/graph.py CHANGED
@@ -45,9 +45,12 @@ def deindent_docstring(doc):
45
45
 
46
46
 
47
47
  class DAGNode(object):
48
- def __init__(self, func_ast, decos, doc):
48
+ def __init__(self, func_ast, decos, doc, source_file, lineno):
49
49
  self.name = func_ast.name
50
- self.func_lineno = func_ast.lineno
50
+ self.source_file = source_file
51
+ # lineno is the start line of decorators in source_file
52
+ # func_ast.lineno is lines from decorators start to def of function
53
+ self.func_lineno = lineno + func_ast.lineno - 1
51
54
  self.decorators = decos
52
55
  self.doc = deindent_docstring(doc)
53
56
  self.parallel_step = any(getattr(deco, "IS_PARALLEL", False) for deco in decos)
@@ -62,7 +65,7 @@ class DAGNode(object):
62
65
  self.foreach_param = None
63
66
  self.num_parallel = 0
64
67
  self.parallel_foreach = False
65
- self._parse(func_ast)
68
+ self._parse(func_ast, lineno)
66
69
 
67
70
  # these attributes are populated by _traverse_graph
68
71
  self.in_funcs = set()
@@ -74,7 +77,7 @@ class DAGNode(object):
74
77
  def _expr_str(self, expr):
75
78
  return "%s.%s" % (expr.value.id, expr.attr)
76
79
 
77
- def _parse(self, func_ast):
80
+ def _parse(self, func_ast, lineno):
78
81
  self.num_args = len(func_ast.args.args)
79
82
  tail = func_ast.body[-1]
80
83
 
@@ -94,7 +97,7 @@ class DAGNode(object):
94
97
 
95
98
  self.has_tail_next = True
96
99
  self.invalid_tail_next = True
97
- self.tail_next_lineno = tail.lineno
100
+ self.tail_next_lineno = lineno + tail.lineno - 1
98
101
  self.out_funcs = [e.attr for e in tail.value.args]
99
102
 
100
103
  keywords = dict(
@@ -131,7 +134,7 @@ class DAGNode(object):
131
134
  return
132
135
 
133
136
  def __str__(self):
134
- return """*[{0.name} {0.type} (line {0.func_lineno})]*
137
+ return """*[{0.name} {0.type} ({0.source_file} line {0.func_lineno})]*
135
138
  in_funcs={in_funcs}
136
139
  out_funcs={out_funcs}
137
140
  split_parents={parents}
@@ -156,18 +159,6 @@ class DAGNode(object):
156
159
  )
157
160
 
158
161
 
159
- class StepVisitor(ast.NodeVisitor):
160
- def __init__(self, nodes, flow):
161
- self.nodes = nodes
162
- self.flow = flow
163
- super(StepVisitor, self).__init__()
164
-
165
- def visit_FunctionDef(self, node):
166
- func = getattr(self.flow, node.name)
167
- if hasattr(func, "is_step"):
168
- self.nodes[node.name] = DAGNode(node, func.decorators, func.__doc__)
169
-
170
-
171
162
  class FlowGraph(object):
172
163
  def __init__(self, flow):
173
164
  self.name = flow.__name__
@@ -179,13 +170,20 @@ class FlowGraph(object):
179
170
  self._postprocess()
180
171
 
181
172
  def _create_nodes(self, flow):
182
- module = __import__(flow.__module__)
183
- tree = ast.parse(inspect.getsource(module)).body
184
- root = [n for n in tree if isinstance(n, ast.ClassDef) and n.name == self.name][
185
- 0
186
- ]
187
173
  nodes = {}
188
- StepVisitor(nodes, flow).visit(root)
174
+ for element in dir(flow):
175
+ func = getattr(flow, element)
176
+ if callable(func) and hasattr(func, "is_step"):
177
+ source_file = inspect.getsourcefile(func)
178
+ source_lines, lineno = inspect.getsourcelines(func)
179
+ # This also works for code (strips out leading whitspace based on
180
+ # first line)
181
+ source_code = deindent_docstring("".join(source_lines))
182
+ function_ast = ast.parse(source_code).body[0]
183
+ node = DAGNode(
184
+ function_ast, func.decorators, func.__doc__, source_file, lineno
185
+ )
186
+ nodes[element] = node
189
187
  return nodes
190
188
 
191
189
  def _postprocess(self):
@@ -201,6 +199,10 @@ class FlowGraph(object):
201
199
 
202
200
  def _traverse_graph(self):
203
201
  def traverse(node, seen, split_parents):
202
+ try:
203
+ self.sorted_nodes.remove(node.name)
204
+ except ValueError:
205
+ pass
204
206
  self.sorted_nodes.append(node.name)
205
207
  if node.type in ("split", "foreach"):
206
208
  node.split_parents = split_parents
@@ -240,9 +242,7 @@ class FlowGraph(object):
240
242
  return iter(self.nodes.values())
241
243
 
242
244
  def __str__(self):
243
- return "\n".join(
244
- str(n) for _, n in sorted((n.func_lineno, n) for n in self.nodes.values())
245
- )
245
+ return "\n".join(str(self[n]) for n in self.sorted_nodes)
246
246
 
247
247
  def output_dot(self):
248
248
  def edge_specs():
@@ -286,6 +286,7 @@ class FlowGraph(object):
286
286
  "name": name,
287
287
  "type": node_to_type(node),
288
288
  "line": node.func_lineno,
289
+ "source_file": node.source_file,
289
290
  "doc": node.doc,
290
291
  "decorators": [
291
292
  {
metaflow/includefile.py CHANGED
@@ -20,6 +20,7 @@ from .parameters import (
20
20
  )
21
21
 
22
22
  from .plugins import DATACLIENTS
23
+ from .user_configs.config_parameters import ConfigValue
23
24
  from .util import get_username
24
25
 
25
26
  import functools
@@ -136,6 +137,7 @@ class FilePathClass(click.ParamType):
136
137
  parameter_name=param.name,
137
138
  logger=ctx.obj.echo,
138
139
  ds_type=ctx.obj.datastore_impl.TYPE,
140
+ configs=None,
139
141
  )
140
142
 
141
143
  if len(value) > 0 and (value.startswith("{") or value.startswith('"{')):
@@ -243,29 +245,63 @@ class IncludeFile(Parameter):
243
245
  default : Union[str, Callable[ParameterContext, str]]
244
246
  Default path to a local file. A function
245
247
  implies that the parameter corresponds to a *deploy-time parameter*.
246
- is_text : bool, default True
248
+ is_text : bool, optional, default None
247
249
  Convert the file contents to a string using the provided `encoding`.
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
250
+ If False, the artifact is stored in `bytes`. A value of None is equivalent to
251
+ True.
252
+ encoding : str, optional, default None
253
+ Use this encoding to decode the file contexts if `is_text=True`. A value of None
254
+ is equivalent to "utf-8".
255
+ required : bool, optional, default None
252
256
  Require that the user specified a value for the parameter.
253
- `required=True` implies that the `default` is not used.
257
+ `required=True` implies that the `default` is not used. A value of None is
258
+ equivalent to False
254
259
  help : str, optional
255
260
  Help text to show in `run --help`.
256
261
  show_default : bool, default True
257
- If True, show the default value in the help text.
262
+ If True, show the default value in the help text. A value of None is equivalent
263
+ to True.
258
264
  """
259
265
 
260
266
  def __init__(
261
267
  self,
262
268
  name: str,
263
- required: bool = False,
264
- is_text: bool = True,
265
- encoding: str = "utf-8",
269
+ required: Optional[bool] = None,
270
+ is_text: Optional[bool] = None,
271
+ encoding: Optional[str] = None,
266
272
  help: Optional[str] = None,
267
273
  **kwargs: Dict[str, str]
268
274
  ):
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, ignore_errors=False):
294
+ super(IncludeFile, self).init(ignore_errors)
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
+
269
305
  # If a default is specified, it needs to be uploaded when the flow is deployed
270
306
  # (for example when doing a `step-functions create`) so we make the default
271
307
  # be a DeployTimeField. This means that it will be evaluated in two cases:
@@ -275,7 +311,7 @@ class IncludeFile(Parameter):
275
311
  # In the first case, we will need to fully upload the file whereas in the
276
312
  # second case, we can just return the string as the FilePath.convert method
277
313
  # will take care of evaluating things.
278
- v = kwargs.get("default")
314
+ v = self.kwargs.get("default")
279
315
  if v is not None:
280
316
  # If the default is a callable, we have two DeployTimeField:
281
317
  # - the callable nature of the default will require us to "call" the default
@@ -288,23 +324,15 @@ class IncludeFile(Parameter):
288
324
  # (call the default)
289
325
  if callable(v) and not isinstance(v, DeployTimeField):
290
326
  # If default is a callable, make it a DeployTimeField (the inner one)
291
- v = DeployTimeField(name, str, "default", v, return_str=True)
292
- kwargs["default"] = DeployTimeField(
293
- name,
327
+ v = DeployTimeField(self.name, str, "default", v, return_str=True)
328
+ self.kwargs["default"] = DeployTimeField(
329
+ self.name,
294
330
  str,
295
331
  "default",
296
332
  IncludeFile._eval_default(is_text, encoding, v),
297
333
  print_representation=v,
298
334
  )
299
335
 
300
- super(IncludeFile, self).__init__(
301
- name,
302
- required=required,
303
- help=help,
304
- type=FilePathClass(is_text, encoding),
305
- **kwargs,
306
- )
307
-
308
336
  def load_parameter(self, v):
309
337
  if v is None:
310
338
  return v