flyte 2.0.0b22__py3-none-any.whl → 2.0.0b30__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.
- flyte/__init__.py +18 -2
- flyte/_bin/runtime.py +43 -5
- flyte/_cache/cache.py +4 -2
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +1 -1
- flyte/_code_bundle/_packaging.py +4 -4
- flyte/_code_bundle/_utils.py +14 -8
- flyte/_code_bundle/bundle.py +13 -5
- flyte/_constants.py +1 -0
- flyte/_context.py +4 -1
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +0 -1
- flyte/_debug/vscode.py +6 -1
- flyte/_deploy.py +223 -59
- flyte/_environment.py +5 -0
- flyte/_excepthook.py +1 -1
- flyte/_image.py +144 -82
- flyte/_initialize.py +95 -12
- flyte/_interface.py +2 -0
- flyte/_internal/controllers/_local_controller.py +65 -24
- flyte/_internal/controllers/_trace.py +1 -1
- flyte/_internal/controllers/remote/_action.py +13 -11
- flyte/_internal/controllers/remote/_client.py +1 -1
- flyte/_internal/controllers/remote/_controller.py +9 -4
- flyte/_internal/controllers/remote/_core.py +16 -16
- flyte/_internal/controllers/remote/_informer.py +4 -4
- flyte/_internal/controllers/remote/_service_protocol.py +7 -7
- flyte/_internal/imagebuild/docker_builder.py +139 -84
- flyte/_internal/imagebuild/image_builder.py +7 -13
- flyte/_internal/imagebuild/remote_builder.py +65 -13
- flyte/_internal/imagebuild/utils.py +51 -3
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +42 -20
- flyte/_internal/runtime/entrypoints.py +24 -1
- flyte/_internal/runtime/io.py +21 -8
- flyte/_internal/runtime/resources_serde.py +20 -6
- flyte/_internal/runtime/reuse.py +1 -1
- flyte/_internal/runtime/rusty.py +20 -5
- flyte/_internal/runtime/task_serde.py +33 -27
- flyte/_internal/runtime/taskrunner.py +10 -1
- flyte/_internal/runtime/trigger_serde.py +160 -0
- flyte/_internal/runtime/types_serde.py +1 -1
- flyte/_keyring/file.py +39 -9
- flyte/_logging.py +79 -12
- flyte/_map.py +31 -12
- flyte/_module.py +70 -0
- flyte/_pod.py +2 -2
- flyte/_resources.py +213 -31
- flyte/_run.py +107 -41
- flyte/_task.py +66 -10
- flyte/_task_environment.py +96 -24
- flyte/_task_plugins.py +4 -2
- flyte/_trigger.py +1000 -0
- flyte/_utils/__init__.py +2 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/module_loader.py +17 -2
- flyte/_version.py +3 -3
- flyte/cli/_abort.py +3 -3
- flyte/cli/_build.py +1 -3
- flyte/cli/_common.py +78 -7
- flyte/cli/_create.py +178 -3
- flyte/cli/_delete.py +23 -1
- flyte/cli/_deploy.py +49 -11
- flyte/cli/_get.py +79 -34
- flyte/cli/_params.py +8 -6
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_run.py +127 -11
- flyte/cli/_serve.py +64 -0
- flyte/cli/_update.py +37 -0
- flyte/cli/_user.py +17 -0
- flyte/cli/main.py +30 -4
- flyte/config/_config.py +2 -0
- flyte/config/_internal.py +1 -0
- flyte/config/_reader.py +3 -3
- 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 +10 -1
- flyte/extend.py +8 -1
- flyte/extras/_container.py +6 -1
- flyte/git/_config.py +11 -9
- flyte/io/__init__.py +2 -0
- flyte/io/_dataframe/__init__.py +2 -0
- flyte/io/_dataframe/basic_dfs.py +1 -1
- flyte/io/_dataframe/dataframe.py +12 -8
- flyte/io/_dir.py +551 -120
- flyte/io/_file.py +538 -141
- flyte/models.py +57 -12
- flyte/remote/__init__.py +6 -1
- flyte/remote/_action.py +18 -16
- flyte/remote/_client/_protocols.py +39 -4
- flyte/remote/_client/auth/_channel.py +10 -6
- flyte/remote/_client/controlplane.py +17 -5
- flyte/remote/_console.py +3 -2
- flyte/remote/_data.py +4 -3
- flyte/remote/_logs.py +3 -3
- flyte/remote/_run.py +47 -7
- flyte/remote/_secret.py +26 -17
- flyte/remote/_task.py +21 -9
- flyte/remote/_trigger.py +306 -0
- flyte/remote/_user.py +33 -0
- flyte/storage/__init__.py +6 -1
- flyte/storage/_parallel_reader.py +274 -0
- flyte/storage/_storage.py +185 -103
- flyte/types/__init__.py +16 -0
- flyte/types/_interface.py +2 -2
- flyte/types/_pickle.py +17 -4
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +26 -19
- flyte/types/_utils.py +1 -1
- {flyte-2.0.0b22.data → flyte-2.0.0b30.data}/scripts/runtime.py +43 -5
- {flyte-2.0.0b22.dist-info → flyte-2.0.0b30.dist-info}/METADATA +8 -1
- flyte-2.0.0b30.dist-info/RECORD +192 -0
- flyte/_protos/__init__.py +0 -0
- flyte/_protos/common/authorization_pb2.py +0 -66
- flyte/_protos/common/authorization_pb2.pyi +0 -108
- flyte/_protos/common/authorization_pb2_grpc.py +0 -4
- flyte/_protos/common/identifier_pb2.py +0 -99
- flyte/_protos/common/identifier_pb2.pyi +0 -120
- flyte/_protos/common/identifier_pb2_grpc.py +0 -4
- flyte/_protos/common/identity_pb2.py +0 -48
- flyte/_protos/common/identity_pb2.pyi +0 -72
- flyte/_protos/common/identity_pb2_grpc.py +0 -4
- flyte/_protos/common/list_pb2.py +0 -36
- flyte/_protos/common/list_pb2.pyi +0 -71
- flyte/_protos/common/list_pb2_grpc.py +0 -4
- flyte/_protos/common/policy_pb2.py +0 -37
- flyte/_protos/common/policy_pb2.pyi +0 -27
- flyte/_protos/common/policy_pb2_grpc.py +0 -4
- flyte/_protos/common/role_pb2.py +0 -37
- flyte/_protos/common/role_pb2.pyi +0 -53
- flyte/_protos/common/role_pb2_grpc.py +0 -4
- flyte/_protos/common/runtime_version_pb2.py +0 -28
- flyte/_protos/common/runtime_version_pb2.pyi +0 -24
- flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/definition_pb2.py +0 -60
- flyte/_protos/imagebuilder/definition_pb2.pyi +0 -153
- flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/payload_pb2.py +0 -32
- flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
- flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
- flyte/_protos/imagebuilder/service_pb2.py +0 -29
- flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
- flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
- flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
- flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/definition_pb2.py +0 -49
- flyte/_protos/secret/definition_pb2.pyi +0 -93
- flyte/_protos/secret/definition_pb2_grpc.py +0 -4
- flyte/_protos/secret/payload_pb2.py +0 -62
- flyte/_protos/secret/payload_pb2.pyi +0 -94
- flyte/_protos/secret/payload_pb2_grpc.py +0 -4
- flyte/_protos/secret/secret_pb2.py +0 -38
- flyte/_protos/secret/secret_pb2.pyi +0 -6
- flyte/_protos/secret/secret_pb2_grpc.py +0 -198
- flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
- flyte/_protos/validate/validate/validate_pb2.py +0 -76
- flyte/_protos/workflow/common_pb2.py +0 -27
- flyte/_protos/workflow/common_pb2.pyi +0 -14
- flyte/_protos/workflow/common_pb2_grpc.py +0 -4
- flyte/_protos/workflow/environment_pb2.py +0 -29
- flyte/_protos/workflow/environment_pb2.pyi +0 -12
- flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
- flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
- flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
- flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
- flyte/_protos/workflow/queue_service_pb2.py +0 -111
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -168
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -123
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -352
- flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
- flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
- flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
- flyte/_protos/workflow/run_service_pb2.py +0 -137
- flyte/_protos/workflow/run_service_pb2.pyi +0 -185
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -446
- flyte/_protos/workflow/state_service_pb2.py +0 -67
- flyte/_protos/workflow/state_service_pb2.pyi +0 -76
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -82
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -88
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -60
- flyte/_protos/workflow/task_service_pb2.pyi +0 -59
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
- flyte-2.0.0b22.dist-info/RECORD +0 -250
- {flyte-2.0.0b22.data → flyte-2.0.0b30.data}/scripts/debug.py +0 -0
- {flyte-2.0.0b22.dist-info → flyte-2.0.0b30.dist-info}/WHEEL +0 -0
- {flyte-2.0.0b22.dist-info → flyte-2.0.0b30.dist-info}/entry_points.txt +0 -0
- {flyte-2.0.0b22.dist-info → flyte-2.0.0b30.dist-info}/licenses/LICENSE +0 -0
- {flyte-2.0.0b22.dist-info → flyte-2.0.0b30.dist-info}/top_level.txt +0 -0
flyte/cli/_delete.py
CHANGED
|
@@ -20,4 +20,26 @@ def secret(cfg: common.CLIConfig, name: str, project: str | None = None, domain:
|
|
|
20
20
|
from flyte.remote import Secret
|
|
21
21
|
|
|
22
22
|
cfg.init(project, domain)
|
|
23
|
-
|
|
23
|
+
console = common.get_console()
|
|
24
|
+
with console.status(f"Deleting secret {name}..."):
|
|
25
|
+
Secret.delete(name=name)
|
|
26
|
+
console.print(f"Successfully deleted secret {name}.")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@delete.command(cls=common.CommandBase)
|
|
30
|
+
@click.argument("name", type=str, required=True)
|
|
31
|
+
@click.argument("task-name", type=str, required=True)
|
|
32
|
+
@click.pass_obj
|
|
33
|
+
def trigger(cfg: common.CLIConfig, name: str, task_name: str, project: str | None = None, domain: str | None = None):
|
|
34
|
+
"""
|
|
35
|
+
Delete a trigger. The name of the trigger is required.
|
|
36
|
+
"""
|
|
37
|
+
from flyte.remote import Trigger
|
|
38
|
+
|
|
39
|
+
cfg.init(project, domain)
|
|
40
|
+
console = common.get_console()
|
|
41
|
+
|
|
42
|
+
with console.status(f"Deleting trigger {name}..."):
|
|
43
|
+
Trigger.delete(name=name, task_name=task_name)
|
|
44
|
+
|
|
45
|
+
console.log(f"[green]Successfully deleted trigger {name}[/green]")
|
flyte/cli/_deploy.py
CHANGED
|
@@ -7,8 +7,8 @@ from typing import Any, Dict, List, cast, get_args
|
|
|
7
7
|
import rich_click as click
|
|
8
8
|
|
|
9
9
|
import flyte
|
|
10
|
+
from flyte._code_bundle._utils import CopyFiles
|
|
10
11
|
|
|
11
|
-
from .._code_bundle._utils import CopyFiles
|
|
12
12
|
from . import _common as common
|
|
13
13
|
from ._common import CLIConfig
|
|
14
14
|
|
|
@@ -83,6 +83,19 @@ class DeployArguments:
|
|
|
83
83
|
)
|
|
84
84
|
},
|
|
85
85
|
)
|
|
86
|
+
no_sync_local_sys_paths: bool = field(
|
|
87
|
+
default=True,
|
|
88
|
+
metadata={
|
|
89
|
+
"click.option": click.Option(
|
|
90
|
+
["--no-sync-local-sys-paths"],
|
|
91
|
+
is_flag=True,
|
|
92
|
+
flag_value=True,
|
|
93
|
+
default=False,
|
|
94
|
+
help="Disable synchronization of local sys.path entries under the root directory "
|
|
95
|
+
"to the remote container.",
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
)
|
|
86
99
|
|
|
87
100
|
@classmethod
|
|
88
101
|
def from_dict(cls, d: Dict[str, Any]) -> "DeployArguments":
|
|
@@ -104,12 +117,15 @@ class DeployEnvCommand(click.RichCommand):
|
|
|
104
117
|
super().__init__(*args, **kwargs)
|
|
105
118
|
|
|
106
119
|
def invoke(self, ctx: click.Context):
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
console = Console()
|
|
120
|
+
console = common.get_console()
|
|
110
121
|
console.print(f"Deploying root - environment: {self.env_name}")
|
|
111
122
|
obj: CLIConfig = ctx.obj
|
|
112
|
-
obj.init(
|
|
123
|
+
obj.init(
|
|
124
|
+
self.deploy_args.project,
|
|
125
|
+
self.deploy_args.domain,
|
|
126
|
+
root_dir=self.deploy_args.root_dir,
|
|
127
|
+
sync_local_sys_paths=not self.deploy_args.no_sync_local_sys_paths,
|
|
128
|
+
)
|
|
113
129
|
with console.status("Deploying...", spinner="dots"):
|
|
114
130
|
deployment = flyte.deploy(
|
|
115
131
|
self.env,
|
|
@@ -119,7 +135,7 @@ class DeployEnvCommand(click.RichCommand):
|
|
|
119
135
|
)
|
|
120
136
|
|
|
121
137
|
console.print(common.format("Environments", deployment[0].env_repr(), obj.output_format))
|
|
122
|
-
console.print(common.format("Tasks", deployment[0].
|
|
138
|
+
console.print(common.format("Tasks", deployment[0].table_repr(), obj.output_format))
|
|
123
139
|
|
|
124
140
|
|
|
125
141
|
class DeployEnvRecursiveCommand(click.Command):
|
|
@@ -135,13 +151,11 @@ class DeployEnvRecursiveCommand(click.Command):
|
|
|
135
151
|
super().__init__(*args, **kwargs)
|
|
136
152
|
|
|
137
153
|
def invoke(self, ctx: click.Context):
|
|
138
|
-
from rich.console import Console
|
|
139
|
-
|
|
140
154
|
from flyte._environment import list_loaded_environments
|
|
141
155
|
from flyte._utils import load_python_modules
|
|
142
156
|
|
|
143
|
-
console = Console()
|
|
144
157
|
obj: CLIConfig = ctx.obj
|
|
158
|
+
console = common.get_console()
|
|
145
159
|
|
|
146
160
|
# Load all python modules
|
|
147
161
|
loaded_modules, failed_paths = load_python_modules(self.path, self.deploy_args.recursive)
|
|
@@ -165,7 +179,11 @@ class DeployEnvRecursiveCommand(click.Command):
|
|
|
165
179
|
f"Failed to load {len(failed_paths)} files. Use --ignore-load-errors to ignore these errors."
|
|
166
180
|
)
|
|
167
181
|
# Now start connection and deploy all environments
|
|
168
|
-
obj.init(
|
|
182
|
+
obj.init(
|
|
183
|
+
self.deploy_args.project,
|
|
184
|
+
self.deploy_args.domain,
|
|
185
|
+
sync_local_sys_paths=not self.deploy_args.no_sync_local_sys_paths,
|
|
186
|
+
)
|
|
169
187
|
with console.status("Deploying...", spinner="dots"):
|
|
170
188
|
deployments = flyte.deploy(
|
|
171
189
|
*all_envs,
|
|
@@ -177,7 +195,7 @@ class DeployEnvRecursiveCommand(click.Command):
|
|
|
177
195
|
console.print(
|
|
178
196
|
common.format("Environments", [env for d in deployments for env in d.env_repr()], obj.output_format)
|
|
179
197
|
)
|
|
180
|
-
console.print(common.format("Tasks", [task for d in deployments for task in d.
|
|
198
|
+
console.print(common.format("Tasks", [task for d in deployments for task in d.table_repr()], obj.output_format))
|
|
181
199
|
|
|
182
200
|
|
|
183
201
|
class EnvPerFileGroup(common.ObjectsPerFileGroup):
|
|
@@ -193,6 +211,26 @@ class EnvPerFileGroup(common.ObjectsPerFileGroup):
|
|
|
193
211
|
def _filter_objects(self, module: ModuleType) -> Dict[str, Any]:
|
|
194
212
|
return {k: v for k, v in module.__dict__.items() if isinstance(v, flyte.Environment)}
|
|
195
213
|
|
|
214
|
+
def list_commands(self, ctx):
|
|
215
|
+
common.initialize_config(
|
|
216
|
+
ctx,
|
|
217
|
+
self.deploy_args.project,
|
|
218
|
+
self.deploy_args.domain,
|
|
219
|
+
self.deploy_args.root_dir,
|
|
220
|
+
sync_local_sys_paths=not self.deploy_args.no_sync_local_sys_paths,
|
|
221
|
+
)
|
|
222
|
+
return super().list_commands(ctx)
|
|
223
|
+
|
|
224
|
+
def get_command(self, ctx, obj_name):
|
|
225
|
+
common.initialize_config(
|
|
226
|
+
ctx,
|
|
227
|
+
self.deploy_args.project,
|
|
228
|
+
self.deploy_args.domain,
|
|
229
|
+
self.deploy_args.root_dir,
|
|
230
|
+
sync_local_sys_paths=not self.deploy_args.no_sync_local_sys_paths,
|
|
231
|
+
)
|
|
232
|
+
return super().get_command(ctx, obj_name)
|
|
233
|
+
|
|
196
234
|
def _get_command_for_obj(self, ctx: click.Context, obj_name: str, obj: Any) -> click.Command:
|
|
197
235
|
obj = cast(flyte.Environment, obj)
|
|
198
236
|
return DeployEnvCommand(
|
flyte/cli/_get.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from typing import Tuple, Union
|
|
2
|
+
from typing import Tuple, Union, get_args
|
|
3
3
|
|
|
4
4
|
import rich_click as click
|
|
5
|
-
from rich.console import Console
|
|
6
5
|
from rich.pretty import pretty_repr
|
|
7
6
|
|
|
7
|
+
import flyte.remote as remote
|
|
8
|
+
|
|
8
9
|
from . import _common as common
|
|
9
10
|
|
|
10
11
|
|
|
@@ -38,20 +39,24 @@ def project(cfg: common.CLIConfig, name: str | None = None):
|
|
|
38
39
|
"""
|
|
39
40
|
Get a list of all projects, or details of a specific project by name.
|
|
40
41
|
"""
|
|
41
|
-
from flyte.remote import Project
|
|
42
|
-
|
|
43
42
|
cfg.init()
|
|
44
43
|
|
|
45
|
-
console =
|
|
44
|
+
console = common.get_console()
|
|
46
45
|
if name:
|
|
47
|
-
console.print(pretty_repr(Project.get(name)))
|
|
46
|
+
console.print(pretty_repr(remote.Project.get(name)))
|
|
48
47
|
else:
|
|
49
|
-
console.print(common.format("Projects", Project.listall(), cfg.output_format))
|
|
48
|
+
console.print(common.format("Projects", remote.Project.listall(), cfg.output_format))
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
@get.command(cls=common.CommandBase)
|
|
53
52
|
@click.argument("name", type=str, required=False)
|
|
54
53
|
@click.option("--limit", type=int, default=100, help="Limit the number of runs to fetch when listing.")
|
|
54
|
+
@click.option(
|
|
55
|
+
"--in-phase", # multiple=True, TODO support multiple phases once values in works
|
|
56
|
+
type=click.Choice(get_args(remote.Phase), case_sensitive=False),
|
|
57
|
+
help="Filter runs by their status.",
|
|
58
|
+
)
|
|
59
|
+
@click.option("--only-mine", is_flag=True, default=False, help="Show only runs created by the current user (you).")
|
|
55
60
|
@click.pass_obj
|
|
56
61
|
def run(
|
|
57
62
|
cfg: common.CLIConfig,
|
|
@@ -59,6 +64,8 @@ def run(
|
|
|
59
64
|
project: str | None = None,
|
|
60
65
|
domain: str | None = None,
|
|
61
66
|
limit: int = 100,
|
|
67
|
+
in_phase: str | Tuple[str, ...] | None = None,
|
|
68
|
+
only_mine: bool = False,
|
|
62
69
|
):
|
|
63
70
|
"""
|
|
64
71
|
Get a list of all runs, or details of a specific run by name.
|
|
@@ -67,16 +74,29 @@ def run(
|
|
|
67
74
|
|
|
68
75
|
If you want to see the actions for a run, use `get action <run_name>`.
|
|
69
76
|
"""
|
|
70
|
-
from flyte.remote import Run, RunDetails
|
|
71
77
|
|
|
72
78
|
cfg.init(project=project, domain=domain)
|
|
73
79
|
|
|
74
|
-
console =
|
|
80
|
+
console = common.get_console()
|
|
75
81
|
if name:
|
|
76
|
-
details = RunDetails.get(name=name)
|
|
82
|
+
details = remote.RunDetails.get(name=name)
|
|
77
83
|
console.print(common.format(f"Run {name}", [details], "json"))
|
|
78
84
|
else:
|
|
79
|
-
|
|
85
|
+
if in_phase and isinstance(in_phase, str):
|
|
86
|
+
in_phase = (in_phase,)
|
|
87
|
+
|
|
88
|
+
subject = None
|
|
89
|
+
if only_mine:
|
|
90
|
+
usr = remote.User.get()
|
|
91
|
+
subject = usr.subject()
|
|
92
|
+
|
|
93
|
+
console.print(
|
|
94
|
+
common.format(
|
|
95
|
+
"Runs",
|
|
96
|
+
remote.Run.listall(limit=limit, in_phase=in_phase, created_by_subject=subject),
|
|
97
|
+
cfg.output_format,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
80
100
|
|
|
81
101
|
|
|
82
102
|
@get.command(cls=common.CommandBase)
|
|
@@ -97,22 +117,22 @@ def task(
|
|
|
97
117
|
|
|
98
118
|
Currently, both `name` and `version` are required to get a specific task.
|
|
99
119
|
"""
|
|
100
|
-
from flyte.remote import Task
|
|
101
|
-
|
|
102
120
|
cfg.init(project=project, domain=domain)
|
|
103
121
|
|
|
104
|
-
console =
|
|
122
|
+
console = common.get_console()
|
|
105
123
|
if name:
|
|
106
124
|
if version:
|
|
107
|
-
v = Task.get(name=name, version=version)
|
|
125
|
+
v = remote.Task.get(name=name, version=version)
|
|
108
126
|
if v is None:
|
|
109
127
|
raise click.BadParameter(f"Task {name} not found.")
|
|
110
128
|
t = v.fetch()
|
|
111
129
|
console.print(common.format(f"Task {name}", [t], "json"))
|
|
112
130
|
else:
|
|
113
|
-
console.print(
|
|
131
|
+
console.print(
|
|
132
|
+
common.format("Tasks", remote.Task.listall(by_task_name=name, limit=limit), cfg.output_format)
|
|
133
|
+
)
|
|
114
134
|
else:
|
|
115
|
-
console.print(common.format("Tasks", Task.listall(limit=limit), cfg.output_format))
|
|
135
|
+
console.print(common.format("Tasks", remote.Task.listall(limit=limit), cfg.output_format))
|
|
116
136
|
|
|
117
137
|
|
|
118
138
|
@get.command(cls=common.CommandBase)
|
|
@@ -129,11 +149,9 @@ def action(
|
|
|
129
149
|
"""
|
|
130
150
|
Get all actions for a run or details for a specific action.
|
|
131
151
|
"""
|
|
132
|
-
import flyte.remote as remote
|
|
133
|
-
|
|
134
152
|
cfg.init(project=project, domain=domain)
|
|
135
153
|
|
|
136
|
-
console =
|
|
154
|
+
console = common.get_console()
|
|
137
155
|
if action_name:
|
|
138
156
|
console.print(
|
|
139
157
|
common.format(
|
|
@@ -196,8 +214,6 @@ def logs(
|
|
|
196
214
|
$ flyte get logs my_run my_action --pretty --lines 50
|
|
197
215
|
```
|
|
198
216
|
"""
|
|
199
|
-
import flyte.remote as remote
|
|
200
|
-
|
|
201
217
|
cfg.init(project=project, domain=domain)
|
|
202
218
|
|
|
203
219
|
async def _run_log_view(_obj):
|
|
@@ -230,11 +246,13 @@ def secret(
|
|
|
230
246
|
"""
|
|
231
247
|
Get a list of all secrets, or details of a specific secret by name.
|
|
232
248
|
"""
|
|
233
|
-
|
|
234
|
-
|
|
249
|
+
if project is None:
|
|
250
|
+
project = ""
|
|
251
|
+
if domain is None:
|
|
252
|
+
domain = ""
|
|
235
253
|
cfg.init(project=project, domain=domain)
|
|
236
254
|
|
|
237
|
-
console =
|
|
255
|
+
console = common.get_console()
|
|
238
256
|
if name:
|
|
239
257
|
console.print(common.format("Secret", [remote.Secret.get(name)], "json"))
|
|
240
258
|
else:
|
|
@@ -275,26 +293,23 @@ def io(
|
|
|
275
293
|
if inputs_only and outputs_only:
|
|
276
294
|
raise click.BadParameter("Cannot use both --inputs-only and --outputs-only")
|
|
277
295
|
|
|
278
|
-
import flyte.remote as remote
|
|
279
|
-
from flyte.remote import ActionDetails, ActionInputs, ActionOutputs
|
|
280
|
-
|
|
281
296
|
cfg.init(project=project, domain=domain)
|
|
282
|
-
console =
|
|
297
|
+
console = common.get_console()
|
|
283
298
|
if action_name:
|
|
284
|
-
obj = ActionDetails.get(run_name=run_name, name=action_name)
|
|
299
|
+
obj = remote.ActionDetails.get(run_name=run_name, name=action_name)
|
|
285
300
|
else:
|
|
286
301
|
obj = remote.RunDetails.get(run_name)
|
|
287
302
|
|
|
288
303
|
async def _get_io(
|
|
289
|
-
details: Union[remote.RunDetails, ActionDetails],
|
|
290
|
-
) -> Tuple[ActionInputs | None, ActionOutputs | None | str]:
|
|
304
|
+
details: Union[remote.RunDetails, remote.ActionDetails],
|
|
305
|
+
) -> Tuple[remote.ActionInputs | None, remote.ActionOutputs | None | str]:
|
|
291
306
|
if inputs_only or outputs_only:
|
|
292
307
|
if inputs_only:
|
|
293
308
|
return await details.inputs(), None
|
|
294
309
|
elif outputs_only:
|
|
295
310
|
return None, await details.outputs()
|
|
296
311
|
inputs = await details.inputs()
|
|
297
|
-
outputs: ActionOutputs | None | str = None
|
|
312
|
+
outputs: remote.ActionOutputs | None | str = None
|
|
298
313
|
try:
|
|
299
314
|
outputs = await details.outputs()
|
|
300
315
|
except Exception:
|
|
@@ -321,5 +336,35 @@ def config(cfg: common.CLIConfig):
|
|
|
321
336
|
|
|
322
337
|
The configuration will include the endpoint, organization, and other settings that are used by the CLI.
|
|
323
338
|
"""
|
|
324
|
-
console =
|
|
339
|
+
console = common.get_console()
|
|
325
340
|
console.print(cfg)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@get.command(cls=common.CommandBase)
|
|
344
|
+
@click.argument("task_name", type=str, required=False)
|
|
345
|
+
@click.argument("name", type=str, required=False)
|
|
346
|
+
@click.option("--limit", type=int, default=100, help="Limit the number of triggers to fetch.")
|
|
347
|
+
@click.pass_obj
|
|
348
|
+
def trigger(
|
|
349
|
+
cfg: common.CLIConfig,
|
|
350
|
+
task_name: str | None = None,
|
|
351
|
+
name: str | None = None,
|
|
352
|
+
limit: int = 100,
|
|
353
|
+
project: str | None = None,
|
|
354
|
+
domain: str | None = None,
|
|
355
|
+
):
|
|
356
|
+
"""
|
|
357
|
+
Get a list of all triggers, or details of a specific trigger by name.
|
|
358
|
+
"""
|
|
359
|
+
if name and not task_name:
|
|
360
|
+
raise click.BadParameter("If you provide a trigger name, you must also provide the task name.")
|
|
361
|
+
|
|
362
|
+
from flyte.remote import Trigger
|
|
363
|
+
|
|
364
|
+
cfg.init(project=project, domain=domain)
|
|
365
|
+
|
|
366
|
+
console = common.get_console()
|
|
367
|
+
if name:
|
|
368
|
+
console.print(pretty_repr(Trigger.get(name=name, task_name=task_name)))
|
|
369
|
+
else:
|
|
370
|
+
console.print(common.format("Triggers", Trigger.listall(task_name=task_name, limit=limit), cfg.output_format))
|
flyte/cli/_params.py
CHANGED
|
@@ -15,9 +15,9 @@ from typing import get_args
|
|
|
15
15
|
import rich_click as click
|
|
16
16
|
import yaml
|
|
17
17
|
from click import Parameter
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from
|
|
18
|
+
from flyteidl2.core.interface_pb2 import Variable
|
|
19
|
+
from flyteidl2.core.literals_pb2 import Literal
|
|
20
|
+
from flyteidl2.core.types_pb2 import BlobType, LiteralType, SimpleType
|
|
21
21
|
from google.protobuf.json_format import MessageToDict
|
|
22
22
|
from mashumaro.codecs.json import JSONEncoder
|
|
23
23
|
|
|
@@ -505,7 +505,7 @@ def to_click_option(
|
|
|
505
505
|
This handles converting workflow input types to supported click parameters with callbacks to initialize
|
|
506
506
|
the input values to their expected types.
|
|
507
507
|
"""
|
|
508
|
-
from
|
|
508
|
+
from flyteidl2.core.types_pb2 import SimpleType
|
|
509
509
|
|
|
510
510
|
if input_name != input_name.lower():
|
|
511
511
|
# Click does not support uppercase option names: https://github.com/pallets/click/issues/837
|
|
@@ -531,17 +531,19 @@ def to_click_option(
|
|
|
531
531
|
if literal_var.type.metadata:
|
|
532
532
|
description_extra = f": {MessageToDict(literal_var.type.metadata)}"
|
|
533
533
|
|
|
534
|
-
# If a query has been specified, the input is never strictly required at this layer
|
|
535
534
|
required = False if default_val is not None else True
|
|
536
535
|
is_flag: typing.Optional[bool] = None
|
|
536
|
+
param_decls = [f"--{input_name}"]
|
|
537
537
|
if literal_converter.is_bool():
|
|
538
538
|
required = False
|
|
539
539
|
is_flag = True
|
|
540
|
+
if default_val is True:
|
|
541
|
+
param_decls = [f"--{input_name}/--no-{input_name}"]
|
|
540
542
|
if literal_converter.is_optional():
|
|
541
543
|
required = False
|
|
542
544
|
|
|
543
545
|
return click.Option(
|
|
544
|
-
param_decls=
|
|
546
|
+
param_decls=param_decls,
|
|
545
547
|
type=literal_converter.click_type,
|
|
546
548
|
is_flag=is_flag,
|
|
547
549
|
default=default_val,
|
flyte/cli/_plugins.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""CLI Plugin System for Flyte.
|
|
2
|
+
|
|
3
|
+
This module provides a plugin system that allows external packages to:
|
|
4
|
+
1. Register new top-level CLI commands (e.g., flyte my-command)
|
|
5
|
+
2. Register new subcommands in existing groups (e.g., flyte get my-object)
|
|
6
|
+
3. Modify behavior of existing commands via hooks
|
|
7
|
+
|
|
8
|
+
Plugins are discovered via Python entry points.
|
|
9
|
+
|
|
10
|
+
Entry Point Groups:
|
|
11
|
+
- flyte.plugins.cli.commands: Register new commands
|
|
12
|
+
- Entry point name "foo" -> flyte foo (top-level command)
|
|
13
|
+
- Entry point name "get.bar" -> flyte get bar (adds subcommand to get group)
|
|
14
|
+
- Note: At most one dot is supported. For nested groups, register the entire
|
|
15
|
+
group hierarchy as a top-level command (without dots).
|
|
16
|
+
|
|
17
|
+
- flyte.plugins.cli.hooks: Modify existing commands
|
|
18
|
+
- Entry point name "run" -> modifies flyte run
|
|
19
|
+
- Entry point name "get.project" -> modifies flyte get project
|
|
20
|
+
- Note: At most one dot is supported.
|
|
21
|
+
|
|
22
|
+
Example Plugin Package:
|
|
23
|
+
# In your-plugin/pyproject.toml
|
|
24
|
+
[project.entry-points."flyte.plugins.cli.commands"]
|
|
25
|
+
my-command = "your_plugin.cli:my_command"
|
|
26
|
+
get.my-object = "your_plugin.cli:get_my_object"
|
|
27
|
+
|
|
28
|
+
[project.entry-points."flyte.plugins.cli.hooks"]
|
|
29
|
+
run = "your_plugin.hooks:modify_run"
|
|
30
|
+
|
|
31
|
+
# In your-plugin/your_plugin/cli.py
|
|
32
|
+
import rich_click as click
|
|
33
|
+
|
|
34
|
+
@click.command()
|
|
35
|
+
def my_command():
|
|
36
|
+
'''My custom top-level command.'''
|
|
37
|
+
click.echo("Hello from plugin!")
|
|
38
|
+
|
|
39
|
+
@click.command()
|
|
40
|
+
def get_my_object():
|
|
41
|
+
'''Get my custom object.'''
|
|
42
|
+
click.echo("Getting my object...")
|
|
43
|
+
|
|
44
|
+
# In your-plugin/your_plugin/hooks.py
|
|
45
|
+
def modify_run(command):
|
|
46
|
+
'''Add behavior to flyte run command.'''
|
|
47
|
+
# Wrap invoke() instead of callback to ensure Click's full machinery runs
|
|
48
|
+
original_invoke = command.invoke
|
|
49
|
+
|
|
50
|
+
def wrapper(ctx):
|
|
51
|
+
# Do something before
|
|
52
|
+
click.echo("Plugin: Starting task...")
|
|
53
|
+
|
|
54
|
+
result = original_invoke(ctx)
|
|
55
|
+
|
|
56
|
+
# Do something after
|
|
57
|
+
click.echo("Plugin: Task completed!")
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
command.invoke = wrapper
|
|
61
|
+
return command
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
from importlib.metadata import entry_points
|
|
65
|
+
from typing import Callable
|
|
66
|
+
|
|
67
|
+
import rich_click as click
|
|
68
|
+
|
|
69
|
+
from flyte._logging import logger
|
|
70
|
+
|
|
71
|
+
# Type alias for command hooks
|
|
72
|
+
CommandHook = Callable[[click.Command], click.Command]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def discover_and_register_plugins(root_group: click.Group):
|
|
76
|
+
"""
|
|
77
|
+
Discover all CLI plugins from installed packages and register them.
|
|
78
|
+
|
|
79
|
+
This function:
|
|
80
|
+
1. Discovers command plugins and adds them to the CLI
|
|
81
|
+
2. Discovers hook plugins and applies them to existing commands
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
root_group: The root Click command group (main CLI group)
|
|
85
|
+
"""
|
|
86
|
+
_load_command_plugins(root_group)
|
|
87
|
+
_load_hook_plugins(root_group)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_command_plugins(root_group: click.Group):
|
|
91
|
+
"""Load and register command plugins."""
|
|
92
|
+
for ep in entry_points(group="flyte.plugins.cli.commands"):
|
|
93
|
+
try:
|
|
94
|
+
command = ep.load()
|
|
95
|
+
if not isinstance(command, click.Command):
|
|
96
|
+
logger.warning(f"Plugin {ep.name} did not return a click.Command, got {type(command).__name__}")
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
# Check if this is a subcommand (contains dot notation)
|
|
100
|
+
if "." in ep.name:
|
|
101
|
+
group_name, command_name = ep.name.split(".", 1)
|
|
102
|
+
|
|
103
|
+
# Validate: only support one level of nesting (group.command)
|
|
104
|
+
if "." in command_name:
|
|
105
|
+
logger.error(
|
|
106
|
+
f"Plugin {ep.name} uses multiple dots, which is not supported. "
|
|
107
|
+
f"Use at most one dot (e.g., 'group.command'). "
|
|
108
|
+
f"For nested groups, register the entire group hierarchy as a top-level command."
|
|
109
|
+
)
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
_add_subcommand_to_group(root_group, group_name, command_name, command)
|
|
113
|
+
else:
|
|
114
|
+
# Top-level command
|
|
115
|
+
root_group.add_command(command, name=ep.name)
|
|
116
|
+
logger.info(f"Registered plugin command: flyte {ep.name}")
|
|
117
|
+
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Failed to load plugin command {ep.name}: {e}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _load_hook_plugins(root_group: click.Group):
|
|
123
|
+
"""Load and apply hook plugins to existing commands."""
|
|
124
|
+
for ep in entry_points(group="flyte.plugins.cli.hooks"):
|
|
125
|
+
try:
|
|
126
|
+
hook = ep.load()
|
|
127
|
+
if not callable(hook):
|
|
128
|
+
logger.warning(f"Plugin hook {ep.name} is not callable")
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Check if this is a subcommand hook (contains dot notation)
|
|
132
|
+
if "." in ep.name:
|
|
133
|
+
group_name, command_name = ep.name.split(".", 1)
|
|
134
|
+
|
|
135
|
+
# Validate: only support one level of nesting (group.command)
|
|
136
|
+
if "." in command_name:
|
|
137
|
+
logger.error(
|
|
138
|
+
f"Plugin hook {ep.name} uses multiple dots, which is not supported. "
|
|
139
|
+
f"Use at most one dot (e.g., 'group.command')."
|
|
140
|
+
)
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
_apply_hook_to_subcommand(root_group, group_name, command_name, hook)
|
|
144
|
+
else:
|
|
145
|
+
# Top-level command hook
|
|
146
|
+
_apply_hook_to_command(root_group, ep.name, hook)
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.error(f"Failed to apply hook {ep.name}: {e}")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _add_subcommand_to_group(root_group: click.Group, group_name: str, command_name: str, command: click.Command):
|
|
153
|
+
"""Add a subcommand to an existing command group."""
|
|
154
|
+
if group_name not in root_group.commands:
|
|
155
|
+
logger.warning(f"Cannot add plugin subcommand '{command_name}' - group '{group_name}' does not exist")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
group = root_group.commands[group_name]
|
|
159
|
+
if not isinstance(group, click.Group):
|
|
160
|
+
logger.warning(f"Cannot add plugin subcommand '{command_name}' - '{group_name}' is not a command group")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
group.add_command(command, name=command_name)
|
|
164
|
+
# lower to debug later
|
|
165
|
+
logger.info(f"Registered plugin subcommand: flyte {group_name} {command_name}")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _apply_hook_to_command(root_group: click.Group, command_name: str, hook: CommandHook):
|
|
169
|
+
"""Apply a hook to a top-level command."""
|
|
170
|
+
if command_name not in root_group.commands:
|
|
171
|
+
logger.warning(f"Cannot apply hook - command '{command_name}' does not exist")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
original_command = root_group.commands[command_name]
|
|
175
|
+
try:
|
|
176
|
+
modified_command = hook(original_command)
|
|
177
|
+
root_group.commands[command_name] = modified_command
|
|
178
|
+
# lower to debug later
|
|
179
|
+
logger.info(f"Applied hook to command: flyte {command_name}")
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(f"Hook failed for command {command_name}: {e}")
|
|
182
|
+
root_group.commands[command_name] = original_command
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _apply_hook_to_subcommand(root_group: click.Group, group_name: str, command_name: str, hook: CommandHook):
|
|
186
|
+
"""Apply a hook to a subcommand within a group."""
|
|
187
|
+
if group_name not in root_group.commands:
|
|
188
|
+
logger.warning(f"Cannot apply hook - group '{group_name}' does not exist")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
group = root_group.commands[group_name]
|
|
192
|
+
if not isinstance(group, click.Group):
|
|
193
|
+
logger.warning(f"Cannot apply hook - '{group_name}' is not a command group")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
if command_name not in group.commands:
|
|
197
|
+
logger.warning(f"Cannot apply hook - subcommand '{command_name}' does not exist in group '{group_name}'")
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
original_command = group.commands[command_name]
|
|
201
|
+
if original_command.callback is not None:
|
|
202
|
+
original_command.callback()
|
|
203
|
+
try:
|
|
204
|
+
modified_command = hook(original_command)
|
|
205
|
+
group.commands[command_name] = modified_command
|
|
206
|
+
logger.info(f"Applied hook to subcommand: flyte {group_name} {command_name}")
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.error(f"Hook failed for subcommand {group_name}.{command_name}: {e}")
|
|
209
|
+
group.commands[command_name] = original_command
|