flyte 0.2.0b7__py3-none-any.whl → 0.2.0b9__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.

flyte/_task.py CHANGED
@@ -1,12 +1,13 @@
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
7
+ from inspect import iscoroutinefunction
6
8
  from typing import (
7
9
  TYPE_CHECKING,
8
10
  Any,
9
- Awaitable,
10
11
  Callable,
11
12
  Coroutine,
12
13
  Dict,
@@ -15,6 +16,7 @@ from typing import (
15
16
  Literal,
16
17
  Optional,
17
18
  ParamSpec,
19
+ TypeAlias,
18
20
  TypeVar,
19
21
  Union,
20
22
  )
@@ -42,6 +44,10 @@ if TYPE_CHECKING:
42
44
  P = ParamSpec("P") # capture the function's parameters
43
45
  R = TypeVar("R") # return type
44
46
 
47
+ AsyncFunctionType: TypeAlias = Callable[P, Coroutine[Any, Any, R]]
48
+ SyncFunctionType: TypeAlias = Callable[P, R]
49
+ FunctionTypes: TypeAlias = Union[AsyncFunctionType, SyncFunctionType]
50
+
45
51
 
46
52
  @dataclass(kw_only=True)
47
53
  class TaskTemplate(Generic[P, R]):
@@ -98,6 +104,9 @@ class TaskTemplate(Generic[P, R]):
98
104
  local: bool = field(default=False, init=False)
99
105
  ref: bool = field(default=False, init=False, repr=False, compare=False)
100
106
 
107
+ # Only used in python 3.10 and 3.11, where we cannot use markcoroutinefunction
108
+ _call_as_synchronous: bool = False
109
+
101
110
  def __post_init__(self):
102
111
  # If pod_template is set to a pod, verify
103
112
  if self.pod_template is not None and not isinstance(self.pod_template, str):
@@ -208,13 +217,60 @@ class TaskTemplate(Generic[P, R]):
208
217
  def native_interface(self) -> NativeInterface:
209
218
  return self.interface
210
219
 
211
- async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R]:
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
+
265
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
212
266
  """
213
267
  This is the entrypoint for an async function task at runtime. It will be called during an execution.
214
268
  Please do not override this method, if you simply want to modify the execution behavior, override the
215
269
  execute method.
216
270
 
217
- # TODO lets provide one hook to implement, _pre, _execute and _post. We do not want actual execute to
271
+ This needs to be overridable to maybe be async.
272
+ The returned thing from here needs to be an awaitable if the underlying task is async, and a regular object
273
+ if the task is not.
218
274
  """
219
275
  try:
220
276
  ctx = internal_ctx()
@@ -225,9 +281,18 @@ class TaskTemplate(Generic[P, R]):
225
281
  from ._internal.controllers import get_controller
226
282
 
227
283
  controller = get_controller()
228
- if controller:
229
- return await controller.submit(self, *args, **kwargs)
230
- return await self.execute(*args, **kwargs)
284
+ if not controller:
285
+ raise RuntimeSystemError("BadContext", "Controller is not initialized.")
286
+
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)
231
296
  except RuntimeSystemError:
232
297
  raise
233
298
  except RuntimeUserError:
@@ -235,6 +300,17 @@ class TaskTemplate(Generic[P, R]):
235
300
  except Exception as e:
236
301
  raise RuntimeUserError(type(e).__name__, str(e)) from e
237
302
 
