flyte 2.0.0b23__py3-none-any.whl → 2.0.0b25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flyte might be problematic. Click here for more details.

Files changed (162) hide show
  1. flyte/__init__.py +11 -2
  2. flyte/_cache/local_cache.py +4 -3
  3. flyte/_code_bundle/_utils.py +3 -3
  4. flyte/_code_bundle/bundle.py +12 -5
  5. flyte/_context.py +4 -1
  6. flyte/_custom_context.py +73 -0
  7. flyte/_deploy.py +31 -7
  8. flyte/_image.py +48 -16
  9. flyte/_initialize.py +69 -26
  10. flyte/_internal/controllers/_local_controller.py +1 -0
  11. flyte/_internal/controllers/_trace.py +1 -1
  12. flyte/_internal/controllers/remote/_action.py +9 -10
  13. flyte/_internal/controllers/remote/_client.py +1 -1
  14. flyte/_internal/controllers/remote/_controller.py +4 -2
  15. flyte/_internal/controllers/remote/_core.py +10 -13
  16. flyte/_internal/controllers/remote/_informer.py +3 -3
  17. flyte/_internal/controllers/remote/_service_protocol.py +7 -7
  18. flyte/_internal/imagebuild/docker_builder.py +45 -59
  19. flyte/_internal/imagebuild/remote_builder.py +51 -11
  20. flyte/_internal/imagebuild/utils.py +51 -3
  21. flyte/_internal/runtime/convert.py +39 -18
  22. flyte/_internal/runtime/io.py +8 -7
  23. flyte/_internal/runtime/resources_serde.py +20 -6
  24. flyte/_internal/runtime/reuse.py +1 -1
  25. flyte/_internal/runtime/task_serde.py +7 -10
  26. flyte/_internal/runtime/taskrunner.py +10 -1
  27. flyte/_internal/runtime/trigger_serde.py +13 -13
  28. flyte/_internal/runtime/types_serde.py +1 -1
  29. flyte/_keyring/file.py +2 -2
  30. flyte/_map.py +65 -13
  31. flyte/_pod.py +2 -2
  32. flyte/_resources.py +175 -31
  33. flyte/_run.py +37 -21
  34. flyte/_task.py +27 -6
  35. flyte/_task_environment.py +37 -10
  36. flyte/_utils/module_loader.py +2 -2
  37. flyte/_version.py +3 -3
  38. flyte/cli/_common.py +47 -5
  39. flyte/cli/_create.py +4 -0
  40. flyte/cli/_deploy.py +8 -0
  41. flyte/cli/_get.py +4 -0
  42. flyte/cli/_params.py +4 -4
  43. flyte/cli/_run.py +50 -7
  44. flyte/cli/_update.py +4 -3
  45. flyte/config/_config.py +2 -0
  46. flyte/config/_internal.py +1 -0
  47. flyte/config/_reader.py +3 -3
  48. flyte/errors.py +1 -1
  49. flyte/extend.py +4 -0
  50. flyte/extras/_container.py +6 -1
  51. flyte/git/_config.py +11 -9
  52. flyte/io/_dataframe/basic_dfs.py +1 -1
  53. flyte/io/_dataframe/dataframe.py +12 -8
  54. flyte/io/_dir.py +48 -15
  55. flyte/io/_file.py +48 -11
  56. flyte/models.py +12 -8
  57. flyte/remote/_action.py +18 -16
  58. flyte/remote/_client/_protocols.py +4 -3
  59. flyte/remote/_client/auth/_channel.py +1 -1
  60. flyte/remote/_client/controlplane.py +4 -8
  61. flyte/remote/_data.py +4 -3
  62. flyte/remote/_logs.py +3 -3
  63. flyte/remote/_run.py +5 -5
  64. flyte/remote/_secret.py +20 -13
  65. flyte/remote/_task.py +7 -8
  66. flyte/remote/_trigger.py +25 -27
  67. flyte/storage/_parallel_reader.py +274 -0
  68. flyte/storage/_storage.py +66 -2
  69. flyte/types/_interface.py +2 -2
  70. flyte/types/_pickle.py +1 -1
  71. flyte/types/_string_literals.py +8 -9
  72. flyte/types/_type_engine.py +25 -17
  73. flyte/types/_utils.py +1 -1
  74. {flyte-2.0.0b23.dist-info → flyte-2.0.0b25.dist-info}/METADATA +2 -1
  75. flyte-2.0.0b25.dist-info/RECORD +184 -0
  76. flyte/_protos/__init__.py +0 -0
  77. flyte/_protos/common/authorization_pb2.py +0 -66
  78. flyte/_protos/common/authorization_pb2.pyi +0 -108
  79. flyte/_protos/common/authorization_pb2_grpc.py +0 -4
  80. flyte/_protos/common/identifier_pb2.py +0 -117
  81. flyte/_protos/common/identifier_pb2.pyi +0 -142
  82. flyte/_protos/common/identifier_pb2_grpc.py +0 -4
  83. flyte/_protos/common/identity_pb2.py +0 -48
  84. flyte/_protos/common/identity_pb2.pyi +0 -72
  85. flyte/_protos/common/identity_pb2_grpc.py +0 -4
  86. flyte/_protos/common/list_pb2.py +0 -36
  87. flyte/_protos/common/list_pb2.pyi +0 -71
  88. flyte/_protos/common/list_pb2_grpc.py +0 -4
  89. flyte/_protos/common/policy_pb2.py +0 -37
  90. flyte/_protos/common/policy_pb2.pyi +0 -27
  91. flyte/_protos/common/policy_pb2_grpc.py +0 -4
  92. flyte/_protos/common/role_pb2.py +0 -37
  93. flyte/_protos/common/role_pb2.pyi +0 -53
  94. flyte/_protos/common/role_pb2_grpc.py +0 -4
  95. flyte/_protos/common/runtime_version_pb2.py +0 -28
  96. flyte/_protos/common/runtime_version_pb2.pyi +0 -24
  97. flyte/_protos/common/runtime_version_pb2_grpc.py +0 -4
  98. flyte/_protos/imagebuilder/definition_pb2.py +0 -60
  99. flyte/_protos/imagebuilder/definition_pb2.pyi +0 -153
  100. flyte/_protos/imagebuilder/definition_pb2_grpc.py +0 -4
  101. flyte/_protos/imagebuilder/payload_pb2.py +0 -32
  102. flyte/_protos/imagebuilder/payload_pb2.pyi +0 -21
  103. flyte/_protos/imagebuilder/payload_pb2_grpc.py +0 -4
  104. flyte/_protos/imagebuilder/service_pb2.py +0 -29
  105. flyte/_protos/imagebuilder/service_pb2.pyi +0 -5
  106. flyte/_protos/imagebuilder/service_pb2_grpc.py +0 -66
  107. flyte/_protos/logs/dataplane/payload_pb2.py +0 -100
  108. flyte/_protos/logs/dataplane/payload_pb2.pyi +0 -177
  109. flyte/_protos/logs/dataplane/payload_pb2_grpc.py +0 -4
  110. flyte/_protos/secret/definition_pb2.py +0 -49
  111. flyte/_protos/secret/definition_pb2.pyi +0 -93
  112. flyte/_protos/secret/definition_pb2_grpc.py +0 -4
  113. flyte/_protos/secret/payload_pb2.py +0 -62
  114. flyte/_protos/secret/payload_pb2.pyi +0 -94
  115. flyte/_protos/secret/payload_pb2_grpc.py +0 -4
  116. flyte/_protos/secret/secret_pb2.py +0 -38
  117. flyte/_protos/secret/secret_pb2.pyi +0 -6
  118. flyte/_protos/secret/secret_pb2_grpc.py +0 -198
  119. flyte/_protos/validate/validate/validate_pb2.py +0 -76
  120. flyte/_protos/workflow/common_pb2.py +0 -38
  121. flyte/_protos/workflow/common_pb2.pyi +0 -63
  122. flyte/_protos/workflow/common_pb2_grpc.py +0 -4
  123. flyte/_protos/workflow/environment_pb2.py +0 -29
  124. flyte/_protos/workflow/environment_pb2.pyi +0 -12
  125. flyte/_protos/workflow/environment_pb2_grpc.py +0 -4
  126. flyte/_protos/workflow/node_execution_service_pb2.py +0 -26
  127. flyte/_protos/workflow/node_execution_service_pb2.pyi +0 -4
  128. flyte/_protos/workflow/node_execution_service_pb2_grpc.py +0 -32
  129. flyte/_protos/workflow/queue_service_pb2.py +0 -117
  130. flyte/_protos/workflow/queue_service_pb2.pyi +0 -182
  131. flyte/_protos/workflow/queue_service_pb2_grpc.py +0 -206
  132. flyte/_protos/workflow/run_definition_pb2.py +0 -123
  133. flyte/_protos/workflow/run_definition_pb2.pyi +0 -354
  134. flyte/_protos/workflow/run_definition_pb2_grpc.py +0 -4
  135. flyte/_protos/workflow/run_logs_service_pb2.py +0 -41
  136. flyte/_protos/workflow/run_logs_service_pb2.pyi +0 -28
  137. flyte/_protos/workflow/run_logs_service_pb2_grpc.py +0 -69
  138. flyte/_protos/workflow/run_service_pb2.py +0 -147
  139. flyte/_protos/workflow/run_service_pb2.pyi +0 -203
  140. flyte/_protos/workflow/run_service_pb2_grpc.py +0 -480
  141. flyte/_protos/workflow/state_service_pb2.py +0 -67
  142. flyte/_protos/workflow/state_service_pb2.pyi +0 -76
  143. flyte/_protos/workflow/state_service_pb2_grpc.py +0 -138
  144. flyte/_protos/workflow/task_definition_pb2.py +0 -86
  145. flyte/_protos/workflow/task_definition_pb2.pyi +0 -105
  146. flyte/_protos/workflow/task_definition_pb2_grpc.py +0 -4
  147. flyte/_protos/workflow/task_service_pb2.py +0 -61
  148. flyte/_protos/workflow/task_service_pb2.pyi +0 -62
  149. flyte/_protos/workflow/task_service_pb2_grpc.py +0 -138
  150. flyte/_protos/workflow/trigger_definition_pb2.py +0 -66
  151. flyte/_protos/workflow/trigger_definition_pb2.pyi +0 -117
  152. flyte/_protos/workflow/trigger_definition_pb2_grpc.py +0 -4
  153. flyte/_protos/workflow/trigger_service_pb2.py +0 -96
  154. flyte/_protos/workflow/trigger_service_pb2.pyi +0 -110
  155. flyte/_protos/workflow/trigger_service_pb2_grpc.py +0 -281
  156. flyte-2.0.0b23.dist-info/RECORD +0 -262
  157. {flyte-2.0.0b23.data → flyte-2.0.0b25.data}/scripts/debug.py +0 -0
  158. {flyte-2.0.0b23.data → flyte-2.0.0b25.data}/scripts/runtime.py +0 -0
  159. {flyte-2.0.0b23.dist-info → flyte-2.0.0b25.dist-info}/WHEEL +0 -0
  160. {flyte-2.0.0b23.dist-info → flyte-2.0.0b25.dist-info}/entry_points.txt +0 -0
  161. {flyte-2.0.0b23.dist-info → flyte-2.0.0b25.dist-info}/licenses/LICENSE +0 -0
  162. {flyte-2.0.0b23.dist-info → flyte-2.0.0b25.dist-info}/top_level.txt +0 -0
