flyte 0.2.0b7__py3-none-any.whl → 0.2.0b8__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/_environment.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  from dataclasses import dataclass, field
4
- from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union
5
6
 
6
7
  import rich.repr
7
8
 
@@ -14,6 +15,10 @@ if TYPE_CHECKING:
14
15
  from kubernetes.client import V1PodTemplate
15
16
 
16
17
 
18
+ def is_snake_or_kebab_with_numbers(s: str) -> bool:
19
+ return re.fullmatch(r"^[a-z0-9]+([_-][a-z0-9]+)*$", s) is not None
20
+
21
+
17
22
  @rich.repr.auto
18
23
  @dataclass(init=True, repr=True)
19
24
  class Environment:
@@ -36,8 +41,44 @@ class Environment:
36
41
  resources: Optional[Resources] = None
37
42
  image: Union[str, Image, Literal["auto"]] = "auto"
38
43
 
44
+ def __post_init__(self):
45
+ if not is_snake_or_kebab_with_numbers(self.name):
46
+ raise ValueError(f"Environment name '{self.name}' must be in snake_case or kebab-case format.")
47
+
39
48
  def add_dependency(self, *env: Environment):
40
49
  """
41
50
  Add a dependency to the environment.
42
51
  """
43
52
  self.env_dep_hints.extend(env)