303
+ def forward(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
304
+ """
305
+ Think of this as a local execute method for your task. This function will be invoked by the __call__ method
306
+ when not in a Flyte task execution context. See the implementation below for an example.
307
+
308
+ :param args:
309
+ :param kwargs:
310
+ :return:
311
+ """
312
+ raise NotImplementedError
313
+
238
314
  def override(
239
315
  self,
240
316
  *,
@@ -290,26 +366,38 @@ class AsyncFunctionTaskTemplate(TaskTemplate[P, R]):
290
366
  is decorated with the task decorator.
291
367
  """
292
368
 
293
- func: Callable[P, Awaitable[R]]
369
+ func: FunctionTypes
370
+
371
+ def __post_init__(self):
372
+ super().__post_init__()
373
+ if not iscoroutinefunction(self.func):
374
+ self._call_as_synchronous = True
294
375
 
295
376
  @cached_property
296
377
  def native_interface(self) -> NativeInterface:
297
378
  return NativeInterface.from_callable(self.func)
298
379
 
380
+ def forward(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
381
+ # In local execution, we want to just call the function. Note we're not awaiting anything here.
382
+ # If the function was a coroutine function, the coroutine is returned and the await that the caller has
383
+ # in front of the task invocation will handle the awaiting.
384
+ return self.func(*args, **kwargs)
385
+
299
386
  async def execute(self, *args: P.args, **kwargs: P.kwargs) -> R:
300
387
  """
301
388
  This is the execute method that will be called when the task is invoked. It will call the actual function.
302
389
  # TODO We may need to keep this as the bare func execute, and need a pre and post execute some other func.
303
390
  """
391
+
304
392
  ctx = internal_ctx()
393
+ assert ctx.data.task_context is not None, "Function should have already returned if not in a task context"
305
394
  ctx_data = await self.pre(*args, **kwargs)
306
- if ctx.data.task_context is not None:
307
- tctx = ctx.data.task_context.replace(data=ctx_data)
308
- with ctx.replace_task_context(tctx):
395
+ tctx = ctx.data.task_context.replace(data=ctx_data)
396
+ with ctx.replace_task_context(tctx):
397
+ if iscoroutinefunction(self.func):
309
398
  v = await self.func(*args, **kwargs)
310
- await self.post(v)
311
- else:
312
- v = await self.func(*args, **kwargs)
399
+ else:
400
+ v = self.func(*args, **kwargs)
313
401
  await self.post(v)
314
402
  return v
315
403
 
@@ -1,11 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import weakref
5
4
  from dataclasses import dataclass, field, replace
6
5
  from datetime import timedelta
7
- from functools import wraps
8
- from typing import TYPE_CHECKING, Awaitable, Callable, Dict, List, Literal, Optional, ParamSpec, TypeVar, Union, cast
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ List,
12
+ Literal,
13
+ Optional,
14
+ Union,
15
+ cast,
16
+ )
9
17
 
10
18
  import rich.repr
11
19
 
@@ -23,8 +31,7 @@ from .models import NativeInterface
23
31
  if TYPE_CHECKING:
24
32
  from kubernetes.client import V1PodTemplate
25
33
 
26
- P = ParamSpec("P") # capture the function's parameters
27
- R = TypeVar("R") # return type
34
+ from ._task import FunctionTypes, P, R
28
35
 
29
36
 
30
37
  @rich.repr.auto
@@ -54,7 +61,7 @@ class TaskEnvironment(Environment):
54
61
  """
55
62
 
56
63
  cache: Union[CacheRequest] = "auto"
57
- reusable: Union[ReusePolicy, Literal["auto"], None] = None
64
+ reusable: ReusePolicy | None = None
58
65
  # TODO Shall we make this union of string or env? This way we can lookup the env by module/file:name
59
66
  # TODO also we could add list of files that are used by this environment
60
67
 
@@ -65,32 +72,42 @@ class TaskEnvironment(Environment):
65
72
  name: str,
66
73
  image: Optional[Union[str, Image, Literal["auto"]]] = None,
67
74
  resources: Optional[Resources] = None,
68
- cache: Union[CacheRequest, None] = None,
69
75
  env: Optional[Dict[str, str]] = None,
70
- reusable: Union[ReusePolicy, None] = None,
71
76
  secrets: Optional[SecretRequest] = None,
72
77
  env_dep_hints: Optional[List[Environment]] = None,
78
+ **kwargs: Any,
73
79
  ) -> TaskEnvironment:
74
80
  """
75
- Clone the environment with new settings.
81
+ Clone the TaskEnvironment with new parameters.
82
+ besides the base environment parameters, you can override, kwargs like `cache`, `reusable`, etc.
83
+
76
84
  """
77
- if image is None:
78
- image = self.image
79
- else:
80
- image = "auto"
81
- return replace(
82
- self,
83
- cache=cache if cache else "auto",
84
- reusable=reusable,
85
- name=name,
86
- image=image,
87
- resources=resources,
88
- env=env,
89
- secrets=secrets,
90
- env_dep_hints=env_dep_hints if env_dep_hints else [],
91
- )
92
-
93
- def _task(
85
+ cache = kwargs.pop("cache", None)
86
+ reusable = kwargs.pop("reusable", None)
87
+
88
+ # validate unknown kwargs if needed
89
+ if kwargs:
90
+ raise TypeError(f"Unexpected keyword arguments: {list(kwargs.keys())}")
91
+
92
+ kwargs = self._get_kwargs()
93
+ kwargs["name"] = name
94
+ if image is not None:
95
+ kwargs["image"] = image
96
+ if resources is not None:
97
+ kwargs["resources"] = resources
98
+ if cache is not None:
99
+ kwargs["cache"] = cache
100
+ if env is not None:
101
+ kwargs["env"] = env
102
+ if reusable is not None:
103
+ kwargs["reusable"] = reusable
104
+ if secrets is not None:
105
+ kwargs["secrets"] = secrets
106
+ if env_dep_hints is not None:
107
+ kwargs["env_dep_hints"] = env_dep_hints
108
+ return replace(self, **kwargs)
109
+
110
+ def task(
94
111
  self,
95
112
  _func=None,
96
113
  *,
@@ -104,6 +121,7 @@ class TaskEnvironment(Environment):
104
121
  report: bool = False,
105
122
  ) -> Union[AsyncFunctionTaskTemplate, Callable[P, R]]:
106
123
  """
124
+ :param _func: Optional The function to decorate. If not provided, the decorator will return a callable that
107
125
  :param name: Optional The name of the task (defaults to the function name)
108
126
  :param cache: Optional The cache policy for the task, defaults to auto, which will cache the results of the
109
127
  task.
@@ -119,25 +137,12 @@ class TaskEnvironment(Environment):
119
137
  if pod_template is not None:
120
138
  raise ValueError("Cannot set pod_template when environment is reusable.")
121
139
 
122
- def decorator(func: Callable[P, Awaitable[R]]) -> AsyncFunctionTaskTemplate[P, R]:
140
+ def decorator(func: FunctionTypes) -> AsyncFunctionTaskTemplate[P, R]:
123
141
  task_name = name or func.__name__
124
142
  task_name = self.name + "." + task_name
125
- if len(task_name) > 30:
126
- # delete this if we can remove the restriction that task names must be <= 30 characters
127
- task_name = task_name[-30:]
128
-
129
- @wraps(func)
130
- async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
131
- return await func(*args, **kwargs)
132
-
133
- if not asyncio.iscoroutinefunction(func):
134
- raise TypeError(
135
- f"Function {func.__name__} is not a coroutine function. Use @env.task decorator for async tasks."
136
- f"You can simply mark your function as async def {func.__name__} to make it a coroutine function, "
137
- f"it is ok to write sync code in async functions, but not the other way around."
138
- )
139
- tmpl = AsyncFunctionTaskTemplate(
140
- func=wrapper,
143
+
144
+ tmpl: AsyncFunctionTaskTemplate = AsyncFunctionTaskTemplate(
145
+ func=func,
141
146
  name=task_name,
142
147
  image=self.image,
143
148
  resources=self.resources,
@@ -160,29 +165,6 @@ class TaskEnvironment(Environment):
160
165
  return cast(AsyncFunctionTaskTemplate, decorator)
161
166
  return cast(AsyncFunctionTaskTemplate, decorator(_func))
162
167
 
163
- @property
164
- def task(self) -> Callable:
165
- """
166
- Decorator to create a new task with the environment settings.
167
- The task will be executed in its own container with the specified image, resources, and environment variables,
168
- unless reusePolicy is set, in which case the same container will be reused for all tasks with the same
169
- environment settings.
170
-
171
- :param name: Optional The name of the task (defaults to the function name)
172
- :param cache: Optional The cache policy for the task, defaults to auto, which will cache the results of the
173
- task.
174
- :param retries: Optional The number of retries for the task, defaults to 0, which means no retries.
175
- :param docs: Optional The documentation for the task, if not provided the function docstring will be used.
176
- :param secrets: Optional The secrets that will be injected into the task at runtime.
177
- :param timeout: Optional The timeout for the task.
178
- :param pod_template: Optional The pod template for the task, if not provided the default pod template will be
179
- used.
180
- :param report: Optional Whether to generate the html report for the task, defaults to False.
181
-
182
- :return: New Task instance or Task decorator
183
- """
184
- return self._task
185
-
186
168
  @property
187
169
  def tasks(self) -> Dict[str, TaskTemplate]:
188
170
  """
@@ -21,5 +21,3 @@ async def run_coros(*coros: typing.Coroutine, return_when: str = asyncio.FIRST_C
21
21
  err = t.exception()
22
22
  if err:
23
23
  raise err
24
- else:
25
- print(f"Task result: {t.result()}")
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.0b7'
21
- __version_tuple__ = version_tuple = (0, 2, 0, 'b7')
20
+ __version__ = version = '0.2.0b9'
21
+ __version_tuple__ = version_tuple = (0, 2, 0, 'b9')
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
@@ -58,7 +58,7 @@ DRY_RUN_OPTION = click.Option(
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
 
@@ -74,6 +74,7 @@ class CLIConfig:
74
74
  """
75
75
 
76
76
  config: Config
77
+ ctx: click.Context
77
78
  log_level: int | None = logging.ERROR
78
79
  endpoint: str | None = None
79
80
  insecure: bool = False
@@ -109,8 +110,8 @@ class CLIConfig:
109
110
 
110
111
  class InvokeBaseMixin:
111
112
  """
112
- Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions and raise them as
113
- gclick.ClickException.
113
+ Mixin to catch grpc.RpcError, flyte.RpcError, other errors and other exceptions
114
+ and raise them as gclick.ClickException.
114
115
  """
115
116
 
116
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
- Create a new task or environment.
13
+ Create resources in a Flyte deployment.
14
14
  """
15
15
 
16
16
 
@@ -32,7 +32,27 @@ def secret(
32
32
  domain: str | None = None,
33
33
  ):
34
34
  """
35
- Create a new secret.
35
+ Create a new secret. The name of the secret is required. For example:
36
+
37
+ ```bash
38
+ $ flyte create secret my_secret --value my_value
39
+ ```
40
+
41
+ If `--from-file` is specified, the value will be read from the file instead of being provided directly:
42
+
43
+ ```bash
44
+ $ flyte create secret my_secret --from-file /path/to/secret_file
45
+ ```
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
+
53
+ ```bash
54
+ $ flyte create secret my_secret --type image_pull
55
+ ```
36
56
  """
37
57
  from flyte.remote import Secret
38
58
 
@@ -45,19 +65,29 @@ def secret(
45
65
 
46
66
  @create.command(cls=common.CommandBase)
47
67
  @click.option("--endpoint", type=str, help="Endpoint of the Flyte backend.")
48
- @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.")
49
69
  @click.option(
50
70
  "--org",
51
71
  type=str,
52
72
  required=False,
53
- 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 configusraion file.",
54
74
  )
55
75
  @click.option(
56
76
  "-o",
57
77
  "--output",
58
- type=click.Path(),
59
- default=Path.cwd(),
60
- help="Path to the output dir where the config will be saved, defaults to current directory.",
78
+ type=click.Path(exists=False, writable=True),
79
+ default=Path.cwd() / "config.yaml",
80
+ help="Path to the output directory where the configuration will be saved. Defaults to current directory.",
81
+ show_default=True,
82
+ )
83
+ @click.option(
84
+ "--force",
85
+ is_flag=True,
86
+ default=False,
87
+ help="Force overwrite of the configuration file if it already exists.",
88
+ show_default=True,
89
+ prompt="Are you sure you want to overwrite the configuration file?",
90
+ confirmation_prompt=True,
61
91
  )
62
92
  def config(
63
93
  output: Path,
@@ -66,25 +96,41 @@ def config(
66
96
  org: str | None = None,
67
97
  project: str | None = None,
68
98
  domain: str | None = None,
99
+ force: bool = False,
69
100
  ):
70
101
  """
71
- Create a new config file.
102
+ Creates a configuration file for Flyte CLI.
103
+ If the `--output` option is not specified, it will create a file named `config.yaml` in the current directory.
104
+ If the file already exists, it will raise an error unless the `--force` option is used.
72
105
  """
73
106
  import yaml
74
107
 
75
- output_file = output / "config.yaml"
76
- with open(output_file, "w") as f:
77
- d = {
78
- "admin": {
79
- "endpoint": endpoint,
80
- "insecure": insecure,
81
- },
82
- "task": {
83
- "org": org,
84
- "project": project,
85
- "domain": domain,
86
- },
87
- }
108
+ if output.exists() and not force:
109
+ raise click.BadParameter(f"Output file {output} already exists. Use --force to overwrite.")
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
+
128
+ with open(output, "w") as f:
129
+ d: Dict[str, Any] = {}
130
+ if admin:
131
+ d["admin"] = admin
132
+ if task:
133
+ d["task"] = task
88
134
  yaml.dump(d, f)
89
135
 
90
- click.echo(f"Config file created at {output_file}")
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
- Delete a task or environment.
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.
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
@@ -138,5 +138,8 @@ class EnvFiles(common.FileGroup):
138
138
 
139
139
  deploy = EnvFiles(
140
140
  name="deploy",
141
- help="deploy one or more environments from a python file.",
141
+ help="""
142
+ Deploy one or more environments from a python file.
143
+ The deploy command will create or update environments in the Flyte system.
144
+ """,
142
145
  )