flyte/__init__.py CHANGED
@@ -9,15 +9,16 @@ import sys
9
9
  from ._build import build
10
10
  from ._cache import Cache, CachePolicy, CacheRequest
11
11
  from ._context import ctx
12
+ from ._custom_context import custom_context, get_custom_context
12
13
  from ._deploy import build_images, deploy
13
14
  from ._environment import Environment
14
15
  from ._excepthook import custom_excepthook
15
16
  from ._group import group
16
17
  from ._image import Image
17
- from ._initialize import init, init_from_config
18
+ from ._initialize import current_domain, init, init_from_config
18
19
  from ._map import map
19
20
  from ._pod import PodTemplate
20
- from ._resources import GPU, TPU, Device, Resources
21
+ from ._resources import AMD_GPU, GPU, HABANA_GAUDI, TPU, Device, DeviceClass, Neuron, Resources
21
22
  from ._retry import RetryStrategy
22
23
  from ._reusable_environment import ReusePolicy
23
24
  from ._run import run, with_runcontext
@@ -60,16 +61,20 @@ def version() -> str:
60
61
 
61
62
 
62
63
  __all__ = [
64
+ "AMD_GPU",
63
65
  "GPU",
66
+ "HABANA_GAUDI",
64
67
  "TPU",
65
68
  "Cache",
66
69
  "CachePolicy",
67
70
  "CacheRequest",
68
71
  "Cron",
69
72
  "Device",
73
+ "DeviceClass",
70
74
  "Environment",
71
75
  "FixedRate",
72
76
  "Image",
77
+ "Neuron",
73
78
  "PodTemplate",
74
79
  "Resources",
75
80
  "RetryStrategy",
@@ -85,12 +90,16 @@ __all__ = [
85
90
  "build",
86
91
  "build_images",
87
92
  "ctx",
93
+ "current_domain",
94
+ "custom_context",
88
95
  "deploy",
96
+ "get_custom_context",
89
97
  "group",
90
98
  "init",
91
99
  "init_from_config",
92
100
  "map",
93
101
  "run",
94
102
  "trace",
103
+ "version",
95
104
  "with_runcontext",
96
105
  ]
@@ -8,9 +8,10 @@ try:
8
8
  except ImportError:
9
9
  HAS_AIOSQLITE = False
10
10
 
11
+ from flyteidl2.task import common_pb2
12
+
11
13
  from flyte._internal.runtime import convert
12
14
  from flyte._logging import logger
13
- from flyte._protos.workflow import run_definition_pb2
14
15
  from flyte.config import auto
15
16
 
16
17
  DEFAULT_CACHE_DIR = "~/.flyte"
@@ -127,7 +128,7 @@ class LocalTaskCache(object):
127
128
  row = await cursor.fetchone()
128
129
  if row:
129
130
  outputs_bytes = row[0]
130
- outputs = run_definition_pb2.Outputs()
131
+ outputs = common_pb2.Outputs()
131
132
  outputs.ParseFromString(outputs_bytes)
132
133
  return convert.Outputs(proto_outputs=outputs)
133
134
  return None
@@ -142,7 +143,7 @@ class LocalTaskCache(object):
142
143
  row = cursor.fetchone()
143
144
  if row:
144
145
  outputs_bytes = row[0]
145
- outputs = run_definition_pb2.Outputs()
146
+ outputs = common_pb2.Outputs()
146
147
  outputs.ParseFromString(outputs_bytes)
147
148
  return convert.Outputs(proto_outputs=outputs)
148
149
  return None
@@ -211,7 +211,7 @@ def list_imported_modules_as_files(source_path: str, modules: List[ModuleType])
211
211
  import flyte
212
212
  from flyte._utils.lazy_module import is_imported
213
213
 
214
- files = []
214
+ files = set()
215
215
  flyte_root = os.path.dirname(flyte.__file__)
216
216
 
217
217
  # These directories contain installed packages or modules from the Python standard library.
@@ -244,9 +244,9 @@ def list_imported_modules_as_files(source_path: str, modules: List[ModuleType])
244
244
  logger.debug(f"{mod_file} is not in {source_path}")
245
245
  continue
246
246
 
247
- files.append(mod_file)
247
+ files.add(mod_file)
248
248
 
249
- return files
249
+ return list(files)
250
250
 
251
251
 
252
252
  def add_imported_modules_from_source(source_path: str, destination: str, modules: List[ModuleType]):
@@ -8,7 +8,7 @@ from pathlib import Path
8
8
  from typing import ClassVar, Type
9
9
 
10
10
  from async_lru import alru_cache
11
- from flyteidl.core.tasks_pb2 import TaskTemplate
11
+ from flyteidl2.core.tasks_pb2 import TaskTemplate
12
12
 
13
13
  from flyte._logging import log, logger
14
14
  from flyte._utils import AsyncLRUCache
@@ -169,6 +169,8 @@ async def download_bundle(bundle: CodeBundle) -> pathlib.Path:
169
169
 
170
170
  :return: The path to the downloaded code bundle.
171
171
  """
172
+ import sys
173
+
172
174
  import flyte.storage as storage
173
175
 
174
176
  dest = pathlib.Path(bundle.destination)
@@ -185,17 +187,22 @@ async def download_bundle(bundle: CodeBundle) -> pathlib.Path:
185
187
  # NOTE the os.path.join(destination, ''). This is to ensure that the given path is in fact a directory and all
186
188
  # downloaded data should be copied into this directory. We do this to account for a difference in behavior in
187
189
  # fsspec, which requires a trailing slash in case of pre-existing directory.
188
- process = await asyncio.create_subprocess_exec(
189
- "tar",
190
- "--overwrite",
190
+ args = [
191
191
  "-xvf",
192
192
  str(downloaded_bundle),
193
193
  "-C",
194
194
  str(dest),
195
+ ]
196
+ if sys.platform != "darwin":
197
+ args.insert(0, "--overwrite")
198
+
199
+ process = await asyncio.create_subprocess_exec(
200
+ "tar",
201
+ *args,
195
202
  stdout=asyncio.subprocess.PIPE,
196
203
  stderr=asyncio.subprocess.PIPE,
197
204
  )
198
- stdout, stderr = await process.communicate()
205
+ _stdout, stderr = await process.communicate()
199
206
 
200
207
  if process.returncode != 0:
201
208
  raise RuntimeError(stderr.decode())
flyte/_context.py CHANGED
@@ -135,7 +135,10 @@ root_context_var = contextvars.ContextVar("root", default=Context(data=ContextDa
135
135
 
136
136
 
137
137
  def ctx() -> Optional[TaskContext]:
138
- """Retrieve the current task context from the context variable."""
138
+ """
139
+ Returns flyte.models.TaskContext if within a task context, else None
140
+ Note: Only use this in task code and not module level.
141
+ """
139
142
  return internal_ctx().data.task_context
140
143
 
141
144
 
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+
5
+ from flyte._context import ctx
6
+
7
+ from ._context import internal_ctx
8
+
9
+
10
+ def get_custom_context() -> dict[str, str]:
11
+ """
12
+ Get the current input context. This can be used within a task to retrieve
13
+ context metadata that was passed to the action.
14
+
15
+ Context will automatically propagate to sub-actions.
16
+
17
+ Example:
18
+ ```python
19
+ import flyte
20
+
21
+ env = flyte.TaskEnvironment(name="...")
22
+
23
+ @env.task
24
+ def t1():
25
+ # context can be retrieved with `get_custom_context`
26
+ ctx = flyte.get_custom_context()
27
+ print(ctx) # {'project': '...', 'entity': '...'}
28
+ ```
29
+
30
+ :return: Dictionary of context key-value pairs
31
+ """
32
+ tctx = ctx()
33
+ if tctx is None or tctx.custom_context is None:
34
+ return {}
35
+ return tctx.custom_context
36
+
37
+
38
+ @contextmanager
39
+ def custom_context(**context: str):
40
+ """
41
+ Synchronous context manager to set input context for tasks spawned within this block.
42
+
43
+ Example:
44
+ ```python
45
+ import flyte
46
+
47
+ env = flyte.TaskEnvironment(name="...")
48
+
49
+ @env.task
50
+ def t1():
51
+ ctx = flyte.get_custom_context()
52
+ print(ctx)
53
+
54
+ @env.task
55
+ def main():
56
+ # context can be passed via a context manager
57
+ with flyte.custom_context(project="my-project"):
58
+ t1() # will have {'project': 'my-project'} as context
59
+ ```
60
+
61
+ :param context: Key-value pairs to set as input context
62
+ """
63
+ ctx = internal_ctx()
64
+ if ctx.data.task_context is None:
65
+ yield
66
+ return
67
+
68
+ tctx = ctx.data.task_context
69
+ new_tctx = tctx.replace(custom_context={**tctx.custom_context, **context})
70
+
71
+ with ctx.replace_task_context(new_tctx):
72
+ yield
73
+ # Exit the context and restore the previous context
flyte/_deploy.py CHANGED
@@ -14,13 +14,14 @@ from flyte.syncify import syncify
14
14
 
15
15
  from ._environment import Environment
16
16
  from ._image import Image
17
- from ._initialize import ensure_client, get_client, get_common_config, requires_initialization
17
+ from ._initialize import ensure_client, get_client, get_init_config, requires_initialization
18
18
  from ._logging import logger
19
19
  from ._task import TaskTemplate
20
20
  from ._task_environment import TaskEnvironment
21
21
 
22
22
  if TYPE_CHECKING:
23
- from flyte._protos.workflow import task_definition_pb2, trigger_definition_pb2
23
+ from flyteidl2.task import task_definition_pb2
24
+ from flyteidl2.trigger import trigger_definition_pb2
24
25
 
25
26
  from ._code_bundle import CopyFiles
26
27
  from ._internal.imagebuild.image_builder import ImageCache
@@ -144,11 +145,11 @@ async def _deploy_task(
144
145
  """
145
146
  ensure_client()
146
147
  import grpc.aio
148
+ from flyteidl2.task import task_definition_pb2, task_service_pb2
147
149
 
148
150
  from ._internal.runtime.convert import convert_upload_default_inputs
149
151
  from ._internal.runtime.task_serde import translate_task_to_wire
150
152
  from ._internal.runtime.trigger_serde import to_task_trigger
151
- from ._protos.workflow import task_definition_pb2, task_service_pb2
152
153
 
153
154
  image_uri = task.image.uri if isinstance(task.image, Image) else task.image
154
155
 
@@ -213,20 +214,41 @@ async def _build_image_bg(env_name: str, image: Image) -> Tuple[str, str]:
213
214
  return env_name, await build.aio(image)
214
215
 
215
216
 
216
- async def _build_images(deployment: DeploymentPlan) -> ImageCache:
217
+ async def _build_images(deployment: DeploymentPlan, image_refs: Dict[str, str] | None = None) -> ImageCache:
217
218
  """
218
219
  Build the images for the given deployment plan and update the environment with the built image.
219
220
  """
220
221
  from ._internal.imagebuild.image_builder import ImageCache
221
222
 
223
+ if image_refs is None:
224
+ image_refs = {}
225
+
222
226
  images = []
223
227
  image_identifier_map = {}
224
228
  for env_name, env in deployment.envs.items():
225
229
  if not isinstance(env.image, str):
230
+ if env.image._ref_name is not None:
231
+ if env.image._ref_name in image_refs:
232
+ # If the image is set in the config, set it as the base_image
233
+ image_uri = image_refs[env.image._ref_name]
234
+ env.image = env.image.clone(base_image=image_uri)
235
+ else:
236
+ raise ValueError(
237
+ f"Image name '{env.image._ref_name}' not found in config. Available: {list(image_refs.keys())}"
238
+ )
239
+ if not env.image._layers:
240
+ # No additional layers, use the base_image directly without building
241
+ image_identifier_map[env_name] = image_uri
242
+ continue
226
243
  logger.debug(f"Building Image for environment {env_name}, image: {env.image}")
227
244
  images.append(_build_image_bg(env_name, env.image))
228
245
 
229
246
  elif env.image == "auto" and "auto" not in image_identifier_map:
247
+ if "default" in image_refs:
248
+ # If the default image is set through CLI, use it instead
249
+ image_uri = image_refs["default"]
250
+ image_identifier_map[env_name] = image_uri
251
+ continue
230
252
  auto_image = Image.from_debian_base()
231
253
  images.append(_build_image_bg(env_name, auto_image))
232
254
  final_images = await asyncio.gather(*images)
@@ -312,9 +334,9 @@ def get_deployer(env_type: Type[Environment | TaskEnvironment]) -> Deployer:
312
334
  async def apply(deployment_plan: DeploymentPlan, copy_style: CopyFiles, dryrun: bool = False) -> Deployment:
313
335
  from ._code_bundle import build_code_bundle
314
336
 
315
- cfg = get_common_config()
337
+ cfg = get_init_config()
316
338
 
317
- image_cache = await _build_images(deployment_plan)
339
+ image_cache = await _build_images(deployment_plan, cfg.images)
318
340
 
319
341
  if copy_style == "none" and not deployment_plan.version:
320
342
  raise flyte.errors.DeploymentError("Version must be set when copy_style is none")
@@ -422,5 +444,7 @@ async def build_images(envs: Environment) -> ImageCache:
422
444
  :param envs: Environment to build images for.
423
445
  :return: ImageCache containing the built images.
424
446
  """
447
+ cfg = get_init_config()
448
+ images = cfg.images if cfg else {}
425
449
  deployment = plan_deploy(envs)
426
- return await _build_images(deployment[0])
450
+ return await _build_images(deployment[0], images)
flyte/_image.py CHANGED
@@ -12,8 +12,6 @@ from typing import TYPE_CHECKING, ClassVar, Dict, List, Literal, Optional, Tuple
12
12
  import rich.repr
13
13
  from packaging.version import Version
14
14
 
15
- from flyte._utils import update_hasher_for_source
16
-
17
15
  if TYPE_CHECKING:
18
16
  from flyte import Secret, SecretRequest
19
17
 
@@ -167,21 +165,22 @@ class Requirements(PipPackages):
167
165
  class UVProject(PipOption, Layer):
168
166
  pyproject: Path
169
167
  uvlock: Path
168
+ project_install_mode: typing.Literal["dependencies_only", "install_project"] = "dependencies_only"
170
169
 
171
170
  def validate(self):
172
171
  if not self.pyproject.exists():
173
- raise FileNotFoundError(f"pyproject.toml file {self.pyproject} does not exist")
172
+ raise FileNotFoundError(f"pyproject.toml file {self.pyproject.resolve()} does not exist")
174
173
  if not self.pyproject.is_file():
175
- raise ValueError(f"Pyproject file {self.pyproject} is not a file")
174
+ raise ValueError(f"Pyproject file {self.pyproject.resolve()} is not a file")
176
175
  if not self.uvlock.exists():
177
- raise ValueError(f"UVLock file {self.uvlock} does not exist")
176
+ raise ValueError(f"UVLock file {self.uvlock.resolve()} does not exist")
178
177
  super().validate()
179
178
 
180
179
  def update_hash(self, hasher: hashlib._Hash):
181
- from ._utils import filehash_update
180
+ from ._utils import filehash_update, update_hasher_for_source
182
181
 
183
182
  super().update_hash(hasher)
184
- if self.extra_args and "--no-install-project" in self.extra_args:
183
+ if self.project_install_mode == "dependencies_only":
185
184
  filehash_update(self.uvlock, hasher)
186
185
  filehash_update(self.pyproject, hasher)
187
186
  else:
@@ -210,7 +209,7 @@ class PoetryProject(Layer):
210
209
  super().validate()
211
210
 
212
211
  def update_hash(self, hasher: hashlib._Hash):
213
- from ._utils import filehash_update
212
+ from ._utils import filehash_update, update_hasher_for_source
214
213
 
215
214
  hash_input = ""
216
215
  if self.extra_args:
@@ -401,6 +400,8 @@ class Image:
401
400
  name: Optional[str] = field(default=None)
402
401
  platform: Tuple[Architecture, ...] = field(default=("linux/amd64",))
403
402
  python_version: Tuple[int, int] = field(default_factory=_detect_python_version)
403
+ # Refer to the image_refs (name:image-uri) set in CLI or config
404
+ _ref_name: Optional[str] = field(default=None)
404
405
 
405
406
  # Layers to be added to the image. In init, because frozen, but users shouldn't access, so underscore.
406
407
  _layers: Tuple[Layer, ...] = field(default_factory=tuple)
@@ -418,6 +419,9 @@ class Image:
418
419
  # class-level token not included in __init__
419
420
  _token: ClassVar[object] = object()
420
421
 
422
+ # Underscore cuz we may rename in the future, don't expose for now,
423
+ _image_registry_secret: Optional[Secret] = None
424
+
421
425
  # check for the guard that we put in place
422
426
  def __post_init__(self):
423
427
  if object.__getattribute__(self, "__dict__").pop("_guard", None) is not Image._token:
@@ -506,6 +510,7 @@ class Image:
506
510
  flyte_version: Optional[str] = None,
507
511
  install_flyte: bool = True,
508
512
  registry: Optional[str] = None,
513
+ registry_secret: Optional[str | Secret] = None,
509
514
  name: Optional[str] = None,
510
515
  platform: Optional[Tuple[Architecture, ...]] = None,
511
516
  ) -> Image:
@@ -517,6 +522,7 @@ class Image:
517
522
  :param flyte_version: Union version to use
518
523
  :param install_flyte: If True, will install the flyte library in the image
519
524
  :param registry: Registry to use for the image
525
+ :param registry_secret: Secret to use to pull/push the private image.
520
526
  :param name: Name of the image if you want to override the default name
521
527
  :param platform: Platform to use for the image, default is linux/amd64, use tuple for multiple values
522
528
  Example: ("linux/amd64", "linux/arm64")
@@ -534,7 +540,7 @@ class Image:
534
540
  )
535
541
 
536
542
  if registry or name:
537
- return base_image.clone(registry=registry, name=name)
543
+ return base_image.clone(registry=registry, name=name, registry_secret=registry_secret)
538
544
 
539
545
  return base_image
540
546
 
@@ -549,6 +555,13 @@ class Image:
549
555
  img = cls._new(base_image=image_uri)
550
556
  return img
551
557
 
558
+ @classmethod
559
+ def from_ref_name(cls, name: str) -> Image:
560
+ # NOTE: set image name as _ref_name to enable adding additional layers.
561
+ # See: https://github.com/flyteorg/flyte-sdk/blob/14de802701aab7b8615ffb99c650a36305ef01f7/src/flyte/_image.py#L642
562
+ img = cls._new(name=name, _ref_name=name)
563
+ return img
564
+
552
565
  @classmethod
553
566
  def from_uv_script(
554
567
  cls,
@@ -556,6 +569,7 @@ class Image:
556
569
  *,
557
570
  name: str,
558
571
  registry: str | None = None,
572
+ registry_secret: Optional[str | Secret] = None,
559
573
  python_version: Optional[Tuple[int, int]] = None,
560
574
  index_url: Optional[str] = None,
561
575
  extra_index_urls: Union[str, List[str], Tuple[str, ...], None] = None,
@@ -584,6 +598,7 @@ class Image:
584
598
 
585
599
  :param name: name of the image
586
600
  :param registry: registry to use for the image
601
+ :param registry_secret: Secret to use to pull/push the private image.
587
602
  :param python_version: Python version to use for the image, if not specified, will use the current Python
588
603
  version
589
604
  :param script: path to the uv script
@@ -609,14 +624,22 @@ class Image:
609
624
  secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None,
610
625
  )
611
626
 
612
- img = cls.from_debian_base(registry=registry, name=name, python_version=python_version, platform=platform)
627
+ img = cls.from_debian_base(
628
+ registry=registry,
629
+ registry_secret=registry_secret,
630
+ name=name,
631
+ python_version=python_version,
632
+ platform=platform,
633
+ )
613
634
 
614
635
  return img.clone(addl_layer=ll)
615
636
 
616
637
  def clone(
617
638
  self,
618
639
  registry: Optional[str] = None,
640
+ registry_secret: Optional[str | Secret] = None,
619
641
  name: Optional[str] = None,
642
+ base_image: Optional[str] = None,
620
643
  python_version: Optional[Tuple[int, int]] = None,
621
644
  addl_layer: Optional[Layer] = None,
622
645
  ) -> Image:
@@ -624,12 +647,14 @@ class Image:
624
647
  Use this method to clone the current image and change the registry and name
625
648
 
626
649
  :param registry: Registry to use for the image
650
+ :param registry_secret: Secret to use to pull/push the private image.
627
651
  :param name: Name of the image
628
652
  :param python_version: Python version for the image, if not specified, will use the current Python version
629
653
  :param addl_layer: Additional layer to add to the image. This will be added to the end of the layers.
630
-
631
654
  :return:
632
655
  """
656
+ from flyte import Secret
657
+
633
658
  if addl_layer and self.dockerfile:
634
659
  # We don't know how to inspect dockerfiles to know what kind it is (OS, python version, uv vs poetry, etc)
635
660
  # so there's no guarantee any of the layering logic will work.
@@ -639,19 +664,23 @@ class Image:
639
664
  )
