flyte 2.0.0b2__py3-none-any.whl → 2.0.0b4__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/_image.py CHANGED
@@ -8,11 +8,14 @@ from abc import abstractmethod
8
8
  from dataclasses import asdict, dataclass, field
9
9
  from functools import cached_property
10
10
  from pathlib import Path
11
- from typing import Callable, ClassVar, Dict, List, Literal, Optional, Tuple, TypeVar, Union
11
+ from typing import TYPE_CHECKING, Callable, ClassVar, Dict, List, Literal, Optional, Tuple, TypeVar, Union
12
12
 
13
13
  import rich.repr
14
14
  from packaging.version import Version
15
15
 
16
+ if TYPE_CHECKING:
17
+ from flyte import Secret, SecretRequest
18
+
16
19
  # Supported Python versions
17
20
  PYTHON_3_10 = (3, 10)
18
21
  PYTHON_3_11 = (3, 11)
@@ -71,6 +74,7 @@ class PipOption:
71
74
  extra_index_urls: Optional[Tuple[str] | Tuple[str, ...] | List[str]] = None
72
75
  pre: bool = False
73
76
  extra_args: Optional[str] = None
77
+ secret_mounts: Optional[Tuple[str | Secret, ...]] = None
74
78
 
75
79
  def get_pip_install_args(self) -> List[str]:
76
80
  pip_install_args = []
@@ -101,6 +105,9 @@ class PipOption:
101
105
  hash_input += str(self.pre)
102
106
  if self.extra_args:
103
107
  hash_input += self.extra_args
108
+ if self.secret_mounts:
109
+ for secret_mount in self.secret_mounts:
110
+ hash_input += str(secret_mount)
104
111
 
105
112
  hasher.update(hash_input.encode("utf-8"))
106
113
 
@@ -110,9 +117,6 @@ class PipOption:
110
117
  class PipPackages(PipOption, Layer):
111
118
  packages: Optional[Tuple[str, ...]] = None
112
119
 
113
- # todo: to be implemented
114
- # secret_mounts: Optional[List[Tuple[str, str]]] = None
115
-
116
120
  def update_hash(self, hasher: hashlib._Hash):
117
121
  """
118
122
  Update the hash with the pip packages
@@ -172,9 +176,15 @@ class UVProject(PipOption, Layer):
172
176
  @dataclass(frozen=True, repr=True)
173
177
  class AptPackages(Layer):
174
178
  packages: Tuple[str, ...]
179
+ secret_mounts: Optional[Tuple[str | Secret, ...]] = None
175
180
 
176
181
  def update_hash(self, hasher: hashlib._Hash):
177
- hasher.update("".join(self.packages).encode("utf-8"))
182
+ hash_input = "".join(self.packages)
183
+
184
+ if self.secret_mounts:
185
+ for secret_mount in self.secret_mounts:
186
+ hash_input += str(secret_mount)
187
+ hasher.update(hash_input.encode("utf-8"))
178
188
 
179
189
 
180
190
  @rich.repr.auto
@@ -690,19 +700,26 @@ class Image:
690
700
  new_image = self.clone(addl_layer=WorkDir(workdir=workdir))
691
701
  return new_image
692
702
 
693
- def with_requirements(self, file: str | Path) -> Image:
703
+ def with_requirements(
704
+ self,
705
+ file: str | Path,
706
+ secret_mounts: Optional[SecretRequest] = None,
707
+ ) -> Image:
694
708
  """
695
709
  Use this method to create a new image with the specified requirements file layered on top of the current image
696
710
  Cannot be used in conjunction with conda
697
711
 
698
712
  :param file: path to the requirements file, must be a .txt file
713
+ :param secret_mounts: list of secret to mount for the build process.
699
714
  :return:
700
715
  """
701
716
  if isinstance(file, str):
702
717
  file = Path(file)
703
718
  if file.suffix != ".txt":
704
719
  raise ValueError(f"Requirements file {file} must have a .txt extension")
705
- new_image = self.clone(addl_layer=Requirements(file=file))
720
+ new_image = self.clone(
721
+ addl_layer=Requirements(file=file, secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None)
722
+ )
706
723
  return new_image
707
724
 
708
725
  def with_pip_packages(
@@ -712,6 +729,7 @@ class Image:
712
729
  extra_index_urls: Union[str, List[str], Tuple[str, ...], None] = None,
713
730
  pre: bool = False,
714
731
  extra_args: Optional[str] = None,
732
+ secret_mounts: Optional[SecretRequest] = None,
715
733
  ) -> Image:
716
734
  """
717
735
  Use this method to create a new image with the specified pip packages layered on top of the current image
@@ -732,8 +750,8 @@ class Image:
732
750
  :param extra_index_urls: extra index urls to use for pip install, default is None
733
751
  :param pre: whether to allow pre-release versions, default is False
734
752
  :param extra_args: extra arguments to pass to pip install, default is None
735
- # :param secret_mounts: todo
736
753
  :param extra_args: extra arguments to pass to pip install, default is None
754
+ :param secret_mounts: list of secret to mount for the build process.
737
755
  :return: Image
738
756
  """
739
757
  new_packages: Optional[Tuple] = packages or None
@@ -745,6 +763,7 @@ class Image:
745
763
  extra_index_urls=new_extra_index_urls,
746
764
  pre=pre,
747
765
  extra_args=extra_args,
766
+ secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None,
748
767
  )
749
768
  new_image = self.clone(addl_layer=ll)
750
769
  return new_image
@@ -792,9 +811,10 @@ class Image:
792
811
  self,
793
812
  pyproject_file: Path,
794
813
  index_url: Optional[str] = None,
795
- extra_index_urls: Union[str, List[str], Tuple[str, ...], None] = None,
814
+ extra_index_urls: Union[List[str], Tuple[str, ...], None] = None,
796
815
  pre: bool = False,
797
816
  extra_args: Optional[str] = None,
817
+ secret_mounts: Optional[SecretRequest] = None,
798
818
  ) -> Image:
799
819
  """
800
820
  Use this method to create a new image with the specified uv.lock file layered on top of the current image
@@ -803,7 +823,12 @@ class Image:
803
823
  In the Union builders, using this will change the virtual env to /root/.venv
804
824
 
805
825
  :param pyproject_file: path to the pyproject.toml file, needs to have a corresponding uv.lock file
806
- :return:
826
+ :param index_url: index url to use for pip install, default is None
827
+ :param extra_index_urls: extra index urls to use for pip install, default is None
828
+ :param pre: whether to allow pre-release versions, default is False
829
+ :param extra_args: extra arguments to pass to pip install, default is None
830
+ :param secret_mounts: list of secret mounts to use for the build process.
831
+ :return: Image
807
832
  """
808
833
  if not pyproject_file.exists():
809
834
  raise FileNotFoundError(f"UVLock file {pyproject_file} does not exist")
@@ -812,17 +837,33 @@ class Image:
812
837
  lock = pyproject_file.parent / "uv.lock"
813
838
  if not lock.exists():
814
839
  raise ValueError(f"UVLock file {lock} does not exist")
815
- new_image = self.clone(addl_layer=UVProject(pyproject=pyproject_file, uvlock=lock))
840
+ new_image = self.clone(
841
+ addl_layer=UVProject(
842
+ pyproject=pyproject_file,
843
+ uvlock=lock,
844
+ index_url=index_url,
845
+ extra_index_urls=extra_index_urls,
846
+ pre=pre,
847
+ extra_args=extra_args,
848
+ secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None,
849
+ )
850
+ )
816
851
  return new_image
817
852
 
818
- def with_apt_packages(self, *packages: str) -> Image:
853
+ def with_apt_packages(self, *packages: str, secret_mounts: Optional[SecretRequest] = None) -> Image:
819
854
  """
820
855
  Use this method to create a new image with the specified apt packages layered on top of the current image
821
856
 
822
857
  :param packages: list of apt packages to install
858
+ :param secret_mounts: list of secret mounts to use for the build process.
823
859
  :return: Image
