flyte 2.0.0b32__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 flyte might be problematic. Click here for more details.
- flyte/__init__.py +108 -0
- flyte/_bin/__init__.py +0 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +195 -0
- flyte/_bin/serve.py +178 -0
- flyte/_build.py +26 -0
- flyte/_cache/__init__.py +12 -0
- flyte/_cache/cache.py +147 -0
- flyte/_cache/defaults.py +9 -0
- flyte/_cache/local_cache.py +216 -0
- flyte/_cache/policy_function_body.py +42 -0
- flyte/_code_bundle/__init__.py +8 -0
- flyte/_code_bundle/_ignore.py +121 -0
- flyte/_code_bundle/_packaging.py +218 -0
- flyte/_code_bundle/_utils.py +347 -0
- flyte/_code_bundle/bundle.py +266 -0
- flyte/_constants.py +1 -0
- flyte/_context.py +155 -0
- flyte/_custom_context.py +73 -0
- flyte/_debug/__init__.py +0 -0
- flyte/_debug/constants.py +38 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +307 -0
- flyte/_deploy.py +408 -0
- flyte/_deployer.py +109 -0
- flyte/_doc.py +29 -0
- flyte/_docstring.py +32 -0
- flyte/_environment.py +122 -0
- flyte/_excepthook.py +37 -0
- flyte/_group.py +32 -0
- flyte/_hash.py +8 -0
- flyte/_image.py +1055 -0
- flyte/_initialize.py +628 -0
- flyte/_interface.py +119 -0
- flyte/_internal/__init__.py +3 -0
- flyte/_internal/controllers/__init__.py +129 -0
- flyte/_internal/controllers/_local_controller.py +239 -0
- flyte/_internal/controllers/_trace.py +48 -0
- flyte/_internal/controllers/remote/__init__.py +58 -0
- flyte/_internal/controllers/remote/_action.py +211 -0
- flyte/_internal/controllers/remote/_client.py +47 -0
- flyte/_internal/controllers/remote/_controller.py +583 -0
- flyte/_internal/controllers/remote/_core.py +465 -0
- flyte/_internal/controllers/remote/_informer.py +381 -0
- flyte/_internal/controllers/remote/_service_protocol.py +50 -0
- flyte/_internal/imagebuild/__init__.py +3 -0
- flyte/_internal/imagebuild/docker_builder.py +706 -0
- flyte/_internal/imagebuild/image_builder.py +277 -0
- flyte/_internal/imagebuild/remote_builder.py +386 -0
- flyte/_internal/imagebuild/utils.py +78 -0
- flyte/_internal/resolvers/__init__.py +0 -0
- flyte/_internal/resolvers/_task_module.py +21 -0
- flyte/_internal/resolvers/common.py +31 -0
- flyte/_internal/resolvers/default.py +28 -0
- flyte/_internal/runtime/__init__.py +0 -0
- flyte/_internal/runtime/convert.py +486 -0
- flyte/_internal/runtime/entrypoints.py +204 -0
- flyte/_internal/runtime/io.py +188 -0
- flyte/_internal/runtime/resources_serde.py +152 -0
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +193 -0
- flyte/_internal/runtime/task_serde.py +362 -0
- flyte/_internal/runtime/taskrunner.py +209 -0
- flyte/_internal/runtime/trigger_serde.py +160 -0
- flyte/_internal/runtime/types_serde.py +54 -0
- flyte/_keyring/__init__.py +0 -0
- flyte/_keyring/file.py +115 -0
- flyte/_logging.py +300 -0
- flyte/_map.py +312 -0
- flyte/_module.py +72 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +473 -0
- flyte/_retry.py +32 -0
- flyte/_reusable_environment.py +102 -0
- flyte/_run.py +724 -0
- flyte/_secret.py +96 -0
- flyte/_task.py +550 -0
- flyte/_task_environment.py +316 -0
- flyte/_task_plugins.py +47 -0
- flyte/_timeout.py +47 -0
- flyte/_tools.py +27 -0
- flyte/_trace.py +119 -0
- flyte/_trigger.py +1000 -0
- flyte/_utils/__init__.py +30 -0
- flyte/_utils/asyn.py +121 -0
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +27 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/file_handling.py +72 -0
- flyte/_utils/helpers.py +134 -0
- flyte/_utils/lazy_module.py +54 -0
- flyte/_utils/module_loader.py +104 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +49 -0
- flyte/_version.py +34 -0
- flyte/app/__init__.py +22 -0
- flyte/app/_app_environment.py +157 -0
- flyte/app/_deploy.py +125 -0
- flyte/app/_input.py +160 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +347 -0
- flyte/app/_types.py +101 -0
- flyte/app/extras/__init__.py +3 -0
- flyte/app/extras/_fastapi.py +151 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +468 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +293 -0
- flyte/cli/_gen.py +176 -0
- flyte/cli/_get.py +370 -0
- flyte/cli/_option.py +33 -0
- flyte/cli/_params.py +554 -0
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_run.py +597 -0
- flyte/cli/_serve.py +64 -0
- flyte/cli/_update.py +37 -0
- flyte/cli/_user.py +17 -0
- flyte/cli/main.py +221 -0
- flyte/config/__init__.py +3 -0
- flyte/config/_config.py +248 -0
- flyte/config/_internal.py +73 -0
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +270 -0
- flyte/connectors/_server.py +197 -0
- flyte/connectors/utils.py +135 -0
- flyte/errors.py +243 -0
- flyte/extend.py +19 -0
- flyte/extras/__init__.py +5 -0
- flyte/extras/_container.py +286 -0
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +21 -0
- flyte/io/__init__.py +29 -0
- flyte/io/_dataframe/__init__.py +131 -0
- flyte/io/_dataframe/basic_dfs.py +223 -0
- flyte/io/_dataframe/dataframe.py +1026 -0
- flyte/io/_dir.py +910 -0
- flyte/io/_file.py +914 -0
- flyte/io/_hashing_io.py +342 -0
- flyte/models.py +479 -0
- flyte/py.typed +0 -0
- flyte/remote/__init__.py +35 -0
- flyte/remote/_action.py +738 -0
- flyte/remote/_app.py +57 -0
- flyte/remote/_client/__init__.py +0 -0
- flyte/remote/_client/_protocols.py +189 -0
- flyte/remote/_client/auth/__init__.py +12 -0
- flyte/remote/_client/auth/_auth_utils.py +14 -0
- flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
- flyte/remote/_client/auth/_authenticators/base.py +403 -0
- flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
- flyte/remote/_client/auth/_authenticators/device_code.py +117 -0
- flyte/remote/_client/auth/_authenticators/external_command.py +79 -0
- flyte/remote/_client/auth/_authenticators/factory.py +200 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +516 -0
- flyte/remote/_client/auth/_channel.py +213 -0
- flyte/remote/_client/auth/_client_config.py +85 -0
- flyte/remote/_client/auth/_default_html.py +32 -0
- flyte/remote/_client/auth/_grpc_utils/__init__.py +0 -0
- flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py +288 -0
- flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +151 -0
- flyte/remote/_client/auth/_keyring.py +152 -0
- flyte/remote/_client/auth/_token_client.py +260 -0
- flyte/remote/_client/auth/errors.py +16 -0
- flyte/remote/_client/controlplane.py +128 -0
- flyte/remote/_common.py +30 -0
- flyte/remote/_console.py +19 -0
- flyte/remote/_data.py +161 -0
- flyte/remote/_logs.py +185 -0
- flyte/remote/_project.py +88 -0
- flyte/remote/_run.py +386 -0
- flyte/remote/_secret.py +142 -0
- flyte/remote/_task.py +527 -0
- flyte/remote/_trigger.py +306 -0
- flyte/remote/_user.py +33 -0
- flyte/report/__init__.py +3 -0
- flyte/report/_report.py +182 -0
- flyte/report/_template.html +124 -0
- flyte/storage/__init__.py +36 -0
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +274 -0
- flyte/storage/_remote_fs.py +34 -0
- flyte/storage/_storage.py +456 -0
- flyte/storage/_utils.py +5 -0
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +375 -0
- flyte/types/__init__.py +52 -0
- flyte/types/_interface.py +40 -0
- flyte/types/_pickle.py +145 -0
- flyte/types/_renderer.py +162 -0
- flyte/types/_string_literals.py +119 -0
- flyte/types/_type_engine.py +2254 -0
- flyte/types/_utils.py +80 -0
- flyte-2.0.0b32.data/scripts/debug.py +38 -0
- flyte-2.0.0b32.data/scripts/runtime.py +195 -0
- flyte-2.0.0b32.dist-info/METADATA +351 -0
- flyte-2.0.0b32.dist-info/RECORD +204 -0
- flyte-2.0.0b32.dist-info/WHEEL +5 -0
- flyte-2.0.0b32.dist-info/entry_points.txt +7 -0
- flyte-2.0.0b32.dist-info/licenses/LICENSE +201 -0
- flyte-2.0.0b32.dist-info/top_level.txt +1 -0
flyte/cli/_run.py
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import inspect
|
|
5
|
+
from dataclasses import dataclass, field, fields
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import ModuleType
|
|
9
|
+
from typing import Any, Dict, List, cast
|
|
10
|
+
|
|
11
|
+
import rich_click as click
|
|
12
|
+
from typing_extensions import get_args
|
|
13
|
+
|
|
14
|
+
from .._code_bundle._utils import CopyFiles
|
|
15
|
+
from .._task import TaskTemplate
|
|
16
|
+
from ..remote import Run
|
|
17
|
+
from . import _common as common
|
|
18
|
+
from ._common import CLIConfig, initialize_config
|
|
19
|
+
from ._params import to_click_option
|
|
20
|
+
|
|
21
|
+
RUN_REMOTE_CMD = "deployed-task"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@lru_cache()
|
|
25
|
+
def _initialize_config(ctx: click.Context, project: str, domain: str, root_dir: str | None = None):
|
|
26
|
+
obj: CLIConfig | None = ctx.obj
|
|
27
|
+
if obj is None:
|
|
28
|
+
import flyte.config
|
|
29
|
+
|
|
30
|
+
obj = CLIConfig(flyte.config.auto(), ctx)
|
|
31
|
+
|
|
32
|
+
obj.init(project, domain, root_dir)
|
|
33
|
+
return obj
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@lru_cache()
|
|
37
|
+
def _list_tasks(
|
|
38
|
+
ctx: click.Context,
|
|
39
|
+
project: str,
|
|
40
|
+
domain: str,
|
|
41
|
+
by_task_name: str | None = None,
|
|
42
|
+
by_task_env: str | None = None,
|
|
43
|
+
) -> list[str]:
|
|
44
|
+
import flyte.remote
|
|
45
|
+
|
|
46
|
+
common.initialize_config(ctx, project, domain)
|
|
47
|
+
return [task.name for task in flyte.remote.Task.listall(by_task_name=by_task_name, by_task_env=by_task_env)]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class RunArguments:
|
|
52
|
+
project: str = field(
|
|
53
|
+
default=cast(str, common.PROJECT_OPTION.default), metadata={"click.option": common.PROJECT_OPTION}
|
|
54
|
+
)
|
|
55
|
+
domain: str = field(
|
|
56
|
+
default=cast(str, common.DOMAIN_OPTION.default), metadata={"click.option": common.DOMAIN_OPTION}
|
|
57
|
+
)
|
|
58
|
+
local: bool = field(
|
|
59
|
+
default=False,
|
|
60
|
+
metadata={
|
|
61
|
+
"click.option": click.Option(
|
|
62
|
+
["--local"],
|
|
63
|
+
is_flag=True,
|
|
64
|
+
help="Run the task locally",
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
copy_style: CopyFiles = field(
|
|
69
|
+
default="loaded_modules",
|
|
70
|
+
metadata={
|
|
71
|
+
"click.option": click.Option(
|
|
72
|
+
["--copy-style"],
|
|
73
|
+
type=click.Choice(get_args(CopyFiles)),
|
|
74
|
+
default="loaded_modules",
|
|
75
|
+
help="Copy style to use when running the task",
|
|
76
|
+
)
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
root_dir: str | None = field(
|
|
80
|
+
default=None,
|
|
81
|
+
metadata={
|
|
82
|
+
"click.option": click.Option(
|
|
83
|
+
["--root-dir"],
|
|
84
|
+
type=str,
|
|
85
|
+
help="Override the root source directory, helpful when working with monorepos.",
|
|
86
|
+
)
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
raw_data_path: str | None = field(
|
|
90
|
+
default=None,
|
|
91
|
+
metadata={
|
|
92
|
+
"click.option": click.Option(
|
|
93
|
+
["--raw-data-path"],
|
|
94
|
+
type=str,
|
|
95
|
+
help="Override the output prefix used to store offloaded data types. e.g. s3://bucket/",
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
service_account: str | None = field(
|
|
100
|
+
default=None,
|
|
101
|
+
metadata={
|
|
102
|
+
"click.option": click.Option(
|
|
103
|
+
["--service-account"],
|
|
104
|
+
type=str,
|
|
105
|
+
help="Kubernetes service account. If not provided, the configured default will be used",
|
|
106
|
+
)
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
name: str | None = field(
|
|
110
|
+
default=None,
|
|
111
|
+
metadata={
|
|
112
|
+
"click.option": click.Option(
|
|
113
|
+
["--name"],
|
|
114
|
+
type=str,
|
|
115
|
+
help="Name of the run. If not provided, a random name will be generated.",
|
|
116
|
+
)
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
follow: bool = field(
|
|
120
|
+
default=False,
|
|
121
|
+
metadata={
|
|
122
|
+
"click.option": click.Option(
|
|
123
|
+
["--follow", "-f"],
|
|
124
|
+
is_flag=True,
|
|
125
|
+
default=False,
|
|
126
|
+
help="Wait and watch logs for the parent action. If not provided, the CLI will exit after "
|
|
127
|
+
"successfully launching a remote execution with a link to the UI.",
|
|
128
|
+
)
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
image: List[str] = field(
|
|
132
|
+
default_factory=list,
|
|
133
|
+
metadata={
|
|
134
|
+
"click.option": click.Option(
|
|
135
|
+
["--image"],
|
|
136
|
+
type=str,
|
|
137
|
+
multiple=True,
|
|
138
|
+
help="Image to be used in the run. Format: imagename=imageuri. Can be specified multiple times.",
|
|
139
|
+
)
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
no_sync_local_sys_paths: bool = field(
|
|
143
|
+
default=True,
|
|
144
|
+
metadata={
|
|
145
|
+
"click.option": click.Option(
|
|
146
|
+
["--no-sync-local-sys-paths"],
|
|
147
|
+
is_flag=True,
|
|
148
|
+
flag_value=True,
|
|
149
|
+
default=False,
|
|
150
|
+
help="Disable synchronization of local sys.path entries under the root directory "
|
|
151
|
+
"to the remote container.",
|
|
152
|
+
)
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_dict(cls, d: Dict[str, Any]) -> RunArguments:
|
|
158
|
+
modified = {k: v for k, v in d.items() if k in {f.name for f in fields(cls)}}
|
|
159
|
+
return cls(**modified)
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def options(cls) -> List[click.Option]:
|
|
163
|
+
"""
|
|
164
|
+
Return the set of base parameters added to run subcommand.
|
|
165
|
+
"""
|
|
166
|
+
return [common.get_option_from_metadata(f.metadata) for f in fields(cls) if f.metadata]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class RunTaskCommand(click.RichCommand):
|
|
170
|
+
def __init__(self, obj_name: str, obj: Any, run_args: RunArguments, *args, **kwargs):
|
|
171
|
+
self.obj_name = obj_name
|
|
172
|
+
self.obj = cast(TaskTemplate, obj)
|
|
173
|
+
self.run_args = run_args
|
|
174
|
+
kwargs.pop("name", None)
|
|
175
|
+
super().__init__(obj_name, *args, **kwargs)
|
|
176
|
+
|
|
177
|
+
def invoke(self, ctx: click.Context):
|
|
178
|
+
obj: CLIConfig = initialize_config(
|
|
179
|
+
ctx,
|
|
180
|
+
self.run_args.project,
|
|
181
|
+
self.run_args.domain,
|
|
182
|
+
self.run_args.root_dir,
|
|
183
|
+
tuple(self.run_args.image) or None,
|
|
184
|
+
not self.run_args.no_sync_local_sys_paths,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
async def _run():
|
|
188
|
+
import flyte
|
|
189
|
+
|
|
190
|
+
console = common.get_console()
|
|
191
|
+
r = await flyte.with_runcontext(
|
|
192
|
+
copy_style=self.run_args.copy_style,
|
|
193
|
+
mode="local" if self.run_args.local else "remote",
|
|
194
|
+
name=self.run_args.name,
|
|
195
|
+
raw_data_path=self.run_args.raw_data_path,
|
|
196
|
+
service_account=self.run_args.service_account,
|
|
197
|
+
log_format=obj.log_format,
|
|
198
|
+
).run.aio(self.obj, **ctx.params)
|
|
199
|
+
if self.run_args.local:
|
|
200
|
+
console.print(
|
|
201
|
+
common.get_panel(
|
|
202
|
+
"Local Run",
|
|
203
|
+
f"[green]Completed Local Run, data stored in path: {r.url} [/green] \n"
|
|
204
|
+
f"➡️ Outputs: {r.outputs()}",
|
|
205
|
+
obj.output_format,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
return
|
|
209
|
+
if isinstance(r, Run) and r.action is not None:
|
|
210
|
+
console.print(
|
|
211
|
+
common.get_panel(
|
|
212
|
+
"Run",
|
|
213
|
+
f"[green bold]Created Run: {r.name} [/green bold] "
|
|
214
|
+
f"(Project: {r.action.action_id.run.project}, Domain: {r.action.action_id.run.domain})\n"
|
|
215
|
+
f"➡️ [blue bold][link={r.url}]{r.url}[/link][/blue bold]",
|
|
216
|
+
obj.output_format,
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
if self.run_args.follow:
|
|
220
|
+
console.print(
|
|
221
|
+
"[dim]Log streaming enabled, will wait for task to start running "
|
|
222
|
+
"and log stream to be available[/dim]"
|
|
223
|
+
)
|
|
224
|
+
await r.show_logs.aio(max_lines=30, show_ts=True, raw=False)
|
|
225
|
+
|
|
226
|
+
asyncio.run(_run())
|
|
227
|
+
|
|
228
|
+
def get_params(self, ctx: click.Context) -> List[click.Parameter]:
|
|
229
|
+
# Note this function may be called multiple times by click.
|
|
230
|
+
task = self.obj
|
|
231
|
+
from .._internal.runtime.types_serde import transform_native_to_typed_interface
|
|
232
|
+
|
|
233
|
+
interface = transform_native_to_typed_interface(task.native_interface)
|
|
234
|
+
if interface is None:
|
|
235
|
+
return super().get_params(ctx)
|
|
236
|
+
inputs_interface = task.native_interface.inputs
|
|
237
|
+
|
|
238
|
+
params: List[click.Parameter] = []
|
|
239
|
+
for name, var in interface.inputs.variables.items():
|
|
240
|
+
default_val = None
|
|
241
|
+
if inputs_interface[name][1] is not inspect._empty:
|
|
242
|
+
default_val = inputs_interface[name][1]
|
|
243
|
+
params.append(to_click_option(name, var, inputs_interface[name][0], default_val))
|
|
244
|
+
|
|
245
|
+
self.params = params
|
|
246
|
+
return super().get_params(ctx)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class TaskPerFileGroup(common.ObjectsPerFileGroup):
|
|
250
|
+
"""
|
|
251
|
+
Group that creates a command for each task in the current directory that is not __init__.py.
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
def __init__(self, filename: Path, run_args: RunArguments, *args, **kwargs):
|
|
255
|
+
if filename.is_absolute():
|
|
256
|
+
filename = filename.relative_to(Path.cwd())
|
|
257
|
+
super().__init__(*(filename, *args), **kwargs)
|
|
258
|
+
self.run_args = run_args
|
|
259
|
+
|
|
260
|
+
def _filter_objects(self, module: ModuleType) -> Dict[str, Any]:
|
|
261
|
+
return {k: v for k, v in module.__dict__.items() if isinstance(v, TaskTemplate)}
|
|
262
|
+
|
|
263
|
+
def list_commands(self, ctx):
|
|
264
|
+
common.initialize_config(
|
|
265
|
+
ctx,
|
|
266
|
+
self.run_args.project,
|
|
267
|
+
self.run_args.domain,
|
|
268
|
+
self.run_args.root_dir,
|
|
269
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
270
|
+
)
|
|
271
|
+
return super().list_commands(ctx)
|
|
272
|
+
|
|
273
|
+
def get_command(self, ctx, obj_name):
|
|
274
|
+
common.initialize_config(
|
|
275
|
+
ctx,
|
|
276
|
+
self.run_args.project,
|
|
277
|
+
self.run_args.domain,
|
|
278
|
+
self.run_args.root_dir,
|
|
279
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
280
|
+
)
|
|
281
|
+
return super().get_command(ctx, obj_name)
|
|
282
|
+
|
|
283
|
+
def _get_command_for_obj(self, ctx: click.Context, obj_name: str, obj: Any) -> click.Command:
|
|
284
|
+
obj = cast(TaskTemplate, obj)
|
|
285
|
+
return RunTaskCommand(
|
|
286
|
+
obj_name=obj_name,
|
|
287
|
+
obj=obj,
|
|
288
|
+
help=obj.docs.__help__str__() if obj.docs else None,
|
|
289
|
+
run_args=self.run_args,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class RunReferenceTaskCommand(click.RichCommand):
|
|
294
|
+
def __init__(self, task_name: str, run_args: RunArguments, version: str | None, *args, **kwargs):
|
|
295
|
+
self.task_name = task_name
|
|
296
|
+
self.run_args = run_args
|
|
297
|
+
self.version = version
|
|
298
|
+
|
|
299
|
+
super().__init__(*args, **kwargs)
|
|
300
|
+
|
|
301
|
+
def invoke(self, ctx: click.Context):
|
|
302
|
+
obj: CLIConfig = common.initialize_config(
|
|
303
|
+
ctx,
|
|
304
|
+
self.run_args.project,
|
|
305
|
+
self.run_args.domain,
|
|
306
|
+
self.run_args.root_dir,
|
|
307
|
+
tuple(self.run_args.image) or None,
|
|
308
|
+
not self.run_args.no_sync_local_sys_paths,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
async def _run():
|
|
312
|
+
import flyte.remote
|
|
313
|
+
|
|
314
|
+
task = flyte.remote.Task.get(self.task_name, version=self.version, auto_version="latest")
|
|
315
|
+
console = common.get_console()
|
|
316
|
+
|
|
317
|
+
r = await flyte.with_runcontext(
|
|
318
|
+
copy_style=self.run_args.copy_style,
|
|
319
|
+
mode="local" if self.run_args.local else "remote",
|
|
320
|
+
name=self.run_args.name,
|
|
321
|
+
).run.aio(task, **ctx.params)
|
|
322
|
+
if isinstance(r, Run) and r.action is not None:
|
|
323
|
+
console.print(
|
|
324
|
+
common.get_panel(
|
|
325
|
+
"Run",
|
|
326
|
+
f"[green bold]Created Run: {r.name} [/green bold] "
|
|
327
|
+
f"(Project: {r.action.action_id.run.project}, Domain: {r.action.action_id.run.domain})\n"
|
|
328
|
+
f"➡️ [blue bold][link={r.url}]{r.url}[/link][/blue bold]",
|
|
329
|
+
obj.output_format,
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
if self.run_args.follow:
|
|
333
|
+
console.print(
|
|
334
|
+
"[dim]Log streaming enabled, will wait for task to start running "
|
|
335
|
+
"and log stream to be available[/dim]"
|
|
336
|
+
)
|
|
337
|
+
await r.show_logs.aio(max_lines=30, show_ts=True, raw=False)
|
|
338
|
+
|
|
339
|
+
asyncio.run(_run())
|
|
340
|
+
|
|
341
|
+
def get_params(self, ctx: click.Context) -> List[click.Parameter]:
|
|
342
|
+
# Note this function may be called multiple times by click.
|
|
343
|
+
import flyte.remote
|
|
344
|
+
from flyte._internal.runtime.types_serde import transform_native_to_typed_interface
|
|
345
|
+
|
|
346
|
+
common.initialize_config(
|
|
347
|
+
ctx,
|
|
348
|
+
self.run_args.project,
|
|
349
|
+
self.run_args.domain,
|
|
350
|
+
sync_local_sys_paths=not self.run_args.no_sync_local_sys_paths,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
task = flyte.remote.Task.get(self.task_name, auto_version="latest")
|
|
354
|
+
task_details = task.fetch()
|
|
355
|
+
|
|
356
|
+
interface = transform_native_to_typed_interface(task_details.interface)
|
|
357
|
+
if interface is None:
|
|
358
|
+
return super().get_params(ctx)
|
|
359
|
+
inputs_interface = task_details.interface.inputs
|
|
360
|
+
|
|
361
|
+
params: List[click.Parameter] = []
|
|
362
|
+
for name, var in interface.inputs.variables.items():
|
|
363
|
+
default_val = None
|
|
364
|
+
if inputs_interface[name][1] is not inspect._empty:
|
|
365
|
+
default_val = inputs_interface[name][1]
|
|
366
|
+
params.append(to_click_option(name, var, inputs_interface[name][0], default_val))
|
|
367
|
+
|
|
368
|
+
self.params = params
|
|
369
|
+
return super().get_params(ctx)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class ReferenceEnvGroup(common.GroupBase):
|
|
373
|
+
def __init__(self, name: str, *args, run_args, env: str, **kwargs):
|
|
374
|
+
super().__init__(*args, **kwargs)
|
|
375
|
+
self.name = name
|
|
376
|
+
self.env = env
|
|
377
|
+
self.run_args = run_args
|
|
378
|
+
|
|
379
|
+
def list_commands(self, ctx):
|
|
380
|
+
return _list_tasks(ctx, self.run_args.project, self.run_args.domain, by_task_env=self.env)
|
|
381
|
+
|
|
382
|
+
def get_command(self, ctx, name):
|
|
383
|
+
return RunReferenceTaskCommand(
|
|
384
|
+
task_name=name,
|
|
385
|
+
run_args=self.run_args,
|
|
386
|
+
name=name,
|
|
387
|
+
version=None,
|
|
388
|
+
help=f"Run deployed task '{name}' from the Flyte backend",
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class ReferenceTaskGroup(common.GroupBase):
|
|
393
|
+
"""
|
|
394
|
+
Group that creates a command for each reference task in the current directory that is not __init__.py.
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
def __init__(self, name: str, *args, run_args, tasks: list[str] | None = None, **kwargs):
|
|
398
|
+
super().__init__(*args, **kwargs)
|
|
399
|
+
self.name = name
|
|
400
|
+
self.run_args = run_args
|
|
401
|
+
|
|
402
|
+
def list_commands(self, ctx):
|
|
403
|
+
# list envs of all reference tasks
|
|
404
|
+
envs = []
|
|
405
|
+
for task in _list_tasks(ctx, self.run_args.project, self.run_args.domain):
|
|
406
|
+
env = task.split(".")[0]
|
|
407
|
+
if env not in envs:
|
|
408
|
+
envs.append(env)
|
|
409
|
+
return envs
|
|
410
|
+
|
|
411
|
+
@staticmethod
|
|
412
|
+
def _parse_task_name(task_name: str) -> tuple[str, str | None, str | None]:
|
|
413
|
+
import re
|
|
414
|
+
|
|
415
|
+
pattern = r"^([^.:]+)(?:\.([^:]+))?(?::(.+))?$"
|
|
416
|
+
match = re.match(pattern, task_name)
|
|
417
|
+
if not match:
|
|
418
|
+
raise click.BadParameter(f"Invalid task name format: {task_name}")
|
|
419
|
+
return match.group(1), match.group(2), match.group(3)
|
|
420
|
+
|
|
421
|
+
def _env_is_task(self, ctx: click.Context, env: str) -> bool:
|
|
422
|
+
# check if the env name is the full task name, since sometimes task
|
|
423
|
+
# names don't have an environment prefix
|
|
424
|
+
tasks = [*_list_tasks(ctx, self.run_args.project, self.run_args.domain, by_task_name=env)]
|
|
425
|
+
return len(tasks) > 0
|
|
426
|
+
|
|
427
|
+
def get_command(self, ctx, name):
|
|
428
|
+
env, task, version = self._parse_task_name(name)
|
|
429
|
+
match env, task, version:
|
|
430
|
+
case env, None, None:
|
|
431
|
+
if self._env_is_task(ctx, env):
|
|
432
|
+
# this handles cases where task names do not have a environment prefix
|
|
433
|
+
task_name = env
|
|
434
|
+
return RunReferenceTaskCommand(
|
|
435
|
+
task_name=task_name,
|
|
436
|
+
run_args=self.run_args,
|
|
437
|
+
name=task_name,
|
|
438
|
+
version=None,
|
|
439
|
+
help=f"Run reference task `{task_name}` from the Flyte backend",
|
|
440
|
+
)
|
|
441
|
+
else:
|
|
442
|
+
return ReferenceEnvGroup(
|
|
443
|
+
name=name,
|
|
444
|
+
run_args=self.run_args,
|
|
445
|
+
env=env,
|
|
446
|
+
help=f"Run reference tasks in the `{env}` environment from the Flyte backend",
|
|
447
|
+
)
|
|
448
|
+
case env, task, None:
|
|
449
|
+
task_name = f"{env}.{task}"
|
|
450
|
+
return RunReferenceTaskCommand(
|
|
451
|
+
task_name=task_name,
|
|
452
|
+
run_args=self.run_args,
|
|
453
|
+
name=task_name,
|
|
454
|
+
version=None,
|
|
455
|
+
help=f"Run reference task '{task_name}' from the Flyte backend",
|
|
456
|
+
)
|
|
457
|
+
case env, task, version:
|
|
458
|
+
task_name = f"{env}.{task}"
|
|
459
|
+
return RunReferenceTaskCommand(
|
|
460
|
+
task_name=task_name,
|
|
461
|
+
run_args=self.run_args,
|
|
462
|
+
version=version,
|
|
463
|
+
name=f"{task_name}:{version}",
|
|
464
|
+
help=f"Run reference task '{task_name}' from the Flyte backend",
|
|
465
|
+
)
|
|
466
|
+
case _:
|
|
467
|
+
raise click.BadParameter(f"Invalid task name format: {task_name}")
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
class TaskFiles(common.FileGroup):
|
|
471
|
+
"""
|
|
472
|
+
Group that creates a command for each file in the current directory that is not __init__.py.
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
common_options_enabled = False
|
|
476
|
+
|
|
477
|
+
def __init__(
|
|
478
|
+
self,
|
|
479
|
+
*args,
|
|
480
|
+
directory: Path | None = None,
|
|
481
|
+
**kwargs,
|
|
482
|
+
):
|
|
483
|
+
if "params" not in kwargs:
|
|
484
|
+
kwargs["params"] = []
|
|
485
|
+
kwargs["params"].extend(RunArguments.options())
|
|
486
|
+
super().__init__(*args, directory=directory, **kwargs)
|
|
487
|
+
|
|
488
|
+
def list_commands(self, ctx):
|
|
489
|
+
v = [
|
|
490
|
+
RUN_REMOTE_CMD,
|
|
491
|
+
*super().list_commands(ctx),
|
|
492
|
+
]
|
|
493
|
+
return v
|
|
494
|
+
|
|
495
|
+
def get_command(self, ctx, cmd_name):
|
|
496
|
+
run_args = RunArguments.from_dict(ctx.params)
|
|
497
|
+
if cmd_name == RUN_REMOTE_CMD:
|
|
498
|
+
return ReferenceTaskGroup(
|
|
499
|
+
name=cmd_name,
|
|
500
|
+
run_args=run_args,
|
|
501
|
+
help="Run reference task from the Flyte backend",
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
fp = Path(cmd_name)
|
|
505
|
+
if not fp.exists():
|
|
506
|
+
raise click.BadParameter(f"File {cmd_name} does not exist")
|
|
507
|
+
if fp.is_dir():
|
|
508
|
+
return TaskFiles(
|
|
509
|
+
directory=fp,
|
|
510
|
+
help=f"Run `*.py` file inside the {fp} directory",
|
|
511
|
+
)
|
|
512
|
+
return TaskPerFileGroup(
|
|
513
|
+
filename=fp,
|
|
514
|
+
run_args=run_args,
|
|
515
|
+
name=cmd_name,
|
|
516
|
+
help=f"Run functions decorated with `env.task` in {cmd_name}",
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
run = TaskFiles(
|
|
521
|
+
name="run",
|
|
522
|
+
help=f"""
|
|
523
|
+
Run a task from a python file or deployed task.
|
|
524
|
+
|
|
525
|
+
To run a remote task that already exists in Flyte, use the {RUN_REMOTE_CMD} command:
|
|
526
|
+
|
|
527
|
+
Example usage:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
flyte run --project my-project --domain development hello.py my_task --arg1 value1 --arg2 value2
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
Arguments to the run command are provided right after the `run` command and before the file name.
|
|
534
|
+
For example, the command above specifies the project and domain.
|
|
535
|
+
|
|
536
|
+
To run a task locally, use the `--local` flag. This will run the task in the local environment instead of the remote
|
|
537
|
+
Flyte environment:
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
flyte run --local hello.py my_task --arg1 value1 --arg2 value2
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
You can provide image mappings with `--image` flag. This allows you to specify
|
|
544
|
+
the image URI for the task environment during CLI execution without changing
|
|
545
|
+
the code. Any images defined with `Image.from_ref_name("name")` will resolve to the
|
|
546
|
+
corresponding URIs you specify here.
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
flyte run hello.py my_task --image my_image=ghcr.io/myorg/my-image:v1.0
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
If the image name is not provided, it is regarded as a default image and will
|
|
553
|
+
be used when no image is specified in TaskEnvironment:
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
flyte run hello.py my_task --image ghcr.io/myorg/default-image:latest
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
You can specify multiple image arguments:
|
|
560
|
+
|
|
561
|
+
```bash
|
|
562
|
+
flyte run hello.py my_task --image ghcr.io/org/default:latest --image gpu=ghcr.io/org/gpu:v2.0
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
To run tasks that you've already deployed to Flyte, use the {RUN_REMOTE_CMD} command:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
flyte run {RUN_REMOTE_CMD} my_env.my_task --arg1 value1 --arg2 value2
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
To run a specific version of a deployed task, use the `env.task:version` syntax:
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
flyte run {RUN_REMOTE_CMD} my_env.my_task:xyz123 --arg1 value1 --arg2 value2
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
You can specify the `--config` flag to point to a specific Flyte cluster:
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
flyte run --config my-config.yaml {RUN_REMOTE_CMD} ...
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
You can discover what deployed tasks are available by running:
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
flyte run {RUN_REMOTE_CMD}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Other arguments to the run command are listed below.
|
|
590
|
+
|
|
591
|
+
Arguments for the task itself are provided after the task name and can be retrieved using `--help`. For example:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
flyte run hello.py my_task --help
|
|
595
|
+
```
|
|
596
|
+
""",
|
|
597
|
+
)
|
flyte/cli/_serve.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@click.group("serve")
|
|
7
|
+
@click.pass_context
|
|
8
|
+
def serve(_: click.Context):
|
|
9
|
+
"""
|
|
10
|
+
Start the specific service. For example:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
flyte serve connector
|
|
14
|
+
```
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@serve.command()
|
|
19
|
+
@click.option(
|
|
20
|
+
"--port",
|
|
21
|
+
default="8000",
|
|
22
|
+
is_flag=False,
|
|
23
|
+
type=int,
|
|
24
|
+
help="Grpc port for the connector service",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--prometheus_port",
|
|
28
|
+
default="9090",
|
|
29
|
+
is_flag=False,
|
|
30
|
+
type=int,
|
|
31
|
+
help="Prometheus port for the connector service",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--worker",
|
|
35
|
+
default="10",
|
|
36
|
+
is_flag=False,
|
|
37
|
+
type=int,
|
|
38
|
+
help="Number of workers for the grpc server",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--timeout",
|
|
42
|
+
default=None,
|
|
43
|
+
is_flag=False,
|
|
44
|
+
type=int,
|
|
45
|
+
help="It will wait for the specified number of seconds before shutting down grpc server. It should only be used "
|
|
46
|
+
"for testing.",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"--modules",
|
|
50
|
+
required=False,
|
|
51
|
+
multiple=True,
|
|
52
|
+
type=str,
|
|
53
|
+
help="List of additional files or module that defines the connector",
|
|
54
|
+
)
|
|
55
|
+
@click.pass_context
|
|
56
|
+
def connector(
|
|
57
|
+
_: click.Context, port: int, prometheus_port: int, worker: int, timeout: int | None, modules: List[str] | None
|
|
58
|
+
):
|
|
59
|
+
"""
|
|
60
|
+
Start a grpc server for the connector service.
|
|
61
|
+
"""
|
|
62
|
+
from flyte.connectors import ConnectorService
|
|
63
|
+
|
|
64
|
+
ConnectorService.run(port, prometheus_port, worker, timeout, modules)
|
flyte/cli/_update.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import rich_click as click
|
|
2
|
+
|
|
3
|
+
import flyte.remote as remote
|
|
4
|
+
|
|
5
|
+
from . import _common as common
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group(name="update")
|
|
9
|
+
def update():
|
|
10
|
+
"""
|
|
11
|
+
Update various flyte entities.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@update.command("trigger", cls=common.CommandBase)
|
|
16
|
+
@click.argument("name", type=str)
|
|
17
|
+
@click.argument("task_name", type=str)
|
|
18
|
+
@click.option("--activate/--deactivate", required=True, help="Activate or deactivate the trigger.")
|
|
19
|
+
@click.pass_obj
|
|
20
|
+
def trigger(cfg: common.CLIConfig, name: str, task_name: str, activate: bool, project: str | None, domain: str | None):
|
|
21
|
+
"""
|
|
22
|
+
Update a trigger.
|
|
23
|
+
|
|
24
|
+
\b
|
|
25
|
+
Example usage:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
flyte update trigger <trigger_name> <task_name> --activate | --deactivate
|
|
29
|
+
[--project <project_name> --domain <domain_name>]
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
cfg.init(project, domain)
|
|
33
|
+
console = common.get_console()
|
|
34
|
+
to_state = "active" if activate else "deactivate"
|
|
35
|
+
with console.status(f"Updating trigger {name} for task {task_name} to {to_state}..."):
|
|
36
|
+
remote.Trigger.update(name, task_name, activate)
|
|
37
|
+
console.print(f"Trigger updated and is set to [fuchsia]{to_state}[/fuchsia]")
|