640
665
  registry = registry if registry else self.registry
641
666
  name = name if name else self.name
667
+ registry_secret = registry_secret if registry_secret else self._image_registry_secret
668
+ base_image = base_image if base_image else self.base_image
642
669
  if addl_layer and (not name):
643
670
  raise ValueError(
644
671
  f"Cannot add additional layer {addl_layer} to an image without name. Please first clone()."
645
672
  )
646
673
  new_layers = (*self._layers, addl_layer) if addl_layer else self._layers
647
674
  img = Image._new(
648
- base_image=self.base_image,
675
+ base_image=base_image,
649
676
  dockerfile=self.dockerfile,
650
677
  registry=registry,
651
678
  name=name,
652
679
  platform=self.platform,
653
680
  python_version=python_version or self.python_version,
654
681
  _layers=new_layers,
682
+ _image_registry_secret=Secret(key=registry_secret) if isinstance(registry_secret, str) else registry_secret,
683
+ _ref_name=self._ref_name,
655
684
  )
656
685
 
657
686
  return img
@@ -876,17 +905,17 @@ class Image:
876
905
  pre: bool = False,
877
906
  extra_args: Optional[str] = None,
878
907
  secret_mounts: Optional[SecretRequest] = None,
908
+ project_install_mode: typing.Literal["dependencies_only", "install_project"] = "dependencies_only",
879
909
  ) -> Image:
880
910
  """
881
911
  Use this method to create a new image with the specified uv.lock file layered on top of the current image
882
912
  Must have a corresponding pyproject.toml file in the same directory
883
913
  Cannot be used in conjunction with conda
884
914
 
885
- By default, this method copies the entire project into the image,
886
- including files such as pyproject.toml, uv.lock, and the src/ directory.
915
+ By default, this method copies the pyproject.toml and uv.lock files into the image.
887
916
 
888
- If you prefer not to install the current project, you can pass the extra argument --no-install-project.
889
- In this case, the image builder will only copy pyproject.toml and uv.lock into the image.
917
+ If `project_install_mode` is "install_project", it will also copy directory
918
+ where the pyproject.toml file is located into the image.
890
919
 
891
920
  :param pyproject_file: path to the pyproject.toml file, needs to have a corresponding uv.lock file
892
921
  :param uvlock: path to the uv.lock file, if not specified, will use the default uv.lock file in the same
@@ -896,6 +925,8 @@ class Image:
896
925
  :param pre: whether to allow pre-release versions, default is False
897
926
  :param extra_args: extra arguments to pass to pip install, default is None
898
927
  :param secret_mounts: list of secret mounts to use for the build process.
928
+ :param project_install_mode: whether to install the project as a package or
929
+ only dependencies, default is "dependencies_only"
899
930
  :return: Image
900
931
  """
901
932
  if isinstance(pyproject_file, str):
@@ -909,6 +940,7 @@ class Image:
909
940
  pre=pre,
910
941
  extra_args=extra_args,
911
942
  secret_mounts=_ensure_tuple(secret_mounts) if secret_mounts else None,
943
+ project_install_mode=project_install_mode,
912
944
  )
913
945
  )
914
946
  return new_image