824
860
  """
825
- new_image = self.clone(addl_layer=AptPackages(packages=packages))
861
+ new_image = self.clone(
862
+ addl_layer=AptPackages(
863
+ packages=packages,
864
+ secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None,
865
+ )
866
+ )
826
867
  return new_image
827
868
 
828
869
  def with_commands(self, commands: List[str]) -> Image:
@@ -41,7 +41,9 @@ class Controller(Protocol):
41
41
  """
42
42
  ...
43
43
 
44
- async def submit_task_ref(self, _task: task_definition_pb2.TaskDetails, *args, **kwargs) -> Any:
44
+ async def submit_task_ref(
45
+ self, _task: task_definition_pb2.TaskDetails, max_inline_io_bytes: int, *args, **kwargs
46
+ ) -> Any:
45
47
  """
46
48
  Submit a task reference to the controller asynchronously and wait for the result. This is async and will block
47
49
  the current coroutine until the result is available.
@@ -192,5 +192,7 @@ class LocalController:
192
192
  assert info.start_time
193
193
  assert info.end_time
194
194
 
195
- async def submit_task_ref(self, _task: task_definition_pb2.TaskDetails, *args, **kwargs) -> Any:
195
+ async def submit_task_ref(
196
+ self, _task: task_definition_pb2.TaskDetails, max_inline_io_bytes: int, *args, **kwargs
197
+ ) -> Any:
196
198
  raise flyte.errors.ReferenceTaskError("Reference tasks cannot be executed locally, only remotely.")
@@ -7,7 +7,7 @@ import threading
7
7
  from collections import defaultdict
8
8
  from collections.abc import Callable
9
9
  from pathlib import Path
10
- from typing import Any, AsyncIterable, Awaitable, DefaultDict, Tuple, TypeVar
10
+ from typing import Any, Awaitable, DefaultDict, Tuple, TypeVar
11
11
 
12
12
  import flyte
13
13
  import flyte.errors
@@ -27,22 +27,30 @@ from flyte._protos.common import identifier_pb2
27
27
  from flyte._protos.workflow import run_definition_pb2, task_definition_pb2
28
28
  from flyte._task import TaskTemplate
29
29
  from flyte._utils.helpers import _selector_policy
30
- from flyte.models import ActionID, NativeInterface, SerializationContext
30
+ from flyte.models import MAX_INLINE_IO_BYTES, ActionID, NativeInterface, SerializationContext
31
31
 
32
32
  R = TypeVar("R")
33
33
 
34
+ MAX_TRACE_BYTES = MAX_INLINE_IO_BYTES
34
35
 
35
- async def upload_inputs_with_retry(serialized_inputs: AsyncIterable[bytes] | bytes, inputs_uri: str) -> None:
36
+
37
+ async def upload_inputs_with_retry(serialized_inputs: bytes, inputs_uri: str, max_bytes: int) -> None:
36
38
  """
37
39
  Upload inputs to the specified URI with error handling.
38
40
 
39
41
  Args:
40
42
  serialized_inputs: The serialized inputs to upload
41
43
  inputs_uri: The destination URI
44
+ max_bytes: Maximum number of bytes to read from the input stream
42
45
 
43
46
  Raises:
44
47
  RuntimeSystemError: If the upload fails
45
48
  """
49
+ if len(serialized_inputs) > max_bytes:
50
+ raise flyte.errors.InlineIOMaxBytesBreached(
51
+ f"Inputs exceed max_bytes limit of {max_bytes / 1024 / 1024} MB,"
52
+ f" actual size: {len(serialized_inputs) / 1024 / 1024} MB"
53
+ )
46
54
  try:
47
55
  # TODO Add retry decorator to this
48
56
  await storage.put_stream(serialized_inputs, to_path=inputs_uri)
@@ -80,19 +88,20 @@ async def handle_action_failure(action: Action, task_name: str) -> Exception:
80
88
  return exc
81
89
 
82
90
 
83
- async def load_and_convert_outputs(iface: NativeInterface, realized_outputs_uri: str) -> Any:
91
+ async def load_and_convert_outputs(iface: NativeInterface, realized_outputs_uri: str, max_bytes: int) -> Any:
84
92
  """
85
93
  Load outputs from the given URI and convert them to native format.
86
94
 
87
95
  Args:
88
96
  iface: The Native interface
89
97
  realized_outputs_uri: The URI where outputs are stored
98
+ max_bytes: Maximum number of bytes to read from the output file
90
99
 
91
100
  Returns:
92
101
  The converted native outputs
