flyte 0.2.0b1__py3-none-any.whl → 2.0.0b46__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 +83 -30
- flyte/_bin/connect.py +61 -0
- flyte/_bin/debug.py +38 -0
- flyte/_bin/runtime.py +87 -19
- flyte/_bin/serve.py +351 -0
- flyte/_build.py +3 -2
- flyte/_cache/cache.py +6 -5
- flyte/_cache/local_cache.py +216 -0
- flyte/_code_bundle/_ignore.py +31 -5
- flyte/_code_bundle/_packaging.py +42 -11
- flyte/_code_bundle/_utils.py +57 -34
- flyte/_code_bundle/bundle.py +130 -27
- flyte/_constants.py +1 -0
- flyte/_context.py +21 -5
- flyte/_custom_context.py +73 -0
- flyte/_debug/constants.py +37 -0
- flyte/_debug/utils.py +17 -0
- flyte/_debug/vscode.py +315 -0
- flyte/_deploy.py +396 -75
- flyte/_deployer.py +109 -0
- flyte/_environment.py +94 -11
- flyte/_excepthook.py +37 -0
- flyte/_group.py +2 -1
- flyte/_hash.py +1 -16
- flyte/_image.py +544 -231
- flyte/_initialize.py +456 -316
- flyte/_interface.py +40 -5
- flyte/_internal/controllers/__init__.py +22 -8
- flyte/_internal/controllers/_local_controller.py +159 -35
- flyte/_internal/controllers/_trace.py +18 -10
- flyte/_internal/controllers/remote/__init__.py +38 -9
- flyte/_internal/controllers/remote/_action.py +82 -12
- flyte/_internal/controllers/remote/_client.py +6 -2
- flyte/_internal/controllers/remote/_controller.py +290 -64
- flyte/_internal/controllers/remote/_core.py +155 -95
- flyte/_internal/controllers/remote/_informer.py +40 -20
- flyte/_internal/controllers/remote/_service_protocol.py +2 -2
- flyte/_internal/imagebuild/__init__.py +2 -10
- flyte/_internal/imagebuild/docker_builder.py +391 -84
- flyte/_internal/imagebuild/image_builder.py +111 -55
- flyte/_internal/imagebuild/remote_builder.py +409 -0
- flyte/_internal/imagebuild/utils.py +79 -0
- flyte/_internal/resolvers/_app_env_module.py +92 -0
- flyte/_internal/resolvers/_task_module.py +5 -38
- flyte/_internal/resolvers/app_env.py +26 -0
- flyte/_internal/resolvers/common.py +8 -1
- flyte/_internal/resolvers/default.py +2 -2
- flyte/_internal/runtime/convert.py +319 -36
- flyte/_internal/runtime/entrypoints.py +106 -18
- flyte/_internal/runtime/io.py +71 -23
- flyte/_internal/runtime/resources_serde.py +21 -7
- flyte/_internal/runtime/reuse.py +125 -0
- flyte/_internal/runtime/rusty.py +196 -0
- flyte/_internal/runtime/task_serde.py +239 -66
- flyte/_internal/runtime/taskrunner.py +48 -8
- flyte/_internal/runtime/trigger_serde.py +162 -0
- flyte/_internal/runtime/types_serde.py +7 -16
- flyte/_keyring/file.py +115 -0
- flyte/_link.py +30 -0
- flyte/_logging.py +241 -42
- flyte/_map.py +312 -0
- flyte/_metrics.py +59 -0
- flyte/_module.py +74 -0
- flyte/_pod.py +30 -0
- flyte/_resources.py +296 -33
- flyte/_retry.py +1 -7
- flyte/_reusable_environment.py +72 -7
- flyte/_run.py +462 -132
- flyte/_secret.py +47 -11
- flyte/_serve.py +333 -0
- flyte/_task.py +245 -56
- flyte/_task_environment.py +219 -97
- flyte/_task_plugins.py +47 -0
- flyte/_tools.py +8 -8
- flyte/_trace.py +15 -24
- flyte/_trigger.py +1027 -0
- flyte/_utils/__init__.py +12 -1
- flyte/_utils/asyn.py +3 -1
- flyte/_utils/async_cache.py +139 -0
- flyte/_utils/coro_management.py +5 -4
- flyte/_utils/description_parser.py +19 -0
- flyte/_utils/docker_credentials.py +173 -0
- flyte/_utils/helpers.py +45 -19
- flyte/_utils/module_loader.py +123 -0
- flyte/_utils/org_discovery.py +57 -0
- flyte/_utils/uv_script_parser.py +8 -1
- flyte/_version.py +16 -3
- flyte/app/__init__.py +27 -0
- flyte/app/_app_environment.py +362 -0
- flyte/app/_connector_environment.py +40 -0
- flyte/app/_deploy.py +130 -0
- flyte/app/_parameter.py +343 -0
- flyte/app/_runtime/__init__.py +3 -0
- flyte/app/_runtime/app_serde.py +383 -0
- flyte/app/_types.py +113 -0
- flyte/app/extras/__init__.py +9 -0
- flyte/app/extras/_auth_middleware.py +217 -0
- flyte/app/extras/_fastapi.py +93 -0
- flyte/app/extras/_model_loader/__init__.py +3 -0
- flyte/app/extras/_model_loader/config.py +7 -0
- flyte/app/extras/_model_loader/loader.py +288 -0
- flyte/cli/__init__.py +12 -0
- flyte/cli/_abort.py +28 -0
- flyte/cli/_build.py +114 -0
- flyte/cli/_common.py +493 -0
- flyte/cli/_create.py +371 -0
- flyte/cli/_delete.py +45 -0
- flyte/cli/_deploy.py +401 -0
- flyte/cli/_gen.py +316 -0
- flyte/cli/_get.py +446 -0
- flyte/cli/_option.py +33 -0
- flyte/{_cli → cli}/_params.py +57 -17
- flyte/cli/_plugins.py +209 -0
- flyte/cli/_prefetch.py +292 -0
- flyte/cli/_run.py +690 -0
- flyte/cli/_serve.py +338 -0
- flyte/cli/_update.py +86 -0
- flyte/cli/_user.py +20 -0
- flyte/cli/main.py +246 -0
- flyte/config/__init__.py +2 -167
- flyte/config/_config.py +215 -163
- flyte/config/_internal.py +10 -1
- flyte/config/_reader.py +225 -0
- flyte/connectors/__init__.py +11 -0
- flyte/connectors/_connector.py +330 -0
- flyte/connectors/_server.py +194 -0
- flyte/connectors/utils.py +159 -0
- flyte/errors.py +134 -2
- flyte/extend.py +24 -0
- flyte/extras/_container.py +69 -56
- flyte/git/__init__.py +3 -0
- flyte/git/_config.py +279 -0
- flyte/io/__init__.py +8 -1
- flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
- flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
- flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
- flyte/io/_dir.py +575 -113
- flyte/io/_file.py +587 -141
- flyte/io/_hashing_io.py +342 -0
- flyte/io/extend.py +7 -0
- flyte/models.py +635 -0
- flyte/prefetch/__init__.py +22 -0
- flyte/prefetch/_hf_model.py +563 -0
- flyte/remote/__init__.py +14 -3
- flyte/remote/_action.py +879 -0
- flyte/remote/_app.py +346 -0
- flyte/remote/_auth_metadata.py +42 -0
- flyte/remote/_client/_protocols.py +62 -4
- flyte/remote/_client/auth/_auth_utils.py +19 -0
- flyte/remote/_client/auth/_authenticators/base.py +8 -2
- flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
- flyte/remote/_client/auth/_authenticators/factory.py +4 -0
- flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
- flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
- flyte/remote/_client/auth/_channel.py +47 -18
- flyte/remote/_client/auth/_client_config.py +5 -3
- flyte/remote/_client/auth/_keyring.py +15 -2
- flyte/remote/_client/auth/_token_client.py +3 -3
- flyte/remote/_client/controlplane.py +206 -18
- flyte/remote/_common.py +66 -0
- flyte/remote/_data.py +107 -22
- flyte/remote/_logs.py +116 -33
- flyte/remote/_project.py +21 -19
- flyte/remote/_run.py +164 -631
- flyte/remote/_secret.py +72 -29
- flyte/remote/_task.py +387 -46
- flyte/remote/_trigger.py +368 -0
- flyte/remote/_user.py +43 -0
- flyte/report/_report.py +10 -6
- flyte/storage/__init__.py +13 -1
- flyte/storage/_config.py +237 -0
- flyte/storage/_parallel_reader.py +289 -0
- flyte/storage/_storage.py +268 -59
- flyte/syncify/__init__.py +56 -0
- flyte/syncify/_api.py +414 -0
- flyte/types/__init__.py +39 -0
- flyte/types/_interface.py +22 -7
- flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
- flyte/types/_string_literals.py +8 -9
- flyte/types/_type_engine.py +226 -126
- flyte/types/_utils.py +1 -1
- flyte-2.0.0b46.data/scripts/debug.py +38 -0
- flyte-2.0.0b46.data/scripts/runtime.py +194 -0
- flyte-2.0.0b46.dist-info/METADATA +352 -0
- flyte-2.0.0b46.dist-info/RECORD +221 -0
- flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
- flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
- flyte/_api_commons.py +0 -3
- flyte/_cli/_common.py +0 -299
- flyte/_cli/_create.py +0 -42
- flyte/_cli/_delete.py +0 -23
- flyte/_cli/_deploy.py +0 -140
- flyte/_cli/_get.py +0 -235
- flyte/_cli/_run.py +0 -174
- flyte/_cli/main.py +0 -98
- flyte/_datastructures.py +0 -342
- flyte/_internal/controllers/pbhash.py +0 -39
- 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 -71
- flyte/_protos/common/identifier_pb2.pyi +0 -82
- 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 -69
- 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/logs/dataplane/payload_pb2.py +0 -96
- flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
- 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/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 -106
- flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
- flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
- flyte/_protos/workflow/run_definition_pb2.py +0 -128
- flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
- 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 -133
- flyte/_protos/workflow/run_service_pb2.pyi +0 -175
- flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
- flyte/_protos/workflow/state_service_pb2.py +0 -58
- flyte/_protos/workflow/state_service_pb2.pyi +0 -71
- flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
- flyte/_protos/workflow/task_definition_pb2.py +0 -72
- flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
- flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
- flyte/_protos/workflow/task_service_pb2.py +0 -44
- flyte/_protos/workflow/task_service_pb2.pyi +0 -31
- flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
- flyte/io/_dataframe.py +0 -0
- flyte/io/pickle/__init__.py +0 -0
- flyte/remote/_console.py +0 -18
- flyte-0.2.0b1.dist-info/METADATA +0 -179
- flyte-0.2.0b1.dist-info/RECORD +0 -204
- flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
- /flyte/{_cli → _debug}/__init__.py +0 -0
- /flyte/{_protos → _keyring}/__init__.py +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
- {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/top_level.txt +0 -0
flyte/cli/_get.py
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
from typing import Tuple, Union
|
|
4
|
+
|
|
5
|
+
import rich_click as click
|
|
6
|
+
from rich.pretty import pretty_repr
|
|
7
|
+
|
|
8
|
+
import flyte.remote as remote
|
|
9
|
+
from flyte.models import ActionPhase
|
|
10
|
+
|
|
11
|
+
from . import _common as common
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group(name="get")
|
|
15
|
+
def get():
|
|
16
|
+
"""
|
|
17
|
+
Retrieve resources from a Flyte deployment.
|
|
18
|
+
|
|
19
|
+
You can get information about projects, runs, tasks, actions, secrets, logs and input/output values.
|
|
20
|
+
|
|
21
|
+
Each command supports optional parameters to filter or specify the resource you want to retrieve.
|
|
22
|
+
|
|
23
|
+
Using a `get` subcommand without any arguments will retrieve a list of available resources to get.
|
|
24
|
+
For example:
|
|
25
|
+
|
|
26
|
+
* `get project` (without specifying a project), will list all projects.
|
|
27
|
+
* `get project my_project` will return the details of the project named `my_project`.
|
|
28
|
+
|
|
29
|
+
In some cases, a partially specified command will act as a filter and return available further parameters.
|
|
30
|
+
For example:
|
|
31
|
+
|
|
32
|
+
* `get action my_run` will return all actions for the run named `my_run`.
|
|
33
|
+
* `get action my_run my_action` will return the details of the action named `my_action` for the run `my_run`.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@get.command()
|
|
38
|
+
@click.argument("name", type=str, required=False)
|
|
39
|
+
@click.pass_obj
|
|
40
|
+
def project(cfg: common.CLIConfig, name: str | None = None):
|
|
41
|
+
"""
|
|
42
|
+
Get a list of all projects, or details of a specific project by name.
|
|
43
|
+
"""
|
|
44
|
+
cfg.init()
|
|
45
|
+
|
|
46
|
+
console = common.get_console()
|
|
47
|
+
if name:
|
|
48
|
+
console.print(pretty_repr(remote.Project.get(name)))
|
|
49
|
+
else:
|
|
50
|
+
console.print(common.format("Projects", remote.Project.listall(), cfg.output_format))
|
|
51
|
+
os._exit(0)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@get.command(cls=common.CommandBase)
|
|
55
|
+
@click.argument("name", type=str, required=False)
|
|
56
|
+
@click.option("--limit", type=int, default=100, help="Limit the number of runs to fetch when listing.")
|
|
57
|
+
@click.option(
|
|
58
|
+
"--in-phase", # multiple=True, TODO support multiple phases once values in works
|
|
59
|
+
type=click.Choice([p.value for p in ActionPhase], case_sensitive=False),
|
|
60
|
+
help="Filter runs by their status.",
|
|
61
|
+
)
|
|
62
|
+
@click.option("--only-mine", is_flag=True, default=False, help="Show only runs created by the current user (you).")
|
|
63
|
+
@click.option("--task-name", type=str, default=None, help="Filter runs by task name.")
|
|
64
|
+
@click.option("--task-version", type=str, default=None, help="Filter runs by task version.")
|
|
65
|
+
@click.pass_obj
|
|
66
|
+
def run(
|
|
67
|
+
cfg: common.CLIConfig,
|
|
68
|
+
name: str | None = None,
|
|
69
|
+
project: str | None = None,
|
|
70
|
+
domain: str | None = None,
|
|
71
|
+
limit: int = 100,
|
|
72
|
+
in_phase: str | Tuple[str, ...] | None = None,
|
|
73
|
+
only_mine: bool = False,
|
|
74
|
+
task_name: str | None = None,
|
|
75
|
+
task_version: str | None = None,
|
|
76
|
+
):
|
|
77
|
+
"""
|
|
78
|
+
Get a list of all runs, or details of a specific run by name.
|
|
79
|
+
|
|
80
|
+
The run details will include information about the run, its status, but only the root action will be shown.
|
|
81
|
+
|
|
82
|
+
If you want to see the actions for a run, use `get action <run_name>`.
|
|
83
|
+
|
|
84
|
+
You can filter runs by task name and optionally task version:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
$ flyte get run --task-name my_task
|
|
88
|
+
$ flyte get run --task-name my_task --task-version v1.0
|
|
89
|
+
```
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
cfg.init(project=project, domain=domain)
|
|
93
|
+
|
|
94
|
+
console = common.get_console()
|
|
95
|
+
if name:
|
|
96
|
+
details = remote.RunDetails.get(name=name)
|
|
97
|
+
console.print(common.format(f"Run {name}", [details], "json"))
|
|
98
|
+
else:
|
|
99
|
+
if in_phase and isinstance(in_phase, str):
|
|
100
|
+
in_phase = (ActionPhase(in_phase),)
|
|
101
|
+
|
|
102
|
+
subject = None
|
|
103
|
+
if only_mine:
|
|
104
|
+
usr = remote.User.get()
|
|
105
|
+
subject = usr.subject()
|
|
106
|
+
|
|
107
|
+
console.print(
|
|
108
|
+
common.format(
|
|
109
|
+
"Runs",
|
|
110
|
+
remote.Run.listall(
|
|
111
|
+
limit=limit,
|
|
112
|
+
in_phase=in_phase,
|
|
113
|
+
created_by_subject=subject,
|
|
114
|
+
task_name=task_name,
|
|
115
|
+
task_version=task_version,
|
|
116
|
+
),
|
|
117
|
+
cfg.output_format,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@get.command(cls=common.CommandBase)
|
|
123
|
+
@click.argument("name", type=str, required=False)
|
|
124
|
+
@click.argument("version", type=str, required=False)
|
|
125
|
+
@click.option("--limit", type=int, default=100, help="Limit the number of tasks to fetch.")
|
|
126
|
+
@click.pass_obj
|
|
127
|
+
def task(
|
|
128
|
+
cfg: common.CLIConfig,
|
|
129
|
+
name: str | None = None,
|
|
130
|
+
limit: int = 100,
|
|
131
|
+
version: str | None = None,
|
|
132
|
+
project: str | None = None,
|
|
133
|
+
domain: str | None = None,
|
|
134
|
+
):
|
|
135
|
+
"""
|
|
136
|
+
Retrieve a list of all tasks, or details of a specific task by name and version.
|
|
137
|
+
|
|
138
|
+
Currently, both `name` and `version` are required to get a specific task.
|
|
139
|
+
"""
|
|
140
|
+
cfg.init(project=project, domain=domain)
|
|
141
|
+
|
|
142
|
+
console = common.get_console()
|
|
143
|
+
if name:
|
|
144
|
+
if version:
|
|
145
|
+
v = remote.Task.get(name=name, version=version)
|
|
146
|
+
if v is None:
|
|
147
|
+
raise click.BadParameter(f"Task {name} not found.")
|
|
148
|
+
t = v.fetch()
|
|
149
|
+
console.print(common.format(f"Task {name}", [t], "json"))
|
|
150
|
+
else:
|
|
151
|
+
console.print(
|
|
152
|
+
common.format("Tasks", remote.Task.listall(by_task_name=name, limit=limit), cfg.output_format)
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
console.print(common.format("Tasks", remote.Task.listall(limit=limit), cfg.output_format))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@get.command(cls=common.CommandBase)
|
|
159
|
+
@click.argument("run_name", type=str, required=True)
|
|
160
|
+
@click.argument("action_name", type=str, required=False)
|
|
161
|
+
@click.option(
|
|
162
|
+
"--in-phase",
|
|
163
|
+
type=click.Choice([p.value for p in ActionPhase], case_sensitive=False),
|
|
164
|
+
help="Filter actions by their phase.",
|
|
165
|
+
)
|
|
166
|
+
@click.pass_obj
|
|
167
|
+
def action(
|
|
168
|
+
cfg: common.CLIConfig,
|
|
169
|
+
run_name: str,
|
|
170
|
+
action_name: str | None = None,
|
|
171
|
+
in_phase: str | None = None,
|
|
172
|
+
project: str | None = None,
|
|
173
|
+
domain: str | None = None,
|
|
174
|
+
):
|
|
175
|
+
"""
|
|
176
|
+
Get all actions for a run or details for a specific action.
|
|
177
|
+
"""
|
|
178
|
+
cfg.init(project=project, domain=domain)
|
|
179
|
+
|
|
180
|
+
console = common.get_console()
|
|
181
|
+
if action_name:
|
|
182
|
+
console.print(
|
|
183
|
+
common.format(
|
|
184
|
+
f"Action {run_name}.{action_name}", [remote.Action.get(run_name=run_name, name=action_name)], "json"
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
# List all actions for the run
|
|
189
|
+
if in_phase:
|
|
190
|
+
in_phase_tuple = (ActionPhase(in_phase),)
|
|
191
|
+
else:
|
|
192
|
+
in_phase_tuple = None
|
|
193
|
+
|
|
194
|
+
console.print(
|
|
195
|
+
common.format(
|
|
196
|
+
f"Actions for {run_name}",
|
|
197
|
+
remote.Action.listall(for_run_name=run_name, in_phase=in_phase_tuple),
|
|
198
|
+
cfg.output_format,
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@get.command(cls=common.CommandBase)
|
|
204
|
+
@click.argument("run_name", type=str, required=True)
|
|
205
|
+
@click.argument("action_name", type=str, required=False)
|
|
206
|
+
@click.option("--lines", "-l", type=int, default=30, help="Number of lines to show, only useful for --pretty")
|
|
207
|
+
@click.option("--show-ts", is_flag=True, help="Show timestamps")
|
|
208
|
+
@click.option(
|
|
209
|
+
"--pretty",
|
|
210
|
+
is_flag=True,
|
|
211
|
+
default=False,
|
|
212
|
+
help="Show logs in an auto-scrolling box, where number of lines is limited to `--lines`",
|
|
213
|
+
)
|
|
214
|
+
@click.option(
|
|
215
|
+
"--attempt", "-a", type=int, default=None, help="Attempt number to show logs for, defaults to the latest attempt."
|
|
216
|
+
)
|
|
217
|
+
@click.option("--filter-system", is_flag=True, default=False, help="Filter all system logs from the output.")
|
|
218
|
+
@click.pass_obj
|
|
219
|
+
def logs(
|
|
220
|
+
cfg: common.CLIConfig,
|
|
221
|
+
run_name: str,
|
|
222
|
+
action_name: str | None = None,
|
|
223
|
+
project: str | None = None,
|
|
224
|
+
domain: str | None = None,
|
|
225
|
+
lines: int = 30,
|
|
226
|
+
show_ts: bool = False,
|
|
227
|
+
pretty: bool = True,
|
|
228
|
+
attempt: int | None = None,
|
|
229
|
+
filter_system: bool = False,
|
|
230
|
+
):
|
|
231
|
+
"""
|
|
232
|
+
Stream logs for the provided run or action.
|
|
233
|
+
If only the run is provided, only the logs for the parent action will be streamed:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
$ flyte get logs my_run
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
If you want to see the logs for a specific action, you can provide the action name as well:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
$ flyte get logs my_run my_action
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
By default, logs will be shown in the raw format and will scroll the terminal.
|
|
246
|
+
If automatic scrolling and only tailing `--lines` number of lines is desired, use the `--pretty` flag:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
$ flyte get logs my_run my_action --pretty --lines 50
|
|
250
|
+
```
|
|
251
|
+
"""
|
|
252
|
+
cfg.init(project=project, domain=domain)
|
|
253
|
+
|
|
254
|
+
async def _run_log_view(_obj):
|
|
255
|
+
task = asyncio.create_task(
|
|
256
|
+
_obj.show_logs.aio(
|
|
257
|
+
max_lines=lines, show_ts=show_ts, raw=not pretty, attempt=attempt, filter_system=filter_system
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
try:
|
|
261
|
+
await task
|
|
262
|
+
except KeyboardInterrupt:
|
|
263
|
+
task.cancel()
|
|
264
|
+
|
|
265
|
+
obj: Union[remote.Action, remote.Run]
|
|
266
|
+
if action_name:
|
|
267
|
+
obj = remote.Action.get(run_name=run_name, name=action_name)
|
|
268
|
+
else:
|
|
269
|
+
obj = remote.Run.get(name=run_name)
|
|
270
|
+
asyncio.run(_run_log_view(obj))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@get.command(cls=common.CommandBase)
|
|
274
|
+
@click.argument("name", type=str, required=False)
|
|
275
|
+
@click.pass_obj
|
|
276
|
+
def secret(
|
|
277
|
+
cfg: common.CLIConfig,
|
|
278
|
+
name: str | None = None,
|
|
279
|
+
project: str | None = None,
|
|
280
|
+
domain: str | None = None,
|
|
281
|
+
):
|
|
282
|
+
"""
|
|
283
|
+
Get a list of all secrets, or details of a specific secret by name.
|
|
284
|
+
"""
|
|
285
|
+
if project is None:
|
|
286
|
+
project = ""
|
|
287
|
+
if domain is None:
|
|
288
|
+
domain = ""
|
|
289
|
+
cfg.init(project=project, domain=domain)
|
|
290
|
+
|
|
291
|
+
console = common.get_console()
|
|
292
|
+
if name:
|
|
293
|
+
console.print(common.format("Secret", [remote.Secret.get(name)], "json"))
|
|
294
|
+
else:
|
|
295
|
+
console.print(common.format("Secrets", remote.Secret.listall(), cfg.output_format))
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@get.command(cls=common.CommandBase)
|
|
299
|
+
@click.argument("run_name", type=str, required=True)
|
|
300
|
+
@click.argument("action_name", type=str, required=False)
|
|
301
|
+
@click.option("--inputs-only", "-i", is_flag=True, help="Show only inputs")
|
|
302
|
+
@click.option("--outputs-only", "-o", is_flag=True, help="Show only outputs")
|
|
303
|
+
@click.pass_obj
|
|
304
|
+
def io(
|
|
305
|
+
cfg: common.CLIConfig,
|
|
306
|
+
run_name: str,
|
|
307
|
+
action_name: str | None = None,
|
|
308
|
+
project: str | None = None,
|
|
309
|
+
domain: str | None = None,
|
|
310
|
+
inputs_only: bool = False,
|
|
311
|
+
outputs_only: bool = False,
|
|
312
|
+
):
|
|
313
|
+
"""
|
|
314
|
+
Get the inputs and outputs of a run or action.
|
|
315
|
+
If only the run name is provided, it will show the inputs and outputs of the root action of that run.
|
|
316
|
+
If an action name is provided, it will show the inputs and outputs for that action.
|
|
317
|
+
If `--inputs-only` or `--outputs-only` is specified, it will only show the inputs or outputs respectively.
|
|
318
|
+
|
|
319
|
+
Examples:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
$ flyte get io my_run
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
$ flyte get io my_run my_action
|
|
327
|
+
```
|
|
328
|
+
"""
|
|
329
|
+
if inputs_only and outputs_only:
|
|
330
|
+
raise click.BadParameter("Cannot use both --inputs-only and --outputs-only")
|
|
331
|
+
|
|
332
|
+
cfg.init(project=project, domain=domain)
|
|
333
|
+
console = common.get_console()
|
|
334
|
+
obj: Union[remote.ActionDetails, remote.RunDetails]
|
|
335
|
+
if action_name:
|
|
336
|
+
obj = remote.ActionDetails.get(run_name=run_name, name=action_name)
|
|
337
|
+
else:
|
|
338
|
+
obj = remote.RunDetails.get(name=run_name)
|
|
339
|
+
|
|
340
|
+
async def _get_io(
|
|
341
|
+
details: Union[remote.RunDetails, remote.ActionDetails],
|
|
342
|
+
) -> Tuple[remote.ActionInputs | None, remote.ActionOutputs | None | str]:
|
|
343
|
+
if inputs_only or outputs_only:
|
|
344
|
+
if inputs_only:
|
|
345
|
+
return await details.inputs(), None
|
|
346
|
+
elif outputs_only:
|
|
347
|
+
return None, await details.outputs()
|
|
348
|
+
inputs = await details.inputs()
|
|
349
|
+
outputs: remote.ActionOutputs | None | str = None
|
|
350
|
+
try:
|
|
351
|
+
outputs = await details.outputs()
|
|
352
|
+
except Exception:
|
|
353
|
+
# If the outputs are not available, we can still show the inputs
|
|
354
|
+
outputs = "[red]not yet available[/red]"
|
|
355
|
+
return inputs, outputs
|
|
356
|
+
|
|
357
|
+
inputs, outputs = asyncio.run(_get_io(obj))
|
|
358
|
+
# Show inputs and outputs side by side
|
|
359
|
+
console.print(
|
|
360
|
+
common.get_panel(
|
|
361
|
+
"Inputs & Outputs",
|
|
362
|
+
f"[green bold]Inputs[/green bold]\n{inputs}\n\n[blue bold]Outputs[/blue bold]\n{outputs}",
|
|
363
|
+
cfg.output_format,
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@get.command(cls=click.RichCommand)
|
|
369
|
+
@click.pass_obj
|
|
370
|
+
def config(cfg: common.CLIConfig):
|
|
371
|
+
"""
|
|
372
|
+
Shows the automatically detected configuration to connect with the remote backend.
|
|
373
|
+
|
|
374
|
+
The configuration will include the endpoint, organization, and other settings that are used by the CLI.
|
|
375
|
+
"""
|
|
376
|
+
console = common.get_console()
|
|
377
|
+
console.print(cfg)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@get.command(cls=common.CommandBase)
|
|
381
|
+
@click.argument("task_name", type=str, required=False)
|
|
382
|
+
@click.argument("name", type=str, required=False)
|
|
383
|
+
@click.option("--limit", type=int, default=100, help="Limit the number of triggers to fetch.")
|
|
384
|
+
@click.pass_obj
|
|
385
|
+
def trigger(
|
|
386
|
+
cfg: common.CLIConfig,
|
|
387
|
+
task_name: str | None = None,
|
|
388
|
+
name: str | None = None,
|
|
389
|
+
limit: int = 100,
|
|
390
|
+
project: str | None = None,
|
|
391
|
+
domain: str | None = None,
|
|
392
|
+
):
|
|
393
|
+
"""
|
|
394
|
+
Get a list of all triggers, or details of a specific trigger by name.
|
|
395
|
+
"""
|
|
396
|
+
if name and not task_name:
|
|
397
|
+
raise click.BadParameter("If you provide a trigger name, you must also provide the task name.")
|
|
398
|
+
|
|
399
|
+
from flyte.remote import Trigger
|
|
400
|
+
|
|
401
|
+
cfg.init(project=project, domain=domain)
|
|
402
|
+
|
|
403
|
+
console = common.get_console()
|
|
404
|
+
if name:
|
|
405
|
+
console.print(pretty_repr(Trigger.get(name=name, task_name=task_name)))
|
|
406
|
+
else:
|
|
407
|
+
console.print(common.format("Triggers", Trigger.listall(task_name=task_name, limit=limit), cfg.output_format))
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
@get.command(cls=common.CommandBase)
|
|
411
|
+
@click.argument("name", type=str, required=False)
|
|
412
|
+
@click.option("--limit", type=int, default=100, help="Limit the number of apps to fetch when listing.")
|
|
413
|
+
@click.option("--only-mine", is_flag=True, default=False, help="Show only apps created by the current user (you).")
|
|
414
|
+
@click.pass_obj
|
|
415
|
+
def app(
|
|
416
|
+
cfg: common.CLIConfig,
|
|
417
|
+
name: str | None = None,
|
|
418
|
+
project: str | None = None,
|
|
419
|
+
domain: str | None = None,
|
|
420
|
+
limit: int = 100,
|
|
421
|
+
only_mine: bool = False,
|
|
422
|
+
):
|
|
423
|
+
"""
|
|
424
|
+
Get a list of all apps, or details of a specific app by name.
|
|
425
|
+
|
|
426
|
+
Apps are long-running services deployed on the Flyte platform.
|
|
427
|
+
"""
|
|
428
|
+
cfg.init(project=project, domain=domain)
|
|
429
|
+
|
|
430
|
+
console = common.get_console()
|
|
431
|
+
if name:
|
|
432
|
+
app_details = remote.App.get(name=name)
|
|
433
|
+
console.print(common.format(f"App {name}", [app_details], "json"))
|
|
434
|
+
else:
|
|
435
|
+
subject = None
|
|
436
|
+
if only_mine:
|
|
437
|
+
usr = remote.User.get()
|
|
438
|
+
subject = usr.subject()
|
|
439
|
+
|
|
440
|
+
console.print(
|
|
441
|
+
common.format(
|
|
442
|
+
"Apps",
|
|
443
|
+
remote.App.listall(limit=limit, created_by_subject=subject),
|
|
444
|
+
cfg.output_format,
|
|
445
|
+
)
|
|
446
|
+
)
|
flyte/cli/_option.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from click import Option, UsageError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MutuallyExclusiveMixin:
|
|
5
|
+
def __init__(self, *args, **kwargs):
|
|
6
|
+
self.mutually_exclusive = set(kwargs.pop("mutually_exclusive", []))
|
|
7
|
+
self.error_format = kwargs.pop(
|
|
8
|
+
"error_msg", "Illegal usage: options '{name}' and '{invalid}' are mutually exclusive"
|
|
9
|
+
)
|
|
10
|
+
super().__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def handle_parse_result(self, ctx, opts, args):
|
|
13
|
+
self_present = self.name in opts and opts[self.name] is not None
|
|
14
|
+
others_intersect = self.mutually_exclusive.intersection(opts)
|
|
15
|
+
others_present = others_intersect and any(opts[value] is not None for value in others_intersect)
|
|
16
|
+
|
|
17
|
+
if others_present:
|
|
18
|
+
if self_present:
|
|
19
|
+
raise UsageError(self.error_format.format(name=self.name, invalid=", ".join(self.mutually_exclusive)))
|
|
20
|
+
else:
|
|
21
|
+
self.prompt = None
|
|
22
|
+
|
|
23
|
+
return super().handle_parse_result(ctx, opts, args)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# See https://stackoverflow.com/a/37491504/499285 and https://stackoverflow.com/a/44349292/499285
|
|
27
|
+
class MutuallyExclusiveOption(MutuallyExclusiveMixin, Option):
|
|
28
|
+
def __init__(self, *args, **kwargs):
|
|
29
|
+
mutually_exclusive = kwargs.get("mutually_exclusive", [])
|
|
30
|
+
help = kwargs.get("help", "")
|
|
31
|
+
if mutually_exclusive:
|
|
32
|
+
kwargs["help"] = help + f" Mutually exclusive with {', '.join(mutually_exclusive)}."
|
|
33
|
+
super().__init__(*args, **kwargs)
|
flyte/{_cli → cli}/_params.py
RENAMED
|
@@ -14,16 +14,16 @@ from typing import get_args
|
|
|
14
14
|
|
|
15
15
|
import rich_click as click
|
|
16
16
|
import yaml
|
|
17
|
-
from click import Parameter
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from
|
|
17
|
+
from click import Context, Parameter
|
|
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
|
|
|
24
24
|
from flyte._logging import logger
|
|
25
25
|
from flyte.io import Dir, File
|
|
26
|
-
from flyte.
|
|
26
|
+
from flyte.types._pickle import FlytePickleTransformer
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class StructuredDataset:
|
|
@@ -69,6 +69,9 @@ def labels_callback(_: typing.Any, param: str, values: typing.List[str]) -> typi
|
|
|
69
69
|
class DirParamType(click.ParamType):
|
|
70
70
|
name = "directory path"
|
|
71
71
|
|
|
72
|
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
|
73
|
+
return "Remote Dir Path"
|
|
74
|
+
|
|
72
75
|
def convert(
|
|
73
76
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
74
77
|
) -> typing.Any:
|
|
@@ -88,6 +91,9 @@ class StructuredDatasetParamType(click.ParamType):
|
|
|
88
91
|
|
|
89
92
|
name = "structured dataset path (dir/file)"
|
|
90
93
|
|
|
94
|
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
|
95
|
+
return "Remote parquet URI"
|
|
96
|
+
|
|
91
97
|
def convert(
|
|
92
98
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
93
99
|
) -> typing.Any:
|
|
@@ -101,6 +107,9 @@ class StructuredDatasetParamType(click.ParamType):
|
|
|
101
107
|
class FileParamType(click.ParamType):
|
|
102
108
|
name = "file path"
|
|
103
109
|
|
|
110
|
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
|
111
|
+
return "Remote File Path"
|
|
112
|
+
|
|
104
113
|
def convert(
|
|
105
114
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
106
115
|
) -> typing.Any:
|
|
@@ -110,14 +119,15 @@ class FileParamType(click.ParamType):
|
|
|
110
119
|
p = pathlib.Path(value)
|
|
111
120
|
if not p.exists() or not p.is_file():
|
|
112
121
|
raise click.BadParameter(f"parameter should be a valid file path, {value}")
|
|
113
|
-
|
|
122
|
+
raise click.BadParameter(f"Only remote paths are supported currently, {value}")
|
|
123
|
+
return File.from_existing_remote(value)
|
|
114
124
|
|
|
115
125
|
|
|
116
126
|
class PickleParamType(click.ParamType):
|
|
117
127
|
name = "pickle"
|
|
118
128
|
|
|
119
|
-
def get_metavar(self, param: "Parameter",
|
|
120
|
-
return "Python Object <Module>:<Object>"
|
|
129
|
+
def get_metavar(self, param: "Parameter", ctx) -> t.Optional[str]:
|
|
130
|
+
return "Python Object Instance <Module>:<Object>"
|
|
121
131
|
|
|
122
132
|
def convert(
|
|
123
133
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
@@ -142,6 +152,9 @@ class PickleParamType(click.ParamType):
|
|
|
142
152
|
class JSONIteratorParamType(click.ParamType):
|
|
143
153
|
name = "json iterator"
|
|
144
154
|
|
|
155
|
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
|
156
|
+
return "JSON Value"
|
|
157
|
+
|
|
145
158
|
def convert(
|
|
146
159
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
147
160
|
) -> typing.Any:
|
|
@@ -257,6 +270,9 @@ class DateTimeType(click.DateTime):
|
|
|
257
270
|
class DurationParamType(click.ParamType):
|
|
258
271
|
name = "[1:24 | :22 | 1 minute | 10 days | ...]"
|
|
259
272
|
|
|
273
|
+
def get_metavar(self, param: Parameter, ctx: Context) -> str | None:
|
|
274
|
+
return "ISO8601 duration"
|
|
275
|
+
|
|
260
276
|
def convert(
|
|
261
277
|
self, value: typing.Any, param: typing.Optional[click.Parameter], ctx: typing.Optional[click.Context]
|
|
262
278
|
) -> typing.Any:
|
|
@@ -283,13 +299,20 @@ class UnionParamType(click.ParamType):
|
|
|
283
299
|
A composite type that allows for multiple types to be specified. This is used for union types.
|
|
284
300
|
"""
|
|
285
301
|
|
|
286
|
-
def __init__(self, types: typing.List[click.ParamType]):
|
|
302
|
+
def __init__(self, types: typing.List[click.ParamType | None]):
|
|
287
303
|
super().__init__()
|
|
288
304
|
self._types = self._sort_precedence(types)
|
|
289
|
-
self.name = "|".join([t.name for t in self._types])
|
|
305
|
+
self.name = "|".join([t.name for t in self._types if t is not None])
|
|
306
|
+
self.optional = False
|
|
307
|
+
if None in types:
|
|
308
|
+
self.name = f"Optional[{self.name}]"
|
|
309
|
+
self.optional = True
|
|
310
|
+
|
|
311
|
+
def get_metavar(self, param: Parameter, ctx: typing.Optional[click.Context]) -> str | None:
|
|
312
|
+
return self.name
|
|
290
313
|
|
|
291
314
|
@staticmethod
|
|
292
|
-
def _sort_precedence(tp: typing.List[click.ParamType]) -> typing.List[click.ParamType]:
|
|
315
|
+
def _sort_precedence(tp: typing.List[click.ParamType | None]) -> typing.List[click.ParamType]:
|
|
293
316
|
unprocessed = []
|
|
294
317
|
str_types = []
|
|
295
318
|
others = []
|
|
@@ -311,6 +334,8 @@ class UnionParamType(click.ParamType):
|
|
|
311
334
|
"""
|
|
312
335
|
for p in self._types:
|
|
313
336
|
try:
|
|
337
|
+
if p is None and value is None:
|
|
338
|
+
return None
|
|
314
339
|
return p.convert(value, param, ctx)
|
|
315
340
|
except Exception as e:
|
|
316
341
|
logger.debug(f"Ignoring conversion error for type {p} trying other variants in Union. Error: {e}")
|
|
@@ -405,7 +430,7 @@ def literal_type_to_click_type(lt: LiteralType, python_type: typing.Type) -> cli
|
|
|
405
430
|
return ct
|
|
406
431
|
if lt.simple in SIMPLE_TYPE_CONVERTER:
|
|
407
432
|
return SIMPLE_TYPE_CONVERTER[lt.simple]
|
|
408
|
-
raise NotImplementedError(f"Type {lt.simple} is not supported in
|
|
433
|
+
raise NotImplementedError(f"Type {lt.simple} is not supported in `flyte run`")
|
|
409
434
|
|
|
410
435
|
if lt.HasField("structured_dataset_type"):
|
|
411
436
|
return StructuredDatasetParamType()
|
|
@@ -429,11 +454,17 @@ def literal_type_to_click_type(lt: LiteralType, python_type: typing.Type) -> cli
|
|
|
429
454
|
return DirParamType()
|
|
430
455
|
|
|
431
456
|
if lt.HasField("union_type"):
|
|
457
|
+
python_args = typing.get_args(python_type)
|
|
458
|
+
if len(python_args) == 0:
|
|
459
|
+
return PickleParamType()
|
|
432
460
|
cts = []
|
|
433
461
|
for i in range(len(lt.union_type.variants)):
|
|
434
462
|
variant = lt.union_type.variants[i]
|
|
435
|
-
variant_python_type =
|
|
436
|
-
|
|
463
|
+
variant_python_type = python_args[i]
|
|
464
|
+
if variant_python_type is type(None):
|
|
465
|
+
cts.append(None)
|
|
466
|
+
else:
|
|
467
|
+
cts.append(literal_type_to_click_type(variant, variant_python_type))
|
|
437
468
|
return UnionParamType(cts)
|
|
438
469
|
|
|
439
470
|
if lt.HasField("enum_type"):
|
|
@@ -461,6 +492,9 @@ class FlyteLiteralConverter(object):
|
|
|
461
492
|
def is_bool(self) -> bool:
|
|
462
493
|
return self.click_type == click.BOOL
|
|
463
494
|
|
|
495
|
+
def is_optional(self) -> bool:
|
|
496
|
+
return isinstance(self.click_type, UnionParamType) and self.click_type.optional
|
|
497
|
+
|
|
464
498
|
def convert(
|
|
465
499
|
self, ctx: click.Context, param: typing.Optional[click.Parameter], value: typing.Any
|
|
466
500
|
) -> typing.Union[Literal, typing.Any]:
|
|
@@ -470,6 +504,8 @@ class FlyteLiteralConverter(object):
|
|
|
470
504
|
try:
|
|
471
505
|
# If the expected Python type is datetime.date, adjust the value to date
|
|
472
506
|
if self._python_type is datetime.date:
|
|
507
|
+
if value is None:
|
|
508
|
+
return None
|
|
473
509
|
# Click produces datetime, so converting to date to avoid type mismatch error
|
|
474
510
|
value = value.date()
|
|
475
511
|
|
|
@@ -493,7 +529,7 @@ def to_click_option(
|
|
|
493
529
|
This handles converting workflow input types to supported click parameters with callbacks to initialize
|
|
494
530
|
the input values to their expected types.
|
|
495
531
|
"""
|
|
496
|
-
from
|
|
532
|
+
from flyteidl2.core.types_pb2 import SimpleType
|
|
497
533
|
|
|
498
534
|
if input_name != input_name.lower():
|
|
499
535
|
# Click does not support uppercase option names: https://github.com/pallets/click/issues/837
|
|
@@ -519,15 +555,19 @@ def to_click_option(
|
|
|
519
555
|
if literal_var.type.metadata:
|
|
520
556
|
description_extra = f": {MessageToDict(literal_var.type.metadata)}"
|
|
521
557
|
|
|
522
|
-
# If a query has been specified, the input is never strictly required at this layer
|
|
523
558
|
required = False if default_val is not None else True
|
|
524
559
|
is_flag: typing.Optional[bool] = None
|
|
560
|
+
param_decls = [f"--{input_name}"]
|
|
525
561
|
if literal_converter.is_bool():
|
|
526
562
|
required = False
|
|
527
563
|
is_flag = True
|
|
564
|
+
if default_val is True:
|
|
565
|
+
param_decls = [f"--{input_name}/--no-{input_name}"]
|
|
566
|
+
if literal_converter.is_optional():
|
|
567
|
+
required = False
|
|
528
568
|
|
|
529
569
|
return click.Option(
|
|
530
|
-
param_decls=
|
|
570
|
+
param_decls=param_decls,
|
|
531
571
|
type=literal_converter.click_type,
|
|
532
572
|
is_flag=is_flag,
|
|
533
573
|
default=default_val,
|