metaflow 2.11.15__py2.py3-none-any.whl → 2.12.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 (73) hide show
  1. metaflow/__init__.py +8 -0
  2. metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
  3. metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
  4. metaflow/_vendor/importlib_metadata/_collections.py +30 -0
  5. metaflow/_vendor/importlib_metadata/_compat.py +71 -0
  6. metaflow/_vendor/importlib_metadata/_functools.py +104 -0
  7. metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
  8. metaflow/_vendor/importlib_metadata/_meta.py +48 -0
  9. metaflow/_vendor/importlib_metadata/_text.py +99 -0
  10. metaflow/_vendor/importlib_metadata/py.typed +0 -0
  11. metaflow/_vendor/typeguard/__init__.py +48 -0
  12. metaflow/_vendor/typeguard/_checkers.py +906 -0
  13. metaflow/_vendor/typeguard/_config.py +108 -0
  14. metaflow/_vendor/typeguard/_decorators.py +237 -0
  15. metaflow/_vendor/typeguard/_exceptions.py +42 -0
  16. metaflow/_vendor/typeguard/_functions.py +307 -0
  17. metaflow/_vendor/typeguard/_importhook.py +213 -0
  18. metaflow/_vendor/typeguard/_memo.py +48 -0
  19. metaflow/_vendor/typeguard/_pytest_plugin.py +100 -0
  20. metaflow/_vendor/typeguard/_suppression.py +88 -0
  21. metaflow/_vendor/typeguard/_transformer.py +1193 -0
  22. metaflow/_vendor/typeguard/_union_transformer.py +54 -0
  23. metaflow/_vendor/typeguard/_utils.py +169 -0
  24. metaflow/_vendor/typeguard/py.typed +0 -0
  25. metaflow/_vendor/typing_extensions.py +3053 -0
  26. metaflow/cli.py +48 -36
  27. metaflow/clone_util.py +6 -0
  28. metaflow/cmd/develop/stubs.py +2 -0
  29. metaflow/extension_support/__init__.py +2 -0
  30. metaflow/extension_support/plugins.py +2 -0
  31. metaflow/metaflow_config.py +24 -0
  32. metaflow/metaflow_environment.py +2 -2
  33. metaflow/parameters.py +1 -0
  34. metaflow/plugins/__init__.py +19 -0
  35. metaflow/plugins/airflow/airflow.py +7 -0
  36. metaflow/plugins/argo/argo_workflows.py +17 -0
  37. metaflow/plugins/aws/batch/batch_decorator.py +3 -3
  38. metaflow/plugins/azure/__init__.py +3 -0
  39. metaflow/plugins/azure/azure_credential.py +53 -0
  40. metaflow/plugins/azure/azure_exceptions.py +1 -1
  41. metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
  42. metaflow/plugins/azure/azure_utils.py +2 -35
  43. metaflow/plugins/azure/blob_service_client_factory.py +4 -2
  44. metaflow/plugins/datastores/azure_storage.py +6 -6
  45. metaflow/plugins/datatools/s3/s3.py +1 -1
  46. metaflow/plugins/gcp/__init__.py +1 -0
  47. metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +169 -0
  48. metaflow/plugins/gcp/gs_storage_client_factory.py +52 -1
  49. metaflow/plugins/kubernetes/kubernetes.py +85 -8
  50. metaflow/plugins/kubernetes/kubernetes_cli.py +24 -1
  51. metaflow/plugins/kubernetes/kubernetes_client.py +4 -1
  52. metaflow/plugins/kubernetes/kubernetes_decorator.py +49 -4
  53. metaflow/plugins/kubernetes/kubernetes_job.py +208 -206
  54. metaflow/plugins/kubernetes/kubernetes_jobsets.py +784 -0
  55. metaflow/plugins/timeout_decorator.py +2 -1
  56. metaflow/runner/__init__.py +0 -0
  57. metaflow/runner/click_api.py +406 -0
  58. metaflow/runner/metaflow_runner.py +452 -0
  59. metaflow/runner/nbrun.py +246 -0
  60. metaflow/runner/subprocess_manager.py +552 -0
  61. metaflow/task.py +1 -12
  62. metaflow/tuple_util.py +27 -0
  63. metaflow/util.py +0 -15
  64. metaflow/vendor.py +0 -1
  65. metaflow/version.py +1 -1
  66. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/METADATA +2 -2
  67. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/RECORD +72 -39
  68. metaflow/_vendor/v3_7/__init__.py +0 -1
  69. /metaflow/_vendor/{v3_7/zipp.py → zipp.py} +0 -0
  70. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/LICENSE +0 -0
  71. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/WHEEL +0 -0
  72. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/entry_points.txt +0 -0
  73. {metaflow-2.11.15.dist-info → metaflow-2.12.0.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import traceback
4
4
  from metaflow.exception import MetaflowException
5
5
  from metaflow.decorators import StepDecorator
6
6
  from metaflow.unbounded_foreach import UBF_CONTROL
7
+ from metaflow.metaflow_config import DEFAULT_RUNTIME_LIMIT
7
8
 
8
9
 
9
10
  class TimeoutException(MetaflowException):
@@ -99,7 +100,7 @@ class TimeoutDecorator(StepDecorator):
99
100
 
100
101
 
101
102
  def get_run_time_limit_for_task(step_decos):
102
- run_time_limit = 5 * 24 * 60 * 60 # 5 days.
103
+ run_time_limit = DEFAULT_RUNTIME_LIMIT
103
104
  for deco in step_decos:
104
105
  if isinstance(deco, TimeoutDecorator):
105
106
  run_time_limit = deco.secs
File without changes
@@ -0,0 +1,406 @@
1
+ import sys
2
+
3
+ if sys.version_info < (3, 7):
4
+ raise RuntimeError(
5
+ """
6
+ The Metaflow Programmatic API is not supported for versions of Python less than 3.7
7
+ """
8
+ )
9
+
10
+ import datetime
11
+ import importlib
12
+ import inspect
13
+ import itertools
14
+ import uuid
15
+ from collections import OrderedDict
16
+ from typing import Any, Callable, Dict, List, Optional
17
+ from typing import OrderedDict as TOrderedDict
18
+ from typing import Union
19
+
20
+ from metaflow import FlowSpec, Parameter
21
+ from metaflow._vendor import click
22
+ from metaflow._vendor.click.types import (
23
+ BoolParamType,
24
+ Choice,
25
+ DateTime,
26
+ File,
27
+ FloatParamType,
28
+ IntParamType,
29
+ Path,
30
+ StringParamType,
31
+ Tuple,
32
+ UUIDParameterType,
33
+ )
34
+ from metaflow._vendor.typeguard import TypeCheckError, check_type
35
+ from metaflow.cli import start
36
+ from metaflow.includefile import FilePathClass
37
+ from metaflow.parameters import JSONTypeClass
38
+
39
+ click_to_python_types = {
40
+ StringParamType: str,
41
+ IntParamType: int,
42
+ FloatParamType: float,
43
+ BoolParamType: bool,
44
+ UUIDParameterType: uuid.UUID,
45
+ Path: str,
46
+ DateTime: datetime.datetime,
47
+ Tuple: tuple,
48
+ Choice: str,
49
+ File: str,
50
+ JSONTypeClass: str,
51
+ FilePathClass: str,
52
+ }
53
+
54
+
55
+ def _method_sanity_check(
56
+ possible_arg_params: TOrderedDict[str, click.Argument],
57
+ possible_opt_params: TOrderedDict[str, click.Option],
58
+ annotations: TOrderedDict[str, Any],
59
+ defaults: TOrderedDict[str, Any],
60
+ **kwargs
61
+ ) -> Dict[str, Any]:
62
+ method_params = {"args": {}, "options": {}}
63
+
64
+ possible_params = OrderedDict()
65
+ possible_params.update(possible_arg_params)
66
+ possible_params.update(possible_opt_params)
67
+
68
+ # supplied kwargs
69
+ for supplied_k, supplied_v in kwargs.items():
70
+ if supplied_k not in possible_params:
71
+ raise ValueError(
72
+ "Unknown argument: '%s', possible args are: %s"
73
+ % (supplied_k, ", ".join(possible_params.keys()))
74
+ )
75
+
76
+ try:
77
+ check_type(supplied_v, annotations[supplied_k])
78
+ except TypeCheckError:
79
+ raise TypeError(
80
+ "Invalid type for '%s', expected: '%s', default is '%s'"
81
+ % (supplied_k, annotations[supplied_k], defaults[supplied_k])
82
+ )
83
+
84
+ if supplied_k in possible_arg_params:
85
+ cli_name = possible_arg_params[supplied_k].opts[0].strip("-")
86
+ method_params["args"][cli_name] = supplied_v
87
+ elif supplied_k in possible_opt_params:
88
+ if possible_opt_params[supplied_k].is_bool_flag:
89
+ # it is a boolean flag..
90
+ if supplied_v == True:
91
+ cli_name = possible_opt_params[supplied_k].opts[0].strip("-")
92
+ elif supplied_v == False:
93
+ if possible_opt_params[supplied_k].secondary_opts:
94
+ cli_name = (
95
+ possible_opt_params[supplied_k].secondary_opts[0].strip("-")
96
+ )
97
+ else:
98
+ continue
99
+ supplied_v = "flag"
100
+ else:
101
+ cli_name = possible_opt_params[supplied_k].opts[0].strip("-")
102
+ method_params["options"][cli_name] = supplied_v
103
+
104
+ # possible kwargs
105
+ for _, possible_v in possible_params.items():
106
+ cli_name = possible_v.opts[0].strip("-")
107
+ if (
108
+ (cli_name not in method_params["args"])
109
+ and (cli_name not in method_params["options"])
110
+ ) and possible_v.required:
111
+ raise ValueError("Missing argument: %s is required." % cli_name)
112
+
113
+ return method_params
114
+
115
+
116
+ def get_annotation(param: Union[click.Argument, click.Option]):
117
+ py_type = click_to_python_types[type(param.type)]
118
+ if not param.required:
119
+ if param.multiple or param.nargs == -1:
120
+ return Optional[List[py_type]]
121
+ else:
122
+ return Optional[py_type]
123
+ else:
124
+ if param.multiple or param.nargs == -1:
125
+ return List[py_type]
126
+ else:
127
+ return py_type
128
+
129
+
130
+ def get_inspect_param_obj(p: Union[click.Argument, click.Option], kind: str):
131
+ return inspect.Parameter(
132
+ name=p.name,
133
+ kind=kind,
134
+ default=p.default,
135
+ annotation=get_annotation(p),
136
+ )
137
+
138
+
139
+ # Cache to store already loaded modules
140
+ loaded_modules = {}
141
+
142
+
143
+ def extract_flowspec_params(flow_file: str) -> List[Parameter]:
144
+ # Check if the module has already been loaded
145
+ if flow_file in loaded_modules:
146
+ module = loaded_modules[flow_file]
147
+ else:
148
+ # Load the module if it's not already loaded
149
+ spec = importlib.util.spec_from_file_location("module", flow_file)
150
+ module = importlib.util.module_from_spec(spec)
151
+ spec.loader.exec_module(module)
152
+ # Cache the loaded module
153
+ loaded_modules[flow_file] = module
154
+ classes = inspect.getmembers(module, inspect.isclass)
155
+
156
+ parameters = []
157
+ for _, kls in classes:
158
+ if kls != FlowSpec and issubclass(kls, FlowSpec):
159
+ for _, obj in inspect.getmembers(kls):
160
+ if isinstance(obj, Parameter):
161
+ parameters.append(obj)
162
+
163
+ return parameters
164
+
165
+
166
+ class MetaflowAPI(object):
167
+ def __init__(self, parent=None, **kwargs):
168
+ self._parent = parent
169
+ self._chain = [{self._API_NAME: kwargs}]
170
+
171
+ @property
172
+ def parent(self):
173
+ if self._parent:
174
+ return self._parent
175
+ return None
176
+
177
+ @property
178
+ def chain(self):
179
+ return self._chain
180
+
181
+ @classmethod
182
+ def from_cli(cls, flow_file: str, cli_collection: Callable) -> Callable:
183
+ flow_parameters = extract_flowspec_params(flow_file)
184
+ class_dict = {"__module__": "metaflow", "_API_NAME": flow_file}
185
+ command_groups = cli_collection.sources
186
+ for each_group in command_groups:
187
+ for _, cmd_obj in each_group.commands.items():
188
+ if isinstance(cmd_obj, click.Group):
189
+ # TODO: possibly check for fake groups with cmd_obj.name in ["cli", "main"]
190
+ class_dict[cmd_obj.name] = extract_group(cmd_obj, flow_parameters)
191
+ elif isinstance(cmd_obj, click.Command):
192
+ class_dict[cmd_obj.name] = extract_command(cmd_obj, flow_parameters)
193
+ else:
194
+ raise RuntimeError(
195
+ "Cannot handle %s of type %s" % (cmd_obj.name, type(cmd_obj))
196
+ )
197
+
198
+ to_return = type(flow_file, (MetaflowAPI,), class_dict)
199
+ to_return.__name__ = flow_file
200
+
201
+ (
202
+ params_sigs,
203
+ possible_arg_params,
204
+ possible_opt_params,
205
+ annotations,
206
+ defaults,
207
+ ) = extract_all_params(cli_collection)
208
+
209
+ def _method(_self, **kwargs):
210
+ method_params = _method_sanity_check(
211
+ possible_arg_params,
212
+ possible_opt_params,
213
+ annotations,
214
+ defaults,
215
+ **kwargs,
216
+ )
217
+ return to_return(parent=None, **method_params)
218
+
219
+ m = _method
220
+ m.__name__ = cmd_obj.name
221
+ m.__doc__ = getattr(cmd_obj, "help", None)
222
+ m.__signature__ = inspect.signature(_method).replace(
223
+ parameters=params_sigs.values()
224
+ )
225
+ m.__annotations__ = annotations
226
+ m.__defaults__ = tuple(defaults.values())
227
+
228
+ return m
229
+
230
+ def execute(self) -> List[str]:
231
+ parents = []
232
+ current = self
233
+ while current.parent:
234
+ parents.append(current.parent)
235
+ current = current.parent
236
+
237
+ parents.reverse()
238
+
239
+ final_chain = list(itertools.chain.from_iterable([p.chain for p in parents]))
240
+ final_chain.extend(self.chain)
241
+
242
+ components = []
243
+ for each_cmd in final_chain:
244
+ for cmd, params in each_cmd.items():
245
+ components.append(cmd)
246
+ args = params.pop("args", {})
247
+ options = params.pop("options", {})
248
+
249
+ for _, v in args.items():
250
+ if isinstance(v, list):
251
+ for i in v:
252
+ components.append(i)
253
+ else:
254
+ components.append(v)
255
+ for k, v in options.items():
256
+ if isinstance(v, list):
257
+ for i in v:
258
+ components.append("--%s" % k)
259
+ components.append(str(i))
260
+ else:
261
+ components.append("--%s" % k)
262
+ if v != "flag":
263
+ components.append(str(v))
264
+
265
+ return components
266
+
267
+
268
+ def extract_all_params(cmd_obj: Union[click.Command, click.Group]):
269
+ arg_params_sigs = OrderedDict()
270
+ opt_params_sigs = OrderedDict()
271
+ params_sigs = OrderedDict()
272
+
273
+ arg_parameters = OrderedDict()
274
+ opt_parameters = OrderedDict()
275
+ annotations = OrderedDict()
276
+ defaults = OrderedDict()
277
+
278
+ for each_param in cmd_obj.params:
279
+ if isinstance(each_param, click.Argument):
280
+ arg_params_sigs[each_param.name] = get_inspect_param_obj(
281
+ each_param, inspect.Parameter.POSITIONAL_ONLY
282
+ )
283
+ arg_parameters[each_param.name] = each_param
284
+ elif isinstance(each_param, click.Option):
285
+ opt_params_sigs[each_param.name] = get_inspect_param_obj(
286
+ each_param, inspect.Parameter.KEYWORD_ONLY
287
+ )
288
+ opt_parameters[each_param.name] = each_param
289
+
290
+ annotations[each_param.name] = get_annotation(each_param)
291
+ defaults[each_param.name] = each_param.default
292
+
293
+ # first, fill in positional arguments
294
+ for name, each_arg_param in arg_params_sigs.items():
295
+ params_sigs[name] = each_arg_param
296
+ # then, fill in keyword arguments
297
+ for name, each_opt_param in opt_params_sigs.items():
298
+ params_sigs[name] = each_opt_param
299
+
300
+ return params_sigs, arg_parameters, opt_parameters, annotations, defaults
301
+
302
+
303
+ def extract_group(cmd_obj: click.Group, flow_parameters: List[Parameter]) -> Callable:
304
+ class_dict = {"__module__": "metaflow", "_API_NAME": cmd_obj.name}
305
+ for _, sub_cmd_obj in cmd_obj.commands.items():
306
+ if isinstance(sub_cmd_obj, click.Group):
307
+ # recursion
308
+ class_dict[sub_cmd_obj.name] = extract_group(sub_cmd_obj, flow_parameters)
309
+ elif isinstance(sub_cmd_obj, click.Command):
310
+ class_dict[sub_cmd_obj.name] = extract_command(sub_cmd_obj, flow_parameters)
311
+ else:
312
+ raise RuntimeError(
313
+ "Cannot handle %s of type %s" % (sub_cmd_obj.name, type(sub_cmd_obj))
314
+ )
315
+
316
+ resulting_class = type(cmd_obj.name, (MetaflowAPI,), class_dict)
317
+ resulting_class.__name__ = cmd_obj.name
318
+
319
+ (
320
+ params_sigs,
321
+ possible_arg_params,
322
+ possible_opt_params,
323
+ annotations,
324
+ defaults,
325
+ ) = extract_all_params(cmd_obj)
326
+
327
+ def _method(_self, **kwargs):
328
+ method_params = _method_sanity_check(
329
+ possible_arg_params, possible_opt_params, annotations, defaults, **kwargs
330
+ )
331
+ return resulting_class(parent=_self, **method_params)
332
+
333
+ m = _method
334
+ m.__name__ = cmd_obj.name
335
+ m.__doc__ = getattr(cmd_obj, "help", None)
336
+ m.__signature__ = inspect.signature(_method).replace(
337
+ parameters=params_sigs.values()
338
+ )
339
+ m.__annotations__ = annotations
340
+ m.__defaults__ = tuple(defaults.values())
341
+
342
+ return m
343
+
344
+
345
+ def extract_command(
346
+ cmd_obj: click.Command, flow_parameters: List[Parameter]
347
+ ) -> Callable:
348
+ if getattr(cmd_obj, "has_flow_params", False):
349
+ for p in flow_parameters[::-1]:
350
+ cmd_obj.params.insert(0, click.Option(("--" + p.name,), **p.kwargs))
351
+
352
+ (
353
+ params_sigs,
354
+ possible_arg_params,
355
+ possible_opt_params,
356
+ annotations,
357
+ defaults,
358
+ ) = extract_all_params(cmd_obj)
359
+
360
+ def _method(_self, **kwargs):
361
+ method_params = _method_sanity_check(
362
+ possible_arg_params, possible_opt_params, annotations, defaults, **kwargs
363
+ )
364
+ _self._chain.append({cmd_obj.name: method_params})
365
+ return _self.execute()
366
+
367
+ m = _method
368
+ m.__name__ = cmd_obj.name
369
+ m.__doc__ = getattr(cmd_obj, "help", None)
370
+ m.__signature__ = inspect.signature(_method).replace(
371
+ parameters=params_sigs.values()
372
+ )
373
+ m.__annotations__ = annotations
374
+ m.__defaults__ = tuple(defaults.values())
375
+
376
+ return m
377
+
378
+
379
+ if __name__ == "__main__":
380
+ api = MetaflowAPI.from_cli("../try.py", start)
381
+
382
+ command = api(metadata="local").run(
383
+ tags=["abc", "def"],
384
+ decospecs=["kubernetes"],
385
+ max_workers=5,
386
+ alpha=3,
387
+ myfile="path/to/file",
388
+ )
389
+ print(" ".join(command))
390
+
391
+ command = (
392
+ api(metadata="local")
393
+ .kubernetes()
394
+ .step(
395
+ step_name="process",
396
+ code_package_sha="some_sha",
397
+ code_package_url="some_url",
398
+ )
399
+ )
400
+ print(" ".join(command))
401
+
402
+ command = api().tag().add(tags=["abc", "def"])
403
+ print(" ".join(command))
404
+
405
+ command = getattr(api(decospecs=["retry"]), "argo-workflows")().create()
406
+ print(" ".join(command))