53
+
54
+ def clone_with(
55
+ self,
56
+ name: str,
57
+ image: Optional[Union[str, Image, Literal["auto"]]] = None,
58
+ resources: Optional[Resources] = None,
59
+ env: Optional[Dict[str, str]] = None,
60
+ secrets: Optional[SecretRequest] = None,
61
+ env_dep_hints: Optional[List[Environment]] = None,
62
+ **kwargs: Any,
63
+ ) -> Environment:
64
+ raise NotImplementedError
65
+
66
+ def _get_kwargs(self) -> Dict[str, Any]:
67
+ """
68
+ Get the keyword arguments for the environment.
69
+ """
70
+ kwargs: Dict[str, Any] = {
71
+ "env_dep_hints": self.env_dep_hints,
72
+ "image": self.image,
73
+ }
74
+ if self.resources is not None:
75
+ kwargs["resources"] = self.resources
76
+ if self.secrets is not None:
77
+ kwargs["secrets"] = self.secrets
78
+ if self.env is not None:
79
+ kwargs["env"] = self.env
80
+ if self.pod_template is not None:
81
+ kwargs["pod_template"] = self.pod_template
82
+ if self.description is not None:
83
+ kwargs["description"] = self.description
84
+ return kwargs
flyte/_image.py CHANGED
@@ -444,8 +444,7 @@ class Image:
444
444
  ```
445
445
 
446
446
  For more information on the uv script format, see the documentation:
447
- <href="https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies">
448
- UV: Declaring script dependencies</href>
447
+ [UV: Declaring script dependencies](https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies)
449
448
 
450
449
  :param name: name of the image
451
450
  :param registry: registry to use for the image
@@ -28,6 +28,8 @@ class Controller(Protocol):
28
28
  """
29
29
  ...
30
30
 
31
+ def submit_sync(self, _task: TaskTemplate, *args, **kwargs) -> Any: ...
32
+
31
33
  async def submit_task_ref(self, _task: task_definition_pb2.TaskDetails, *args, **kwargs) -> Any:
32
34
  """
33
35
  Submit a task reference to the controller asynchronously and wait for the result. This is async and will block
@@ -8,6 +8,7 @@ from flyte._internal.runtime.entrypoints import direct_dispatch
8
8
  from flyte._logging import log, logger
9
9
  from flyte._protos.workflow import task_definition_pb2
10
10
  from flyte._task import TaskTemplate
11
+ from flyte._utils.asyn import loop_manager
11
12
  from flyte.models import ActionID, NativeInterface, RawDataPath
12
13
 
13
14
  R = TypeVar("R")
@@ -58,6 +59,8 @@ class LocalController:
58
59
  return result
59
60
  return out
60
61
 
62
+ submit_sync = loop_manager.synced(submit)
63
+
61
64
  async def finalize_parent_action(self, action: ActionID):
62
65
  pass
63
66
 
@@ -21,6 +21,7 @@ from flyte._internal.runtime.task_serde import translate_task_to_wire
21
21
  from flyte._logging import logger
22
22
  from flyte._protos.workflow import run_definition_pb2, task_definition_pb2
23
23
  from flyte._task import TaskTemplate
24
+ from flyte._utils.asyn import loop_manager
24
25
  from flyte.models import ActionID, NativeInterface, SerializationContext
25
26
 
26
27
  R = TypeVar("R")
@@ -235,6 +236,8 @@ class RemoteController(Controller):
235
236
  async with self._parent_action_semaphore[unique_action_name(current_action_id)]:
236
237
  return await self._submit(task_call_seq, _task, *args, **kwargs)
237
238
 
239
+ submit_sync = loop_manager.synced(submit)
240
+
238
241
  async def finalize_parent_action(self, action_id: ActionID):
239
242
  """
240
243
  This method is invoked when the parent action is finished. It will finalize the run and upload the outputs
@@ -38,11 +38,11 @@ class ActionCache:
38
38
  """
39
39
  Add an action to the cache if it doesn't exist. This is invoked by the watch.
40
40
  """
41
- logger.info(f"Observing phase {run_definition_pb2.Phase.Name(state.phase)} for {state.action_id.name}")
41
+ logger.debug(f"Observing phase {run_definition_pb2.Phase.Name(state.phase)} for {state.action_id.name}")
42
42
  if state.output_uri:
43
- logger.info(f"Output URI: {state.output_uri}")
43
+ logger.debug(f"Output URI: {state.output_uri}")
44
44
  else:
45
- logger.info(f"{state.action_id.name} has no output URI")
45
+ logger.warning(f"{state.action_id.name} has no output URI")
46
46
  if state.phase == run_definition_pb2.Phase.PHASE_FAILED:
47
47
  logger.error(
48
48
  f"Action {state.action_id.name} failed with error (msg):"
flyte/_task.py CHANGED
@@ -3,10 +3,10 @@ from __future__ import annotations
3
3
  import weakref
4
4
  from dataclasses import dataclass, field, replace
5
5
  from functools import cached_property
6
+ from inspect import iscoroutinefunction
6
7
  from typing import (
7
8
  TYPE_CHECKING,
8
9
  Any,
9
- Awaitable,
10
10
  Callable,
11
11
  Coroutine,
12
12
  Dict,
@@ -15,6 +15,7 @@ from typing import (
15
15
  Literal,
16
16
  Optional,
17
17
  ParamSpec,
18
+ TypeAlias,
18
19
  TypeVar,
19
20
  Union,
20
21
  )
@@ -42,6 +43,10 @@ if TYPE_CHECKING:
42
43
  P = ParamSpec("P") # capture the function's parameters
43
44
  R = TypeVar("R") # return type
44
45
 
46
+ AsyncFunctionType: TypeAlias = Callable[P, Coroutine[Any, Any, R]]
47
+ SyncFunctionType: TypeAlias = Callable[P, R]
48
+ FunctionTypes: TypeAlias = Union[AsyncFunctionType, SyncFunctionType]
49
+
45
50
 
46
51
  @dataclass(kw_only=True)
47
52
  class TaskTemplate(Generic[P, R]):
@@ -98,6 +103,9 @@ class TaskTemplate(Generic[P, R]):
98
103
  local: bool = field(default=False, init=False)
99
104
  ref: bool = field(default=False, init=False, repr=False, compare=False)
100
105
 
106
+ # Only used in python 3.10 and 3.11, where we cannot use markcoroutinefunction
107
+ _call_as_synchronous: bool = False
108
+
101
109
  def __post_init__(self):
102
110
  # If pod_template is set to a pod, verify
103
111
  if self.pod_template is not None and not isinstance(self.pod_template, str):
@@ -208,13 +216,15 @@ class TaskTemplate(Generic[P, R]):
208
216
  def native_interface(self) -> NativeInterface:
209
217
  return self.interface
210
218
 
211
- async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R]:
219
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
212
220
  """
213
221
  This is the entrypoint for an async function task at runtime. It will be called during an execution.
214
222
  Please do not override this method, if you simply want to modify the execution behavior, override the
215
223
  execute method.
216
224
 
217
- # TODO lets provide one hook to implement, _pre, _execute and _post. We do not want actual execute to
225
+ This needs to be overridable to maybe be async.
226
+ The returned thing from here needs to be an awaitable if the underlying task is async, and a regular object
227
+ if the task is not.
218
228
  """
219
229
  try:
220
230
  ctx = internal_ctx()
@@ -226,8 +236,14 @@ class TaskTemplate(Generic[P, R]):
226
236
 
227
237
  controller = get_controller()
228
238
  if controller:
229
- return await controller.submit(self, *args, **kwargs)
230
- return await self.execute(*args, **kwargs)
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:
244
+ raise RuntimeSystemError("BadContext", "Controller is not initialized.")
245
+
246
+ return self.forward(*args, **kwargs)
231
247
  except RuntimeSystemError:
232
248
  raise
233
249
  except RuntimeUserError:
@@ -235,6 +251,17 @@ class TaskTemplate(Generic[P, R]):
235
251
  except Exception as e:
236
252
  raise RuntimeUserError(type(e).__name__, str(e)) from e
237
253
 
254
+ def forward(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
255
+ """
256
+ Think of this as a local execute method for your task. This function will be invoked by the __call__ method
257
+ when not in a Flyte task execution context. See the implementation below for an example.
258
+
259
+ :param args:
260
+ :param kwargs:
261
+ :return:
262
+ """
263
+ raise NotImplementedError
264
+
238
265
  def override(
239
266
  self,
240
267
  *,
@@ -290,26 +317,38 @@ class AsyncFunctionTaskTemplate(TaskTemplate[P, R]):
290
317
  is decorated with the task decorator.
291
318
  """
292
319
 
293
- func: Callable[P, Awaitable[R]]
320
+ func: FunctionTypes
321
+
322
+ def __post_init__(self):
323
+ super().__post_init__()
324
+ if not iscoroutinefunction(self.func):
325
+ self._call_as_synchronous = True
294
326
 
295
327
  @cached_property
296
328
  def native_interface(self) -> NativeInterface:
297
329
  return NativeInterface.from_callable(self.func)
298
330
 
331
+ def forward(self, *args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, R] | R:
332
+ # In local execution, we want to just call the function. Note we're not awaiting anything here.
333
+ # If the function was a coroutine function, the coroutine is returned and the await that the caller has
334
+ # in front of the task invocation will handle the awaiting.
335
+ return self.func(*args, **kwargs)
336
+
299
337
  async def execute(self, *args: P.args, **kwargs: P.kwargs) -> R:
300
338
  """
301
339
  This is the execute method that will be called when the task is invoked. It will call the actual function.
302
340
  # TODO We may need to keep this as the bare func execute, and need a pre and post execute some other func.
303
341
  """
342
+
304
343
  ctx = internal_ctx()
344
+ assert ctx.data.task_context is not None, "Function should have already returned if not in a task context"
305
345
  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):
346
+ tctx = ctx.data.task_context.replace(data=ctx_data)
347
+ with ctx.replace_task_context(tctx):
348
+ if iscoroutinefunction(self.func):
309
349
  v = await self.func(*args, **kwargs)
310
- await self.post(v)
311
- else:
312
- v = await self.func(*args, **kwargs)
350
+ else:
351
+ v = self.func(*args, **kwargs)
313
352
  await self.post(v)
314
353
  return v
315
354
 
@@ -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/_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.0b8'
21
+ __version_tuple__ = version_tuple = (0, 2, 0, 'b8')
flyte/cli/_common.py CHANGED
@@ -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
flyte/cli/_create.py CHANGED
@@ -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
+ The create subcommand allows you to create resources in a Flyte deployment.
14
14
  """
15
15
 
16
16
 
@@ -32,7 +32,23 @@ 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.
36
+
37
+ Examples:
38
+ ```bash
39
+ flyte create secret my_secret --value my_value
40
+ ```
41
+ If `--from-file` is specified, the value will be read from the file instead of being provided directly.
42
+ Example:
43
+ ```bash
44
+ flyte create secret my_secret --from-file /path/to/secret_file
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:
49
+ ```bash
50
+ flyte create secret my_secret --type image_pull
51
+ ```
36
52
  """
37
53
  from flyte.remote import Secret
38
54
 
@@ -55,9 +71,19 @@ def secret(
55
71
  @click.option(
56
72
  "-o",
57
73
  "--output",
58
- type=click.Path(),
59
- default=Path.cwd(),
74
+ type=click.Path(exists=False, writable=True),
75
+ default=Path.cwd() / "config.yaml",
60
76
  help="Path to the output dir where the config will be saved, defaults to current directory.",
77
+ show_default=True,
78
+ )
79
+ @click.option(
80
+ "--force",
81
+ is_flag=True,
82
+ default=False,
83
+ help="Force overwrite the config file if it already exists.",
84
+ show_default=True,
85
+ prompt="Are you sure you want to overwrite the config file?",
86
+ confirmation_prompt=True,
61
87
  )
62
88
  def config(
63
89
  output: Path,
@@ -66,14 +92,19 @@ def config(
66
92
  org: str | None = None,
67
93
  project: str | None = None,
68
94
  domain: str | None = None,
95
+ force: bool = False,
69
96
  ):
70
97
  """
