flyte 0.1.0__py3-none-any.whl → 0.2.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flyte might be problematic. Click here for more details.

Files changed (219) hide show
  1. flyte/__init__.py +78 -2
  2. flyte/_bin/__init__.py +0 -0
  3. flyte/_bin/runtime.py +152 -0
  4. flyte/_build.py +26 -0
  5. flyte/_cache/__init__.py +12 -0
  6. flyte/_cache/cache.py +145 -0
  7. flyte/_cache/defaults.py +9 -0
  8. flyte/_cache/policy_function_body.py +42 -0
  9. flyte/_code_bundle/__init__.py +8 -0
  10. flyte/_code_bundle/_ignore.py +113 -0
  11. flyte/_code_bundle/_packaging.py +187 -0
  12. flyte/_code_bundle/_utils.py +323 -0
  13. flyte/_code_bundle/bundle.py +209 -0
  14. flyte/_context.py +152 -0
  15. flyte/_deploy.py +243 -0
  16. flyte/_doc.py +29 -0
  17. flyte/_docstring.py +32 -0
  18. flyte/_environment.py +84 -0
  19. flyte/_excepthook.py +37 -0
  20. flyte/_group.py +32 -0
  21. flyte/_hash.py +23 -0
  22. flyte/_image.py +762 -0
  23. flyte/_initialize.py +492 -0
  24. flyte/_interface.py +84 -0
  25. flyte/_internal/__init__.py +3 -0
  26. flyte/_internal/controllers/__init__.py +128 -0
  27. flyte/_internal/controllers/_local_controller.py +193 -0
  28. flyte/_internal/controllers/_trace.py +41 -0
  29. flyte/_internal/controllers/remote/__init__.py +60 -0
  30. flyte/_internal/controllers/remote/_action.py +146 -0
  31. flyte/_internal/controllers/remote/_client.py +47 -0
  32. flyte/_internal/controllers/remote/_controller.py +494 -0
  33. flyte/_internal/controllers/remote/_core.py +410 -0
  34. flyte/_internal/controllers/remote/_informer.py +361 -0
  35. flyte/_internal/controllers/remote/_service_protocol.py +50 -0
  36. flyte/_internal/imagebuild/__init__.py +11 -0
  37. flyte/_internal/imagebuild/docker_builder.py +427 -0
  38. flyte/_internal/imagebuild/image_builder.py +246 -0
  39. flyte/_internal/imagebuild/remote_builder.py +0 -0
  40. flyte/_internal/resolvers/__init__.py +0 -0
  41. flyte/_internal/resolvers/_task_module.py +54 -0
  42. flyte/_internal/resolvers/common.py +31 -0
  43. flyte/_internal/resolvers/default.py +28 -0
  44. flyte/_internal/runtime/__init__.py +0 -0
  45. flyte/_internal/runtime/convert.py +342 -0
  46. flyte/_internal/runtime/entrypoints.py +135 -0
  47. flyte/_internal/runtime/io.py +136 -0
  48. flyte/_internal/runtime/resources_serde.py +138 -0
  49. flyte/_internal/runtime/task_serde.py +330 -0
  50. flyte/_internal/runtime/taskrunner.py +191 -0
  51. flyte/_internal/runtime/types_serde.py +54 -0
  52. flyte/_logging.py +135 -0
  53. flyte/_map.py +215 -0
  54. flyte/_pod.py +19 -0
  55. flyte/_protos/__init__.py +0 -0
  56. flyte/_protos/common/authorization_pb2.py +66 -0
  57. flyte/_protos/common/authorization_pb2.pyi +108 -0
  58. flyte/_protos/common/authorization_pb2_grpc.py +4 -0
  59. flyte/_protos/common/identifier_pb2.py +71 -0
  60. flyte/_protos/common/identifier_pb2.pyi +82 -0
  61. flyte/_protos/common/identifier_pb2_grpc.py +4 -0
  62. flyte/_protos/common/identity_pb2.py +48 -0
  63. flyte/_protos/common/identity_pb2.pyi +72 -0
  64. flyte/_protos/common/identity_pb2_grpc.py +4 -0
  65. flyte/_protos/common/list_pb2.py +36 -0
  66. flyte/_protos/common/list_pb2.pyi +71 -0
  67. flyte/_protos/common/list_pb2_grpc.py +4 -0
  68. flyte/_protos/common/policy_pb2.py +37 -0
  69. flyte/_protos/common/policy_pb2.pyi +27 -0
  70. flyte/_protos/common/policy_pb2_grpc.py +4 -0
  71. flyte/_protos/common/role_pb2.py +37 -0
  72. flyte/_protos/common/role_pb2.pyi +53 -0
  73. flyte/_protos/common/role_pb2_grpc.py +4 -0
  74. flyte/_protos/common/runtime_version_pb2.py +28 -0
  75. flyte/_protos/common/runtime_version_pb2.pyi +24 -0
  76. flyte/_protos/common/runtime_version_pb2_grpc.py +4 -0
  77. flyte/_protos/logs/dataplane/payload_pb2.py +100 -0
  78. flyte/_protos/logs/dataplane/payload_pb2.pyi +177 -0
  79. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +4 -0
  80. flyte/_protos/secret/definition_pb2.py +49 -0
  81. flyte/_protos/secret/definition_pb2.pyi +93 -0
  82. flyte/_protos/secret/definition_pb2_grpc.py +4 -0
  83. flyte/_protos/secret/payload_pb2.py +62 -0
  84. flyte/_protos/secret/payload_pb2.pyi +94 -0
  85. flyte/_protos/secret/payload_pb2_grpc.py +4 -0
  86. flyte/_protos/secret/secret_pb2.py +38 -0
  87. flyte/_protos/secret/secret_pb2.pyi +6 -0
  88. flyte/_protos/secret/secret_pb2_grpc.py +198 -0
  89. flyte/_protos/secret/secret_pb2_grpc_grpc.py +198 -0
  90. flyte/_protos/validate/validate/validate_pb2.py +76 -0
  91. flyte/_protos/workflow/common_pb2.py +27 -0
  92. flyte/_protos/workflow/common_pb2.pyi +14 -0
  93. flyte/_protos/workflow/common_pb2_grpc.py +4 -0
  94. flyte/_protos/workflow/environment_pb2.py +29 -0
  95. flyte/_protos/workflow/environment_pb2.pyi +12 -0
  96. flyte/_protos/workflow/environment_pb2_grpc.py +4 -0
  97. flyte/_protos/workflow/node_execution_service_pb2.py +26 -0
  98. flyte/_protos/workflow/node_execution_service_pb2.pyi +4 -0
  99. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +32 -0
  100. flyte/_protos/workflow/queue_service_pb2.py +105 -0
  101. flyte/_protos/workflow/queue_service_pb2.pyi +146 -0
  102. flyte/_protos/workflow/queue_service_pb2_grpc.py +172 -0
  103. flyte/_protos/workflow/run_definition_pb2.py +128 -0
  104. flyte/_protos/workflow/run_definition_pb2.pyi +314 -0
  105. flyte/_protos/workflow/run_definition_pb2_grpc.py +4 -0
  106. flyte/_protos/workflow/run_logs_service_pb2.py +41 -0
  107. flyte/_protos/workflow/run_logs_service_pb2.pyi +28 -0
  108. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +69 -0
  109. flyte/_protos/workflow/run_service_pb2.py +129 -0
  110. flyte/_protos/workflow/run_service_pb2.pyi +171 -0
  111. flyte/_protos/workflow/run_service_pb2_grpc.py +412 -0
  112. flyte/_protos/workflow/state_service_pb2.py +66 -0
  113. flyte/_protos/workflow/state_service_pb2.pyi +75 -0
  114. flyte/_protos/workflow/state_service_pb2_grpc.py +138 -0
  115. flyte/_protos/workflow/task_definition_pb2.py +79 -0
  116. flyte/_protos/workflow/task_definition_pb2.pyi +81 -0
  117. flyte/_protos/workflow/task_definition_pb2_grpc.py +4 -0
  118. flyte/_protos/workflow/task_service_pb2.py +60 -0
  119. flyte/_protos/workflow/task_service_pb2.pyi +59 -0
  120. flyte/_protos/workflow/task_service_pb2_grpc.py +138 -0
  121. flyte/_resources.py +226 -0
  122. flyte/_retry.py +32 -0
  123. flyte/_reusable_environment.py +25 -0
  124. flyte/_run.py +482 -0
  125. flyte/_secret.py +61 -0
  126. flyte/_task.py +449 -0
  127. flyte/_task_environment.py +183 -0
  128. flyte/_timeout.py +47 -0
  129. flyte/_tools.py +27 -0
  130. flyte/_trace.py +120 -0
  131. flyte/_utils/__init__.py +26 -0
  132. flyte/_utils/asyn.py +119 -0
  133. flyte/_utils/async_cache.py +139 -0
  134. flyte/_utils/coro_management.py +23 -0
  135. flyte/_utils/file_handling.py +72 -0
  136. flyte/_utils/helpers.py +134 -0
  137. flyte/_utils/lazy_module.py +54 -0
  138. flyte/_utils/org_discovery.py +57 -0
  139. flyte/_utils/uv_script_parser.py +49 -0
  140. flyte/_version.py +21 -0
  141. flyte/cli/__init__.py +3 -0
  142. flyte/cli/_abort.py +28 -0
  143. flyte/cli/_common.py +337 -0
  144. flyte/cli/_create.py +145 -0
  145. flyte/cli/_delete.py +23 -0
  146. flyte/cli/_deploy.py +152 -0
  147. flyte/cli/_gen.py +163 -0
  148. flyte/cli/_get.py +310 -0
  149. flyte/cli/_params.py +538 -0
  150. flyte/cli/_run.py +231 -0
  151. flyte/cli/main.py +166 -0
  152. flyte/config/__init__.py +3 -0
  153. flyte/config/_config.py +216 -0
  154. flyte/config/_internal.py +64 -0
  155. flyte/config/_reader.py +207 -0
  156. flyte/connectors/__init__.py +0 -0
  157. flyte/errors.py +172 -0
  158. flyte/extras/__init__.py +5 -0
  159. flyte/extras/_container.py +263 -0
  160. flyte/io/__init__.py +27 -0
  161. flyte/io/_dir.py +448 -0
  162. flyte/io/_file.py +467 -0
  163. flyte/io/_structured_dataset/__init__.py +129 -0
  164. flyte/io/_structured_dataset/basic_dfs.py +219 -0
  165. flyte/io/_structured_dataset/structured_dataset.py +1061 -0
  166. flyte/models.py +391 -0
  167. flyte/remote/__init__.py +26 -0
  168. flyte/remote/_client/__init__.py +0 -0
  169. flyte/remote/_client/_protocols.py +133 -0
  170. flyte/remote/_client/auth/__init__.py +12 -0
  171. flyte/remote/_client/auth/_auth_utils.py +14 -0
  172. flyte/remote/_client/auth/_authenticators/__init__.py +0 -0
  173. flyte/remote/_client/auth/_authenticators/base.py +397 -0
  174. flyte/remote/_client/auth/_authenticators/client_credentials.py +73 -0
  175. flyte/remote/_client/auth/_authenticators/device_code.py +118 -0
  176. flyte/remote/_client/auth/_authenticators/external_command.py +79 -0
  177. flyte/remote/_client/auth/_authenticators/factory.py +200 -0
  178. flyte/remote/_client/auth/_authenticators/pkce.py +516 -0
  179. flyte/remote/_client/auth/_channel.py +215 -0
  180. flyte/remote/_client/auth/_client_config.py +83 -0
  181. flyte/remote/_client/auth/_default_html.py +32 -0
  182. flyte/remote/_client/auth/_grpc_utils/__init__.py +0 -0
  183. flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py +288 -0
  184. flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py +151 -0
  185. flyte/remote/_client/auth/_keyring.py +143 -0
  186. flyte/remote/_client/auth/_token_client.py +260 -0
  187. flyte/remote/_client/auth/errors.py +16 -0
  188. flyte/remote/_client/controlplane.py +95 -0
  189. flyte/remote/_console.py +18 -0
  190. flyte/remote/_data.py +159 -0
  191. flyte/remote/_logs.py +176 -0
  192. flyte/remote/_project.py +85 -0
  193. flyte/remote/_run.py +970 -0
  194. flyte/remote/_secret.py +132 -0
  195. flyte/remote/_task.py +391 -0
  196. flyte/report/__init__.py +3 -0
  197. flyte/report/_report.py +178 -0
  198. flyte/report/_template.html +124 -0
  199. flyte/storage/__init__.py +29 -0
  200. flyte/storage/_config.py +233 -0
  201. flyte/storage/_remote_fs.py +34 -0
  202. flyte/storage/_storage.py +271 -0
  203. flyte/storage/_utils.py +5 -0
  204. flyte/syncify/__init__.py +56 -0
  205. flyte/syncify/_api.py +371 -0
  206. flyte/types/__init__.py +36 -0
  207. flyte/types/_interface.py +40 -0
  208. flyte/types/_pickle.py +118 -0
  209. flyte/types/_renderer.py +162 -0
  210. flyte/types/_string_literals.py +120 -0
  211. flyte/types/_type_engine.py +2287 -0
  212. flyte/types/_utils.py +80 -0
  213. flyte-0.2.0a0.dist-info/METADATA +249 -0
  214. flyte-0.2.0a0.dist-info/RECORD +218 -0
  215. {flyte-0.1.0.dist-info → flyte-0.2.0a0.dist-info}/WHEEL +2 -1
  216. flyte-0.2.0a0.dist-info/entry_points.txt +3 -0
  217. flyte-0.2.0a0.dist-info/top_level.txt +1 -0
  218. flyte-0.1.0.dist-info/METADATA +0 -6
  219. flyte-0.1.0.dist-info/RECORD +0 -5
