flyte 0.2.0b8__py3-none-any.whl → 0.2.0b10__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 (42) hide show
  1. flyte/__init__.py +4 -2
  2. flyte/_context.py +7 -1
  3. flyte/_deploy.py +3 -0
  4. flyte/_group.py +1 -0
  5. flyte/_initialize.py +15 -5
  6. flyte/_internal/controllers/__init__.py +13 -2
  7. flyte/_internal/controllers/_local_controller.py +67 -5
  8. flyte/_internal/controllers/remote/_controller.py +47 -2
  9. flyte/_internal/runtime/taskrunner.py +2 -1
  10. flyte/_map.py +215 -0
  11. flyte/_run.py +109 -64
  12. flyte/_task.py +56 -7
  13. flyte/_utils/helpers.py +15 -0
  14. flyte/_version.py +2 -2
  15. flyte/cli/__init__.py +0 -7
  16. flyte/cli/_abort.py +1 -1
  17. flyte/cli/_common.py +7 -7
  18. flyte/cli/_create.py +44 -29
  19. flyte/cli/_delete.py +2 -2
  20. flyte/cli/_deploy.py +3 -3
  21. flyte/cli/_gen.py +12 -4
  22. flyte/cli/_get.py +35 -27
  23. flyte/cli/_params.py +1 -1
  24. flyte/cli/main.py +32 -29
  25. flyte/extras/_container.py +29 -32
  26. flyte/io/__init__.py +17 -1
  27. flyte/io/_file.py +2 -0
  28. flyte/io/{structured_dataset → _structured_dataset}/basic_dfs.py +1 -1
  29. flyte/io/{structured_dataset → _structured_dataset}/structured_dataset.py +1 -1
  30. flyte/models.py +11 -1
  31. flyte/syncify/_api.py +43 -15
  32. flyte/types/__init__.py +23 -0
  33. flyte/{io/pickle/transformer.py → types/_pickle.py} +2 -1
  34. flyte/types/_type_engine.py +4 -4
  35. {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/METADATA +7 -6
  36. {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/RECORD +40 -41
  37. flyte/io/_dataframe.py +0 -0
  38. flyte/io/pickle/__init__.py +0 -0
  39. /flyte/io/{structured_dataset → _structured_dataset}/__init__.py +0 -0
  40. {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/WHEEL +0 -0
  41. {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/entry_points.txt +0 -0
  42. {flyte-0.2.0b8.dist-info → flyte-0.2.0b10.dist-info}/top_level.txt +0 -0
flyte/_run.py CHANGED
@@ -1,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import pathlib
4
5
  import uuid
5
6
  from typing import TYPE_CHECKING, Any, Literal, Optional, Tuple, Union, cast
6
7
 
8
+ import flyte.errors
7
9
  from flyte.errors import InitializationError
8
10
  from flyte.models import ActionID, Checkpoints, CodeBundle, RawDataPath, SerializationContext, TaskContext
9
11
  from flyte.syncify import syncify
@@ -24,6 +26,7 @@ from ._tools import ipython_check
24
26
 
25
27
  if TYPE_CHECKING:
26
28
  from flyte.remote import Run
29
+ from flyte.remote._task import LazyEntity
27
30
 
28
31
  from ._code_bundle import CopyFiles
29
32
 
@@ -80,8 +83,11 @@ class _Runner:
80
83
  self._run_base_dir = run_base_dir or "/tmp/base"
81
84
 
82
85
  @requires_initialization
83
- async def _run_remote(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> Run:
86
+ async def _run_remote(self, obj: TaskTemplate[P, R] | LazyEntity, *args: P.args, **kwargs: P.kwargs) -> Run:
87
+ import grpc
88
+
84
89
  from flyte.remote import Run
90
+ from flyte.remote._task import LazyEntity
85
91
 
86
92
  from ._code_bundle import build_code_bundle, build_pkl_bundle
87
93
  from ._deploy import build_images, plan_deploy
@@ -92,37 +98,47 @@ class _Runner:
92
98
 
93
99
  cfg = get_common_config()
94
100
 
95
- if obj.parent_env is None:
96
- raise ValueError("Task is not attached to an environment. Please attach the task to an environment.")
101
+ if isinstance(obj, LazyEntity):
102
+ task = await obj.fetch.aio()
103
+ task_spec = task.pb2.spec
104
+ inputs = await convert_from_native_to_inputs(task.interface, *args, **kwargs)
105
+ version = task.pb2.task_id.version
106
+ code_bundle = None
107
+ else:
108
+ if obj.parent_env is None:
109
+ raise ValueError("Task is not attached to an environment. Please attach the task to an environment")
97
110
 
98
- deploy_plan = plan_deploy(cast(Environment, obj.parent_env()))
99
- image_cache = await build_images(deploy_plan)
111
+ deploy_plan = plan_deploy(cast(Environment, obj.parent_env()))
112
+ image_cache = await build_images(deploy_plan)
100
113
 
101
- if self._interactive_mode:
102
- code_bundle = await build_pkl_bundle(
103
- obj, upload_to_controlplane=not self._dry_run, copy_bundle_to=self._copy_bundle_to
104
- )
105
- else:
106
- if self._copy_files != "none":
107
- code_bundle = await build_code_bundle(
108
- from_dir=cfg.root_dir, dryrun=self._dry_run, copy_bundle_to=self._copy_bundle_to
114
+ if self._interactive_mode:
115
+ code_bundle = await build_pkl_bundle(
116
+ obj, upload_to_controlplane=not self._dry_run, copy_bundle_to=self._copy_bundle_to
109
117
  )
110
118
  else:
111
- code_bundle = None
119
+ if self._copy_files != "none":
120
+ code_bundle = await build_code_bundle(
121
+ from_dir=cfg.root_dir,
122
+ dryrun=self._dry_run,
123
+ copy_bundle_to=self._copy_bundle_to,
124
+ copy_style=self._copy_files,
125
+ )
126
+ else:
127
+ code_bundle = None
112
128
 
113
- version = self._version or (
114
- code_bundle.computed_version if code_bundle and code_bundle.computed_version else None
115
- )
116
- if not version:
117
- raise ValueError("Version is required when running a task")
118
- s_ctx = SerializationContext(
119
- code_bundle=code_bundle,
120
- version=version,
121
- image_cache=image_cache,
122
- root_dir=cfg.root_dir,
123
- )
124
- task_spec = translate_task_to_wire(obj, s_ctx)
125
- inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
129
+ version = self._version or (
130
+ code_bundle.computed_version if code_bundle and code_bundle.computed_version else None
131
+ )
132
+ if not version:
133
+ raise ValueError("Version is required when running a task")
134
+ s_ctx = SerializationContext(
135
+ code_bundle=code_bundle,
136
+ version=version,
137
+ image_cache=image_cache,
138
+ root_dir=cfg.root_dir,
139
+ )
140
+ task_spec = translate_task_to_wire(obj, s_ctx)
141
+ inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
126
142
 
127
143
  if not self._dry_run:
128
144
  if get_client() is None:
@@ -159,15 +175,35 @@ class _Runner:
159
175
  if task_spec.task_template.id.version == "":
160
176
  task_spec.task_template.id.version = version
161
177
 
162
- resp = await get_client().run_service.CreateRun(
163
- run_service_pb2.CreateRunRequest(
164
- run_id=run_id,
165
- project_id=project_id,
166
- task_spec=task_spec,
167
- inputs=inputs.proto_inputs,
168
- ),
169
- )
170
- return Run(pb2=resp.run)
178
+ try:
179
+ resp = await get_client().run_service.CreateRun(
180
+ run_service_pb2.CreateRunRequest(
181
+ run_id=run_id,
182
+ project_id=project_id,
183
+ task_spec=task_spec,
184
+ inputs=inputs.proto_inputs,
185
+ ),
186
+ )
187
+ return Run(pb2=resp.run)
188
+ except grpc.aio.AioRpcError as e:
189
+ if e.code() == grpc.StatusCode.UNAVAILABLE:
190
+ raise flyte.errors.RuntimeSystemError(
191
+ "SystemUnavailableError",
192
+ "Flyte system is currently unavailable. check your configuration, or the service status.",
193
+ ) from e
194
+ elif e.code() == grpc.StatusCode.INVALID_ARGUMENT:
195
+ raise flyte.errors.RuntimeUserError("InvalidArgumentError", e.details())
196
+ elif e.code() == grpc.StatusCode.ALREADY_EXISTS:
197
+ # TODO maybe this should be a pass and return existing run?
198
+ raise flyte.errors.RuntimeUserError(
199
+ "RunAlreadyExistsError",
200
+ f"A run with the name '{self._name}' already exists. Please choose a different name.",
201
+ )
202
+ else:
203
+ raise flyte.errors.RuntimeSystemError(
204
+ "RunCreationError",
205
+ f"Failed to create run: {e.details()}",
206
+ ) from e
171
207
 
172
208
  class DryRun(Run):
173
209
  def __init__(self, _task_spec, _inputs, _code_bundle):
@@ -224,7 +260,10 @@ class _Runner:
224
260
  else:
225
261
  if self._copy_files != "none":
226
262
  code_bundle = await build_code_bundle(
227
- from_dir=cfg.root_dir, dryrun=self._dry_run, copy_bundle_to=self._copy_bundle_to
263
+ from_dir=cfg.root_dir,
264
+ dryrun=self._dry_run,
265
+ copy_bundle_to=self._copy_bundle_to,
266
+ copy_style=self._copy_files,
228
267
  )
229
268
  else:
230
269
  code_bundle = None
@@ -287,43 +326,44 @@ class _Runner:
287
326
 
288
327
  async def _run_local(self, obj: TaskTemplate[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
289
328
  from flyte._internal.controllers import create_controller
290
- from flyte._internal.runtime.convert import (
291
- convert_error_to_native,
292
- convert_from_native_to_inputs,
293
- convert_outputs_to_native,
294
- )
295
- from flyte._internal.runtime.entrypoints import direct_dispatch
329
+ from flyte._internal.controllers._local_controller import LocalController
330
+ from flyte.report import Report
331
+
332
+ controller = cast(LocalController, create_controller("local"))
296
333
 
297
- controller = create_controller("local")
298
- inputs = await convert_from_native_to_inputs(obj.native_interface, *args, **kwargs)
299
334
  if self._name is None:
300
335
  action = ActionID.create_random()
301
336
  else:
302
337
  action = ActionID(name=self._name)
303
- out, err = await direct_dispatch(
304
- obj,
338
+
339
+ ctx = internal_ctx()
340
+ tctx = TaskContext(
305
341
  action=action,
306
- raw_data_path=internal_ctx().raw_data,
307
- version="na",
308
- controller=controller,
309
- inputs=inputs,
310
- output_path=self._metadata_path,
311
- run_base_dir=self._metadata_path,
312
342
  checkpoints=Checkpoints(
313
343
  prev_checkpoint_path=internal_ctx().raw_data.path, checkpoint_path=internal_ctx().raw_data.path
314
344
  ),
315
- ) # type: ignore
316
- if err:
317
- native_err = convert_error_to_native(err)
318
- if native_err:
319
- raise native_err
320
- if obj.native_interface.outputs and len(obj.native_interface.outputs) > 0:
321
- if out is not None:
322
- return cast(R, await convert_outputs_to_native(obj.native_interface, out))
323
- return cast(R, None)
345
+ code_bundle=None,
346
+ output_path=self._metadata_path,
347
+ run_base_dir=self._metadata_path,
348
+ version="na",
349
+ raw_data_path=internal_ctx().raw_data,
350
+ compiled_image_cache=None,
351
+ report=Report(name=action.name),
352
+ mode="local",
353
+ )
354
+ with ctx.replace_task_context(tctx):
355
+ # make the local version always runs on a different thread, returns a wrapped future.
356
+ if obj._call_as_synchronous:
357
+ fut = controller.submit_sync(obj, *args, **kwargs)
358
+ awaitable = asyncio.wrap_future(fut)
359
+ return await awaitable
360
+ else:
361
+ return await controller.submit(obj, *args, **kwargs)
324
362
 
325
363
  @syncify
326
- async def run(self, task: TaskTemplate[P, Union[R, Run]], *args: P.args, **kwargs: P.kwargs) -> Union[R, Run]:
364
+ async def run(
365
+ self, task: TaskTemplate[P, Union[R, Run]] | LazyEntity, *args: P.args, **kwargs: P.kwargs
366
+ ) -> Union[R, Run]:
327
367
  """
328
368
  Run an async `@env.task` or `TaskTemplate` instance. The existing async context will be used.
329
369
 
@@ -345,13 +385,18 @@ class _Runner:
345
385
  :param kwargs: Keyword arguments to pass to the Task
346
386
  :return: Run instance or the result of the task
347
387
  """
388
+ from flyte.remote._task import LazyEntity
389
+
390
+ if isinstance(task, LazyEntity) and self._mode != "remote":
391
+ raise ValueError("Remote task can only be run in remote mode.")
348
392
  if self._mode == "remote":
349
393
  return await self._run_remote(task, *args, **kwargs)
394
+ task = cast(TaskTemplate, task)
350
395
  if self._mode == "hybrid":
351
396
  return await self._run_hybrid(task, *args, **kwargs)
352
397
 
353
398
  # TODO We could use this for remote as well and users could simply pass flyte:// or s3:// or file://
354
- async with internal_ctx().new_raw_data_path(
399
+ with internal_ctx().new_raw_data_path(
355
400
  raw_data_path=RawDataPath.from_local_folder(local_folder=self._raw_data_path)
356
401
  ):
357
402
  return await self._run_local(task, *args, **kwargs)
flyte/_task.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import weakref
4
5
  from dataclasses import dataclass, field, replace
5
6
  from functools import cached_property
@@ -216,6 +217,51 @@ class TaskTemplate(Generic[P, R]):
216
217
  def native_interface(self) -> NativeInterface:
217
218
  return self.interface
218
219
 
220
+ async def aio(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
221
+ """
222
+ The aio function allows executing "sync" tasks, in an async context. This helps with migrating v1 defined sync
223
+ tasks to be used within an asyncio parent task.
224
+ This function will also re-raise exceptions from the underlying task.
225
+
226
+ Example:
227
+ ```python
228
+ @env.task
229
+ def my_legacy_task(x: int) -> int:
230
+ return x
231
+
232
+ @env.task
233
+ async def my_new_parent_task(n: int) -> List[int]:
234
+ collect = []
235
+ for x in range(n):
236
+ collect.append(my_legacy_task.aio(x))
237
+ return asyncio.gather(*collect)
238
+ ```
239
+ :param args:
240
+ :param kwargs:
241
+ :return:
242
+ """
243
+
244
+ ctx = internal_ctx()
245
+ if ctx.is_task_context():
246
+ from ._internal.controllers import get_controller
247
+
248
+ # If we are in a task context, that implies we are executing a Run.
249
+ # In this scenario, we should submit the task to the controller.
250
+ controller = get_controller()
251
+ if controller:
252
+ if self._call_as_synchronous:
253
+ fut = controller.submit_sync(self, *args, **kwargs)
254
+ asyncio_future = asyncio.wrap_future(fut) # Wrap the future to make it awaitable
255
+ return await asyncio_future
256
+ else:
257
+ return await controller.submit(self, *args, **kwargs)
258
+ else:
259
+ raise RuntimeSystemError("BadContext", "Controller is not initialized.")
260
+ else:
261
+ # Local execute, just stay out of the way, but because .aio is used, we want to return an awaitable,
262
+ # even for synchronous tasks. This is to support migration.
263
+ return self.forward(*args, **kwargs)
264
+
219
265
  def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
220
266
  """
221
267
  This is the entrypoint for an async function task at runtime. It will be called during an execution.
@@ -235,15 +281,18 @@ class TaskTemplate(Generic[P, R]):
235
281
  from ._internal.controllers import get_controller
236
282
 
237
283
  controller = get_controller()
238
- if controller:
239
- if self._call_as_synchronous:
240
- return controller.submit_sync(self, *args, **kwargs)
241
- else:
242
- return controller.submit(self, *args, **kwargs)
243
- else:
284
+ if not controller:
244
285
  raise RuntimeSystemError("BadContext", "Controller is not initialized.")
245
286
 
246
- return self.forward(*args, **kwargs)
287
+ if self._call_as_synchronous:
288
+ fut = controller.submit_sync(self, *args, **kwargs)
289
+ x = fut.result(None)
290
+ return x
291
+ else:
292
+ return controller.submit(self, *args, **kwargs)
293
+ else:
294
+ # If not in task context, purely function run, stay out of the way
295
+ return self.forward(*args, **kwargs)
247
296
  except RuntimeSystemError:
248
297
  raise
249
298
  except RuntimeUserError:
flyte/_utils/helpers.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import string
3
3
  import typing
4
+ from contextlib import contextmanager
4
5
  from pathlib import Path
5
6
 
6
7
 
@@ -106,3 +107,17 @@ def get_cwd_editable_install() -> typing.Optional[Path]:
106
107
  return install # note we want the install folder, not the parent
107
108
 
108
109
  return None
110
+
111
+
112
+ @contextmanager
113
+ def _selector_policy():
114
+ import asyncio
115
+
116
+ original_policy = asyncio.get_event_loop_policy()
117
+ try:
118
+ if os.name == "nt" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"):
119
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
120
+
121
+ yield
122
+ finally:
123
+ asyncio.set_event_loop_policy(original_policy)
flyte/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.2.0b8'
21
- __version_tuple__ = version_tuple = (0, 2, 0, 'b8')
20
+ __version__ = version = '0.2.0b10'
21
+ __version_tuple__ = version_tuple = (0, 2, 0, 'b10')
flyte/cli/__init__.py CHANGED
@@ -1,10 +1,3 @@
1
- """
2
- # CLI for Flyte
3
-
4
- The flyte cli follows a simple verb based structure, where the top-level commands are verbs that describe the action
5
- to be taken, and the subcommands are nouns that describe the object of the action.
6
- """
7
-
8
1
  from flyte.cli.main import main
9
2
 
10
3
  __all__ = ["main"]
flyte/cli/_abort.py CHANGED
@@ -6,7 +6,7 @@ from flyte.cli import _common as common
6
6
  @click.group(name="abort")
7
7
  def abort():
8
8
  """
9
- Abort a run.
9
+ Abort an ongoing process.
10
10
  """
11
11
 
12
12
 
flyte/cli/_common.py CHANGED
@@ -32,7 +32,7 @@ PROJECT_OPTION = click.Option(
32
32
  required=False,
33
33
  type=str,
34
34
  default=None,
35
- help="Project to operate on",
35
+ help="Project to which this command applies.",
36
36
  show_default=True,
37
37
  )
38
38
 
@@ -41,7 +41,7 @@ DOMAIN_OPTION = click.Option(
41
41
  required=False,
42
42
  type=str,
43
43
  default=None,
44
- help="Domain to operate on",
44
+ help="Domain to which this command applies.",
45
45
  show_default=True,
46
46
  )
47
47
 
@@ -51,14 +51,14 @@ DRY_RUN_OPTION = click.Option(
51
51
  type=bool,
52
52
  is_flag=True,
53
53
  default=False,
54
- help="Dry run, do not actually call the backend service.",
54
+ help="Dry run. Do not actually call the backend service.",
55
55
  show_default=True,
56
56
  )
57
57
 
58
58
 
59
59
  def _common_options() -> List[click.Option]:
60
60
  """
61
- Common options for that will be added to all commands and groups that inherit from CommandBase or GroupBase.
61
+ Common options that will be added to all commands and groups that inherit from CommandBase or GroupBase.
62
62
  """
63
63
  return [PROJECT_OPTION, DOMAIN_OPTION]
64
64
 
@@ -105,13 +105,13 @@ class CLIConfig:
105
105
  updated_config = self.config.with_params(platform_cfg, task_cfg)
106
106
 
107
107
  logger.debug(f"Initializing CLI with config: {updated_config}")
108
- flyte.init_auto_from_config(updated_config)
108
+ flyte.init_from_config(updated_config)
109
109
 
110
110
 
111
111
  class InvokeBaseMixin:
112
112
  """
113
- Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions and raise them as
114
- gclick.ClickException.
113
+ Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions
114
+ and raise them as gclick.ClickException.
115
115
  """
116
116
 
117
117
  def invoke(self, ctx):
flyte/cli/_create.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import get_args
2
+ from typing import Any, Dict, get_args
3
3
 
4
4
  import rich_click as click
5
5
 
@@ -10,7 +10,7 @@ from flyte.remote._secret import SecretTypes
10
10
  @click.group(name="create")
11
11
  def create():
12
12
  """
13
- The create subcommand allows you to create resources in a Flyte deployment.
13
+ Create resources in a Flyte deployment.
14
14
  """
15
15
 
16
16
 
@@ -32,22 +32,26 @@ def secret(
32
32
  domain: str | None = None,
33
33
  ):
34
34
  """
35
- Create a new secret, the name of the secret is required.
35
+ Create a new secret. The name of the secret is required. For example:
36
36
 
37
- Examples:
38
37
  ```bash
39
- flyte create secret my_secret --value my_value
38
+ $ flyte create secret my_secret --value my_value
40
39
  ```
41
- If `--from-file` is specified, the value will be read from the file instead of being provided directly.
42
- Example:
40
+
41
+ If `--from-file` is specified, the value will be read from the file instead of being provided directly:
42
+
43
43
  ```bash
44
- flyte create secret my_secret --from-file /path/to/secret_file
44
+ $ flyte create secret my_secret --from-file /path/to/secret_file
45
45
  ```
46
- Secret types can be used to create specific types of secrets. Some secrets are useful for image pull, while some
47
- are `regular` / general purpose secrets.
48
- Example:
46
+
47
+ The `--type` option can be used to create specific types of secrets.
48
+ Either `regular` or `image_pull` can be specified.
49
+ Secrets intended to access container images should be specified as `image_pull`.
50
+ Other secrets should be specified as `regular`.
51
+ If no type is specified, `regular` is assumed.
52
+
49
53
  ```bash
50
- flyte create secret my_secret --type image_pull
54
+ $ flyte create secret my_secret --type image_pull
51
55
  ```
52
56
  """
53
57
  from flyte.remote import Secret
@@ -61,28 +65,28 @@ def secret(
61
65
 
62
66
  @create.command(cls=common.CommandBase)
63
67
  @click.option("--endpoint", type=str, help="Endpoint of the Flyte backend.")
64
- @click.option("--insecure", is_flag=True, help="Use insecure connection to the Flyte backend.")
68
+ @click.option("--insecure", is_flag=True, help="Use an insecure connection to the Flyte backend.")
65
69
  @click.option(
66
70
  "--org",
67
71
  type=str,
68
72
  required=False,
69
- help="Organization to use, this will override the organization in the config file.",
73
+ help="Organization to use. This will override the organization in the configuration file.",
70
74
  )
71
75
  @click.option(
72
76
  "-o",
73
77
  "--output",
74
78
  type=click.Path(exists=False, writable=True),
75
79
  default=Path.cwd() / "config.yaml",
76
- help="Path to the output dir where the config will be saved, defaults to current directory.",
80
+ help="Path to the output directory where the configuration will be saved. Defaults to current directory.",
77
81
  show_default=True,
78
82
  )
79
83
  @click.option(
80
84
  "--force",
81
85
  is_flag=True,
82
86
  default=False,
83
- help="Force overwrite the config file if it already exists.",
87
+ help="Force overwrite of the configuration file if it already exists.",
84
88
  show_default=True,
85
- prompt="Are you sure you want to overwrite the config file?",
89
+ prompt="Are you sure you want to overwrite the configuration file?",
86
90
  confirmation_prompt=True,
87
91
  )
88
92
  def config(
@@ -95,7 +99,7 @@ def config(
95
99
  force: bool = False,
96
100
  ):
97
101
  """
98
- This command creates a configuration file for Flyte CLI.
102
+ Creates a configuration file for Flyte CLI.
99
103
  If the `--output` option is not specified, it will create a file named `config.yaml` in the current directory.
100
104
  If the file already exists, it will raise an error unless the `--force` option is used.
101
105
  """
@@ -104,18 +108,29 @@ def config(
104
108
  if output.exists() and not force:
105
109
  raise click.BadParameter(f"Output file {output} already exists. Use --force to overwrite.")
106
110
 
111
+ admin: Dict[str, Any] = {}
112
+ if endpoint:
113
+ admin["endpoint"] = endpoint
114
+ if insecure:
115
+ admin["insecure"] = insecure
116
+
117
+ task: Dict[str, str] = {}
118
+ if org:
119
+ task["org"] = org
120
+ if project:
121
+ task["project"] = project
122
+ if domain:
123
+ task["domain"] = domain
124
+
125
+ if not admin and not task:
126
+ raise click.BadParameter("At least one of --endpoint or --org must be provided.")
127
+
107
128
  with open(output, "w") as f:
108
- d = {
109
- "admin": {
110
- "endpoint": endpoint,
111
- "insecure": insecure,
112
- },
113
- "task": {
114
- "org": org,
115
- "project": project,
116
- "domain": domain,
117
- },
118
- }
129
+ d: Dict[str, Any] = {}
130
+ if admin:
131
+ d["admin"] = admin
132
+ if task:
133
+ d["task"] = task
119
134
  yaml.dump(d, f)
120
135
 
121
136
  click.echo(f"Config file created at {output}")
flyte/cli/_delete.py CHANGED
@@ -6,7 +6,7 @@ import flyte.cli._common as common
6
6
  @click.group(name="delete")
7
7
  def delete():
8
8
  """
9
- The delete subcommand allows you to remove resources from a Flyte deployment.
9
+ Remove resources from a Flyte deployment.
10
10
  """
11
11
 
12
12
 
@@ -15,7 +15,7 @@ def delete():
15
15
  @click.pass_obj
16
16
  def secret(cfg: common.CLIConfig, name: str, project: str | None = None, domain: str | None = None):
17
17
  """
18
- Delete a secret, the name of the secret is required.
18
+ Delete a secret. The name of the secret is required.
19
19
  """
20
20
  from flyte.remote import Secret
21
21
 
flyte/cli/_deploy.py CHANGED
@@ -61,7 +61,7 @@ class DeployArguments:
61
61
  @classmethod
62
62
  def options(cls) -> List[click.Option]:
63
63
  """
64
- Return the set of base parameters added to every pyflyte run workflow subcommand.
64
+ Return the set of base parameters added to every flyte run workflow subcommand.
65
65
  """
66
66
  return [common.get_option_from_metadata(f.metadata) for f in fields(cls) if f.metadata]
67
67
 
@@ -87,7 +87,7 @@ class DeployEnvCommand(click.Command):
87
87
 
88
88
  class EnvPerFileGroup(common.ObjectsPerFileGroup):
89
89
  """
90
- Group that creates a command for each task in the current directory that is not __init__.py.
90
+ Group that creates a command for each task in the current directory that is not `__init__.py`.
91
91
  """
92
92
 
93
93
  def __init__(self, filename: Path, deploy_args: DeployArguments, *args, **kwargs):
@@ -111,7 +111,7 @@ class EnvPerFileGroup(common.ObjectsPerFileGroup):
111
111
 
112
112
  class EnvFiles(common.FileGroup):
113
113
  """
114
- Group that creates a command for each file in the current directory that is not __init__.py.
114
+ Group that creates a command for each file in the current directory that is not `__init__.py`.
115
115
  """
116
116
 
117
117
  common_options_enabled = False