metaflow 2.11.16__py2.py3-none-any.whl → 2.12.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.
- metaflow/__init__.py +5 -0
- metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/typeguard/__init__.py +48 -0
- metaflow/_vendor/typeguard/_checkers.py +906 -0
- metaflow/_vendor/typeguard/_config.py +108 -0
- metaflow/_vendor/typeguard/_decorators.py +237 -0
- metaflow/_vendor/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/typeguard/_functions.py +307 -0
- metaflow/_vendor/typeguard/_importhook.py +213 -0
- metaflow/_vendor/typeguard/_memo.py +48 -0
- metaflow/_vendor/typeguard/_pytest_plugin.py +100 -0
- metaflow/_vendor/typeguard/_suppression.py +88 -0
- metaflow/_vendor/typeguard/_transformer.py +1193 -0
- metaflow/_vendor/typeguard/_union_transformer.py +54 -0
- metaflow/_vendor/typeguard/_utils.py +169 -0
- metaflow/_vendor/typeguard/py.typed +0 -0
- metaflow/_vendor/typing_extensions.py +3053 -0
- metaflow/cli.py +100 -43
- metaflow/cmd/develop/stubs.py +2 -0
- metaflow/decorators.py +16 -3
- metaflow/extension_support/__init__.py +2 -0
- metaflow/metaflow_config.py +21 -0
- metaflow/parameters.py +1 -0
- metaflow/plugins/argo/argo_workflows.py +10 -5
- metaflow/plugins/aws/batch/batch_decorator.py +3 -3
- metaflow/plugins/kubernetes/kubernetes_job.py +0 -5
- metaflow/runner/__init__.py +0 -0
- metaflow/runner/click_api.py +406 -0
- metaflow/runner/metaflow_runner.py +452 -0
- metaflow/runner/nbrun.py +246 -0
- metaflow/runner/subprocess_manager.py +552 -0
- metaflow/vendor.py +0 -1
- metaflow/version.py +1 -1
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/METADATA +2 -2
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/RECORD +48 -20
- metaflow/_vendor/v3_7/__init__.py +0 -1
- /metaflow/_vendor/{v3_7/zipp.py → zipp.py} +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/LICENSE +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/WHEEL +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/entry_points.txt +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.1.dist-info}/top_level.txt +0 -0
@@ -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))
|