93
102
  """
94
103
  outputs_file_path = io.outputs_path(realized_outputs_uri)
95
- outputs = await io.load_outputs(outputs_file_path)
104
+ outputs = await io.load_outputs(outputs_file_path, max_bytes=max_bytes)
96
105
  return await convert.convert_outputs_to_native(iface, outputs)
97
106
 
98
107
 
@@ -144,7 +153,7 @@ class RemoteController(Controller):
144
153
  name = task_obj.__name__
145
154
  elif hasattr(task_obj, "name"):
146
155
  name = task_obj.name
147
- logger.warning(f"For action {uniq}, task {name} call sequence is {new_seq}")
156
+ logger.info(f"For action {uniq}, task {name} call sequence is {new_seq}")
148
157
  return new_seq
149
158
 
150
159
  async def _submit(self, _task_call_seq: int, _task: TaskTemplate, *args, **kwargs) -> Any:
@@ -187,11 +196,11 @@ class RemoteController(Controller):
187
196
  sub_action_id, sub_action_output_path = convert.generate_sub_action_id_and_output_path(
188
197
  tctx, task_spec, inputs_hash, _task_call_seq
189
198
  )
190
- logger.warning(f"Sub action {sub_action_id} output path {sub_action_output_path}")
199
+ logger.info(f"Sub action {sub_action_id} output path {sub_action_output_path}")
191
200
 
192
201
  serialized_inputs = inputs.proto_inputs.SerializeToString(deterministic=True)
193
202
  inputs_uri = io.inputs_path(sub_action_output_path)
194
- await upload_inputs_with_retry(serialized_inputs, inputs_uri)
203
+ await upload_inputs_with_retry(serialized_inputs, inputs_uri, max_bytes=_task.max_inline_io_bytes)
195
204
 
196
205
  md = task_spec.task_template.metadata
197
206
  ignored_input_vars = []
@@ -254,7 +263,9 @@ class RemoteController(Controller):
254
263
  "RuntimeError",
255
264
  f"Task {n.action_id.name} did not return an output path, but the task has outputs defined.",
256
265
  )
257
- return await load_and_convert_outputs(_task.native_interface, n.realized_outputs_uri)
266
+ return await load_and_convert_outputs(
267
+ _task.native_interface, n.realized_outputs_uri, max_bytes=_task.max_inline_io_bytes
268
+ )
258
269
  return None
259
270
 
260
271
  async def submit(self, _task: TaskTemplate, *args, **kwargs) -> Any:
@@ -357,7 +368,7 @@ class RemoteController(Controller):
357
368
  )
358
369
 
359
370
  inputs_uri = io.inputs_path(sub_action_output_path)
360
- await upload_inputs_with_retry(serialized_inputs, inputs_uri)
371
+ await upload_inputs_with_retry(serialized_inputs, inputs_uri, max_bytes=MAX_TRACE_BYTES)
361
372
  # Clear to free memory
362
373
  serialized_inputs = None # type: ignore
363
374
 
@@ -388,7 +399,7 @@ class RemoteController(Controller):
388
399
  logger.warning(f"Action {prev_action.action_id.name} failed, but no error was found, re-running trace!")
389
400
  elif prev_action.realized_outputs_uri is not None:
390
401
  outputs_file_path = io.outputs_path(prev_action.realized_outputs_uri)
391
- o = await io.load_outputs(outputs_file_path)
402
+ o = await io.load_outputs(outputs_file_path, max_bytes=MAX_TRACE_BYTES)
392
403
  outputs = await convert.convert_outputs_to_native(_interface, o)
393
404
  return (
394
405
  TraceInfo(func_name, sub_action_id, _interface, inputs_uri, output=outputs),
@@ -410,18 +421,13 @@ class RemoteController(Controller):
410
421
 
411
422
  current_action_id = tctx.action
412
423
  sub_run_output_path = storage.join(tctx.run_base_dir, info.action.name)
413
- print(f"Sub run output path for {info.name} is {sub_run_output_path}", flush=True)
414
424
 
415
425
  if info.interface.has_outputs():
416
426
  outputs_file_path: str = ""
417
427
  if info.output:
418
428
  outputs = await convert.convert_from_native_to_outputs(info.output, info.interface)
419
429
  outputs_file_path = io.outputs_path(sub_run_output_path)
420
- print(
421
- f"Uploading outputs for {info.name} Outputs file path: {outputs_file_path}",
422
- flush=True,
423
- )
424
- await io.upload_outputs(outputs, sub_run_output_path)
430
+ await io.upload_outputs(outputs, sub_run_output_path, max_bytes=MAX_TRACE_BYTES)
425
431
  elif info.error:
426
432
  err = convert.convert_from_native_to_error(info.error)
427
433
  await io.upload_error(err.err, sub_run_output_path)
@@ -450,18 +456,23 @@ class RemoteController(Controller):
450
456
  end_time=info.end_time,
451
457
  typed_interface=typed_interface if typed_interface else None,
452
458
  )
453
- try:
454
- logger.info(
455
- f"Submitting Trace action Run:[{trace_action.run_name}, Parent:[{trace_action.parent_action_name}],"
456
- f" Trace fn:[{info.name}], action:[{info.action.name}]"
457
- )
458
- await self.submit_action(trace_action)
459
- logger.info(f"Trace Action for [{info.name}] action id: {info.action.name}, completed!")
460
- except asyncio.CancelledError:
461
- # If the action is cancelled, we need to cancel the action on the server as well
462
- raise
463
459
 
464
- async def submit_task_ref(self, _task: task_definition_pb2.TaskDetails, *args, **kwargs) -> Any:
460
+ async with self._parent_action_semaphore[unique_action_name(current_action_id)]:
461
+ try:
462
+ logger.info(
463
+ f"Submitting Trace action Run:[{trace_action.run_name},"
464
+ f" Parent:[{trace_action.parent_action_name}],"
465
+ f" Trace fn:[{info.name}], action:[{info.action.name}]"
466
+ )
467
+ await self.submit_action(trace_action)
468
+ logger.info(f"Trace Action for [{info.name}] action id: {info.action.name}, completed!")
469
+ except asyncio.CancelledError:
470
+ # If the action is cancelled, we need to cancel the action on the server as well
471
+ raise
472
+
473
+ async def _submit_task_ref(
474
+ self, invoke_seq_num: int, _task: task_definition_pb2.TaskDetails, max_inline_io_bytes: int, *args, **kwargs
475
+ ) -> Any:
465
476
  ctx = internal_ctx()
466
477
  tctx = ctx.data.task_context
467
478
  if tctx is None:
@@ -469,8 +480,6 @@ class RemoteController(Controller):
469
480
  current_action_id = tctx.action
470
481
  task_name = _task.spec.task_template.id.name
471
482
 
472
- invoke_seq_num = self.generate_task_call_sequence(_task, current_action_id)
473
-
474
483
  native_interface = types.guess_interface(
475
484
  _task.spec.task_template.interface, default_inputs=_task.spec.default_inputs
476
485
  )
@@ -482,7 +491,7 @@ class RemoteController(Controller):
482
491
 
483
492
  serialized_inputs = inputs.proto_inputs.SerializeToString(deterministic=True)
484
493
  inputs_uri = io.inputs_path(sub_action_output_path)
485
- await upload_inputs_with_retry(serialized_inputs, inputs_uri)
494
+ await upload_inputs_with_retry(serialized_inputs, inputs_uri, max_inline_io_bytes)
486
495
  # cache key - task name, task signature, inputs, cache version
487
496
  cache_key = None
488
497
  md = _task.spec.task_template.metadata
@@ -545,5 +554,17 @@ class RemoteController(Controller):
545
554
  "RuntimeError",
546
555
  f"Task {n.action_id.name} did not return an output path, but the task has outputs defined.",
547
556
  )
548
- return await load_and_convert_outputs(native_interface, n.realized_outputs_uri)
557
+ return await load_and_convert_outputs(native_interface, n.realized_outputs_uri, max_inline_io_bytes)
549
558
  return None
559
+
560
+ async def submit_task_ref(
561
+ self, _task: task_definition_pb2.TaskDetails, max_inline_io_bytes: int, *args, **kwargs
562
+ ) -> Any:
563
+ ctx = internal_ctx()
564
+ tctx = ctx.data.task_context
565
+ if tctx is None:
566
+ raise flyte.errors.RuntimeSystemError("BadContext", "Task context not initialized")
567
+ current_action_id = tctx.action
568
+ task_call_seq = self.generate_task_call_sequence(_task, current_action_id)
569
+ async with self._parent_action_semaphore[unique_action_name(current_action_id)]:
570
+ return await self._submit_task_ref(task_call_seq, _task, max_inline_io_bytes, *args, **kwargs)
@@ -86,10 +86,7 @@ class Controller:
86
86
 
87
87
  async def get_action(self, action_id: identifier_pb2.ActionIdentifier, parent_action_name: str) -> Optional[Action]:
88
88
  """Get the action from the informer"""
89
- informer = await self._informers.get(run_name=action_id.run.name, parent_action_name=parent_action_name)
90
- if informer:
91
- return await informer.get(action_id.name)
92
- return None
89
+ return await self._run_coroutine_in_controller_thread(self._bg_get_action(action_id, parent_action_name))
93
90
 
94
91
  @log
95
92
  async def cancel_action(self, action: Action):
@@ -11,6 +11,7 @@ from typing import ClassVar, Optional, Protocol, cast
11
11
  import aiofiles
12
12
  import click
13
13
 
14
+ from flyte import Secret
14
15
  from flyte._image import (
15
16
  AptPackages,
16
17
  Commands,
@@ -19,6 +20,7 @@ from flyte._image import (
19
20
  Env,
20
21
  Image,
21
22
  Layer,
23
+ PipOption,
22
24
  PipPackages,
23
25
  PythonWheels,
24
26
  Requirements,
@@ -44,6 +46,7 @@ WORKDIR /root
44
46
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
45
47
  --mount=type=bind,target=uv.lock,src=uv.lock \
46
48
  --mount=type=bind,target=pyproject.toml,src=pyproject.toml \
49
+ $SECRET_MOUNT \
47
50
  uv sync $PIP_INSTALL_ARGS
48
51
  WORKDIR /
49
52
 
@@ -56,31 +59,35 @@ ENV PATH="/root/.venv/bin:$$PATH" \
56
59
  UV_PACKAGE_INSTALL_COMMAND_TEMPLATE = Template("""\
57
60
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
58
61
  --mount=type=bind,target=requirements_uv.txt,src=requirements_uv.txt \
59
- uv pip install --prerelease=allow --python $$UV_PYTHON $PIP_INSTALL_ARGS
62
+ $SECRET_MOUNT \
63
+ uv pip install --python $$UV_PYTHON $PIP_INSTALL_ARGS
60
64
  """)
61
65
 
62
66
  UV_WHEEL_INSTALL_COMMAND_TEMPLATE = Template("""\
63
67
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=wheel \
64
68
  --mount=source=/dist,target=/dist,type=bind \
65
- uv pip install --prerelease=allow --python $$UV_PYTHON $PIP_INSTALL_ARGS
69
+ $SECRET_MOUNT \
70
+ uv pip install --python $$UV_PYTHON $PIP_INSTALL_ARGS
66
71
  """)
67
72
 
68
73
  APT_INSTALL_COMMAND_TEMPLATE = Template("""\
69
74
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/var/cache/apt,id=apt \
75
+ $SECRET_MOUNT \
70
76
  apt-get update && apt-get install -y --no-install-recommends \
71
77
  $APT_PACKAGES
72
78
  """)
73
79
 
74
80
  UV_PYTHON_INSTALL_COMMAND = Template("""\
75
81
  RUN --mount=type=cache,sharing=locked,mode=0777,target=/root/.cache/uv,id=uv \
