metaflow 2.11.16__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.
- 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 +48 -36
- metaflow/cmd/develop/stubs.py +2 -0
- metaflow/extension_support/__init__.py +2 -0
- metaflow/parameters.py +1 -0
- 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.0.dist-info}/METADATA +2 -2
- {metaflow-2.11.16.dist-info → metaflow-2.12.0.dist-info}/RECORD +45 -17
- 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.0.dist-info}/LICENSE +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.0.dist-info}/WHEEL +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.0.dist-info}/entry_points.txt +0 -0
- {metaflow-2.11.16.dist-info → metaflow-2.12.0.dist-info}/top_level.txt +0 -0
metaflow/cli.py
CHANGED
@@ -3,39 +3,16 @@ import sys
|
|
3
3
|
import traceback
|
4
4
|
from datetime import datetime
|
5
5
|
from functools import wraps
|
6
|
-
import metaflow.tracing as tracing
|
7
6
|
|
7
|
+
import metaflow.tracing as tracing
|
8
8
|
from metaflow._vendor import click
|
9
9
|
|
10
|
-
from . import lint
|
11
|
-
from . import plugins
|
12
|
-
from . import parameters
|
13
|
-
from . import decorators
|
14
|
-
from . import metaflow_version
|
15
|
-
from . import namespace
|
16
|
-
from .metaflow_current import current
|
10
|
+
from . import decorators, lint, metaflow_version, namespace, parameters, plugins
|
17
11
|
from .cli_args import cli_args
|
18
|
-
from .
|
19
|
-
from .
|
20
|
-
resolve_identity,
|
21
|
-
decompress_list,
|
22
|
-
write_latest_run_id,
|
23
|
-
get_latest_run_id,
|
24
|
-
)
|
25
|
-
from .task import MetaflowTask
|
12
|
+
from .client.core import get_metadata
|
13
|
+
from .datastore import FlowDataStore, TaskDataStore, TaskDataStoreSet
|
26
14
|
from .exception import CommandException, MetaflowException
|
27
15
|
from .graph import FlowGraph
|
28
|
-
from .datastore import FlowDataStore, TaskDataStoreSet, TaskDataStore
|
29
|
-
|
30
|
-
from .runtime import NativeRuntime
|
31
|
-
from .package import MetaflowPackage
|
32
|
-
from .plugins import (
|
33
|
-
DATASTORES,
|
34
|
-
ENVIRONMENTS,
|
35
|
-
LOGGING_SIDECARS,
|
36
|
-
METADATA_PROVIDERS,
|
37
|
-
MONITOR_SIDECARS,
|
38
|
-
)
|
39
16
|
from .metaflow_config import (
|
40
17
|
DEFAULT_DATASTORE,
|
41
18
|
DEFAULT_ENVIRONMENT,
|
@@ -44,12 +21,29 @@ from .metaflow_config import (
|
|
44
21
|
DEFAULT_MONITOR,
|
45
22
|
DEFAULT_PACKAGE_SUFFIXES,
|
46
23
|
)
|
24
|
+
from .metaflow_current import current
|
47
25
|
from .metaflow_environment import MetaflowEnvironment
|
26
|
+
from .mflog import LOG_SOURCES, mflog
|
27
|
+
from .package import MetaflowPackage
|
28
|
+
from .plugins import (
|
29
|
+
DATASTORES,
|
30
|
+
ENVIRONMENTS,
|
31
|
+
LOGGING_SIDECARS,
|
32
|
+
METADATA_PROVIDERS,
|
33
|
+
MONITOR_SIDECARS,
|
34
|
+
)
|
48
35
|
from .pylint_wrapper import PyLint
|
49
|
-
from .R import
|
50
|
-
from .
|
36
|
+
from .R import metaflow_r_version, use_r
|
37
|
+
from .runtime import NativeRuntime
|
38
|
+
from .tagging_util import validate_tags
|
39
|
+
from .task import MetaflowTask
|
51
40
|
from .unbounded_foreach import UBF_CONTROL, UBF_TASK
|
52
|
-
|
41
|
+
from .util import (
|
42
|
+
decompress_list,
|
43
|
+
get_latest_run_id,
|
44
|
+
resolve_identity,
|
45
|
+
write_latest_run_id,
|
46
|
+
)
|
53
47
|
|
54
48
|
ERASE_TO_EOL = "\033[K"
|
55
49
|
HIGHLIGHT = "red"
|
@@ -557,6 +551,13 @@ def common_run_options(func):
|
|
557
551
|
type=str,
|
558
552
|
help="Write the ID of this run to the file specified.",
|
559
553
|
)
|
554
|
+
@click.option(
|
555
|
+
"--runner-attribute-file",
|
556
|
+
default=None,
|
557
|
+
show_default=True,
|
558
|
+
type=str,
|
559
|
+
help="Write the metadata and pathspec of this run to the file specified. Used internally for Metaflow's Runner API.",
|
560
|
+
)
|
560
561
|
@wraps(func)
|
561
562
|
def wrapper(*args, **kwargs):
|
562
563
|
return func(*args, **kwargs)
|
@@ -615,6 +616,7 @@ def resume(
|
|
615
616
|
decospecs=None,
|
616
617
|
run_id_file=None,
|
617
618
|
resume_identifier=None,
|
619
|
+
runner_attribute_file=None,
|
618
620
|
):
|
619
621
|
before_run(obj, tags, decospecs + obj.environment.decospecs())
|
620
622
|
|
@@ -670,9 +672,14 @@ def resume(
|
|
670
672
|
max_log_size=max_log_size * 1024 * 1024,
|
671
673
|
resume_identifier=resume_identifier,
|
672
674
|
)
|
673
|
-
|
675
|
+
write_file(run_id_file, runtime.run_id)
|
674
676
|
runtime.print_workflow_info()
|
677
|
+
|
675
678
|
runtime.persist_constants()
|
679
|
+
write_file(
|
680
|
+
runner_attribute_file,
|
681
|
+
"%s:%s" % (get_metadata(), "/".join((obj.flow.name, runtime.run_id))),
|
682
|
+
)
|
676
683
|
if clone_only:
|
677
684
|
runtime.clone_original_run()
|
678
685
|
else:
|
@@ -703,6 +710,7 @@ def run(
|
|
703
710
|
max_log_size=None,
|
704
711
|
decospecs=None,
|
705
712
|
run_id_file=None,
|
713
|
+
runner_attribute_file=None,
|
706
714
|
user_namespace=None,
|
707
715
|
**kwargs
|
708
716
|
):
|
@@ -726,18 +734,22 @@ def run(
|
|
726
734
|
max_log_size=max_log_size * 1024 * 1024,
|
727
735
|
)
|
728
736
|
write_latest_run_id(obj, runtime.run_id)
|
729
|
-
|
737
|
+
write_file(run_id_file, runtime.run_id)
|
730
738
|
|
731
739
|
obj.flow._set_constants(obj.graph, kwargs)
|
732
740
|
runtime.print_workflow_info()
|
733
741
|
runtime.persist_constants()
|
742
|
+
write_file(
|
743
|
+
runner_attribute_file,
|
744
|
+
"%s:%s" % (get_metadata(), "/".join((obj.flow.name, runtime.run_id))),
|
745
|
+
)
|
734
746
|
runtime.execute()
|
735
747
|
|
736
748
|
|
737
|
-
def
|
738
|
-
if
|
739
|
-
with open(
|
740
|
-
f.write(str(
|
749
|
+
def write_file(file_path, content):
|
750
|
+
if file_path is not None:
|
751
|
+
with open(file_path, "w") as f:
|
752
|
+
f.write(str(content))
|
741
753
|
|
742
754
|
|
743
755
|
def before_run(obj, tags, decospecs):
|
metaflow/cmd/develop/stubs.py
CHANGED
@@ -23,6 +23,8 @@ def _check_stubs_supported():
|
|
23
23
|
if _py_ver >= (3, 4):
|
24
24
|
if _py_ver >= (3, 8):
|
25
25
|
from importlib import metadata
|
26
|
+
elif _py_ver >= (3, 7):
|
27
|
+
from metaflow._vendor import importlib_metadata as metadata
|
26
28
|
elif _py_ver >= (3, 6):
|
27
29
|
from metaflow._vendor.v3_6 import importlib_metadata as metadata
|
28
30
|
else:
|
@@ -262,6 +262,8 @@ if _py_ver >= (3, 4):
|
|
262
262
|
|
263
263
|
if _py_ver >= (3, 8):
|
264
264
|
from importlib import metadata
|
265
|
+
elif _py_ver >= (3, 7):
|
266
|
+
from metaflow._vendor import importlib_metadata as metadata
|
265
267
|
elif _py_ver >= (3, 6):
|
266
268
|
from metaflow._vendor.v3_6 import importlib_metadata as metadata
|
267
269
|
else:
|
metaflow/parameters.py
CHANGED
@@ -388,6 +388,7 @@ def add_custom_parameters(deploy_mode=False):
|
|
388
388
|
# deploy_mode determines whether deploy-time functions should or should
|
389
389
|
# not be evaluated for this command
|
390
390
|
def wrapper(cmd):
|
391
|
+
cmd.has_flow_params = True
|
391
392
|
# Iterate over parameters in reverse order so cmd.params lists options
|
392
393
|
# in the order they are defined in the FlowSpec subclass
|
393
394
|
for arg in parameters[::-1]:
|
@@ -88,15 +88,15 @@ class BatchDecorator(StepDecorator):
|
|
88
88
|
Alias for inferentia. Use only one of the two.
|
89
89
|
efa : int, default 0
|
90
90
|
Number of elastic fabric adapter network devices to attach to container
|
91
|
-
ephemeral_storage: int, default None
|
92
|
-
The total amount, in GiB, of ephemeral storage to set for the task
|
91
|
+
ephemeral_storage : int, default None
|
92
|
+
The total amount, in GiB, of ephemeral storage to set for the task, 21-200GiB.
|
93
93
|
This is only relevant for Fargate compute environments
|
94
94
|
log_driver: str, optional, default None
|
95
95
|
The log driver to use for the Amazon ECS container.
|
96
96
|
log_options: List[str], optional, default None
|
97
97
|
List of strings containing options for the chosen log driver. The configurable values
|
98
98
|
depend on the `log driver` chosen. Validation of these options is not supported yet.
|
99
|
-
Example
|
99
|
+
Example: [`awslogs-group:aws/batch/job`]
|
100
100
|
"""
|
101
101
|
|
102
102
|
name = "batch"
|
@@ -275,11 +275,6 @@ class KubernetesJob(object):
|
|
275
275
|
# (unique UID) per Metaflow task attempt.
|
276
276
|
client = self._client.get()
|
277
277
|
|
278
|
-
# tmpfs variables
|
279
|
-
use_tmpfs = self._kwargs["use_tmpfs"]
|
280
|
-
tmpfs_size = self._kwargs["tmpfs_size"]
|
281
|
-
tmpfs_enabled = use_tmpfs or (tmpfs_size and not use_tmpfs)
|
282
|
-
|
283
278
|
self._job = client.V1Job(
|
284
279
|
api_version="batch/v1",
|
285
280
|
kind="Job",
|
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))
|