flyte/cli/_delete.py ADDED
@@ -0,0 +1,23 @@
1
+ import rich_click as click
2
+
3
+ import flyte.cli._common as common
4
+
5
+
6
+ @click.group(name="delete")
7
+ def delete():
8
+ """
9
+ Remove resources from a Flyte deployment.
10
+ """
11
+
12
+
13
+ @delete.command(cls=common.CommandBase)
14
+ @click.argument("name", type=str, required=True)
15
+ @click.pass_obj
16
+ def secret(cfg: common.CLIConfig, name: str, project: str | None = None, domain: str | None = None):
17
+ """
18
+ Delete a secret. The name of the secret is required.
19
+ """
20
+ from flyte.remote import Secret
21
+
22
+ cfg.init(project, domain)
23
+ Secret.delete(name=name)
flyte/cli/_deploy.py ADDED
@@ -0,0 +1,152 @@
1
+ from dataclasses import dataclass, field, fields
2
+ from pathlib import Path
3
+ from types import ModuleType
4
+ from typing import Any, Dict, List, cast, get_args
5
+
6
+ import click
7
+ from click import Context
8
+
9
+ import flyte
10
+
11
+ from .._code_bundle._utils import CopyFiles
12
+ from . import _common as common
13
+ from ._common import CLIConfig
14
+
15
+
16
+ @dataclass
17
+ class DeployArguments:
18
+ project: str = field(
19
+ default=cast(str, common.PROJECT_OPTION.default), metadata={"click.option": common.PROJECT_OPTION}
20
+ )
21
+ domain: str = field(
22
+ default=cast(str, common.DOMAIN_OPTION.default), metadata={"click.option": common.DOMAIN_OPTION}
23
+ )
24
+ version: str = field(
25
+ default="",
26
+ metadata={
27
+ "click.option": click.Option(
28
+ ["--version"],
29
+ type=str,
30
+ help="Version of the environment to deploy",
31
+ )
32
+ },
33
+ )
34
+ dry_run: bool = field(default=False, metadata={"click.option": common.DRY_RUN_OPTION})
35
+ local: bool = field(
36
+ default=False,
37
+ metadata={
38
+ "click.option": click.Option(
39
+ ["--local"],
40
+ is_flag=True,
41
+ help="Run the task locally",
42
+ )
43
+ },
44
+ )
45
+ copy_style: CopyFiles = field(
46
+ default="loaded_modules",
47
+ metadata={
48
+ "click.option": click.Option(
49
+ ["--copy-style"],
50
+ type=click.Choice(get_args(CopyFiles)),
51
+ default="loaded_modules",
52
+ help="Copy style to use when running the task",
53
+ )
54
+ },
55
+ )
56
+
57
+ @classmethod
58
+ def from_dict(cls, d: Dict[str, Any]) -> "DeployArguments":
59
+ return cls(**d)
60
+
61
+ @classmethod
62
+ def options(cls) -> List[click.Option]:
63
+ """
64
+ Return the set of base parameters added to every flyte run workflow subcommand.
65
+ """
66
+ return [common.get_option_from_metadata(f.metadata) for f in fields(cls) if f.metadata]
67
+
68
+
69
+ class DeployEnvCommand(click.Command):
70
+ def __init__(self, obj_name: str, obj: Any, deploy_args: DeployArguments, *args, **kwargs):
71
+ self.obj_name = obj_name
72
+ self.obj = obj
73
+ self.deploy_args = deploy_args
74
+ super().__init__(*args, **kwargs)
75
+
76
+ def invoke(self, ctx: Context):
77
+ from rich.console import Console
78
+
79
+ console = Console()
80
+ console.print(f"Deploying root - environment: {self.obj_name}")
81
+ obj: CLIConfig = ctx.obj
82
+ obj.init(self.deploy_args.project, self.deploy_args.domain)
83
+ with console.status("Deploying...", spinner="dots"):
84
+ deployment = flyte.deploy(
85
+ self.obj,
86
+ dryrun=self.deploy_args.dry_run,
87
+ copy_style=self.deploy_args.copy_style,
88
+ version=self.deploy_args.version,
89
+ )
90
+
91
+ console.print(common.get_table("Environments", deployment.env_repr()))
92
+ console.print(common.get_table("Tasks", deployment.task_repr()))
93
+
94
+
95
+ class EnvPerFileGroup(common.ObjectsPerFileGroup):
96
+ """
97
+ Group that creates a command for each task in the current directory that is not `__init__.py`.
98
+ """
99
+
100
+ def __init__(self, filename: Path, deploy_args: DeployArguments, *args, **kwargs):
101
+ args = (filename, *args)
102
+ super().__init__(*args, **kwargs)
103
+ self.deploy_args = deploy_args
104
+
105
+ def _filter_objects(self, module: ModuleType) -> Dict[str, Any]:
106
+ return {k: v for k, v in module.__dict__.items() if isinstance(v, flyte.Environment)}
107
+
108
+ def _get_command_for_obj(self, ctx: click.Context, obj_name: str, obj: Any) -> click.Command:
109
+ obj = cast(flyte.Environment, obj)
110
+ return DeployEnvCommand(
111
+ name=obj_name,
112
+ obj_name=obj_name,
113
+ obj=obj,
114
+ help=f"{obj.name}" + (f": {obj.description}" if obj.description else ""),
115
+ deploy_args=self.deploy_args,
116
+ )
117
+
118
+
119
+ class EnvFiles(common.FileGroup):
120
+ """
121
+ Group that creates a command for each file in the current directory that is not `__init__.py`.
122
+ """
123
+
124
+ common_options_enabled = False
125
+
126
+ def __init__(
127
+ self,
128
+ *args,
129
+ **kwargs,
130
+ ):
131
+ if "params" not in kwargs:
132
+ kwargs["params"] = []
133
+ kwargs["params"].extend(DeployArguments.options())
134
+ super().__init__(*args, **kwargs)
135
+
136
+ def get_command(self, ctx, filename):
137
+ deploy_args = DeployArguments.from_dict(ctx.params)
138
+ return EnvPerFileGroup(
139
+ filename=Path(filename),
140
+ deploy_args=deploy_args,
141
+ name=filename,
142
+ help=f"Run, functions decorated `env.task` or instances of Tasks in {filename}",
143
+ )
144
+
145
+
146
+ deploy = EnvFiles(
147
+ name="deploy",
148
+ help="""
149
+ Deploy one or more environments from a python file.
150
+ This command will create or update environments in the Flyte system.
151
+ """,
152
+ )
flyte/cli/_gen.py ADDED
@@ -0,0 +1,163 @@
1
+ import textwrap
2
+ from os import getcwd
3
+ from typing import Generator, Tuple
4
+
5
+ import rich_click as click
6
+
7
+ import flyte.cli._common as common
8
+
9
+
10
+ @click.group(name="gen")
11
+ def gen():
12
+ """
13
+ Generate documentation.
14
+ """
15
+
16
+
17
+ @gen.command(cls=common.CommandBase)
18
+ @click.option("--type", "doc_type", type=str, required=True, help="Type of documentation (valid: markdown)")
19
+ @click.pass_obj
20
+ def docs(cfg: common.CLIConfig, doc_type: str, project: str | None = None, domain: str | None = None):
21
+ """
22
+ Generate documentation.
23
+ """
24
+ if doc_type == "markdown":
25
+ markdown(cfg)
26
+ else:
27
+ raise click.ClickException("Invalid documentation type: {}".format(doc_type))
28
+
29
+
30
+ def walk_commands(ctx: click.Context) -> Generator[Tuple[str, click.Command], None, None]:
31
+ """
32
+ Recursively walk a Click command tree, starting from the given context.
33
+
34
+ Yields:
35
+ (full_command_path, command_object)
36
+ """
37
+ command = ctx.command
38
+
39
+ if not isinstance(command, click.Group):
40
+ yield ctx.command_path, command
41
+ else:
42
+ for name in command.list_commands(ctx):
43
+ subcommand = command.get_command(ctx, name)
44
+ if subcommand is None:
45
+ continue
46
+
47
+ full_name = f"{ctx.command_path} {name}".strip()
48
+ yield full_name, subcommand
49
+
50
+ # Recurse if subcommand is a MultiCommand (i.e., has its own subcommands)
51
+ if isinstance(subcommand, click.Group):
52
+ sub_ctx = click.Context(subcommand, info_name=name, parent=ctx)
53
+ yield from walk_commands(sub_ctx)
54
+
55
+
56
+ def markdown(cfg: common.CLIConfig):
57
+ """
58
+ Generate documentation in Markdown format
59
+ """
60
+ ctx = cfg.ctx
61
+
62
+ output = []
63
+ output_verb_groups: dict[str, list[str]] = {}
64
+ output_noun_groups: dict[str, list[str]] = {}
65
+
66
+ commands = [*[("flyte", ctx.command)], *walk_commands(ctx)]
67
+ for cmd_path, cmd in commands:
68
+ output.append("")
69
+
70
+ cmd_path_parts = cmd_path.split(" ")
71
+
72
+ if len(cmd_path_parts) > 1:
73
+ if cmd_path_parts[1] not in output_verb_groups:
74
+ output_verb_groups[cmd_path_parts[1]] = []
75
+ if len(cmd_path_parts) > 2:
76
+ output_verb_groups[cmd_path_parts[1]].append(cmd_path_parts[2])
77
+
78
+ if len(cmd_path_parts) == 3:
79
+ if cmd_path_parts[2] not in output_noun_groups:
80
+ output_noun_groups[cmd_path_parts[2]] = []
81
+ output_noun_groups[cmd_path_parts[2]].append(cmd_path_parts[1])
82
+
83
+ output.append(f"{'#' * (len(cmd_path_parts) + 1)} {cmd_path}")
84
+ if cmd.help:
85
+ output.append("")
86
+ output.append(f"{dedent(cmd.help)}")
87
+
88
+ if not cmd.params:
89
+ continue
90
+
91
+ params = cmd.get_params(click.Context(cmd))
92
+
93
+ # Collect all data first to calculate column widths
94
+ table_data = []
95
+ for param in params:
96
+ if isinstance(param, click.Option):
97
+ # Format each option with backticks before joining
98
+ all_opts = param.opts + param.secondary_opts
99
+ if len(all_opts) == 1:
100
+ opts = f"`{all_opts[0]}`"
101
+ else:
102
+ opts = "".join(
103
+ [
104
+ "{{< multiline >}}",
105
+ "\n".join([f"`{opt}`" for opt in all_opts]),
106
+ "{{< /multiline >}}",
107
+ ]
108
+ )
109
+ default_value = ""
110
+ if param.default is not None:
111
+ default_value = f"`{param.default}`"
112
+ default_value = default_value.replace(f"{getcwd()}/", "")
113
+ help_text = dedent(param.help) if param.help else ""
114
+ table_data.append([opts, f"`{param.type.name}`", default_value, help_text])
115
+
116
+ if not table_data:
117
+ continue
118
+
119
+ # Add table header with proper alignment
120
+ output.append("")
121
+ output.append("| Option | Type | Default | Description |")
122
+ output.append("|--------|------|---------|-------------|")
123
+
124
+ # Add table rows with proper alignment
125
+ for row in table_data:
126
+ output.append(f"| {row[0]} | {row[1]} | {row[2]} | {row[3]} |")
127
+
128
+ output_verb_index = []
129
+
130
+ if len(output_verb_groups) > 0:
131
+ output_verb_index.append("| Action | On |")
132
+ output_verb_index.append("| ------ | -- |")
133
+ for verb, nouns in output_verb_groups.items():
134
+ entries = [f"[`{noun}`](#flyte-{verb}-{noun})" for noun in nouns]
135
+ output_verb_index.append(f"| `{verb}` | {', '.join(entries)} |")
136
+
137
+ output_noun_index = []
138
+
139
+ if len(output_noun_groups) > 0:
140
+ output_noun_index.append("| Object | Action |")
141
+ output_noun_index.append("| ------ | -- |")
142
+ for obj, actions in output_noun_groups.items():
143
+ entries = [f"[`{action}`](#flyte-{action}-{obj})" for action in actions]
144
+ output_noun_index.append(f"| `{obj}` | {', '.join(entries)} |")
145
+
146
+ print()
147
+ print("{{< grid >}}")
148
+ print("{{< markdown >}}")
149
+ print("\n".join(output_noun_index))
150
+ print("{{< /markdown >}}")
151
+ print("{{< markdown >}}")
152
+ print("\n".join(output_verb_index))
153
+ print("{{< /markdown >}}")
154
+ print("{{< /grid >}}")
155
+ print()
156
+ print("\n".join(output))
157
+
158
+
159
+ def dedent(text: str) -> str:
160
+ """
161
+ Remove leading whitespace from a string.
162
+ """
163
+ return textwrap.dedent(text).strip("\n")
flyte/cli/_get.py ADDED
@@ -0,0 +1,310 @@
1
+ import asyncio
2
+ from typing import Tuple, Union
3
+
4
+ import rich_click as click
5
+ from rich.console import Console
6
+ from rich.pretty import pretty_repr
7
+
8
+ from . import _common as common
9
+
10
+
11
+ @click.group(name="get")
12
+ def get():
13
+ """
14
+ Retrieve resources from a Flyte deployment.
15
+
16
+ You can get information about projects, runs, tasks, actions, secrets, logs and input/output values.
17
+
18
+ Each command supports optional parameters to filter or specify the resource you want to retrieve.
19
+
20
+ Using a `get` subcommand without any arguments will retrieve a list of available resources to get.
21
+ For example:
22
+
23
+ * `get project` (without specifying a project), will list all projects.
24
+ * `get project my_project` will return the details of the project named `my_project`.
25
+
26
+ In some cases, a partially specified command will act as a filter and return available further parameters.
27
+ For example:
28
+
29
+ * `get action my_run` will return all actions for the run named `my_run`.
30
+ * `get action my_run my_action` will return the details of the action named `my_action` for the run `my_run`.
31
+ """
32
+
33
+
34
+ @get.command()
35
+ @click.argument("name", type=str, required=False)
36
+ @click.pass_obj
37
+ def project(cfg: common.CLIConfig, name: str | None = None):
38
+ """
39
+ Get a list of all projects, or details of a specific project by name.
40
+ """
41
+ from flyte.remote import Project
42
+
43
+ cfg.init()
44
+
45
+ console = Console()
46
+ if name:
47
+ console.print(pretty_repr(Project.get(name)))
48
+ else:
49
+ console.print(common.get_table("Projects", Project.listall()))
50
+
51
+
52
+ @get.command(cls=common.CommandBase)
53
+ @click.argument("name", type=str, required=False)
54
+ @click.pass_obj
55
+ def run(cfg: common.CLIConfig, name: str | None = None, project: str | None = None, domain: str | None = None):
56
+ """
57
+ Get a list of all runs, or details of a specific run by name.
58
+
59
+ The run details will include information about the run, its status, but only the root action will be shown.
60
+
61
+ If you want to see the actions for a run, use `get action <run_name>`.
62
+ """
63
+ from flyte.remote import Run, RunDetails
64
+
65
+ cfg.init(project=project, domain=domain)
66
+
67
+ console = Console()
68
+ if name:
69
+ details = RunDetails.get(name=name)
70
+ console.print(pretty_repr(details))
71
+ else:
72
+ console.print(common.get_table("Runs", Run.listall()))
73
+
74
+
75
+ @get.command(cls=common.CommandBase)
76
+ @click.argument("name", type=str, required=False)
77
+ @click.argument("version", type=str, required=False)
78
+ @click.option("--limit", type=int, default=100, help="Limit the number of tasks to show.")
79
+ @click.pass_obj
80
+ def task(
81
+ cfg: common.CLIConfig,
82
+ name: str | None = None,
83
+ limit: int = 100,
84
+ version: str | None = None,
85
+ project: str | None = None,
86
+ domain: str | None = None,
87
+ ):
88
+ """
89
+ Retrieve a list of all tasks, or details of a specific task by name and version.
90
+
91
+ Currently, both `name` and `version` are required to get a specific task.
92
+ """
93
+ from flyte.remote import Task
94
+
95
+ cfg.init(project=project, domain=domain)
96
+
97
+ console = Console()
98
+ if name:
99
+ if version:
100
+ v = Task.get(name=name, version=version)
101
+ if v is None:
102
+ raise click.BadParameter(f"Task {name} not found.")
103
+ t = v.fetch()
104
+ console.print(pretty_repr(t))
105
+ else:
106
+ console.print(common.get_table("Tasks", Task.listall(by_task_name=name, limit=limit)))
107
+ else:
108
+ console.print(common.get_table("Tasks", Task.listall(limit=limit)))
109
+
110
+
111
+ @get.command(cls=common.CommandBase)
112
+ @click.argument("run_name", type=str, required=True)
113
+ @click.argument("action_name", type=str, required=False)
114
+ @click.pass_obj
115
+ def action(
116
+ cfg: common.CLIConfig,
117
+ run_name: str,
118
+ action_name: str | None = None,
119
+ project: str | None = None,
120
+ domain: str | None = None,
121
+ ):
122
+ """
123
+ Get all actions for a run or details for a specific action.
124
+ """
125
+ import flyte.remote as remote
126
+
127
+ cfg.init(project=project, domain=domain)
128
+
129
+ console = Console()
130
+ if action_name:
131
+ console.print(pretty_repr(remote.Action.get(run_name=run_name, name=action_name)))
132
+ else:
133
+ # List all actions for the run
134
+ console.print(common.get_table(f"Actions for {run_name}", remote.Action.listall(for_run_name=run_name)))
135
+
136
+
137
+ @get.command(cls=common.CommandBase)
138
+ @click.argument("run_name", type=str, required=True)
139
+ @click.argument("action_name", type=str, required=False)
140
+ @click.option("--lines", "-l", type=int, default=30, help="Number of lines to show, only useful for --pretty")
141
+ @click.option("--show-ts", is_flag=True, help="Show timestamps")
142
+ @click.option(
143
+ "--pretty",
144
+ is_flag=True,
145
+ default=False,
146
+ help="Show logs in an auto-scrolling box, where number of lines is limited to `--lines`",
147
+ )
148
+ @click.option(
149
+ "--attempt", "-a", type=int, default=None, help="Attempt number to show logs for, defaults to the latest attempt."
150
+ )
151
+ @click.option("--filter-system", is_flag=True, default=False, help="Filter all system logs from the output.")
152
+ @click.pass_obj
153
+ def logs(
154
+ cfg: common.CLIConfig,
155
+ run_name: str,
156
+ action_name: str | None = None,
157
+ project: str | None = None,
158
+ domain: str | None = None,
159
+ lines: int = 30,
160
+ show_ts: bool = False,
161
+ pretty: bool = True,
162
+ attempt: int | None = None,
163
+ filter_system: bool = False,
164
+ ):
165
+ """
166
+ Stream logs for the provided run or action.
167
+ If only the run is provided, only the logs for the parent action will be streamed:
168
+
169
+ ```bash
170
+ $ flyte get logs my_run
171
+ ```
172
+
173
+ If you want to see the logs for a specific action, you can provide the action name as well:
174
+
175
+ ```bash
176
+ $ flyte get logs my_run my_action
177
+ ```
178
+
179
+ By default, logs will be shown in the raw format and will scroll the terminal.
180
+ If automatic scrolling and only tailing `--lines` number of lines is desired, use the `--pretty` flag:
181
+
182
+ ```bash
183
+ $ flyte get logs my_run my_action --pretty --lines 50
184
+ ```
185
+ """
186
+ import flyte.remote as remote
187
+
188
+ cfg.init(project=project, domain=domain)
189
+
190
+ async def _run_log_view(_obj):
191
+ task = asyncio.create_task(
192
+ _obj.show_logs(
193
+ max_lines=lines, show_ts=show_ts, raw=not pretty, attempt=attempt, filter_system=filter_system
194
+ )
195
+ )
196
+ try:
197
+ await task
198
+ except KeyboardInterrupt:
199
+ task.cancel()
200
+
201
+ if action_name:
202
+ obj = remote.Action.get(run_name=run_name, name=action_name)
203
+ else:
204
+ obj = remote.Run.get(run_name)
205
+ asyncio.run(_run_log_view(obj))
206
+
207
+
208
+ @get.command(cls=common.CommandBase)
209
+ @click.argument("name", type=str, required=False)
210
+ @click.pass_obj
211
+ def secret(
212
+ cfg: common.CLIConfig,
213
+ name: str | None = None,
214
+ project: str | None = None,
215
+ domain: str | None = None,
216
+ ):
217
+ """
218
+ Get a list of all secrets, or details of a specific secret by name.
219
+ """
220
+ import flyte.remote as remote
221
+
222
+ cfg.init(project=project, domain=domain)
223
+
224
+ console = Console()
225
+ if name:
226
+ console.print(pretty_repr(remote.Secret.get(name)))
227
+ else:
228
+ console.print(common.get_table("Secrets", remote.Secret.listall()))
229
+
230
+
231
+ @get.command(cls=common.CommandBase)
232
+ @click.argument("run_name", type=str, required=True)
233
+ @click.argument("action_name", type=str, required=False)
234
+ @click.option("--inputs-only", "-i", is_flag=True, help="Show only inputs")
235
+ @click.option("--outputs-only", "-o", is_flag=True, help="Show only outputs")
236
+ @click.pass_obj
237
+ def io(
238
+ cfg: common.CLIConfig,
239
+ run_name: str,
240
+ action_name: str | None = None,
241
+ project: str | None = None,
242
+ domain: str | None = None,
243
+ inputs_only: bool = False,
244
+ outputs_only: bool = False,
245
+ ):
246
+ """
247
+ Get the inputs and outputs of a run or action.
248
+ If only the run name is provided, it will show the inputs and outputs of the root action of that run.
249
+ If an action name is provided, it will show the inputs and outputs for that action.
250
+ If `--inputs-only` or `--outputs-only` is specified, it will only show the inputs or outputs respectively.
251
+
252
+ Examples:
253
+
254
+ ```bash
255
+ $ flyte get io my_run
256
+ ```
257
+
258
+ ```bash
259
+ $ flyte get io my_run my_action
260
+ ```
261
+ """
262
+ if inputs_only and outputs_only:
263
+ raise click.BadParameter("Cannot use both --inputs-only and --outputs-only")
264
+
265
+ import flyte.remote as remote
266
+
267
+ cfg.init(project=project, domain=domain)
268
+ console = Console()
269
+ if action_name:
270
+ obj = remote.ActionDetails.get(run_name=run_name, name=action_name)
271
+ else:
272
+ obj = remote.RunDetails.get(run_name)
273
+
274
+ async def _get_io(
275
+ details: Union[remote.RunDetails, remote.ActionDetails],
276
+ ) -> Tuple[remote.ActionInputs | None, remote.ActionOutputs | None | str]:
277
+ if inputs_only or outputs_only:
278
+ if inputs_only:
279
+ return await details.inputs(), None
280
+ elif outputs_only:
281
+ return None, await details.outputs()
282
+ inputs = await details.inputs()
283
+ outputs: remote.ActionOutputs | None | str = None
284
+ try:
285
+ outputs = await details.outputs()
286
+ except Exception:
287
+ # If the outputs are not available, we can still show the inputs
288
+ outputs = "[red]not yet available[/red]"
289
+ return inputs, outputs
290
+
291
+ inputs, outputs = asyncio.run(_get_io(obj))
292
+ # Show inputs and outputs side by side
293
+ console.print(
294
+ common.get_panel(
295
+ "Inputs & Outputs",
296
+ f"[green bold]Inputs[/green bold]\n{inputs}\n\n[blue bold]Outputs[/blue bold]\n{outputs}",
297
+ )
298
+ )
299
+
300
+
301
+ @get.command(cls=click.RichCommand)
302
+ @click.pass_obj
303
+ def config(cfg: common.CLIConfig):
304
+ """
305
+ Shows the automatically detected configuration to connect with the remote backend.
306
+
307
+ The configuration will include the endpoint, organization, and other settings that are used by the CLI.
308
+ """
309
+ console = Console()
310
+ console.print(cfg)