71
- Create a new config file.
98
+ This command creates a configuration file for Flyte CLI.
99
+ If the `--output` option is not specified, it will create a file named `config.yaml` in the current directory.
100
+ If the file already exists, it will raise an error unless the `--force` option is used.
72
101
  """
73
102
  import yaml
74
103
 
75
- output_file = output / "config.yaml"
76
- with open(output_file, "w") as f:
104
+ if output.exists() and not force:
105
+ raise click.BadParameter(f"Output file {output} already exists. Use --force to overwrite.")
106
+
107
+ with open(output, "w") as f:
77
108
  d = {
78
109
  "admin": {
79
110
  "endpoint": endpoint,
@@ -87,4 +118,4 @@ def config(
87
118
  }
88
119
  yaml.dump(d, f)
89
120
 
90
- click.echo(f"Config file created at {output_file}")
121
+ 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
+ The delete subcommand allows you to 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
@@ -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
  )
flyte/cli/_gen.py ADDED
@@ -0,0 +1,155 @@
1
+ from os import getcwd
2
+ from typing import Generator, Tuple
3
+
4
+ import rich_click as click
5
+
6
+ import flyte.cli._common as common
7
+
8
+
9
+ @click.group(name="gen")
10
+ def gen():
11
+ """
12
+ Generate documentation
13
+ """
14
+
15
+
16
+ @gen.command(cls=common.CommandBase)
17
+ @click.option("--type", "doc_type", type=str, required=True, help="Type of documentation (valid: markdown)")
18
+ @click.pass_obj
19
+ def docs(cfg: common.CLIConfig, doc_type: str, project: str | None = None, domain: str | None = None):
20
+ """
21
+ Generate documentation
22
+ """
23
+ if doc_type == "markdown":
24
+ markdown(cfg)
25
+ else:
26
+ raise click.ClickException("Invalid documentation type: {}".format(doc_type))
27
+
28
+
29
+ def walk_commands(ctx: click.Context) -> Generator[Tuple[str, click.Command], None, None]:
30
+ """
31
+ Recursively walk a Click command tree, starting from the given context.
32
+
33
+ Yields:
34
+ (full_command_path, command_object)
35
+ """
36
+ command = ctx.command
37
+
38
+ if not isinstance(command, click.Group):
39
+ yield ctx.command_path, command
40
+ else:
41
+ for name in command.list_commands(ctx):
42
+ subcommand = command.get_command(ctx, name)
43
+ if subcommand is None:
44
+ continue
45
+
46
+ full_name = f"{ctx.command_path} {name}".strip()
47
+ yield full_name, subcommand
48
+
49
+ # Recurse if subcommand is a MultiCommand (i.e., has its own subcommands)
50
+ if isinstance(subcommand, click.Group):
51
+ sub_ctx = click.Context(subcommand, info_name=name, parent=ctx)
52
+ yield from walk_commands(sub_ctx)
53
+
54
+
55
+ def markdown(cfg: common.CLIConfig):
56
+ """
57
+ Generate documentation in Markdown format
58
+ """
59
+ ctx = cfg.ctx
60
+
61
+ output = []
62
+ output_verb_groups: dict[str, list[str]] = {}
63
+ output_noun_groups: dict[str, list[str]] = {}
64
+
65
+ commands = [*[("flyte", ctx.command)], *walk_commands(ctx)]
66
+ for cmd_path, cmd in commands:
67
+ output.append("")
68
+
69
+ cmd_path_parts = cmd_path.split(" ")
70
+
71
+ if len(cmd_path_parts) > 1:
72
+ if cmd_path_parts[1] not in output_verb_groups:
73
+ output_verb_groups[cmd_path_parts[1]] = []
74
+ if len(cmd_path_parts) > 2:
75
+ output_verb_groups[cmd_path_parts[1]].append(cmd_path_parts[2])
76
+
77
+ if len(cmd_path_parts) == 3:
78
+ if cmd_path_parts[2] not in output_noun_groups:
79
+ output_noun_groups[cmd_path_parts[2]] = []
80
+ output_noun_groups[cmd_path_parts[2]].append(cmd_path_parts[1])
81
+
82
+ output.append(f"{'#' * (len(cmd_path_parts) + 1)} {cmd_path}")
83
+ if cmd.help:
84
+ output.append("")
85
+ output.append(f"{cmd.help.strip()}")
86
+
87
+ if not cmd.params:
88
+ continue
89
+
90
+ params = cmd.get_params(click.Context(cmd))
91
+
92
+ # Collect all data first to calculate column widths
93
+ table_data = []
94
+ for param in params:
95
+ if isinstance(param, click.Option):
96
+ # Format each option with backticks before joining
97
+ all_opts = param.opts + param.secondary_opts
98
+ if len(all_opts) == 1:
99
+ opts = f"`{all_opts[0]}`"
100
+ else:
101
+ opts = "".join(
102
+ [
103
+ "{{< multiline >}}",
104
+ "\n".join([f"`{opt}`" for opt in all_opts]),
105
+ "{{< /multiline >}}",
106
+ ]
107
+ )
108
+ default_value = ""
109
+ if param.default is not None:
110
+ default_value = f"`{param.default}`"
111
+ default_value = default_value.replace(f"{getcwd()}/", "")
112
+ help_text = param.help.strip() if param.help else ""
113
+ table_data.append([opts, f"`{param.type.name}`", default_value, help_text])
114
+
115
+ if not table_data:
116
+ continue
117
+
118
+ # Add table header with proper alignment
119
+ output.append("")
120
+ output.append("| Option | Type | Default | Description |")
121
+ output.append("|--------|------|---------|-------------|")
122
+
123
+ # Add table rows with proper alignment
124
+ for row in table_data:
125
+ output.append(f"| {row[0]} | {row[1]} | {row[2]} | {row[3]} |")
126
+
127
+ output_verb_index = []
128
+
129
+ if len(output_verb_groups) > 0:
130
+ output_verb_index.append("| Action | On |")
131
+ output_verb_index.append("| ------ | -- |")
132
+ for verb, nouns in output_verb_groups.items():
133
+ entries = [f"[`{noun}`](#flyte-{verb}-{noun})" for noun in nouns]
134
+ output_verb_index.append(f"| `{verb}` | {', '.join(entries)} |")
135
+
136
+ output_noun_index = []
137
+
138
+ if len(output_noun_groups) > 0:
139
+ output_noun_index.append("| Object | Action |")
140
+ output_noun_index.append("| ------ | -- |")
141
+ for obj, actions in output_noun_groups.items():
142
+ entries = [f"[`{action}`](#flyte-{action}-{obj})" for action in actions]
143
+ output_noun_index.append(f"| `{obj}` | {', '.join(entries)} |")
144
+
145
+ print()
146
+ print("{{< grid >}}")
147
+ print("{{< markdown >}}")
148
+ print("\n".join(output_noun_index))
149
+ print("{{< /markdown >}}")
150
+ print("{{< markdown >}}")
151
+ print("\n".join(output_verb_index))
152
+ print("{{< /markdown >}}")
153
+ print("{{< /grid >}}")
154
+ print()
155
+ print("\n".join(output))
flyte/cli/_get.py CHANGED
@@ -11,7 +11,16 @@ from . import _common as common
11
11
  @click.group(name="get")
12
12
  def get():
13
13
  """
