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/_run.py ADDED
@@ -0,0 +1,231 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import inspect
5
+ from dataclasses import dataclass, field, fields
6
+ from pathlib import Path
7
+ from types import ModuleType
8
+ from typing import Any, Dict, List, cast
9
+
10
+ import click
11
+ from click import Context, Parameter
12
+ from rich.console import Console
13
+ from typing_extensions import get_args
14
+
15
+ from .._code_bundle._utils import CopyFiles
16
+ from .._task import TaskTemplate
17
+ from ..remote import Run
18
+ from . import _common as common
19
+ from ._common import CLIConfig
20
+ from ._params import to_click_option
21
+
22
+
23
+ @dataclass
24
+ class RunArguments:
25
+ project: str = field(
26
+ default=cast(str, common.PROJECT_OPTION.default), metadata={"click.option": common.PROJECT_OPTION}
27
+ )
28
+ domain: str = field(
29
+ default=cast(str, common.DOMAIN_OPTION.default), metadata={"click.option": common.DOMAIN_OPTION}
30
+ )
31
+ local: bool = field(
32
+ default=False,
33
+ metadata={
34
+ "click.option": click.Option(
35
+ ["--local"],
36
+ is_flag=True,
37
+ help="Run the task locally",
38
+ )
39
+ },
40
+ )
41
+ copy_style: CopyFiles = field(
42
+ default="loaded_modules",
43
+ metadata={
44
+ "click.option": click.Option(
45
+ ["--copy-style"],
46
+ type=click.Choice(get_args(CopyFiles)),
47
+ default="loaded_modules",
48
+ help="Copy style to use when running the task",
49
+ )
50
+ },
51
+ )
52
+ name: str | None = field(
53
+ default=None,
54
+ metadata={
55
+ "click.option": click.Option(
56
+ ["--name"],
57
+ type=str,
58
+ help="Name of the run. If not provided, a random name will be generated.",
59
+ )
60
+ },
61
+ )
62
+ follow: bool = field(
63
+ default=True,
64
+ metadata={
65
+ "click.option": click.Option(
66
+ ["--follow", "-f"],
67
+ is_flag=True,
68
+ default=False,
69
+ help="Wait and watch logs for the parent action. If not provided, the CLI will exit after "
70
+ "successfully launching a remote execution with a link to the UI.",
71
+ )
72
+ },
73
+ )
74
+
75
+ @classmethod
76
+ def from_dict(cls, d: Dict[str, Any]) -> RunArguments:
77
+ return cls(**d)
78
+
79
+ @classmethod
80
+ def options(cls) -> List[click.Option]:
81
+ """
82
+ Return the set of base parameters added to run subcommand.
83
+ """
84
+ return [common.get_option_from_metadata(f.metadata) for f in fields(cls) if f.metadata]
85
+
86
+
87
+ class RunTaskCommand(click.Command):
88
+ def __init__(self, obj_name: str, obj: Any, run_args: RunArguments, *args, **kwargs):
89
+ self.obj_name = obj_name
90
+ self.obj = cast(TaskTemplate, obj)
91
+ self.run_args = run_args
92
+ kwargs.pop("name", None)
93
+ super().__init__(obj_name, *args, **kwargs)
94
+
95
+ def invoke(self, ctx: Context):
96
+ obj: CLIConfig = ctx.obj
97
+ if obj is None:
98
+ import flyte.config
99
+
100
+ obj = CLIConfig(flyte.config.auto(), ctx)
101
+
102
+ obj.init(self.run_args.project, self.run_args.domain)
103
+
104
+ async def _run():
105
+ import flyte
106
+
107
+ r = flyte.with_runcontext(
108
+ copy_style=self.run_args.copy_style,
109
+ mode="local" if self.run_args.local else "remote",
110
+ name=self.run_args.name,
111
+ ).run(self.obj, **ctx.params)
112
+ if isinstance(r, Run) and r.action is not None:
113
+ console = Console()
114
+ console.print(
115
+ common.get_panel(
116
+ "Run",
117
+ f"[green bold]Created Run: {r.name} [/green bold] "
118
+ f"(Project: {r.action.action_id.run.project}, Domain: {r.action.action_id.run.domain})\n"
119
+ f"➡️ [blue bold]{r.url}[/blue bold]",
120
+ )
121
+ )
122
+ if self.run_args.follow:
123
+ console.print(
124
+ "[dim]Log streaming enabled, will wait for task to start running "
125
+ "and log stream to be available[/dim]"
126
+ )
127
+ await r.show_logs(max_lines=30, show_ts=True, raw=False)
128
+
129
+ asyncio.run(_run())
130
+
131
+ def get_params(self, ctx: Context) -> List[Parameter]:
132
+ # Note this function may be called multiple times by click.
133
+ task = self.obj
134
+ from .._internal.runtime.types_serde import transform_native_to_typed_interface
135
+
136
+ interface = transform_native_to_typed_interface(task.native_interface)
137
+ if interface is None:
138
+ return super().get_params(ctx)
139
+ inputs_interface = task.native_interface.inputs
140
+
141
+ params: List[Parameter] = []
142
+ for name, var in interface.inputs.variables.items():
143
+ default_val = None
144
+ if inputs_interface[name][1] is not inspect._empty:
145
+ default_val = inputs_interface[name][1]
146
+ params.append(to_click_option(name, var, inputs_interface[name][0], default_val))
147
+
148
+ self.params = params
149
+ return super().get_params(ctx)
150
+
151
+
152
+ class TaskPerFileGroup(common.ObjectsPerFileGroup):
153
+ """
154
+ Group that creates a command for each task in the current directory that is not __init__.py.
155
+ """
156
+
157
+ def __init__(self, filename: Path, run_args: RunArguments, *args, **kwargs):
158
+ args = (filename, *args)
159
+ super().__init__(*args, **kwargs)
160
+ self.run_args = run_args
161
+
162
+ def _filter_objects(self, module: ModuleType) -> Dict[str, Any]:
163
+ return {k: v for k, v in module.__dict__.items() if isinstance(v, TaskTemplate)}
164
+
165
+ def _get_command_for_obj(self, ctx: click.Context, obj_name: str, obj: Any) -> click.Command:
166
+ obj = cast(TaskTemplate, obj)
167
+ return RunTaskCommand(
168
+ obj_name=obj_name,
169
+ obj=obj,
170
+ help=obj.docs.__help__str__() if obj.docs else None,
171
+ run_args=self.run_args,
172
+ )
173
+
174
+
175
+ class TaskFiles(common.FileGroup):
176
+ """
177
+ Group that creates a command for each file in the current directory that is not __init__.py.
178
+ """
179
+
180
+ common_options_enabled = False
181
+
182
+ def __init__(
183
+ self,
184
+ *args,
185
+ directory: Path | None = None,
186
+ **kwargs,
187
+ ):
188
+ if "params" not in kwargs:
189
+ kwargs["params"] = []
190
+ kwargs["params"].extend(RunArguments.options())
191
+ super().__init__(*args, directory=directory, **kwargs)
192
+
193
+ def get_command(self, ctx, filename):
194
+ run_args = RunArguments.from_dict(ctx.params)
195
+ fp = Path(filename)
196
+ if not fp.exists():
197
+ raise click.BadParameter(f"File {filename} does not exist")
198
+ if fp.is_dir():
199
+ return TaskFiles(directory=fp)
200
+ return TaskPerFileGroup(
201
+ filename=Path(filename),
202
+ run_args=run_args,
203
+ name=filename,
204
+ help=f"Run, functions decorated with `env.task` in {filename}",
205
+ )
206
+
207
+
208
+ run = TaskFiles(
209
+ name="run",
210
+ help="""
211
+ Run a task from a python file.
212
+
213
+ Example usage:
214
+ ```bash
215
+ flyte run --name examples/basics/hello.py my_task --arg1 value1 --arg2 value2
216
+ ```
217
+ Note: all arguments for the run command are provided right after the `run` command and before the file name.
218
+
219
+ You can also specify the project and domain using the `--project` and `--domain` options, respectively. These
220
+ options can be set in the config file or passed as command line arguments.
221
+
222
+ Note: The arguments for the task are provided after the task name and can be retrieved using `--help`
223
+ Example:
224
+ ```bash
225
+ flyte run --name examples/basics/hello.py my_task --help
226
+ ```
227
+
228
+ To run a task locally, use the `--local` flag. This will run the task in the local environment instead of the remote
229
+ Flyte environment.
230
+ """,
231
+ )
flyte/cli/main.py ADDED
@@ -0,0 +1,166 @@
1
+ import rich_click as click
2
+
3
+ from flyte._logging import initialize_logger, logger
4
+
5
+ from ._abort import abort
6
+ from ._common import CLIConfig
7
+ from ._create import create
8
+ from ._delete import delete
9
+ from ._deploy import deploy
10
+ from ._gen import gen
11
+ from ._get import get
12
+ from ._run import run
13
+
14
+ help_config = click.RichHelpConfiguration(
15
+ use_markdown=True,
16
+ use_markdown_emoji=True,
17
+ command_groups={
18
+ "flyte": [
19
+ {
20
+ "name": "Run and stop tasks",
21
+ "commands": ["run", "abort"],
22
+ },
23
+ {
24
+ "name": "Management",
25
+ "commands": ["create", "deploy", "get", "delete"],
26
+ },
27
+ {
28
+ "name": "Documentation generation",
29
+ "commands": ["gen"],
30
+ },
31
+ ]
32
+ },
33
+ )
34
+
35
+
36
+ def _verbosity_to_loglevel(verbosity: int) -> int | None:
37
+ """
38
+ Converts a verbosity level from the CLI to a logging level.
39
+
40
+ :param verbosity: verbosity level from the CLI
41
+ :return: logging level
42
+ """
43
+ import logging
44
+
45
+ match verbosity:
46
+ case 0:
47
+ return None
48
+ case 1:
49
+ return logging.WARNING
50
+ case 2:
51
+ return logging.INFO
52
+ case _:
53
+ return logging.DEBUG
54
+
55
+
56
+ @click.group(cls=click.RichGroup)
57
+ @click.option(
58
+ "--endpoint",
59
+ type=str,
60
+ required=False,
61
+ help="The endpoint to connect to. This will override any configuration file and simply use `pkce` to connect.",
62
+ )
63
+ @click.option(
64
+ "--insecure",
65
+ is_flag=True,
66
+ required=False,
67
+ help="Use an insecure connection to the endpoint. If not specified, the CLI will use TLS.",
68
+ type=bool,
69
+ default=None,
70
+ show_default=True,
71
+ )
72
+ @click.option(
73
+ "-v",
74
+ "--verbose",
75
+ required=False,
76
+ help="Show verbose messages and exception traces. Repeating multiple times increases the verbosity (e.g., -vvv).",
77
+ count=True,
78
+ default=0,
79
+ type=int,
80
+ )
81
+ @click.option(
82
+ "--org",
83
+ type=str,
84
+ required=False,
85
+ help="The organization to which the command applies.",
86
+ )
87
+ @click.option(
88
+ "-c",
89
+ "--config",
90
+ "config_file",
91
+ required=False,
92
+ type=click.Path(exists=True),
93
+ help="Path to the configuration file to use. If not specified, the default configuration file is used.",
94
+ )
95
+ @click.rich_config(help_config=help_config)
96
+ @click.pass_context
97
+ def main(
98
+ ctx: click.Context,
99
+ endpoint: str | None,
100
+ insecure: bool,
101
+ verbose: int,
102
+ org: str | None,
103
+ config_file: str | None,
104
+ ):
105
+ """
106
+ The Flyte CLI is the command line interface for working with the Flyte SDK and backend.
107
+
108
+ It follows a simple verb/noun structure,
109
+ where the top-level commands are verbs that describe the action to be taken,
110
+ and the subcommands are nouns that describe the object of the action.
111
+
112
+ The root command can be used to configure the CLI for persistent settings,
113
+ such as the endpoint, organization, and verbosity level.
114
+
115
+ Set endpoint and organization:
116
+
117
+ ```bash
118
+ $ flyte --endpoint <endpoint> --org <org> get project <project_name>
119
+ ```
120
+
121
+ Increase verbosity level (This is useful for debugging,
122
+ this will show more logs and exception traces):
123
+
124
+ ```bash
125
+ $ flyte -vvv get logs <run-name>
126
+ ```
127
+
128
+ Override the default config file:
129
+
130
+ ```bash
131
+ $ flyte --config /path/to/config.yaml run ...
132
+ ```
133
+
134
+ * [Documentation](https://www.union.ai/docs/flyte/user-guide/)
135
+ * [GitHub](https://github.com/flyteorg/flyte): Please leave a star if you like Flyte!
136
+ * [Slack](https://slack.flyte.org): Join the community and ask questions.
137
+ * [Issues](https://github.com/flyteorg/flyte/issues)
138
+
139
+ """
140
+ import flyte.config as config
141
+
142
+ log_level = _verbosity_to_loglevel(verbose)
143
+ if log_level is not None:
144
+ initialize_logger(log_level)
145
+
146
+ cfg = config.auto(config_file=config_file)
147
+ if cfg.source:
148
+ logger.debug(f"Using config file discovered at location `{cfg.source.absolute()}`")
149
+
150
+ ctx.obj = CLIConfig(
151
+ log_level=log_level,
152
+ endpoint=endpoint,
153
+ insecure=insecure,
154
+ org=org,
155
+ config=cfg,
156
+ ctx=ctx,
157
+ )
158
+
159
+
160
+ main.add_command(run)
161
+ main.add_command(deploy)
162
+ main.add_command(get) # type: ignore
163
+ main.add_command(create) # type: ignore
164
+ main.add_command(abort) # type: ignore
165
+ main.add_command(gen) # type: ignore
166
+ main.add_command(delete) # type: ignore
@@ -0,0 +1,3 @@
1
+ from flyte.config._config import Config, auto, get_config_file, set_if_exists
2
+
3
+ __all__ = ["Config", "auto", "get_config_file", "set_if_exists"]
@@ -0,0 +1,216 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import os
5
+ import pathlib
6
+ import typing
7
+ from dataclasses import dataclass, field
8
+ from typing import TYPE_CHECKING
9
+
10
+ import rich.repr
11
+
12
+ from flyte._logging import logger
13
+ from flyte.config import _internal
14
+ from flyte.config._reader import ConfigFile, get_config_file, read_file_if_exists
15
+
16
+ _all__ = ["ConfigFile", "PlatformConfig", "TaskConfig"]
17
+
18
+ if TYPE_CHECKING:
19
+ from flyte.remote._client.auth import AuthType
20
+
21
+
22
+ @rich.repr.auto
23
+ @dataclass(init=True, repr=True, eq=True, frozen=True)
24
+ class PlatformConfig(object):
25
+ """
26
+ This object contains the settings to talk to a Flyte backend (the DNS location of your Admin server basically).
27
+
28
+ :param endpoint: DNS for Flyte backend
29
+ :param insecure: Whether or not to use SSL
30
+ :param insecure_skip_verify: Whether to skip SSL certificate verification
31
+ :param console_endpoint: endpoint for console if different from Flyte backend
32
+ :param command: This command is executed to return a token using an external process
33
+ :param proxy_command: This command is executed to return a token for proxy authorization using an external process
34
+ :param client_id: This is the public identifier for the app which handles authorization for a Flyte deployment.
35
+ More details here: https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/.
36
+ :param client_credentials_secret: Used for service auth, which is automatically called during pyflyte. This will
37
+ allow the Flyte engine to read the password directly from the environment variable. Note that this is
38
+ less secure! Please only use this if mounting the secret as a file is impossible
39
+ :param scopes: List of scopes to request. This is only applicable to the client credentials flow
40
+ :param auth_mode: The OAuth mode to use. Defaults to pkce flow
41
+ :param ca_cert_file_path: [optional] str Root Cert to be loaded and used to verify admin
42
+ :param http_proxy_url: [optional] HTTP Proxy to be used for OAuth requests
43
+ """
44
+
45
+ endpoint: str | None = None
46
+ insecure: bool = False
47
+ insecure_skip_verify: bool = False
48
+ ca_cert_file_path: typing.Optional[str] = None
49
+ console_endpoint: typing.Optional[str] = None
50
+ command: typing.Optional[typing.List[str]] = None
51
+ proxy_command: typing.Optional[typing.List[str]] = None
52
+ client_id: typing.Optional[str] = None
53
+ client_credentials_secret: typing.Optional[str] = None
54
+ scopes: typing.List[str] = field(default_factory=list)
55
+ auth_mode: "AuthType" = "Pkce"
56
+ audience: typing.Optional[str] = None
57
+ rpc_retries: int = 3
58
+ http_proxy_url: typing.Optional[str] = None
59
+
60
+ @classmethod
61
+ def auto(cls, config_file: typing.Optional[typing.Union[str, ConfigFile]] = None) -> "PlatformConfig":
62
+ """
63
+ Reads from a config file, and overrides from Environment variables. Refer to ConfigEntry for details
64
+ :param config_file:
65
+ :return:
66
+ """
67
+
68
+ config_file = get_config_file(config_file)
69
+ kwargs: typing.Dict[str, typing.Any] = {}
70
+ kwargs = set_if_exists(kwargs, "insecure", _internal.Platform.INSECURE.read(config_file))
71
+ kwargs = set_if_exists(
72
+ kwargs, "insecure_skip_verify", _internal.Platform.INSECURE_SKIP_VERIFY.read(config_file)
73
+ )
74
+ kwargs = set_if_exists(kwargs, "ca_cert_file_path", _internal.Platform.CA_CERT_FILE_PATH.read(config_file))
75
+ kwargs = set_if_exists(kwargs, "command", _internal.Credentials.COMMAND.read(config_file))
76
+ kwargs = set_if_exists(kwargs, "proxy_command", _internal.Credentials.PROXY_COMMAND.read(config_file))
77
+ kwargs = set_if_exists(kwargs, "client_id", _internal.Credentials.CLIENT_ID.read(config_file))
78
+
79
+ is_client_secret = False
80
+ client_credentials_secret = read_file_if_exists(
81
+ _internal.Credentials.CLIENT_CREDENTIALS_SECRET_LOCATION.read(config_file)
82
+ )
83
+ if client_credentials_secret:
84
+ is_client_secret = True
85
+ if client_credentials_secret.endswith("\n"):
86
+ logger.info("Newline stripped from client secret")
87
+ client_credentials_secret = client_credentials_secret.strip()
88
+ kwargs = set_if_exists(
89
+ kwargs,
90
+ "client_credentials_secret",
91
+ client_credentials_secret,
92
+ )
93
+
94
+ client_credentials_secret_env_var = _internal.Credentials.CLIENT_CREDENTIALS_SECRET_ENV_VAR.read(config_file)
95
+ if client_credentials_secret_env_var:
96
+ client_credentials_secret = os.getenv(client_credentials_secret_env_var)
97
+ if client_credentials_secret:
98
+ is_client_secret = True
99
+ kwargs = set_if_exists(kwargs, "client_credentials_secret", client_credentials_secret)
100
+ kwargs = set_if_exists(kwargs, "scopes", _internal.Credentials.SCOPES.read(config_file))
101
+ kwargs = set_if_exists(kwargs, "auth_mode", _internal.Credentials.AUTH_MODE.read(config_file))
102
+ if is_client_secret:
103
+ kwargs = set_if_exists(kwargs, "auth_mode", "ClientSecret")
104
+ kwargs = set_if_exists(kwargs, "endpoint", _internal.Platform.URL.read(config_file))
105
+ kwargs = set_if_exists(kwargs, "console_endpoint", _internal.Platform.CONSOLE_ENDPOINT.read(config_file))
106
+
107
+ kwargs = set_if_exists(kwargs, "http_proxy_url", _internal.Platform.HTTP_PROXY_URL.read(config_file))
108
+ return PlatformConfig(**kwargs)
109
+
110
+ def replace(self, **kwargs: typing.Any) -> "PlatformConfig":
111
+ """
112
+ Returns a new PlatformConfig instance with the values from the kwargs overriding the current instance.
113
+ """
114
+ return dataclasses.replace(self, **kwargs)
115
+
116
+ @classmethod
117
+ def for_endpoint(cls, endpoint: str, insecure: bool = False) -> "PlatformConfig":
118
+ return PlatformConfig(endpoint=endpoint, insecure=insecure)
119
+
120
+
121
+ @rich.repr.auto
122
+ @dataclass(init=True, repr=True, eq=True, frozen=True)
123
+ class TaskConfig(object):
124
+ org: str | None = None
125
+ project: str | None = None
126
+ domain: str | None = None
127
+
128
+ @classmethod
129
+ def auto(cls, config_file: typing.Optional[typing.Union[str, ConfigFile]] = None) -> "TaskConfig":
130
+ """
131
+ Reads from a config file, and overrides from Environment variables. Refer to ConfigEntry for details
132
+ :param config_file:
133
+ :return:
134
+ """
135
+ config_file = get_config_file(config_file)
136
+ kwargs: typing.Dict[str, typing.Any] = {}
137
+ kwargs = set_if_exists(kwargs, "org", _internal.Task.ORG.read(config_file))
138
+ kwargs = set_if_exists(kwargs, "project", _internal.Task.PROJECT.read(config_file))
139
+ kwargs = set_if_exists(kwargs, "domain", _internal.Task.DOMAIN.read(config_file))
140
+ return TaskConfig(**kwargs)
141
+
142
+
143
+ @rich.repr.auto
144
+ @dataclass(init=True, repr=True, eq=True, frozen=True)
145
+ class Config(object):
146
+ """
147
+ This the parent configuration object and holds all the underlying configuration object types. An instance of
148
+ this object holds all the config necessary to
149
+
150
+ 1. Interactive session with Flyte backend
151
+ 2. Some parts are required for Serialization, for example Platform Config is not required
152
+ 3. Runtime of a task
153
+ """
154
+
155
+ platform: PlatformConfig = field(default=PlatformConfig())
156
+ task: TaskConfig = field(default=TaskConfig())
157
+ source: pathlib.Path | None = None
158
+
159
+ def with_params(
160
+ self,
161
+ platform: PlatformConfig | None = None,
162
+ task: TaskConfig | None = None,
163
+ ) -> "Config":
164
+ return Config(
165
+ platform=platform or self.platform,
166
+ task=task or self.task,
167
+ )
168
+
169
+ @classmethod
170
+ def auto(cls, config_file: typing.Union[str, ConfigFile, None] = None) -> "Config":
171
+ """
172
+ Automatically constructs the Config Object. The order of precedence is as follows
173
+ 1. first try to find any env vars that match the config vars specified in the FLYTE_CONFIG format.
174
+ 2. If not found in environment then values ar read from the config file
175
+ 3. If not found in the file, then the default values are used.
176
+
177
+ :param config_file: file path to read the config from, if not specified default locations are searched
178
+ :return: Config
179
+ """
180
+ config_file = get_config_file(config_file)
181
+ if config_file is None:
182
+ logger.debug("No config file found, using default values")
183
+ return Config()
184
+ return Config(
185
+ platform=PlatformConfig.auto(config_file), task=TaskConfig.auto(config_file), source=config_file.path
186
+ )
187
+
188
+
189
+ def set_if_exists(d: dict, k: str, val: typing.Any) -> dict:
190
+ """
191
+ Given a dict ``d`` sets the key ``k`` with value of config ``v``, if the config value ``v`` is set
192
+ and return the updated dictionary.
193
+ """
194
+ exists = isinstance(val, bool) or bool(val is not None and val)
195
+ if exists:
196
+ d[k] = val
197
+ return d
198
+
199
+
200
+ def auto(config_file: typing.Union[str, ConfigFile, None] = None) -> Config:
201
+ """
202
+ Automatically constructs the Config Object. The order of precedence is as follows
203
+ 1. If specified, read the config from the provided file path.
204
+ 2. If not specified, the config file is searched in the default locations.
205
+ a. ./config.yaml if it exists (current working directory)
206
+ b. `UCTL_CONFIG` environment variable
207
+ c. `FLYTECTL_CONFIG` environment variable
208
+ d. ~/.union/config.yaml if it exists
209
+ e. ~/.flyte/config.yaml if it exists
210
+ 3. If any value is not found in the config file, the default value is used.
211
+ 4. For any value there are environment variables that match the config variable names, those will override
212
+
213
+ :param config_file: file path to read the config from, if not specified default locations are searched
214
+ :return: Config
215
+ """
216
+ return Config.auto(config_file)
@@ -0,0 +1,64 @@
1
+ from flyte.config._reader import ConfigEntry, YamlConfigEntry
2
+
3
+
4
+ class Platform(object):
5
+ URL = ConfigEntry(YamlConfigEntry("admin.endpoint"))
6
+ INSECURE = ConfigEntry(YamlConfigEntry("admin.insecure", bool))
7
+ INSECURE_SKIP_VERIFY = ConfigEntry(YamlConfigEntry("admin.insecureSkipVerify", bool))
8
+ CONSOLE_ENDPOINT = ConfigEntry(YamlConfigEntry("console.endpoint"))
9
+ CA_CERT_FILE_PATH = ConfigEntry(YamlConfigEntry("admin.caCertFilePath"))
10
+ HTTP_PROXY_URL = ConfigEntry(YamlConfigEntry("admin.httpProxyURL"))
11
+
12
+
13
+ class Credentials(object):
14
+ SECTION = "credentials"
15
+ COMMAND = ConfigEntry(YamlConfigEntry("admin.command", list))
16
+ """
17
+ This command is executed to return a token using an external process.
18
+ """
19
+
20
+ PROXY_COMMAND = ConfigEntry(YamlConfigEntry("admin.proxyCommand", list))
21
+ """
22
+ This command is executed to return a token for authorization with a proxy
23
+ in front of Flyte using an external process.
24
+ """
25
+
26
+ CLIENT_ID = ConfigEntry(YamlConfigEntry("admin.clientId"))
27
+ """
28
+ This is the public identifier for the app which handles authorization for a Flyte deployment.
29
+ More details here: https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/.
30
+ """
31
+
32
+ CLIENT_CREDENTIALS_SECRET_LOCATION = ConfigEntry(YamlConfigEntry("admin.clientSecretLocation"))
33
+ """
34
+ Used for basic auth, which is automatically called during pyflyte. This will allow the Flyte engine to read the
35
+ password from a mounted file.
36
+ """
37
+
38
+ CLIENT_CREDENTIALS_SECRET_ENV_VAR = ConfigEntry(YamlConfigEntry("admin.clientSecretEnvVar"))
39
+ """
40
+ Used for basic auth, which is automatically called during pyflyte. This will allow the Flyte engine to read the
41
+ password from a mounted environment variable.
42
+ """
43
+
44
+ SCOPES = ConfigEntry(YamlConfigEntry("admin.scopes", list))
45
+ """
46
+ This setting can be used to manually pass in scopes into authenticator flows - eg.) for Auth0 compatibility
47
+ """
48
+
49
+ AUTH_MODE = ConfigEntry(YamlConfigEntry("admin.authType"))
50
+ """
51
+ The auth mode defines the behavior used to request and refresh credentials. The currently supported modes include:
52
+ - 'standard' or 'Pkce': This uses the pkce-enhanced authorization code flow by opening a browser window to initiate
53
+ credentials access.
54
+ - "DeviceFlow": This uses the Device Authorization Flow
55
+ - 'basic', 'client_credentials' or 'clientSecret': This uses symmetric key auth in which the end user enters a
56
+ client id and a client secret and public key encryption is used to facilitate authentication.
57
+ - None: No auth will be attempted.
58
+ """
59
+
60
+
61
+ class Task(object):
62
+ ORG = ConfigEntry(YamlConfigEntry("task.org"))
63
+ PROJECT = ConfigEntry(YamlConfigEntry("task.project"))
64
+ DOMAIN = ConfigEntry(YamlConfigEntry("task.domain"))