82
+ $SECRET_MOUNT \
76
83
  uv pip install $PIP_INSTALL_ARGS
77
84
  """)
78
85
 
79
86
  # uv pip install --python /root/env/bin/python
80
87
  # new template
81
88
  DOCKER_FILE_UV_BASE_TEMPLATE = Template("""\
82
- #syntax=docker/dockerfile:1.5
83
- FROM ghcr.io/astral-sh/uv:0.6.12 as uv
89
+ # syntax=docker/dockerfile:1.10
90
+ FROM ghcr.io/astral-sh/uv:0.6.12 AS uv
84
91
  FROM $BASE_IMAGE
85
92
 
86
93
  USER root
@@ -139,8 +146,10 @@ class PipAndRequirementsHandler:
139
146
 
140
147
  pip_install_args = layer.get_pip_install_args()
141
148
  pip_install_args.extend(["--requirement", "requirements_uv.txt"])
142
-
143
- delta = UV_PACKAGE_INSTALL_COMMAND_TEMPLATE.substitute(PIP_INSTALL_ARGS=" ".join(pip_install_args))
149
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
150
+ delta = UV_PACKAGE_INSTALL_COMMAND_TEMPLATE.substitute(
151
+ PIP_INSTALL_ARGS=" ".join(pip_install_args), SECRET_MOUNT=secret_mounts
152
+ )
144
153
  dockerfile += delta
145
154
 
146
155
  return dockerfile
@@ -152,8 +161,10 @@ class PythonWheelHandler:
152
161
  shutil.copytree(layer.wheel_dir, context_path / "dist", dirs_exist_ok=True)
153
162
  pip_install_args = layer.get_pip_install_args()
154
163
  pip_install_args.extend(["/dist/*.whl"])
155
-
156
- delta = UV_WHEEL_INSTALL_COMMAND_TEMPLATE.substitute(PIP_INSTALL_ARGS=" ".join(pip_install_args))
164
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
165
+ delta = UV_WHEEL_INSTALL_COMMAND_TEMPLATE.substitute(
166
+ PIP_INSTALL_ARGS=" ".join(pip_install_args), SECRET_MOUNT=secret_mounts
167
+ )
157
168
  dockerfile += delta
158
169
 
159
170
  return dockerfile
@@ -181,9 +192,10 @@ class EnvHandler:
181
192
 
182
193
  class AptPackagesHandler:
183
194
  @staticmethod
184
- async def handle(layer: AptPackages, context_path: Path, dockerfile: str) -> str:
195
+ async def handle(layer: AptPackages, _: Path, dockerfile: str) -> str:
185
196
  packages = layer.packages
186
- delta = APT_INSTALL_COMMAND_TEMPLATE.substitute(APT_PACKAGES=" ".join(packages))
197
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
198
+ delta = APT_INSTALL_COMMAND_TEMPLATE.substitute(APT_PACKAGES=" ".join(packages), SECRET_MOUNT=secret_mounts)
187
199
  dockerfile += delta
188
200
 
189
201
  return dockerfile
@@ -200,7 +212,10 @@ class UVProjectHandler:
200
212
  # --no-dev: Omit the development dependency group
201
213
  # --no-install-project: Do not install the current project
202
214
  additional_pip_install_args = ["--locked", "--no-dev", "--no-install-project"]
203
- delta = UV_LOCK_INSTALL_TEMPLATE.substitute(PIP_INSTALL_ARGS=" ".join(additional_pip_install_args))
215
+ secret_mounts = _get_secret_mounts_layer(layer.secret_mounts)
216
+ delta = UV_LOCK_INSTALL_TEMPLATE.substitute(
217
+ PIP_INSTALL_ARGS=" ".join(additional_pip_install_args), SECRET_MOUNT=secret_mounts
218
+ )
204
219
  dockerfile += delta
205
220
 
206
221
  return dockerfile
@@ -250,13 +265,60 @@ class CommandsHandler:
250
265
 
251
266
  class WorkDirHandler:
252
267
  @staticmethod
253
- async def handle(layer: WorkDir, context_path: Path, dockerfile: str) -> str:
268
+ async def handle(layer: WorkDir, _: Path, dockerfile: str) -> str:
254
269
  # cd to the workdir
255
270
  dockerfile += f"\nWORKDIR {layer.workdir}\n"
256
271
 
257
272
  return dockerfile
258
273
 
259
274
 
275
+ def _get_secret_commands(layers: typing.Tuple[Layer, ...]) -> typing.List[str]:
276
+ commands = []
277
+
278
+ def _get_secret_command(secret: str | Secret) -> typing.List[str]:
279
+ secret_id = hash(secret)
280
+ if isinstance(secret, str):
281
+ if not os.path.exists(secret):
282
+ raise FileNotFoundError(f"Secret file '{secret}' not found")
283
+ return ["--secret", f"id={secret_id},src={secret}"]
284
+ secret_env_key = "_".join([k.upper() for k in filter(None, (secret.group, secret.key))])
285
+ secret_env = os.getenv(secret_env_key)
286
+ if secret_env:
287
+ return ["--secret", f"id={secret_id},env={secret_env}"]
288
+ secret_file_name = "_".join(list(filter(None, (secret.group, secret.key))))
289
+ secret_file_path = f"/etc/secrets/{secret_file_name}"
290
+ if not os.path.exists(secret_file_path):
291
+ raise FileNotFoundError(f"Secret not found in Env Var {secret_env_key} or file {secret_file_path}")
292
+ return ["--secret", f"id={secret_id},src={secret_file_path}"]
293
+
294
+ for layer in layers:
295
+ if isinstance(layer, (PipOption, AptPackages)):
296
+ if layer.secret_mounts:
297
+ for secret_mount in layer.secret_mounts:
298
+ commands.extend(_get_secret_command(secret_mount))
299
+ return commands
300
+
301
+
302
+ def _get_secret_mounts_layer(secrets: typing.Tuple[str | Secret, ...] | None) -> str:
303
+ if secrets is None:
304
+ return ""
305
+ secret_mounts_layer = ""
306
+ for secret in secrets:
307
+ secret_id = hash(secret)
308
+ if isinstance(secret, str):
309
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},target=/run/secrets/{os.path.basename(secret)}"
310
+ elif isinstance(secret, Secret):
311
+ if secret.mount:
312
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},target={secret.mount}"
313
+ elif secret.as_env_var:
314
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},env={secret.as_env_var}"
315
+ else:
316
+ secret_file_name = "_".join(list(filter(None, (secret.group, secret.key))))
317
+ secret_mounts_layer += f"--mount=type=secret,id={secret_id},src=/run/secrets/{secret_file_name}"
318
+
319
+ return secret_mounts_layer
320
+
321
+
260
322
  async def _process_layer(layer: Layer, context_path: Path, dockerfile: str) -> str:
261
323
  match layer:
262
324
  case PythonWheels():
@@ -357,6 +419,8 @@ class DockerImageBuilder(ImageBuilder):
357
419
  else:
358
420
  command.append("--load")
359
421
 
422
+ command.extend(_get_secret_commands(layers=image._layers))
423
+
360
424
  concat_command = " ".join(command)
361
425
  logger.debug(f"Build command: {concat_command}")
362
426
  click.secho(f"Run command: {concat_command} ", fg="blue")
@@ -464,6 +528,8 @@ class DockerImageBuilder(ImageBuilder):
464
528
  command.append("--push")
465
529
  else:
466
530
  command.append("--load")
531
+
532
+ command.extend(_get_secret_commands(layers=image._layers))
467
533
  command.append(tmp_dir)
468
534
 
469
535
  concat_command = " ".join(command)
@@ -5,14 +5,11 @@ It uses the storage module to handle the actual uploading and downloading of fil
5
5
  TODO: Convert to use streaming apis
