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.
Files changed (266) hide show
  1. flyte/__init__.py +83 -30
  2. flyte/_bin/connect.py +61 -0
  3. flyte/_bin/debug.py +38 -0
  4. flyte/_bin/runtime.py +87 -19
  5. flyte/_bin/serve.py +351 -0
  6. flyte/_build.py +3 -2
  7. flyte/_cache/cache.py +6 -5
  8. flyte/_cache/local_cache.py +216 -0
  9. flyte/_code_bundle/_ignore.py +31 -5
  10. flyte/_code_bundle/_packaging.py +42 -11
  11. flyte/_code_bundle/_utils.py +57 -34
  12. flyte/_code_bundle/bundle.py +130 -27
  13. flyte/_constants.py +1 -0
  14. flyte/_context.py +21 -5
  15. flyte/_custom_context.py +73 -0
  16. flyte/_debug/constants.py +37 -0
  17. flyte/_debug/utils.py +17 -0
  18. flyte/_debug/vscode.py +315 -0
  19. flyte/_deploy.py +396 -75
  20. flyte/_deployer.py +109 -0
  21. flyte/_environment.py +94 -11
  22. flyte/_excepthook.py +37 -0
  23. flyte/_group.py +2 -1
  24. flyte/_hash.py +1 -16
  25. flyte/_image.py +544 -231
  26. flyte/_initialize.py +456 -316
  27. flyte/_interface.py +40 -5
  28. flyte/_internal/controllers/__init__.py +22 -8
  29. flyte/_internal/controllers/_local_controller.py +159 -35
  30. flyte/_internal/controllers/_trace.py +18 -10
  31. flyte/_internal/controllers/remote/__init__.py +38 -9
  32. flyte/_internal/controllers/remote/_action.py +82 -12
  33. flyte/_internal/controllers/remote/_client.py +6 -2
  34. flyte/_internal/controllers/remote/_controller.py +290 -64
  35. flyte/_internal/controllers/remote/_core.py +155 -95
  36. flyte/_internal/controllers/remote/_informer.py +40 -20
  37. flyte/_internal/controllers/remote/_service_protocol.py +2 -2
  38. flyte/_internal/imagebuild/__init__.py +2 -10
  39. flyte/_internal/imagebuild/docker_builder.py +391 -84
  40. flyte/_internal/imagebuild/image_builder.py +111 -55
  41. flyte/_internal/imagebuild/remote_builder.py +409 -0
  42. flyte/_internal/imagebuild/utils.py +79 -0
  43. flyte/_internal/resolvers/_app_env_module.py +92 -0
  44. flyte/_internal/resolvers/_task_module.py +5 -38
  45. flyte/_internal/resolvers/app_env.py +26 -0
  46. flyte/_internal/resolvers/common.py +8 -1
  47. flyte/_internal/resolvers/default.py +2 -2
  48. flyte/_internal/runtime/convert.py +319 -36
  49. flyte/_internal/runtime/entrypoints.py +106 -18
  50. flyte/_internal/runtime/io.py +71 -23
  51. flyte/_internal/runtime/resources_serde.py +21 -7
  52. flyte/_internal/runtime/reuse.py +125 -0
  53. flyte/_internal/runtime/rusty.py +196 -0
  54. flyte/_internal/runtime/task_serde.py +239 -66
  55. flyte/_internal/runtime/taskrunner.py +48 -8
  56. flyte/_internal/runtime/trigger_serde.py +162 -0
  57. flyte/_internal/runtime/types_serde.py +7 -16
  58. flyte/_keyring/file.py +115 -0
  59. flyte/_link.py +30 -0
  60. flyte/_logging.py +241 -42
  61. flyte/_map.py +312 -0
  62. flyte/_metrics.py +59 -0
  63. flyte/_module.py +74 -0
  64. flyte/_pod.py +30 -0
  65. flyte/_resources.py +296 -33
  66. flyte/_retry.py +1 -7
  67. flyte/_reusable_environment.py +72 -7
  68. flyte/_run.py +462 -132
  69. flyte/_secret.py +47 -11
  70. flyte/_serve.py +333 -0
  71. flyte/_task.py +245 -56
  72. flyte/_task_environment.py +219 -97
  73. flyte/_task_plugins.py +47 -0
  74. flyte/_tools.py +8 -8
  75. flyte/_trace.py +15 -24
  76. flyte/_trigger.py +1027 -0
  77. flyte/_utils/__init__.py +12 -1
  78. flyte/_utils/asyn.py +3 -1
  79. flyte/_utils/async_cache.py +139 -0
  80. flyte/_utils/coro_management.py +5 -4
  81. flyte/_utils/description_parser.py +19 -0
  82. flyte/_utils/docker_credentials.py +173 -0
  83. flyte/_utils/helpers.py +45 -19
  84. flyte/_utils/module_loader.py +123 -0
  85. flyte/_utils/org_discovery.py +57 -0
  86. flyte/_utils/uv_script_parser.py +8 -1
  87. flyte/_version.py +16 -3
  88. flyte/app/__init__.py +27 -0
  89. flyte/app/_app_environment.py +362 -0
  90. flyte/app/_connector_environment.py +40 -0
  91. flyte/app/_deploy.py +130 -0
  92. flyte/app/_parameter.py +343 -0
  93. flyte/app/_runtime/__init__.py +3 -0
  94. flyte/app/_runtime/app_serde.py +383 -0
  95. flyte/app/_types.py +113 -0
  96. flyte/app/extras/__init__.py +9 -0
  97. flyte/app/extras/_auth_middleware.py +217 -0
  98. flyte/app/extras/_fastapi.py +93 -0
  99. flyte/app/extras/_model_loader/__init__.py +3 -0
  100. flyte/app/extras/_model_loader/config.py +7 -0
  101. flyte/app/extras/_model_loader/loader.py +288 -0
  102. flyte/cli/__init__.py +12 -0
  103. flyte/cli/_abort.py +28 -0
  104. flyte/cli/_build.py +114 -0
  105. flyte/cli/_common.py +493 -0
  106. flyte/cli/_create.py +371 -0
  107. flyte/cli/_delete.py +45 -0
  108. flyte/cli/_deploy.py +401 -0
  109. flyte/cli/_gen.py +316 -0
  110. flyte/cli/_get.py +446 -0
  111. flyte/cli/_option.py +33 -0
  112. flyte/{_cli → cli}/_params.py +57 -17
  113. flyte/cli/_plugins.py +209 -0
  114. flyte/cli/_prefetch.py +292 -0
  115. flyte/cli/_run.py +690 -0
  116. flyte/cli/_serve.py +338 -0
  117. flyte/cli/_update.py +86 -0
  118. flyte/cli/_user.py +20 -0
  119. flyte/cli/main.py +246 -0
  120. flyte/config/__init__.py +2 -167
  121. flyte/config/_config.py +215 -163
  122. flyte/config/_internal.py +10 -1
  123. flyte/config/_reader.py +225 -0
  124. flyte/connectors/__init__.py +11 -0
  125. flyte/connectors/_connector.py +330 -0
  126. flyte/connectors/_server.py +194 -0
  127. flyte/connectors/utils.py +159 -0
  128. flyte/errors.py +134 -2
  129. flyte/extend.py +24 -0
  130. flyte/extras/_container.py +69 -56
  131. flyte/git/__init__.py +3 -0
  132. flyte/git/_config.py +279 -0
  133. flyte/io/__init__.py +8 -1
  134. flyte/io/{structured_dataset → _dataframe}/__init__.py +32 -30
  135. flyte/io/{structured_dataset → _dataframe}/basic_dfs.py +75 -68
  136. flyte/io/{structured_dataset/structured_dataset.py → _dataframe/dataframe.py} +207 -242
  137. flyte/io/_dir.py +575 -113
  138. flyte/io/_file.py +587 -141
  139. flyte/io/_hashing_io.py +342 -0
  140. flyte/io/extend.py +7 -0
  141. flyte/models.py +635 -0
  142. flyte/prefetch/__init__.py +22 -0
  143. flyte/prefetch/_hf_model.py +563 -0
  144. flyte/remote/__init__.py +14 -3
  145. flyte/remote/_action.py +879 -0
  146. flyte/remote/_app.py +346 -0
  147. flyte/remote/_auth_metadata.py +42 -0
  148. flyte/remote/_client/_protocols.py +62 -4
  149. flyte/remote/_client/auth/_auth_utils.py +19 -0
  150. flyte/remote/_client/auth/_authenticators/base.py +8 -2
  151. flyte/remote/_client/auth/_authenticators/device_code.py +4 -5
  152. flyte/remote/_client/auth/_authenticators/factory.py +4 -0
  153. flyte/remote/_client/auth/_authenticators/passthrough.py +79 -0
  154. flyte/remote/_client/auth/_authenticators/pkce.py +17 -18
  155. flyte/remote/_client/auth/_channel.py +47 -18
  156. flyte/remote/_client/auth/_client_config.py +5 -3
  157. flyte/remote/_client/auth/_keyring.py +15 -2
  158. flyte/remote/_client/auth/_token_client.py +3 -3
  159. flyte/remote/_client/controlplane.py +206 -18
  160. flyte/remote/_common.py +66 -0
  161. flyte/remote/_data.py +107 -22
  162. flyte/remote/_logs.py +116 -33
  163. flyte/remote/_project.py +21 -19
  164. flyte/remote/_run.py +164 -631
  165. flyte/remote/_secret.py +72 -29
  166. flyte/remote/_task.py +387 -46
  167. flyte/remote/_trigger.py +368 -0
  168. flyte/remote/_user.py +43 -0
  169. flyte/report/_report.py +10 -6
  170. flyte/storage/__init__.py +13 -1
  171. flyte/storage/_config.py +237 -0
  172. flyte/storage/_parallel_reader.py +289 -0
  173. flyte/storage/_storage.py +268 -59
  174. flyte/syncify/__init__.py +56 -0
  175. flyte/syncify/_api.py +414 -0
  176. flyte/types/__init__.py +39 -0
  177. flyte/types/_interface.py +22 -7
  178. flyte/{io/pickle/transformer.py → types/_pickle.py} +37 -9
  179. flyte/types/_string_literals.py +8 -9
  180. flyte/types/_type_engine.py +226 -126
  181. flyte/types/_utils.py +1 -1
  182. flyte-2.0.0b46.data/scripts/debug.py +38 -0
  183. flyte-2.0.0b46.data/scripts/runtime.py +194 -0
  184. flyte-2.0.0b46.dist-info/METADATA +352 -0
  185. flyte-2.0.0b46.dist-info/RECORD +221 -0
  186. flyte-2.0.0b46.dist-info/entry_points.txt +8 -0
  187. flyte-2.0.0b46.dist-info/licenses/LICENSE +201 -0
  188. flyte/_api_commons.py +0 -3
  189. flyte/_cli/_common.py +0 -299
  190. flyte/_cli/_create.py +0 -42
  191. flyte/_cli/_delete.py +0 -23
  192. flyte/_cli/_deploy.py +0 -140
  193. flyte/_cli/_get.py +0 -235
  194. flyte/_cli/_run.py +0 -174
  195. flyte/_cli/main.py +0 -98
  196. flyte/_datastructures.py +0 -342
  197. flyte/_internal/controllers/pbhash.py +0 -39
  198. flyte/_protos/common/authorization_pb2.py +0 -66
  199. flyte/_protos/common/authorization_pb2.pyi +0 -108
  200. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  201. flyte/_protos/common/identifier_pb2.py +0 -71
  202. flyte/_protos/common/identifier_pb2.pyi +0 -82
  203. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  204. flyte/_protos/common/identity_pb2.py +0 -48
  205. flyte/_protos/common/identity_pb2.pyi +0 -72
  206. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  207. flyte/_protos/common/list_pb2.py +0 -36
  208. flyte/_protos/common/list_pb2.pyi +0 -69
  209. flyte/_protos/common/list_pb2_grpc.py +0 -4
  210. flyte/_protos/common/policy_pb2.py +0 -37
  211. flyte/_protos/common/policy_pb2.pyi +0 -27
  212. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  213. flyte/_protos/common/role_pb2.py +0 -37
  214. flyte/_protos/common/role_pb2.pyi +0 -53
  215. flyte/_protos/common/role_pb2_grpc.py +0 -4
  216. flyte/_protos/common/runtime_version_pb2.py +0 -28
  217. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  218. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  219. flyte/_protos/logs/dataplane/payload_pb2.py +0 -96
  220. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -168
  221. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  222. flyte/_protos/secret/definition_pb2.py +0 -49
  223. flyte/_protos/secret/definition_pb2.pyi +0 -93
  224. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  225. flyte/_protos/secret/payload_pb2.py +0 -62
  226. flyte/_protos/secret/payload_pb2.pyi +0 -94
  227. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  228. flyte/_protos/secret/secret_pb2.py +0 -38
  229. flyte/_protos/secret/secret_pb2.pyi +0 -6
  230. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  231. flyte/_protos/secret/secret_pb2_grpc_grpc.py +0 -198
  232. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  233. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  234. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  235. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  236. flyte/_protos/workflow/queue_service_pb2.py +0 -106
  237. flyte/_protos/workflow/queue_service_pb2.pyi +0 -141
  238. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -172
  239. flyte/_protos/workflow/run_definition_pb2.py +0 -128
  240. flyte/_protos/workflow/run_definition_pb2.pyi +0 -310
  241. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  242. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  243. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  244. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  245. flyte/_protos/workflow/run_service_pb2.py +0 -133
  246. flyte/_protos/workflow/run_service_pb2.pyi +0 -175
  247. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -412
  248. flyte/_protos/workflow/state_service_pb2.py +0 -58
  249. flyte/_protos/workflow/state_service_pb2.pyi +0 -71
  250. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  251. flyte/_protos/workflow/task_definition_pb2.py +0 -72
  252. flyte/_protos/workflow/task_definition_pb2.pyi +0 -65
  253. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  254. flyte/_protos/workflow/task_service_pb2.py +0 -44
  255. flyte/_protos/workflow/task_service_pb2.pyi +0 -31
  256. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -104
  257. flyte/io/_dataframe.py +0 -0
  258. flyte/io/pickle/__init__.py +0 -0
  259. flyte/remote/_console.py +0 -18
  260. flyte-0.2.0b1.dist-info/METADATA +0 -179
  261. flyte-0.2.0b1.dist-info/RECORD +0 -204
  262. flyte-0.2.0b1.dist-info/entry_points.txt +0 -3
  263. /flyte/{_cli → _debug}/__init__.py +0 -0
  264. /flyte/{_protos → _keyring}/__init__.py +0 -0
  265. {flyte-0.2.0b1.dist-info → flyte-2.0.0b46.dist-info}/WHEEL +0 -0
  266. {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)
@@ -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 flyteidl.core.interface_pb2 import Variable
19
- from flyteidl.core.literals_pb2 import Literal
20
- from flyteidl.core.types_pb2 import BlobType, LiteralType, SimpleType
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.io.pickle.transformer import FlytePickleTransformer
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
- return File(path=value)
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", *args) -> t.Optional[str]:
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 pyflyte run")
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 = typing.get_args(python_type)[i]
436
- cts.append(literal_type_to_click_type(variant, variant_python_type))
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 flyteidl.core.types_pb2 import SimpleType
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=[f"--{input_name}"],
570
+ param_decls=param_decls,
531
571
  type=literal_converter.click_type,
532
572
  is_flag=is_flag,
533
573
  default=default_val,