14
- Get the value of a task or environment.
14
+ The `get` subcommand allows you to retrieve various resources from a Flyte deployment.
15
+
16
+ You can get information about projects, runs, tasks, actions, secrets, and more.
17
+ Each command supports optional parameters to filter or specify the resource you want to retrieve.
18
+
19
+ Every `get` subcommand for example ``get project` without any arguments will list all projects.
20
+ `get project my_project` will return the details of the project named `my_project`.
21
+
22
+ In some cases `get action my_run` will return all actions for the run named `my_run` and
23
+ `get action my_run my_action` will return the details of the action named `my_action` for the run `my_run`.
15
24
  """
16
25
 
17
26
 
@@ -20,7 +29,7 @@ def get():
20
29
  @click.pass_obj
21
30
  def project(cfg: common.CLIConfig, name: str | None = None):
22
31
  """
23
- Get the current project.
32
+ Retrieve a list of all projects or details of a specific project by name.
24
33
  """
25
34
  from flyte.remote import Project
26
35
 
@@ -39,7 +48,11 @@ def project(cfg: common.CLIConfig, name: str | None = None):
39
48
  @click.pass_obj
40
49
  def run(cfg: common.CLIConfig, name: str | None = None, project: str | None = None, domain: str | None = None):
41
50
  """
42
- Get the current run.
51
+ Get list of all runs or details of a specific run by name.
52
+
53
+ The run details will include information about the run, its status, but only the root action will be shown.
54
+
55
+ If you want to see the actions for a run, use `get action <run_name>`.
43
56
  """
44
57
  from flyte.remote import Run, RunDetails
45
58
 
@@ -65,7 +78,9 @@ def task(
65
78
  domain: str | None = None,
66
79
  ):
67
80
  """
68
- Get the current task.
81
+ Retrieve a list of all tasks or details of a specific task by name and version.
82
+
83
+ Currently name+version are required to get a specific task.
69
84
  """
70
85
  from flyte.remote import Task
71
86
 
@@ -140,8 +155,24 @@ def logs(
140
155
  filter_system: bool = False,
141
156
  ):
142
157
  """
143
- Stream logs for the provided run or action. If the run is provided, only the logs for the parent action will be
144
- streamed.
158
+ Stream logs for the provided run or action.
159
+ If only the run is provided, only the logs for the parent action will be streamed.
160
+
161
+ Example:
162
+ ```bash
163
+ flyte get logs my_run
164
+ ```
165
+
166
+ But, if you want to see the logs for a specific action, you can provide the action name as well:
167
+ ```bash
168
+ flyte get logs my_run my_action
169
+ ```
170
+ By default logs will be shown in the raw format, will scroll on the terminal. If automatic scrolling and only
171
+ tailing --lines lines is desired, use the `--pretty` flag:
172
+ ```bash
173
+ flyte get logs my_run my_action --pretty --lines 50
174
+ ```
175
+
145
176
  """
146
177
  import flyte.remote as remote
147
178
 
@@ -175,7 +206,7 @@ def secret(
175
206
  domain: str | None = None,
176
207
  ):
177
208
  """
178
- Get the current secret.
209
+ Retrieve a list of all secrets or details of a specific secret by name.
179
210
  """
180
211
  import flyte.remote as remote
181
212
 
@@ -205,6 +236,19 @@ def io(
205
236
  ):
206
237
  """
207
238
  Get the inputs and outputs of a run or action.
239
+ if only the run name is provided, it will show the inputs and outputs of the root action of that run.
240
+ If an action name is provided, it will show the inputs and outputs for that action.
241
+
242
+ If `--inputs-only` or `--outputs-only` is specified, it will only show the inputs or outputs respectively.
243
+
244
+ Example:
245
+ ```bash
246
+ flyte get io my_run
247
+ ```
248
+ or
249
+ ```bash
250
+ flyte get io my_run my_action
251
+ ```
208
252
  """
209
253
  if inputs_only and outputs_only:
210
254
  raise click.BadParameter("Cannot use both --inputs-only and --outputs-only")
@@ -250,6 +294,8 @@ def io(
250
294
  def config(cfg: common.CLIConfig):
251
295
  """
252
296
  Shows the automatically detected configuration to connect with remote Flyte services.
297
+
298
+ The configuration will include the endpoint, organization, and other settings that are used by the CLI.
253
299
  """
254
300
  console = Console()
255
301
  console.print(cfg)
flyte/cli/_run.py CHANGED
@@ -204,11 +204,31 @@ class TaskFiles(common.FileGroup):
204
204
  filename=Path(filename),
205
205
  run_args=run_args,
206
206
  name=filename,
207
- help=f"Run, functions decorated `env.task` {filename}",
207
+ help=f"Run, functions decorated with `env.task` in {filename}",
208
208
  )
209
209
 
210
210
 
211
211
  run = TaskFiles(
212
212
  name="run",
213
- help="Run a task from a python file.",
213
+ help="""
214
+ Run a task from a python file.
215
+
216
+ Example usage:
217
+ ```bash
218
+ flyte run --name examples/basics/hello.py my_task --arg1 value1 --arg2 value2
219
+ ```
220
+ Note: all arguments for the run command are provided right after the `run` command and before the file name.
221
+
222
+ You can also specify the project and domain using the `--project` and `--domain` options, respectively. These
223
+ options can be set in the config file or passed as command line arguments.
224
+
225
+ Note: The arguments for the task are provided after the task name and can be retrieved using `--help`
226
+ Example:
227
+ ```bash
228
+ flyte run --name examples/basics/hello.py my_task --help
229
+ ```
230
+
231
+ To run a task locally, use the `--local` flag. This will run the task in the local environment instead of the remote
232
+ Flyte environment.
233
+ """,
214
234
  )
flyte/cli/main.py CHANGED
@@ -6,9 +6,32 @@ from ._abort import abort
6
6
  from ._common import CLIConfig
7
7
  from ._create import create
8
8
  from ._deploy import deploy
9
+ from ._gen import gen
9
10
  from ._get import get
10
11
  from ._run import run
11
12
 
13
+ click.rich_click.COMMAND_GROUPS = {
14
+ "flyte": [
15
+ {
16
+ "name": "Running Workflows",
17
+ "commands": ["run", "abort"],
18
+ },
19
+ {
20
+ "name": "Management",
21
+ "commands": ["create", "deploy", "get"],
22
+ },
23
+ {
24
+ "name": "Documentation Generation",
25
+ "commands": ["gen"],
26
+ },
27
+ ]
28
+ }
29
+
30
+ help_config = click.RichHelpConfiguration(
31
+ use_markdown=True,
32
+ use_markdown_emoji=True,
33
+ )
34
+
12
35
 
13
36
  def _verbosity_to_loglevel(verbosity: int) -> int | None:
14
37
  """
@@ -41,7 +64,7 @@ def _verbosity_to_loglevel(verbosity: int) -> int | None:
41
64
  "--insecure/--secure",
42
65
  is_flag=True,
43
66
  required=False,
44
- help="Use insecure connection to the endpoint. If secure is specified, the CLI will use TLS",
67
+ help="Use insecure connection to the endpoint. If secure is specified, the CLI will use TLS.",
45
68
  type=bool,
46
69
  default=None,
47
70
  show_default=True,
@@ -50,7 +73,7 @@ def _verbosity_to_loglevel(verbosity: int) -> int | None:
50
73
  "-v",
51
74
  "--verbose",
52
75
  required=False,
53
- help="Show verbose messages and exception traces",
76
+ help="Show verbose messages and exception traces, multiple times increases verbosity (e.g., -vvv).",
54
77
  count=True,
55
78
  default=0,
56
79
  type=int,
@@ -70,6 +93,7 @@ def _verbosity_to_loglevel(verbosity: int) -> int | None:
70
93
  help="Path to config file (YAML format) to use for the CLI. If not specified,"
71
94
  " the default config file will be used.",
72
95
  )
96
+ @click.rich_config(help_config=help_config)
73
97
  @click.pass_context
74
98
  def main(
75
99
  ctx: click.Context,
@@ -80,14 +104,35 @@ def main(
80
104
  config_file: str | None,
81
105
  ):
82
106
  """
83
-
84
- ____ __ _ _ ____ ____ _ _ ____ __
85
- ( __)( ) ( \\/ )(_ _)( __) / )( \\(___ \\ / \
86
- ) _) / (_/\\ ) / )( ) _) \\ \\/ / / __/ _( 0 )
87
- (__) \\____/(__/ (__) (____) \\__/ (____)(_)\\__/
107
+ ### Flyte entrypoint for the CLI
108
+ The Flyte CLI is a command line interface for interacting with Flyte.
88
109
 
89
110
  The flyte cli follows a simple verb based structure, where the top-level commands are verbs that describe the action
90
- to be taken, and the subcommands are nouns that describe the object of the action.
111
+ to be taken, and the subcommands are nouns that describe the object of the action.
112
+
113
+ The root command can be used to configure the CLI for most commands, such as setting the endpoint,
114
+ organization, and verbosity level.
115
+
116
+ Example: Set endpoint and organization
117
+ ```bash
118
+ flyte --endpoint <endpoint> --org <org> get project <project_name>
119
+ ```
120
+
121
+ Example: Increase verbosity level (This is useful for debugging, this will show more logs and exception traces)
122
+ ```bash
123
+ flyte -vvv get logs <run-name>
124
+ ```
125
+
126
+ Example: Override the default config file
127
+ ```bash
128
+ flyte --config /path/to/config.yaml run ...
129
+ ```
130
+
131
+ 👉 [Documentation](https://www.union.ai/docs/flyte/user-guide/) \n
132
+ 👉 [GitHub](https://github.com/flyteorg/flyte) - Please leave a ⭐. \n
133
+ 👉 [Slack](https://slack.flyte.org) - Join the community and ask questions.
134
+ 👉 [Issues](https://github.com/flyteorg/flyte/issues)
135
+
91
136
  """
92
137
  import flyte.config as config
93
138
 
@@ -108,6 +153,7 @@ to be taken, and the subcommands are nouns that describe the object of the actio
108
153
  insecure=final_insecure,
109
154
  org_override=org or cfg.task.org,
110
155
  config=cfg,
156
+ ctx=ctx,
111
157
  )
112
158
  logger.debug(f"Final materialized Cli config: {ctx.obj}")
113
159
 
@@ -117,3 +163,4 @@ main.add_command(deploy)
117
163
  main.add_command(get) # type: ignore
118
164
  main.add_command(create) # type: ignore
119
165
  main.add_command(abort) # type: ignore
166
+ main.add_command(gen) # type: ignore
@@ -215,7 +215,7 @@ class ContainerTask(TaskTemplate):
215
215
  output_dict[k] = self._convert_output_val_to_correct_type(output_val, output_type)
216
216
  return output_dict
217
217
 
218
- def execute(self, **kwargs) -> Any:
218
+ async def execute(self, **kwargs) -> Any:
219
219
  try:
220
220
  import docker
221
221
  except ImportError:
flyte/remote/_run.py CHANGED
@@ -50,6 +50,7 @@ def _action_rich_repr(action: run_definition_pb2.Action) -> rich.repr.Result:
50
50
  """
51
51
  Rich representation of the action.
52
52
  """
53
+ yield "run", action.id.run.name
53
54
  if action.metadata.HasField("task"):
54
55
  yield "task", action.metadata.task.id.name
55
56
  yield "type", "task"
flyte/syncify/__init__.py CHANGED
@@ -1,3 +1,54 @@
1
+ """
2
+ # Syncify Module
3
+ This module provides the `syncify` decorator and the `Syncify` class.
4
+ The decorator can be used to convert asynchronous functions or methods into synchronous ones.
5
+ This is useful for integrating async code into synchronous contexts.
6
+
7
+ Every asynchronous function or method wrapped with `syncify` can be called synchronously using the
8
+ parenthesis `()` operator, or asynchronously using the `.aio()` method.
9
+
10
+ Example::
11
+
12
+ ```python
13
+ from flyte.syncify import syncify
14
+
15
+ @syncify
16
+ async def async_function(x: str) -> str:
17
+ return f"Hello, Async World {x}!"
18
+
19
+
20
+ # now you can call it synchronously
21
+ result = async_function("Async World") # Note: no .aio() needed for sync calls
22
+ print(result)
23
+ # Output: Hello, Async World Async World!
24
+
25
+ # or call it asynchronously
26
+ async def main():
27
+ result = await async_function.aio("World") # Note the use of .aio() for async calls
28
+ print(result)
29
+ ```
30
+
31
+ ## Creating a Syncify Instance
32
+ ```python
33
+ from flyte.syncify. import Syncify
34
+
35
+ syncer = Syncify("my_syncer")
36
+
37
+ # Now you can use `syncer` to decorate your async functions or methods
38
+
39
+ ```
40
+
41
+ ## How does it work?
42
+ The Syncify class wraps asynchronous functions, classmethods, instance methods, and static methods to
43
+ provide a synchronous interface. The wrapped methods are always executed in the context of a background loop,
44
+ whether they are called synchronously or asynchronously. This allows for seamless integration of async code, as
45
+ certain async libraries capture the event loop. An example is grpc.aio, which captures the event loop.
46
+ In such a case, the Syncify class ensures that the async function is executed in the context of the background loop.
47
+
48
+ To use it correctly with grpc.aio, you should wrap every grpc.aio channel creation, and client invocation
49
+ with the same `Syncify` instance. This ensures that the async code runs in the correct event loop context.
50
+ """
51
+
1
52
  from flyte.syncify._api import Syncify
2
53
 
3
54
  syncify = Syncify()
flyte/syncify/_api.py CHANGED
@@ -54,14 +54,13 @@ class _BackgroundLoop:
54
54
  functions or methods synchronously.
55
55
  """
56
56
 
57
- def __init__(self):
58
- self.loop = None
59
- self.thread = threading.Thread(name="syncify_bg", target=self._run, daemon=True)
57
+ def __init__(self, name: str):
58
+ self.loop = asyncio.new_event_loop()
59
+ self.thread = threading.Thread(name=name, target=self._run, daemon=True)
60
60
  self.thread.start()
61
61
  atexit.register(self.stop)
62
62
 
63
63
  def _run(self):
64
- self.loop = asyncio.new_event_loop()
65
64
  asyncio.set_event_loop(self.loop)
66
65
  self.loop.run_forever()
67
66
 
@@ -242,8 +241,8 @@ class Syncify:
242
241
 
243
242
  """
244
243
 
245
- def __init__(self):
246
- self._bg_loop = _BackgroundLoop()
244
+ def __init__(self, name: str = "flyte_syncify"):
245
+ self._bg_loop = _BackgroundLoop(name=name)
247
246
 
248
247
  @overload
249
248
  def __call__(self, func: Callable[P, Awaitable[R_co]]) -> Any: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyte
3
- Version: 0.2.0b7
3
+ Version: 0.2.0b8
4
4
  Summary: Add your description here
5
5
  Author-email: Ketan Umare <kumare3@users.noreply.github.com>
6
6
  Requires-Python: >=3.10
@@ -24,11 +24,35 @@ Requires-Dist: async-lru>=2.0.5
24
24
  Requires-Dist: mashumaro
25
25
  Requires-Dist: dataclasses_json
26
26
 
27
- # Flyte 2
27
+ # Flyte 2 SDK
28
28
 
29
- Next-gen of SDK for Flyte.
29
+ The next-generation SDK for Flyte.
30
30
 
31
- ## Get started
31
+ ## Quickstart
32
+ 1. Clone this repo and set `unionv2` to working directory.
33
+ 2. Run `uv venv`, and `source .venv/bin/activate` to create a new virtualenv
34
+ 3. Install the latest version of the SDK by running the following:
35
+
36
+ ```
37
+ uv pip install --no-cache --prerelease=allow --upgrade flyte
38
+ ```
39
+ 4. Create the config and point it to the Dogfood GCP cluster by running the following:
40
+
41
+ ```
42
+ flyte create config --endpoint dns:///dogfood-gcp.cloud-staging.union.ai --org dogfood-gcp --project andrew --domain development
43
+ ```
44
+ This will create a `config.yaml` file in the current directory which will be referenced ahead of any other `config.yaml`s found in your system.
45
+
46
+ 5. Now you can run stuff with the CLI:
47
+
48
+ ```
49
+ flyte run --follow examples/basics/devbox_one.py say_hello_nested
50
+ ```
51
+ Note that the `--follow` command is optional. Use this to stream updates to the terminal!
52
+
53
+
54
+
55
+ ## Hello World Example
32
56
 
33
57
  1. Only async tasks are supported right now. Style recommended, `import flyte` and then `flyte.TaskEnvironment` etc
34
58
  2. You have to create environment for even a single task
@@ -4,10 +4,10 @@ flyte/_context.py,sha256=pYa43ut8gp6i-Y_zOy1WW_N2IbP9Vd-zIORO11vqK1E,4995
4
4
  flyte/_deploy.py,sha256=hHZKLU3U7t1ZF_8x6LykkPu_KSDyaHL3f2WzyjLj9BQ,7723
5
5
  flyte/_doc.py,sha256=_OPCf3t_git6UT7kSJISFaWO9cfNzJhhoe6JjVdyCJo,706
6
6
  flyte/_docstring.py,sha256=SsG0Ab_YMAwy2ABJlEo3eBKlyC3kwPdnDJ1FIms-ZBQ,1127
7
- flyte/_environment.py,sha256=ft0EsyFg6OnoIFPFbwkABLcq676veIH3TTR4SNilrj8,1499
7
+ flyte/_environment.py,sha256=BkChtdVhWB3SwMSvetDZ-KiNBgRFlAXgq69PHT4zyG0,2942
8
8
  flyte/_group.py,sha256=64q2GFDp3koIkx3IV4GBeGEbu4v-GPUxTlxU_sV2fPk,743
9
9
  flyte/_hash.py,sha256=Of_Zl_DzzzF2jp4ZsLm-3o-xJFCCJ8_GubmLI1htx78,504
10
- flyte/_image.py,sha256=8xEGmAALY6jQAsLfJQH9NweeVUaSTWivFEQt-JchN24,29068
10
+ flyte/_image.py,sha256=NeBvjCdwFAVGd666ufi1q-YOvhwdTEzAeJl5YBfl0bI,29043
11
11
  flyte/_initialize.py,sha256=ihTIvoMHs67UKbtFLR_zy9M1e7OK26ywoc_yMfLYwMw,16499
12
12
  flyte/_interface.py,sha256=MP5o_qpIwfBNtAc7zo_cLSjMugsPyanuO6EgUSk4fBE,3644
13
13
  flyte/_logging.py,sha256=FQvF3W1kkFypbARcOQ7WZVXO0XJasXp8EhozF6E6-aQ,3379
@@ -16,12 +16,12 @@ flyte/_retry.py,sha256=rfLv0MvWxzPByKESTglEmjPsytEAKiIvvmzlJxXwsfE,941
16
16
  flyte/_reusable_environment.py,sha256=P4FBATVKAYcIKpdFN98sI8acPyKy8eIGx6V0kUb9YdM,1289
17
17
  flyte/_run.py,sha256=2ugAk4tpvSAnNAlfhx4YysSrdFoce-hZHQ6XMHYxp0A,17783
18
18
  flyte/_secret.py,sha256=SqIHs6mi8hEkIIBZe3bI9jJsPt65Mt6dV5uh9_op1ME,2392
19
- flyte/_task.py,sha256=cqWfbMDMkEg1Q0sOkaSi1h_9Vn81DbGCOgNFZo8bMfI,14622
20
- flyte/_task_environment.py,sha256=svSJJMEiiYsqz403s_urMgPdjguHJJSGVuBobT3uwVo,8403
19
+ flyte/_task.py,sha256=UDsnSs0lYXZe-0HvZHk4FQ5X9DGRyy1qldm7zpUEXT4,16373
20
+ flyte/_task_environment.py,sha256=J1LFH9Zz1nPhlsrc_rYny1SS3QC1b55X7tRYoTG7Vk4,6815
21
21
  flyte/_timeout.py,sha256=zx5sFcbYmjJAJbZWSGzzX-BpC9HC7Jfs35T7vVhKwkk,1571
22
22
  flyte/_tools.py,sha256=JewkQZBR_M85tS6QY8e4xXue75jbOE48nID4ZHnc9jY,632
23
23
  flyte/_trace.py,sha256=7OQtQNosIlycTwaMjdc3GW4h3T3N0bYTsY6og4clPl8,5234
24
- flyte/_version.py,sha256=aew4sh3XJsC-oHvLUBV1yrPYnRPf3pvzP7MaVQdQXzs,519
24
+ flyte/_version.py,sha256=vlbuxWySGV761QhtXKWRtZJy0eos4BpfR3ZShXEd4sQ,519
25
25
  flyte/errors.py,sha256=m2JUNqLC6anVW6UiDK_ihuA06q_Hkw1mIUMDskb2OW8,4289
26
26
  flyte/models.py,sha256=GTRuR6GXc0RAbLmPEnnH54oRF7__2TNFhmYjFoYMjZA,12660
27
27
  flyte/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -37,15 +37,15 @@ flyte/_code_bundle/_packaging.py,sha256=_wEozcQTYgqvAAaKQYna9ptvShIMlXk3vEdccwAO
37
37
  flyte/_code_bundle/_utils.py,sha256=b0s3ZVKSRwaa_2CMTCqt2iRrUvTTW3FmlyqCD9k5BS0,12028
38
38
  flyte/_code_bundle/bundle.py,sha256=8T0gcXck6dmg-8L2-0G3B2iNjC-Xwydu806iyKneMMY,8789
39
39
  flyte/_internal/__init__.py,sha256=vjXgGzAAjy609YFkAy9_RVPuUlslsHSJBXCLNTVnqOY,136
40
- flyte/_internal/controllers/__init__.py,sha256=qaawXUgYdC5yHh5JfQ9mCH3u9a7oTYriDChADekzuzo,3750
41
- flyte/_internal/controllers/_local_controller.py,sha256=wTrJitUZMKodvvZMiy4bQbmIv0doF8Pb-OCsa8lAHgA,4708
40
+ flyte/_internal/controllers/__init__.py,sha256=wPugf6_9BUVqwCXClnmScwyKVaGYIo0PTd-sdYjxVWA,3827
41
+ flyte/_internal/controllers/_local_controller.py,sha256=fT-mFBs05vxHf43U24AKGJg5NuSTO5F8LBRcZAfgX2g,4798
42
42
  flyte/_internal/controllers/_trace.py,sha256=Ga2b65sn9q2IoOwHBZV2inMYyO6-CSDwzN7E3pDxsEI,1126
43
43
  flyte/_internal/controllers/remote/__init__.py,sha256=9_azH1eHLqY6VULpDugXi7Kf1kK1ODqEnsQ_3wM6IqU,1919
44
44
  flyte/_internal/controllers/remote/_action.py,sha256=w6vE1vPz1BwxvwfotDWjTNbDXfGEPrRBA8N3UVQ6P0w,4905
45
45
  flyte/_internal/controllers/remote/_client.py,sha256=HPbzbfaWZVv5wpOvKNtFXR6COiZDwd1cUJQqi60A7oU,1421
46
- flyte/_internal/controllers/remote/_controller.py,sha256=uqZYQDGG70DeJiqAU4y7n7VhXQ0gvD4ktWu15-zg86I,17387
46
+ flyte/_internal/controllers/remote/_controller.py,sha256=5jhm6UG3DLCUiwQMkVGeIF4pIx_cZkR9it6tLMK7DRw,17477
47
47
  flyte/_internal/controllers/remote/_core.py,sha256=2dka1rDnA8Ui_qhfE1ymZuN8E2BYQPn123h_eMixSiM,18091
48
- flyte/_internal/controllers/remote/_informer.py,sha256=6WPaT1EmDcIwQ3VlujGWICzHy-kaGhMut_zBh2ShZnE,14186
48
+ flyte/_internal/controllers/remote/_informer.py,sha256=JC35aHbEdwBN7cwDKbQj6koPUypTapYyK0wKxOBRBCo,14191
49
49
  flyte/_internal/controllers/remote/_service_protocol.py,sha256=B9qbIg6DiGeac-iSccLmX_AL2xUgX4ezNUOiAbSy4V0,1357
50
50
  flyte/_internal/imagebuild/__init__.py,sha256=cLXVxkAyFpbdC1y-k3Rb6FRW9f_xpoRQWVn__G9IqKs,354
51
51
  flyte/_internal/imagebuild/docker_builder.py,sha256=bkr2fs9jInTq8jqU8ka7NGvp0RPfYhbTfX1RqtqTvvs,13986
@@ -129,28 +129,29 @@ flyte/_protos/workflow/task_service_pb2_grpc.py,sha256=PdhEfPraBIeN-UQulZsA2D0on
129
129
  flyte/_utils/__init__.py,sha256=ZlVA1bLeAEnzwbkK7eEVAVmeVQnbBCuGqfd2UIk-yNc,599
130
130
  flyte/_utils/asyn.py,sha256=KeJKarXNIyD16g6oPM0T9cH7JDmh1KY7JLbwo7i0IlQ,3673
131
131
  flyte/_utils/async_cache.py,sha256=JtZJmWO62OowJ0QFNl6wryWqh-kuDi76aAASMie87QY,4596
132
- flyte/_utils/coro_management.py,sha256=hlWzxdrsBYfUwzQS7qtltclG56XPxBMNgWE5lkuYdrY,760
132
+ flyte/_utils/coro_management.py,sha256=_JTt9x9fOc_1OSe03DSheYoKOvlonBB_4WNCS9XSaoU,698
133
133
  flyte/_utils/file_handling.py,sha256=iU4TxW--fCho_Eg5xTMODn96P03SxzF-V-5f-7bZAZY,2233
134
134
  flyte/_utils/helpers.py,sha256=Ntrs1WilJS7a4oLmcIPLXMi0cuzRDiCr_wwgtpV30uk,3953
135
135
  flyte/_utils/lazy_module.py,sha256=fvXPjvZLzCfcI8Vzs4pKedUDdY0U_RQ1ZVrp9b8qBQY,1994
136
136
  flyte/_utils/uv_script_parser.py,sha256=PxqD8lSMi6xv0uDd1s8LKB2IPZr4ttZJCUweqlyMTKk,1483
137
137
  flyte/cli/__init__.py,sha256=Hx_mrERToVkrvORPB56ZnUED86T4S50ac1nwLQfvsgo,278
138
138
  flyte/cli/_abort.py,sha256=WkXmjAOcrBU9NXXndcwF7YW7QcUTJzyUrvIRW0fjpSE,580
139
- flyte/cli/_common.py,sha256=u9Vf4VR601cEawlKw-o9bJJuQVNlLMMsFgCTWl-umU4,10748
140
- flyte/cli/_create.py,sha256=8g9LgrjhpJiusUkmWeIRB3XviTVmMfo1dGCEt8Hna00,2377
141
- flyte/cli/_delete.py,sha256=xOZN7Y13AQjAEQvdEod9qk1MQPU9l7bjQCkYzf_l4uY,497
142
- flyte/cli/_deploy.py,sha256=u30rb6KfZnr52M6zHlLueaOkgdCGoS2pIpfb0wFoTvY,4371
143
- flyte/cli/_get.py,sha256=u5PNAeJTPkGzdcz5gd2oiFrNvIfm8oRGK3MsjyY4Dzc,7553
139
+ flyte/cli/_common.py,sha256=KSn6MxJng_WJDrA20Wl_RKWiCcqZ7ZS68UFlqYyR7xs,10771
140
+ flyte/cli/_create.py,sha256=bB_kkM54LLWVBaAy8LKPJGfLjC3NSIDELB8niQlG50c,3667
141
+ flyte/cli/_delete.py,sha256=WnHn9m_1b3nAXaD772bOirsN73ICUi-BVzjFP8s_Hyg,581
142
+ flyte/cli/_deploy.py,sha256=zCHvy88UPYz_iXXG7b9Pi61GFdB52-niYw2pVIum2b4,4464
143
+ flyte/cli/_gen.py,sha256=onJIXj2PiJxxHCOFo5A9LNwjt-rRo-a_SojdtuvQ5is,5302
144
+ flyte/cli/_get.py,sha256=f5e8h6P6LBBPNZKHEx8aYeoym_Bih7eIsxqiAqAHtso,9641
144
145
  flyte/cli/_params.py,sha256=X3GpuftXmtfIsYQ7vBilD4kmlkXTc7_AxpaxohRjSuY,19458
145
- flyte/cli/_run.py,sha256=F8Io2WB4dGHvNWbCmvCtyx4YGQhmoCAxeARwAwNSn78,7150
146
- flyte/cli/main.py,sha256=1-Qm3IAPIRQSKaWVL1iGajiJoOO0mRqJsRnNtfd_uw4,3064
146
+ flyte/cli/_run.py,sha256=zRu4DTO__BVsLgpSLUbYR1Twvzc_2zivUhcviymCRf0,7884
147
+ flyte/cli/main.py,sha256=PhH8RgpNFP9owpjN8purGvM41qlm9VcR2QF2oLi_6FA,4480
147
148
  flyte/config/__init__.py,sha256=MiwEYK5Iv7MRR22z61nzbsbvZ9Q6MdmAU_g9If1Pmb8,144
148
149
  flyte/config/_config.py,sha256=QE3T0W8xOULjJaqDMdMF90f9gFVjGR6h8QPOLsyqjYw,9831
149
150
  flyte/config/_internal.py,sha256=Bj0uzn3PYgxKbzM-q2GKXxp7Y6cyzhPzUB-Y2i6cQKo,2836
150
151
  flyte/config/_reader.py,sha256=c16jm0_IYxwEAjXENtllLeO_sT5Eg2RNLG4UjnAv_x4,7157
151
152
  flyte/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
152
153
  flyte/extras/__init__.py,sha256=FhB0uK7H1Yo5De9vOuF7UGnezTKncj3u2Wo5uQdWN0g,74
153
- flyte/extras/_container.py,sha256=JM-JNsj9-Mjf7E4OQcAS2Z5IJBXhB-HtQkGn_mu7gvk,11249
154
+ flyte/extras/_container.py,sha256=R7tMIy2EuprEGowYhjPMwnszSutgWIzAeKPeXNc2nmI,11255
154
155
  flyte/io/__init__.py,sha256=e2wHVEoZ84TGOtOPrtTg6hJpeuxiYI56Sg011yq6nUQ,236
155
156
  flyte/io/_dataframe.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
157
  flyte/io/_dir.py,sha256=rih9CY1YjNX05bcAu5LG62Xoyij5GXAlv7jLyVF0je8,15310
@@ -165,7 +166,7 @@ flyte/remote/_console.py,sha256=avmELJPx8nQMAVPrHlh6jEIRPjrMwFpdZjJsWOOa9rE,660
165
166
  flyte/remote/_data.py,sha256=DPK85gB6M71RjxqIh1Q5PdZ9xcJ0m1w_3cT2lAO0r7w,5795
166
167
  flyte/remote/_logs.py,sha256=EOXg4OS8yYclsT6NASgOLMo0TA2sZpKb2MWZXpWBPuI,6404
167
168
  flyte/remote/_project.py,sha256=dTBYqORDAbLvh9WnPO1Ytuzw2vxNYZwwNsKE2_b0o14,2807
168
- flyte/remote/_run.py,sha256=Dk7LQaB_edxSd6H93H-khjeZKXT76PgHPSLKIuGJQfw,31021
169
+ flyte/remote/_run.py,sha256=9euHjYRX-xyxXuhn0MunYb9dmgl0FMU3a-FZNjJA4F8,31057
169
170
  flyte/remote/_secret.py,sha256=l5xeMS83uMcWWeSSTRsSZUNhS0N--1Dze09C-thSOQs,4341
170
171
  flyte/remote/_task.py,sha256=6TBdjPWgxHmdY9OJMMPGZax8h7Qs7q9dprNktjnZ77E,7904
171
172
  flyte/remote/_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -197,16 +198,16 @@ flyte/storage/_config.py,sha256=xVibWJaioOnkeTb_M30azgiUe1jvmQaOWRZEkpdoTao,8680
197
198
  flyte/storage/_remote_fs.py,sha256=kM_iszbccjVD5VtVdgfkl1FHS8NPnY__JOo_CPQUE4c,1124
198
199
  flyte/storage/_storage.py,sha256=mBy7MKII2M1UTVm_EUUDwVb7uT1_AOPzQr2wCJ-fgW0,9873
199
200
  flyte/storage/_utils.py,sha256=8oLCM-7D7JyJhzUi1_Q1NFx8GBUPRfou0T_5tPBmPbE,309
200
- flyte/syncify/__init__.py,sha256=tmGOUw3XvDXpaxQk95oRmUTObcIzjEvBFaceSzrXATU,94
201
- flyte/syncify/_api.py,sha256=Ayq9xzb1IJJrlHuTJ160C3k7tdrAjhX4z-FgQM-3T8M,9799
201
+ flyte/syncify/__init__.py,sha256=WgTk-v-SntULnI55CsVy71cxGJ9Q6pxpTrhbPFuouJ0,1974
202
+ flyte/syncify/_api.py,sha256=Jek0sIpu6RyOCAYQZFxuW-xv_lpr6DBOjs2kGA8tfvc,9815
202
203
  flyte/types/__init__.py,sha256=xMIYOolT3Vq0qXy7unw90IVdYztdMDpKg0oG0XAPC9o,364
203
204
  flyte/types/_interface.py,sha256=mY7mb8v2hJPGk7AU99gdOWl4_jArA1VFtjYGlE31SK0,953
204
205
  flyte/types/_renderer.py,sha256=ygcCo5l60lHufyQISFddZfWwLlQ8kJAKxUT_XnR_6dY,4818
205
206
  flyte/types/_string_literals.py,sha256=NlG1xV8RSA-sZ-n-IFQCAsdB6jXJOAKkHWtnopxVVDk,4231
206
207
  flyte/types/_type_engine.py,sha256=QxyoDWRG_whfLCz88YqEVVoTTnca0FZv9eHeLLT0_-s,93645
207
208
  flyte/types/_utils.py,sha256=pbts9E1_2LTdLygAY0UYTLYJ8AsN3BZyviSXvrtcutc,2626
208
- flyte-0.2.0b7.dist-info/METADATA,sha256=PEzzLl143Af5aF5AtaQFINAedUl2-txdzh36uHWd6fQ,3751
209
- flyte-0.2.0b7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
210
- flyte-0.2.0b7.dist-info/entry_points.txt,sha256=MIq2z5dBurdCJfpXfMKzgBv7sJOakKRYxr8G0cMiTrg,75
211
- flyte-0.2.0b7.dist-info/top_level.txt,sha256=7dkyFbikvA12LEZEqawx8oDG1CMod6hTliPj7iWzgYo,6
212
- flyte-0.2.0b7.dist-info/RECORD,,
209
+ flyte-0.2.0b8.dist-info/METADATA,sha256=143TeXr4H7BtrVp2r0GwVQ0jviOe8S5fXn3t1s439wY,4636
210
+ flyte-0.2.0b8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
211
+ flyte-0.2.0b8.dist-info/entry_points.txt,sha256=MIq2z5dBurdCJfpXfMKzgBv7sJOakKRYxr8G0cMiTrg,75
212
+ flyte-0.2.0b8.dist-info/top_level.txt,sha256=7dkyFbikvA12LEZEqawx8oDG1CMod6hTliPj7iWzgYo,6
213
+ flyte-0.2.0b8.dist-info/RECORD,,