6
6
  """
7
7
 
8
- import logging
9
-
10
8
  from flyteidl.core import errors_pb2, execution_pb2
11
9
 
12
10
  import flyte.storage as storage
13
11
  from flyte._protos.workflow import run_definition_pb2
14
12
 
15
- from ..._logging import log
16
13
  from .convert import Inputs, Outputs, _clean_error_code
17
14
 
18
15
  # ------------------------------- CONSTANTS ------------------------------- #
@@ -55,11 +52,19 @@ async def upload_inputs(inputs: Inputs, input_path: str):
55
52
  await storage.put_stream(data_iterable=inputs.proto_inputs.SerializeToString(), to_path=input_path)
56
53
 
57
54
 
58
- async def upload_outputs(outputs: Outputs, output_path: str):
55
+ async def upload_outputs(outputs: Outputs, output_path: str, max_bytes: int = -1):
59
56
  """
60
57
  :param outputs: Outputs
61
58
  :param output_path: The path to upload the output file.
59
+ :param max_bytes: Maximum number of bytes to write to the output file. Default is -1, which means no limit.
62
60
  """
61
+ if max_bytes != -1 and outputs.proto_outputs.ByteSize() > max_bytes:
62
+ import flyte.errors
63
+
64
+ raise flyte.errors.InlineIOMaxBytesBreached(
65
+ f"Output file at {output_path} exceeds max_bytes limit of {max_bytes},"
66
+ f" size: {outputs.proto_outputs.ByteSize()}"
67
+ )
63
68
  output_uri = outputs_path(output_path)
64
69
  await storage.put_stream(data_iterable=outputs.proto_outputs.SerializeToString(), to_path=output_uri)
65
70
 
@@ -85,25 +90,59 @@ async def upload_error(err: execution_pb2.ExecutionError, output_prefix: str):
85
90
 
86
91
 
87
92
  # ------------------------------- DOWNLOAD Methods ------------------------------- #
88
- @log(level=logging.INFO)
89
- async def load_inputs(path: str) -> Inputs:
93
+ async def load_inputs(path: str, max_bytes: int = -1) -> Inputs:
90
94
  """
91
95
  :param path: Input file to be downloaded
96
+ :param max_bytes: Maximum number of bytes to read from the input file. Default is -1, which means no limit.
92
97
  :return: Inputs object
93
98
  """
94
99
  lm = run_definition_pb2.Inputs()
95
- proto_str = b"".join([c async for c in storage.get_stream(path=path)])
100
+
101
+ if max_bytes == -1:
102
+ proto_str = b"".join([c async for c in storage.get_stream(path=path)])
103
+ else:
104
+ proto_bytes = []
105
+ total_bytes = 0
106
+ async for chunk in storage.get_stream(path=path):
107
+ if total_bytes + len(chunk) > max_bytes:
108
+ import flyte.errors
109
+
110
+ raise flyte.errors.InlineIOMaxBytesBreached(
111
+ f"Input file at {path} exceeds max_bytes limit of {max_bytes}"
112
+ )
113
+ proto_bytes.append(chunk)
114
+ total_bytes += len(chunk)
115
+ proto_str = b"".join(proto_bytes)
116
+
96
117
  lm.ParseFromString(proto_str)
97
118
  return Inputs(proto_inputs=lm)
98
119
 
99
120
 
100
- async def load_outputs(path: str) -> Outputs:
121
+ async def load_outputs(path: str, max_bytes: int = -1) -> Outputs:
101
122
  """
102
123
  :param path: output file to be loaded
124
+ :param max_bytes: Maximum number of bytes to read from the output file.
125
+ If -1, reads the entire file.
103
126
  :return: Outputs object
104
127
  """
105
128
  lm = run_definition_pb2.Outputs()
106
- proto_str = b"".join([c async for c in storage.get_stream(path=path)])
129
+
130
+ if max_bytes == -1:
131
+ proto_str = b"".join([c async for c in storage.get_stream(path=path)])
132
+ else:
133
+ proto_bytes = []
134
+ total_bytes = 0
135
+ async for chunk in storage.get_stream(path=path):
136
+ if total_bytes + len(chunk) > max_bytes:
137
+ import flyte.errors
138
+
139
+ raise flyte.errors.InlineIOMaxBytesBreached(
140
+ f"Output file at {path} exceeds max_bytes limit of {max_bytes}"
141
+ )
142
+ proto_bytes.append(chunk)
143
+ total_bytes += len(chunk)
144
+ proto_str = b"".join(proto_bytes)
145
+
107
146
  lm.ParseFromString(proto_str)
108
147
  return Outputs(proto_outputs=lm)
109
148
 
flyte/_secret.py CHANGED
@@ -7,8 +7,9 @@ from typing import List, Optional, Union
7
7
  @dataclass
8
8
  class Secret:
9
9
  """
10
- Secrets are used to inject sensitive information into tasks. Secrets can be mounted as environment variables or
11
- files. The secret key is the name of the secret in the secret store. The group is optional and maybe used with some
10
+ Secrets are used to inject sensitive information into tasks or image build context.
11
+ Secrets can be mounted as environment variables or files.
12
+ The secret key is the name of the secret in the secret store. The group is optional and maybe used with some
12
13
  secret stores to organize secrets. The secret_mount is used to specify how the secret should be mounted. If the
13
14
  secret_mount is set to "env" the secret will be mounted as an environment variable. If the secret_mount is set to
14
15
  "file" the secret will be mounted as a file. The as_env_var is an optional parameter that can be used to specify the
flyte/_task.py CHANGED
@@ -33,7 +33,7 @@ from ._retry import RetryStrategy
33
33
  from ._reusable_environment import ReusePolicy
34
34
  from ._secret import SecretRequest
35
35
  from ._timeout import TimeoutType
36
- from .models import NativeInterface, SerializationContext
36
+ from .models import MAX_INLINE_IO_BYTES, NativeInterface, SerializationContext
37
37
 
38
38
  if TYPE_CHECKING:
39
39
  from flyteidl.core.tasks_pb2 import DataLoadingConfig
@@ -79,6 +79,8 @@ class TaskTemplate(Generic[P, R]):
79
79
  :param env: Optional The environment variables to set for the task.
80
80
  :param secrets: Optional The secrets that will be injected into the task at runtime.
81
81
  :param timeout: Optional The timeout for the task.
82
+ :param max_inline_io_bytes: Maximum allowed size (in bytes) for all inputs and outputs passed directly to the task
83
+ (e.g., primitives, strings, dicts). Does not apply to files, directories, or dataframes.
82
84
  """
83
85
 
84
86
  name: str
@@ -100,8 +102,8 @@ class TaskTemplate(Generic[P, R]):
100
102
  report: bool = False
101
103
 
102
104
  parent_env: Optional[weakref.ReferenceType[TaskEnvironment]] = None
103
- local: bool = field(default=False, init=False)
104
105
  ref: bool = field(default=False, init=False, repr=False, compare=False)
106
+ max_inline_io_bytes: int = MAX_INLINE_IO_BYTES
105
107
 
106
108
  # Only used in python 3.10 and 3.11, where we cannot use markcoroutinefunction
107
109
  _call_as_synchronous: bool = False
@@ -315,6 +317,7 @@ class TaskTemplate(Generic[P, R]):
315
317
  reusable: Union[ReusePolicy, Literal["off"], None] = None,
316
318
  env: Optional[Dict[str, str]] = None,
317
319
  secrets: Optional[SecretRequest] = None,
320
+ max_inline_io_bytes: int | None = None,
318
321
  **kwargs: Any,
319
322
  ) -> TaskTemplate:
320
323
  """
@@ -324,6 +327,8 @@ class TaskTemplate(Generic[P, R]):
324
327
  cache = cache or self.cache
325
328
  retries = retries or self.retries
326
329
  timeout = timeout or self.timeout
330
+ max_inline_io_bytes = max_inline_io_bytes or self.max_inline_io_bytes
331
+
327
332
  reusable = reusable or self.reusable
328
333
  if reusable == "off":
329
334
  reusable = None
@@ -371,6 +376,7 @@ class TaskTemplate(Generic[P, R]):
371
376
  reusable=cast(Optional[ReusePolicy], reusable),
372
377
  env=env,
373
378
  secrets=secrets,
379
+ max_inline_io_bytes=max_inline_io_bytes,
374
380
  )
375
381
 
376
382
 
@@ -27,7 +27,7 @@ from ._retry import RetryStrategy
27
27
  from ._reusable_environment import ReusePolicy
28
28
  from ._secret import SecretRequest
29
29
  from ._task import AsyncFunctionTaskTemplate, TaskTemplate
30
- from .models import NativeInterface
30
+ from .models import MAX_INLINE_IO_BYTES, NativeInterface
31
31
 
32
32
  if TYPE_CHECKING:
33
33
  from kubernetes.client import V1PodTemplate
@@ -141,6 +141,7 @@ class TaskEnvironment(Environment):
141
141
  secrets: Optional[SecretRequest] = None,
142
142
  pod_template: Optional[Union[str, "V1PodTemplate"]] = None,
143
143
  report: bool = False,
144
+ max_inline_io_bytes: int = MAX_INLINE_IO_BYTES,
144
145
  ) -> Union[AsyncFunctionTaskTemplate, Callable[P, R]]:
145
146
  """
146
147
  Decorate a function to be a task.
@@ -157,6 +158,8 @@ class TaskEnvironment(Environment):
157
158
  :param pod_template: Optional The pod template for the task, if not provided the default pod template will be
158
159
  used.
159
160
  :param report: Optional Whether to generate the html report for the task, defaults to False.
161
+ :param max_inline_io_bytes: Maximum allowed size (in bytes) for all inputs and outputs passed directly to the
162
+ task (e.g., primitives, strings, dicts). Does not apply to files, directories, or dataframes.
160
163
  """
161
164
  from ._task import P, R
162
165
 
@@ -208,6 +211,7 @@ class TaskEnvironment(Environment):
208
211
  report=report,
209
212
  friendly_name=friendly_name,
210
213
  plugin_config=self.plugin_config,
214
+ max_inline_io_bytes=max_inline_io_bytes,
211
215
  )
212
216
  self._tasks[task_name] = tmpl
213
217
  return tmpl
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 = '2.0.0b2'
21
- __version_tuple__ = version_tuple = (2, 0, 0, 'b2')
20
+ __version__ = version = '2.0.0b4'
21
+ __version_tuple__ = version_tuple = (2, 0, 0, 'b4')
flyte/errors.py CHANGED
@@ -179,3 +179,13 @@ class ImageBuildError(RuntimeUserError):
179
179
 
180
180
  def __init__(self, message: str):
181
181
  super().__init__("ImageBuildError", message, "user")
182
+
183
+
184
+ class InlineIOMaxBytesBreached(RuntimeUserError):
185
+ """
186
+ This error is raised when the inline IO max bytes limit is breached.
187
+ This can be adjusted per task by setting max_inline_io_bytes in the task definition.
188
+ """
189
+
190
+ def __init__(self, message: str):
191
+ super().__init__("InlineIOMaxBytesBreached", message, "user")
@@ -192,7 +192,9 @@ class ContainerTask(TaskTemplate):
192
192
  microseconds=microseconds,
193
193
  )
194
194
 
195
- async def _convert_output_val_to_correct_type(self, output_path: pathlib.Path, output_val: Any, output_type: Type) -> Any:
195
+ async def _convert_output_val_to_correct_type(
196
+ self, output_path: pathlib.Path, output_val: Any, output_type: Type
197
+ ) -> Any:
196
198
  import datetime
197
199
 
198
200
  if issubclass(output_type, bool):
flyte/models.py CHANGED
@@ -18,6 +18,9 @@ if TYPE_CHECKING:
18
18
  from flyte._internal.imagebuild.image_builder import ImageCache
19
19
  from flyte.report import Report
20
20
 
21
+ # --- Constants ----
22
+ MAX_INLINE_IO_BYTES = 10 * 1024 * 1024 # 100 MB
23
+
21
24
 
22
25
  def generate_random_name() -> str:
23
26
  """
flyte/remote/_task.py CHANGED
@@ -81,6 +81,7 @@ AutoVersioning = Literal["latest", "current"]
81
81
  @dataclass
82
82
  class TaskDetails:
83
83
  pb2: task_definition_pb2.TaskDetails
84
+ max_inline_io_bytes: int = 10 * 1024 * 1024 # 10 MB
84
85
 
85
86
  @classmethod
86
87
  def get(
@@ -252,7 +253,7 @@ class TaskDetails:
252
253
 
253
254
  controller = get_controller()
254
255
  if controller:
255
- return await controller.submit_task_ref(self.pb2, *args, **kwargs)
256
+ return await controller.submit_task_ref(self.pb2, self.max_inline_io_bytes, *args, **kwargs)
256
257
  raise flyte.errors
257
258
 
258
259
  def override(
@@ -267,6 +268,7 @@ class TaskDetails:
267
268
  reusable: Union[flyte.ReusePolicy, Literal["auto"], None] = None,
268
269
  env: Optional[Dict[str, str]] = None,
269
270
  secrets: Optional[flyte.SecretRequest] = None,
271
+ max_inline_io_bytes: int | None = None,
270
272
  **kwargs: Any,
271
273
  ) -> TaskDetails:
272
274
  raise NotImplementedError
flyte/report/_report.py CHANGED
@@ -145,7 +145,11 @@ async def flush():
145
145
  assert report_html is not None
146
146
  assert isinstance(report_html, str)
147
147
  report_path = io.report_path(internal_ctx().data.task_context.output_path)
148
- final_path = await storage.put_stream(report_html.encode("utf-8"), to_path=report_path)
148
+ content_types = {
149
+ "Content-Type": "text/html", # For s3
150
+ "content_type": "text/html", # For gcs
151
+ }
152
+ final_path = await storage.put_stream(report_html.encode("utf-8"), to_path=report_path, attributes=content_types)
149
153
  logger.debug(f"Report flushed to {final_path}")
150
154
 
151
155
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flyte
3
- Version: 2.0.0b2
3
+ Version: 2.0.0b4
4
4
  Summary: Add your description here
5
5
  Author-email: Ketan Umare <kumare3@users.noreply.github.com>
6
6
  Requires-Python: >=3.10
@@ -12,7 +12,7 @@ Requires-Dist: flyteidl>=1.15.4b0
12
12
  Requires-Dist: cloudpickle>=3.1.1
13
13
  Requires-Dist: fsspec>=2025.3.0
14
14
  Requires-Dist: grpcio>=1.71.0
15
- Requires-Dist: obstore>=0.6.0
15
+ Requires-Dist: obstore>=0.7.3
16
16
  Requires-Dist: protobuf>=6.30.1
17
17
  Requires-Dist: pydantic>=2.10.6
18
18
  Requires-Dist: pyyaml>=6.0.2
@@ -28,7 +28,7 @@ Dynamic: license-file
28
28
 
29
29
  # Flyte 2 SDK 🚀
30
30
 
31
- **The next-generation Python SDK for scalable, distributed workflows**
31
+ **Type-safe, distributed orchestration of agents, ML pipelines, and more — in pure Python with async/await or sync!**
32
32
 
33
33
  [![Version](https://img.shields.io/pypi/v/flyte?label=version&color=blue)](https://pypi.org/project/flyte/)
34
34
  [![Python](https://img.shields.io/pypi/pyversions/flyte?color=brightgreen)](https://pypi.org/project/flyte/)
@@ -8,7 +8,7 @@ flyte/_environment.py,sha256=oKVXLBX0ky2eE_wjBdzvQGI_2LiT2Nbx58ur7GMt50c,3231
8
8
  flyte/_excepthook.py,sha256=nXts84rzEg6-7RtFarbKzOsRZTQR4plnbWVIFMAEprs,1310
9
9
  flyte/_group.py,sha256=7o1j16sZyUmYB50mOiq1ui4TBAKhRpDqLakV8Ya1kw4,803
10
10
  flyte/_hash.py,sha256=Of_Zl_DzzzF2jp4ZsLm-3o-xJFCCJ8_GubmLI1htx78,504
11
- flyte/_image.py,sha256=wAJO-WTEadRmLdW4q7SvInH03QD_virbvVMB4Wjsd90,33452
11
+ flyte/_image.py,sha256=G6o2z3JY1epk8oSDuUkTbj-5bNuSetC4w-ciG64FoHQ,35322
12
12
  flyte/_initialize.py,sha256=xKl_LYMluRt21wWqa6RTKuLo0_DCbSaTfUk27_brtNk,18232
13
13
  flyte/_interface.py,sha256=1B9zIwFDjiVp_3l_mk8EpA4g3Re-6DUBEBi9z9vDvPs,3504
14
14
  flyte/_logging.py,sha256=QrT4Z30C2tsZ-yIojisQODTuq6Y6zSJYuTrLgF58UYc,3664
@@ -18,17 +18,17 @@ flyte/_resources.py,sha256=L2JuvQDlMo1JLJeUmJPRwtWbunhR2xJEhFgQW5yc72c,9690
18
18
  flyte/_retry.py,sha256=rfLv0MvWxzPByKESTglEmjPsytEAKiIvvmzlJxXwsfE,941
19
19
  flyte/_reusable_environment.py,sha256=f8Y1GilUwGcXH4n2Fckrnx0SrZmhk3nCfoe-TqUKivI,3740
20
20
  flyte/_run.py,sha256=HkTD3rHL34pAwvn1WPN6OXYmk-GWX0txLdRH1OIMvEA,24338
21
- flyte/_secret.py,sha256=ogXmCNfYYIphV6p-2iiWmwr2cNUES5Cq01PPjY6uQNA,3217
22
- flyte/_task.py,sha256=pPq44DDr3PzaWXOJro0W2_mY4wdbS8Hwn0VFd-kel3Q,18985
23
- flyte/_task_environment.py,sha256=JnPniMJ1gIXKsTaYJeZJkhTA1bQ_om7VEWFjxVQNoS4,9485
21
+ flyte/_secret.py,sha256=89VIihdXI03irHb217GMfipt7jzXBafm17YYmyv6gHo,3245
22
+ flyte/_task.py,sha256=rYR7SVGihfBp7UYrhdmqhHp0ngl2K7Za8IJfhv1K2oc,19402
23
+ flyte/_task_environment.py,sha256=Zpfr8gjwXg5KuCfIbT4s3l2mtJCFqDxXwv6ZStHWBuc,9840
24
24
  flyte/_task_plugins.py,sha256=9MH3nFPOH_e8_92BT4sFk4oyAnj6GJFvaPYWaraX7yE,1037
25
25
  flyte/_timeout.py,sha256=zx5sFcbYmjJAJbZWSGzzX-BpC9HC7Jfs35T7vVhKwkk,1571
26
26
  flyte/_tools.py,sha256=tWb0sx3t3mm4jbaQVjCTc9y39oR_Ibo3z_KHToP3Lto,966
27
27
  flyte/_trace.py,sha256=SSE1nzUgmVTS2xFNtchEOjEjlRavMOIInasXzY8i9lU,4911
28
- flyte/_version.py,sha256=v70IkMWWrKvPe78KjW1MSbPy-TiziGMFOdm2dhMtnCk,519
29
- flyte/errors.py,sha256=3PCjGAxqifLBY6RE1n_ttmrmhcHwwtzS4UWDPql-FX4,5175
28
+ flyte/_version.py,sha256=_khCeGxTPxDDeGWBrQG01YziZI4eo5tW-iu_1TCc42w,519
29
+ flyte/errors.py,sha256=MsVZxq6oeIOXe6AVWpxqA3ZiyxCGM0A6UqnYuvmSUCU,5514
30
30
  flyte/extend.py,sha256=GB4ZedGzKa30vYWRVPOdxEeK62xnUVFY4z2tD6H9eEw,376
31
- flyte/models.py,sha256=k0hLoyOYQHnu5DXclEtKK2owDOXCdy0UKdGcl3xR7BQ,15250
31
+ flyte/models.py,sha256=AJZXL8eRmZ2RHEvjL6VSdpTmgF5S1Ekh_tAKRn0b6mM,15321
32
32
  flyte/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  flyte/_bin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  flyte/_bin/runtime.py,sha256=2jTy3ccvrJ__Xrfdo2t0Fxhsojc5o2zIxDHt98RE_eU,6475
@@ -42,18 +42,18 @@ flyte/_code_bundle/_packaging.py,sha256=5QUuea6kg9s-ebBg7gFAHaxOMchxR5MhTQ8KohWs
42
42
  flyte/_code_bundle/_utils.py,sha256=qlAVmik9rLasfd1oNrCxhL870w5ntk5ZlNGeaKSKaAU,12028
43
43
  flyte/_code_bundle/bundle.py,sha256=nUAwYTVAE3Z9dfgkBtsqCoKJImjSl4AicG36yweWHLc,8797
44
44
  flyte/_internal/__init__.py,sha256=vjXgGzAAjy609YFkAy9_RVPuUlslsHSJBXCLNTVnqOY,136
45
- flyte/_internal/controllers/__init__.py,sha256=5CBnS9lb1VFMzZuRXUiaPhlN3G9qh7Aq9kTwxW5hsRw,4301
46
- flyte/_internal/controllers/_local_controller.py,sha256=tbX6x8TBKwZHN1vYumftUcaX9fy162hGibo3AQZHYKA,7260
45
+ flyte/_internal/controllers/__init__.py,sha256=TVAc4ydsldcIFmN3PW9-IX5UkKeD8oOmuIukIgEae9M,4341
46
+ flyte/_internal/controllers/_local_controller.py,sha256=__-eEira0k18DsBu1LBXeEjhFGFcp1Uai9K0YEBbwKM,7300
47
47
  flyte/_internal/controllers/_trace.py,sha256=ywFg_M2nGrCKYLbh4iVdsVlRtPT1K2S-XZMvDJU8AKU,1499
48
48
  flyte/_internal/controllers/remote/__init__.py,sha256=9_azH1eHLqY6VULpDugXi7Kf1kK1ODqEnsQ_3wM6IqU,1919
49
49
  flyte/_internal/controllers/remote/_action.py,sha256=ENV1thRXllSpi2s4idL-vCVcmXQNS17hmP2lMDKzNdo,7397
50
50
  flyte/_internal/controllers/remote/_client.py,sha256=HPbzbfaWZVv5wpOvKNtFXR6COiZDwd1cUJQqi60A7oU,1421
51
- flyte/_internal/controllers/remote/_controller.py,sha256=oubfymmNgZtVEDJF8HiV3r2i_6lGgBZBzh0437jY0zQ,23766
52
- flyte/_internal/controllers/remote/_core.py,sha256=49l1X3YFyX-QYeSlvkOWdyEU2xc1Fr7I8qHvC3nQ6MA,19069
51
+ flyte/_internal/controllers/remote/_controller.py,sha256=tnHHWYd2nc16_1Slm8lShrbQdrfHjyNiSiByFk3A1mg,24988
52
+ flyte/_internal/controllers/remote/_core.py,sha256=PhqI_qwKieH0abOzXYzUZt3v166DxX9HkZPqLWXqH6w,18975
53
53
  flyte/_internal/controllers/remote/_informer.py,sha256=w4p29_dzS_ns762eNBljvnbJLgCm36d1Ogo2ZkgV1yg,14418
54
54
  flyte/_internal/controllers/remote/_service_protocol.py,sha256=B9qbIg6DiGeac-iSccLmX_AL2xUgX4ezNUOiAbSy4V0,1357
55
55
  flyte/_internal/imagebuild/__init__.py,sha256=dwXdJ1jMhw9RF8itF7jkPLanvX1yCviSns7hE5eoIts,102
56
- flyte/_internal/imagebuild/docker_builder.py,sha256=L2J1cAJCZ67SAcO_76J1mP44aCXAePgQe8OsJKHxGgE,16853
56
+ flyte/_internal/imagebuild/docker_builder.py,sha256=8k8YPqhr22p_SRx3_EaOlaXB67CGWFP3reRdQ0iOS8E,19770
57
57
  flyte/_internal/imagebuild/image_builder.py,sha256=dXBXl62qcPabus6dR3eP8P9mBGNhpZHZ2Xm12AymKkk,11150
58
58
  flyte/_internal/imagebuild/remote_builder.py,sha256=vxPhosMbFXQGQY0Um6il4C8I4vZEWsDyBK3uJQN3icg,10602
59
59
  flyte/_internal/resolvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -63,7 +63,7 @@ flyte/_internal/resolvers/default.py,sha256=nX4DHUYod1nRvEsl_vSgutQVEdExu2xL8pRk
63
63
  flyte/_internal/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
64
  flyte/_internal/runtime/convert.py,sha256=yK5Fy25-CVSqTtWF8BuBel2jwlVoh8R5F4UhIMYpgmg,16086
65
65
  flyte/_internal/runtime/entrypoints.py,sha256=9Ng-aQ45M-_MMWeOe9uGmgx69qO9b0xaMRiu542ZI9g,6581
66
- flyte/_internal/runtime/io.py,sha256=Lgdy4iPjlKjUO-V_AkoPZff6lywaFjZUG-PErRukmx4,4248
66
+ flyte/_internal/runtime/io.py,sha256=ysL7hMpfVumvsEYWOM-_VPa8MXn5_X_CZorKbOThyv4,5935
67
67
  flyte/_internal/runtime/resources_serde.py,sha256=TObMVsSjVcQhcY8-nY81pbvrz7TP-adDD5xV-LqAaxM,4813
68
68
  flyte/_internal/runtime/reuse.py,sha256=WEuBfC9tBezxaIXeUQDgnJfnRHiUmPK0S25nTOFle4E,4676
69
69
  flyte/_internal/runtime/rusty.py,sha256=puMaa6aLaoR4Tl5xxZulC4AzY58nmGg-5ok4ydHHjdM,6145
@@ -172,7 +172,7 @@ flyte/config/_internal.py,sha256=LMcAtDjvTjf5bGlsJVxPuLxQQ82mLd00xK5-JlYGCi8,298
172
172
  flyte/config/_reader.py,sha256=coidKV5CVODEnqDnsHZ9-VMC0UGXanXhAZE1SmYRhxI,7021
173
173
  flyte/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
174
174
  flyte/extras/__init__.py,sha256=FhB0uK7H1Yo5De9vOuF7UGnezTKncj3u2Wo5uQdWN0g,74
175
- flyte/extras/_container.py,sha256=nNKHuWe_H6lIQRVgU22Qen3kq04shv6nzXWRw7JntvM,11804
175
+ flyte/extras/_container.py,sha256=xfbxaGrx2JyQyrvQx1UWTzho2ion8_xujPW5V4uDEIs,11818
176
176
  flyte/io/__init__.py,sha256=qF8jq_IKuocuGU0LAvoy_uxjMs4G-vXjDA9Prba0JGU,538
177
177
  flyte/io/_dir.py,sha256=rih9CY1YjNX05bcAu5LG62Xoyij5GXAlv7jLyVF0je8,15310
178
178
  flyte/io/_file.py,sha256=kp5700SKPy5htmMhm4hE2ybb99Ykny1b0Kwm3huCWXs,15572
@@ -187,7 +187,7 @@ flyte/remote/_logs.py,sha256=aDG18-uPVb2J3PxmqmAY1C0Z4Iv1P1agg-iF4nSQR4U,6709
187
187
  flyte/remote/_project.py,sha256=CFNTGpgXU3X599tkJ_oxijs9zPzzCWOB6mAWn6WeDEU,2828
188
188
  flyte/remote/_run.py,sha256=fpr_YcGZIv6K7Jt1if3-HHHVB2TVt_8AWcZ55rN_fgk,9750
189
189
  flyte/remote/_secret.py,sha256=l5xeMS83uMcWWeSSTRsSZUNhS0N--1Dze09C-thSOQs,4341
190
- flyte/remote/_task.py,sha256=pIQ2pIIcaEaOo5J1ua3eRlj9NAue9WOSyw64f1-A2oY,15183
190
+ flyte/remote/_task.py,sha256=xHfWChnC-L7Hdp2IB9qDnbNyaOgM2TWOlnkmevoc2dA,15314
191
191
  flyte/remote/_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
192
  flyte/remote/_client/_protocols.py,sha256=JyBWHs5WsVOxEDUyG9X7wPLDzzzjkoaNhJlU-X4YlN0,5599
193
193
  flyte/remote/_client/controlplane.py,sha256=FsOfj4rO4MIMnYrpAT53F8q588VVf5t4sDuwoPuc840,3102
@@ -210,7 +210,7 @@ flyte/remote/_client/auth/_grpc_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
210
210
  flyte/remote/_client/auth/_grpc_utils/auth_interceptor.py,sha256=JCjdoWV41sjdvfJcUmrJdIfQ0meuGFwv2ArU7FQGDDA,12403
211
211
  flyte/remote/_client/auth/_grpc_utils/default_metadata_interceptor.py,sha256=IoMGWM42_VyzxqIVYe458o0uKsqhH-mcERUs9CY1L5U,6194
212
212
  flyte/report/__init__.py,sha256=yLbeUxYaVaDlgBod3Oh34zGBSotl1UlXq1vUkb9q7cs,152
213
- flyte/report/_report.py,sha256=jZXl-dqkZHiVAPBuUCmE4e1vE9FpHLkermOeZehi8Tc,5175
213
+ flyte/report/_report.py,sha256=eIk0q-aZi5Yfm63cHJb72qndlDV4kWvl2jxZ2LItGGw,5324
214
214
  flyte/report/_template.html,sha256=YehmLJG3QMYQ10UT1YZBu2ncVmAJ4iyqVp5hF3sXRAs,3458
215
215
  flyte/storage/__init__.py,sha256=0tcI9qtIVf0Fxczkno03vpwBDVlKMDSNN38uxMTH1bE,569
216
216
  flyte/storage/_config.py,sha256=xVibWJaioOnkeTb_M30azgiUe1jvmQaOWRZEkpdoTao,8680
@@ -226,10 +226,10 @@ flyte/types/_renderer.py,sha256=ygcCo5l60lHufyQISFddZfWwLlQ8kJAKxUT_XnR_6dY,4818
226
226
  flyte/types/_string_literals.py,sha256=NlG1xV8RSA-sZ-n-IFQCAsdB6jXJOAKkHWtnopxVVDk,4231
227
227
  flyte/types/_type_engine.py,sha256=Tas_OXYddOi0nDuORjqan2SkJ96wKD8937I2l1bo8vk,97916
228
228
  flyte/types/_utils.py,sha256=pbts9E1_2LTdLygAY0UYTLYJ8AsN3BZyviSXvrtcutc,2626
229
- flyte-2.0.0b2.data/scripts/runtime.py,sha256=2jTy3ccvrJ__Xrfdo2t0Fxhsojc5o2zIxDHt98RE_eU,6475
230
- flyte-2.0.0b2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
231
- flyte-2.0.0b2.dist-info/METADATA,sha256=wpYiUGf3r0Fif7gYArT5zD7QfM96VqABHkdU00uWw3c,9955
232
- flyte-2.0.0b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
233
- flyte-2.0.0b2.dist-info/entry_points.txt,sha256=MIq2z5dBurdCJfpXfMKzgBv7sJOakKRYxr8G0cMiTrg,75
234
- flyte-2.0.0b2.dist-info/top_level.txt,sha256=7dkyFbikvA12LEZEqawx8oDG1CMod6hTliPj7iWzgYo,6
235
- flyte-2.0.0b2.dist-info/RECORD,,
229
+ flyte-2.0.0b4.data/scripts/runtime.py,sha256=2jTy3ccvrJ__Xrfdo2t0Fxhsojc5o2zIxDHt98RE_eU,6475
230
+ flyte-2.0.0b4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
231
+ flyte-2.0.0b4.dist-info/METADATA,sha256=hrYyZeScWhM_utQk4oz22SQDBQi6V4W5OqOa2rRnSTw,10004
232
+ flyte-2.0.0b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
233
+ flyte-2.0.0b4.dist-info/entry_points.txt,sha256=MIq2z5dBurdCJfpXfMKzgBv7sJOakKRYxr8G0cMiTrg,75
234
+ flyte-2.0.0b4.dist-info/top_level.txt,sha256=7dkyFbikvA12LEZEqawx8oDG1CMod6hTliPj7iWzgYo,6
235
+ flyte-2.0.0b4.dist-info/RECORD,,