modal 1.0.6.dev58__py3-none-any.whl → 1.2.3.dev7__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 modal might be problematic. Click here for more details.

Files changed (147) hide show
  1. modal/__main__.py +3 -4
  2. modal/_billing.py +80 -0
  3. modal/_clustered_functions.py +7 -3
  4. modal/_clustered_functions.pyi +4 -2
  5. modal/_container_entrypoint.py +41 -49
  6. modal/_functions.py +424 -195
  7. modal/_grpc_client.py +171 -0
  8. modal/_load_context.py +105 -0
  9. modal/_object.py +68 -20
  10. modal/_output.py +58 -45
  11. modal/_partial_function.py +36 -11
  12. modal/_pty.py +7 -3
  13. modal/_resolver.py +21 -35
  14. modal/_runtime/asgi.py +4 -3
  15. modal/_runtime/container_io_manager.py +301 -186
  16. modal/_runtime/container_io_manager.pyi +70 -61
  17. modal/_runtime/execution_context.py +18 -2
  18. modal/_runtime/execution_context.pyi +4 -1
  19. modal/_runtime/gpu_memory_snapshot.py +170 -63
  20. modal/_runtime/user_code_imports.py +28 -58
  21. modal/_serialization.py +57 -1
  22. modal/_utils/async_utils.py +33 -12
  23. modal/_utils/auth_token_manager.py +2 -5
  24. modal/_utils/blob_utils.py +110 -53
  25. modal/_utils/function_utils.py +49 -42
  26. modal/_utils/grpc_utils.py +80 -50
  27. modal/_utils/mount_utils.py +26 -1
  28. modal/_utils/name_utils.py +17 -3
  29. modal/_utils/task_command_router_client.py +536 -0
  30. modal/_utils/time_utils.py +34 -6
  31. modal/app.py +219 -83
  32. modal/app.pyi +229 -56
  33. modal/billing.py +5 -0
  34. modal/{requirements → builder}/2025.06.txt +1 -0
  35. modal/{requirements → builder}/PREVIEW.txt +1 -0
  36. modal/cli/_download.py +19 -3
  37. modal/cli/_traceback.py +3 -2
  38. modal/cli/app.py +4 -4
  39. modal/cli/cluster.py +15 -7
  40. modal/cli/config.py +5 -3
  41. modal/cli/container.py +7 -6
  42. modal/cli/dict.py +22 -16
  43. modal/cli/entry_point.py +12 -5
  44. modal/cli/environment.py +5 -4
  45. modal/cli/import_refs.py +3 -3
  46. modal/cli/launch.py +102 -5
  47. modal/cli/network_file_system.py +9 -13
  48. modal/cli/profile.py +3 -2
  49. modal/cli/programs/launch_instance_ssh.py +94 -0
  50. modal/cli/programs/run_jupyter.py +1 -1
  51. modal/cli/programs/run_marimo.py +95 -0
  52. modal/cli/programs/vscode.py +1 -1
  53. modal/cli/queues.py +57 -26
  54. modal/cli/run.py +58 -16
  55. modal/cli/secret.py +48 -22
  56. modal/cli/utils.py +3 -4
  57. modal/cli/volume.py +28 -25
  58. modal/client.py +13 -116
  59. modal/client.pyi +9 -91
  60. modal/cloud_bucket_mount.py +5 -3
  61. modal/cloud_bucket_mount.pyi +5 -1
  62. modal/cls.py +130 -102
  63. modal/cls.pyi +45 -85
  64. modal/config.py +29 -10
  65. modal/container_process.py +291 -13
  66. modal/container_process.pyi +95 -32
  67. modal/dict.py +282 -63
  68. modal/dict.pyi +423 -73
  69. modal/environments.py +15 -27
  70. modal/environments.pyi +5 -15
  71. modal/exception.py +8 -0
  72. modal/experimental/__init__.py +143 -38
  73. modal/experimental/flash.py +247 -78
  74. modal/experimental/flash.pyi +137 -9
  75. modal/file_io.py +14 -28
  76. modal/file_io.pyi +2 -2
  77. modal/file_pattern_matcher.py +25 -16
  78. modal/functions.pyi +134 -61
  79. modal/image.py +255 -86
  80. modal/image.pyi +300 -62
  81. modal/io_streams.py +436 -126
  82. modal/io_streams.pyi +236 -171
  83. modal/mount.py +62 -157
  84. modal/mount.pyi +45 -172
  85. modal/network_file_system.py +30 -53
  86. modal/network_file_system.pyi +16 -76
  87. modal/object.pyi +42 -8
  88. modal/parallel_map.py +821 -113
  89. modal/parallel_map.pyi +134 -0
  90. modal/partial_function.pyi +4 -1
  91. modal/proxy.py +16 -7
  92. modal/proxy.pyi +10 -2
  93. modal/queue.py +263 -61
  94. modal/queue.pyi +409 -66
  95. modal/runner.py +112 -92
  96. modal/runner.pyi +45 -27
  97. modal/sandbox.py +451 -124
  98. modal/sandbox.pyi +513 -67
  99. modal/secret.py +291 -67
  100. modal/secret.pyi +425 -19
  101. modal/serving.py +7 -11
  102. modal/serving.pyi +7 -8
  103. modal/snapshot.py +11 -8
  104. modal/token_flow.py +4 -4
  105. modal/volume.py +344 -98
  106. modal/volume.pyi +464 -68
  107. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/METADATA +9 -8
  108. modal-1.2.3.dev7.dist-info/RECORD +195 -0
  109. modal_docs/mdmd/mdmd.py +11 -1
  110. modal_proto/api.proto +399 -67
  111. modal_proto/api_grpc.py +241 -1
  112. modal_proto/api_pb2.py +1395 -1000
  113. modal_proto/api_pb2.pyi +1239 -79
  114. modal_proto/api_pb2_grpc.py +499 -4
  115. modal_proto/api_pb2_grpc.pyi +162 -14
  116. modal_proto/modal_api_grpc.py +175 -160
  117. modal_proto/sandbox_router.proto +145 -0
  118. modal_proto/sandbox_router_grpc.py +105 -0
  119. modal_proto/sandbox_router_pb2.py +149 -0
  120. modal_proto/sandbox_router_pb2.pyi +333 -0
  121. modal_proto/sandbox_router_pb2_grpc.py +203 -0
  122. modal_proto/sandbox_router_pb2_grpc.pyi +75 -0
  123. modal_proto/task_command_router.proto +144 -0
  124. modal_proto/task_command_router_grpc.py +105 -0
  125. modal_proto/task_command_router_pb2.py +149 -0
  126. modal_proto/task_command_router_pb2.pyi +333 -0
  127. modal_proto/task_command_router_pb2_grpc.py +203 -0
  128. modal_proto/task_command_router_pb2_grpc.pyi +75 -0
  129. modal_version/__init__.py +1 -1
  130. modal-1.0.6.dev58.dist-info/RECORD +0 -183
  131. modal_proto/modal_options_grpc.py +0 -3
  132. modal_proto/options.proto +0 -19
  133. modal_proto/options_grpc.py +0 -3
  134. modal_proto/options_pb2.py +0 -35
  135. modal_proto/options_pb2.pyi +0 -20
  136. modal_proto/options_pb2_grpc.py +0 -4
  137. modal_proto/options_pb2_grpc.pyi +0 -7
  138. /modal/{requirements → builder}/2023.12.312.txt +0 -0
  139. /modal/{requirements → builder}/2023.12.txt +0 -0
  140. /modal/{requirements → builder}/2024.04.txt +0 -0
  141. /modal/{requirements → builder}/2024.10.txt +0 -0
  142. /modal/{requirements → builder}/README.md +0 -0
  143. /modal/{requirements → builder}/base-images.json +0 -0
  144. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/WHEEL +0 -0
  145. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/entry_points.txt +0 -0
  146. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/licenses/LICENSE +0 -0
  147. {modal-1.0.6.dev58.dist-info → modal-1.2.3.dev7.dist-info}/top_level.txt +0 -0
modal/mount.py CHANGED
@@ -13,18 +13,19 @@ from pathlib import Path, PurePosixPath
13
13
  from typing import Callable, Optional, Sequence, Union
14
14
 
15
15
  from google.protobuf.message import Message
16
+ from grpclib import GRPCError
16
17
 
17
18
  import modal.exception
18
19
  import modal.file_pattern_matcher
19
20
  from modal_proto import api_pb2
20
21
  from modal_version import __version__
21
22
 
22
- from ._object import _get_environment_name, _Object
23
+ from ._load_context import LoadContext
24
+ from ._object import _Object
23
25
  from ._resolver import Resolver
24
26
  from ._utils.async_utils import TaskContext, aclosing, async_map, synchronize_api
25
27
  from ._utils.blob_utils import FileUploadSpec, blob_upload_file, get_file_upload_spec_from_path
26
- from ._utils.deprecation import deprecation_warning
27
- from ._utils.grpc_utils import retry_transient_errors
28
+ from ._utils.grpc_utils import Retry
28
29
  from ._utils.name_utils import check_object_name
29
30
  from ._utils.package_utils import get_module_mount_info
30
31
  from .client import _Client
@@ -310,7 +311,7 @@ class _Mount(_Object, type_prefix="mo"):
310
311
  _entries: Optional[list[_MountEntry]] = None
311
312
  _deployment_name: Optional[str] = None
312
313
  _namespace: Optional[int] = None
313
- _environment_name: Optional[str] = None
314
+
314
315
  _allow_overwrite: bool = False
315
316
  _content_checksum_sha256_hex: Optional[str] = None
316
317
 
@@ -325,7 +326,12 @@ class _Mount(_Object, type_prefix="mo"):
325
326
  return None
326
327
  return (_Mount._type_prefix, "local", frozenset(included_files))
327
328
 
328
- obj = _Mount._from_loader(_Mount._load_mount, rep, deduplication_key=mount_content_deduplication_key)
329
+ obj = _Mount._from_loader(
330
+ _Mount._load_mount,
331
+ rep,
332
+ deduplication_key=mount_content_deduplication_key,
333
+ load_context_overrides=LoadContext.empty(),
334
+ )
329
335
  obj._entries = entries
330
336
  obj._is_local = True
331
337
  return obj
@@ -411,39 +417,6 @@ class _Mount(_Object, type_prefix="mo"):
411
417
  ),
412
418
  )
413
419
 
414
- @staticmethod
415
- def from_local_dir(
416
- local_path: Union[str, Path],
417
- *,
418
- # Where the directory is placed within in the mount
419
- remote_path: Union[str, PurePosixPath, None] = None,
420
- # Predicate filter function for file selection, which should accept a filepath and return `True` for inclusion.
421
- # Defaults to including all files.
422
- condition: Optional[Callable[[str], bool]] = None,
423
- # add files from subdirectories as well
424
- recursive: bool = True,
425
- ) -> "_Mount":
426
- """
427
- **Deprecated:** Use image.add_local_dir() instead
428
-
429
- Create a `Mount` from a local directory.
430
-
431
- **Usage**
432
-
433
- ```python notest
434
- assets = modal.Mount.from_local_dir(
435
- "~/assets",
436
- condition=lambda pth: not ".venv" in pth,
437
- remote_path="/assets",
438
- )
439
- ```
440
- """
441
- deprecation_warning(
442
- (2025, 1, 8),
443
- MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_dir"),
444
- )
445
- return _Mount._from_local_dir(local_path, remote_path=remote_path, condition=condition, recursive=recursive)
446
-
447
420
  @staticmethod
448
421
  def _from_local_dir(
449
422
  local_path: Union[str, Path],
@@ -479,29 +452,6 @@ class _Mount(_Object, type_prefix="mo"):
479
452
  ),
480
453
  )
481
454
 
482
- @staticmethod
483
- def from_local_file(local_path: Union[str, Path], remote_path: Union[str, PurePosixPath, None] = None) -> "_Mount":
484
- """
485
- **Deprecated**: Use image.add_local_file() instead
486
-
487
- Create a `Mount` mounting a single local file.
488
-
489
- **Usage**
490
-
491
- ```python notest
492
- # Mount the DBT profile in user's home directory into container.
493
- dbt_profiles = modal.Mount.from_local_file(
494
- local_path="~/profiles.yml",
495
- remote_path="/root/dbt_profile/profiles.yml",
496
- )
497
- ```
498
- """
499
- deprecation_warning(
500
- (2025, 1, 8),
501
- MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_file"),
502
- )
503
- return _Mount._from_local_file(local_path, remote_path)
504
-
505
455
  @staticmethod
506
456
  def _from_local_file(local_path: Union[str, Path], remote_path: Union[str, PurePosixPath, None] = None) -> "_Mount":
507
457
  return _Mount._new().add_local_file(local_path, remote_path=remote_path)
@@ -533,6 +483,7 @@ class _Mount(_Object, type_prefix="mo"):
533
483
  async def _load_mount(
534
484
  self: "_Mount",
535
485
  resolver: Resolver,
486
+ load_context: LoadContext,
536
487
  existing_object_id: Optional[str],
537
488
  ):
538
489
  t0 = time.monotonic()
@@ -574,7 +525,7 @@ class _Mount(_Object, type_prefix="mo"):
574
525
 
575
526
  request = api_pb2.MountPutFileRequest(sha256_hex=file_spec.sha256_hex)
576
527
  accounted_hashes.add(file_spec.sha256_hex)
577
- response = await retry_transient_errors(resolver.client.stub.MountPutFile, request, base_delay=1)
528
+ response = await load_context.client.stub.MountPutFile(request, retry=Retry(base_delay=1))
578
529
 
579
530
  if response.exists:
580
531
  n_finished += 1
@@ -588,7 +539,7 @@ class _Mount(_Object, type_prefix="mo"):
588
539
  async with blob_upload_concurrency:
589
540
  with file_spec.source() as fp:
590
541
  blob_id = await blob_upload_file(
591
- fp, resolver.client.stub, sha256_hex=file_spec.sha256_hex, md5_hex=file_spec.md5_hex
542
+ fp, load_context.client.stub, sha256_hex=file_spec.sha256_hex, md5_hex=file_spec.md5_hex
592
543
  )
593
544
  logger.debug(f"Uploading blob file {file_spec.source_description} as {remote_filename}")
594
545
  request2 = api_pb2.MountPutFileRequest(data_blob_id=blob_id, sha256_hex=file_spec.sha256_hex)
@@ -600,7 +551,7 @@ class _Mount(_Object, type_prefix="mo"):
600
551
 
601
552
  start_time = time.monotonic()
602
553
  while time.monotonic() - start_time < MOUNT_PUT_FILE_CLIENT_TIMEOUT:
603
- response = await retry_transient_errors(resolver.client.stub.MountPutFile, request2, base_delay=1)
554
+ response = await load_context.client.stub.MountPutFile(request2, retry=Retry(base_delay=1))
604
555
  if response.exists:
605
556
  n_finished += 1
606
557
  return mount_file
@@ -608,7 +559,7 @@ class _Mount(_Object, type_prefix="mo"):
608
559
  raise modal.exception.MountUploadTimeoutError(f"Mounting of {file_spec.source_description} timed out")
609
560
 
610
561
  # Upload files, or check if they already exist.
611
- n_concurrent_uploads = 512
562
+ n_concurrent_uploads = 64
612
563
  files: list[api_pb2.MountFile] = []
613
564
  async with aclosing(
614
565
  async_map(_Mount._get_files(self._entries), _put_file, concurrency=n_concurrent_uploads)
@@ -630,67 +581,28 @@ class _Mount(_Object, type_prefix="mo"):
630
581
  req = api_pb2.MountGetOrCreateRequest(
631
582
  deployment_name=self._deployment_name,
632
583
  namespace=self._namespace,
633
- environment_name=self._environment_name,
584
+ environment_name=load_context.environment_name,
634
585
  object_creation_type=creation_type,
635
586
  files=files,
636
587
  )
637
- elif resolver.app_id is not None:
588
+ elif load_context.app_id is not None:
638
589
  req = api_pb2.MountGetOrCreateRequest(
639
590
  object_creation_type=api_pb2.OBJECT_CREATION_TYPE_ANONYMOUS_OWNED_BY_APP,
640
591
  files=files,
641
- app_id=resolver.app_id,
592
+ app_id=load_context.app_id,
642
593
  )
643
594
  else:
644
595
  req = api_pb2.MountGetOrCreateRequest(
645
596
  object_creation_type=api_pb2.OBJECT_CREATION_TYPE_EPHEMERAL,
646
597
  files=files,
647
- environment_name=resolver.environment_name,
598
+ environment_name=load_context.environment_name,
648
599
  )
649
600
 
650
- resp = await retry_transient_errors(resolver.client.stub.MountGetOrCreate, req, base_delay=1)
601
+ resp = await load_context.client.stub.MountGetOrCreate(req, retry=Retry(base_delay=1))
651
602
  status_row.finish(f"Created mount {message_label}")
652
603
 
653
604
  logger.debug(f"Uploaded {total_uploads} new files and {total_bytes} bytes in {time.monotonic() - t0}s")
654
- self._hydrate(resp.mount_id, resolver.client, resp.handle_metadata)
655
-
656
- @staticmethod
657
- def from_local_python_packages(
658
- *module_names: str,
659
- remote_dir: Union[str, PurePosixPath] = ROOT_DIR.as_posix(),
660
- # Predicate filter function for file selection, which should accept a filepath and return `True` for inclusion.
661
- # Defaults to including all files.
662
- condition: Optional[Callable[[str], bool]] = None,
663
- ignore: Optional[Union[Sequence[str], Callable[[Path], bool]]] = None,
664
- ) -> "_Mount":
665
- """
666
- **Deprecated**: Use image.add_local_python_source instead
667
-
668
- Returns a `modal.Mount` that makes local modules listed in `module_names` available inside the container.
669
- This works by mounting the local path of each module's package to a directory inside the container
670
- that's on `PYTHONPATH`.
671
-
672
- **Usage**
673
-
674
- ```python notest
675
- import modal
676
- import my_local_module
677
-
678
- app = modal.App()
679
-
680
- @app.function(mounts=[
681
- modal.Mount.from_local_python_packages("my_local_module", "my_other_module"),
682
- ])
683
- def f():
684
- my_local_module.do_stuff()
685
- ```
686
- """
687
- deprecation_warning(
688
- (2025, 1, 8),
689
- MOUNT_DEPRECATION_MESSAGE_PATTERN.format(replacement="image.add_local_python_source"),
690
- )
691
- return _Mount._from_local_python_packages(
692
- *module_names, remote_dir=remote_dir, condition=condition, ignore=ignore
693
- )
605
+ self._hydrate(resp.mount_id, load_context.client, resp.handle_metadata)
694
606
 
695
607
  @staticmethod
696
608
  def _from_local_python_packages(
@@ -723,41 +635,25 @@ class _Mount(_Object, type_prefix="mo"):
723
635
  *,
724
636
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
725
637
  environment_name: Optional[str] = None,
638
+ client: Optional[_Client] = None,
726
639
  ) -> "_Mount":
727
640
  """mdmd:hidden"""
728
641
 
729
- async def _load(provider: _Mount, resolver: Resolver, existing_object_id: Optional[str]):
642
+ async def _load(provider: _Mount, resolver: Resolver, load_context, existing_object_id: Optional[str]):
730
643
  req = api_pb2.MountGetOrCreateRequest(
731
644
  deployment_name=name,
732
645
  namespace=namespace,
733
- environment_name=_get_environment_name(environment_name, resolver),
646
+ environment_name=load_context.environment_name,
734
647
  )
735
- response = await resolver.client.stub.MountGetOrCreate(req)
736
- provider._hydrate(response.mount_id, resolver.client, response.handle_metadata)
737
-
738
- return _Mount._from_loader(_load, "Mount()", hydrate_lazily=True)
739
-
740
- @classmethod
741
- async def lookup(
742
- cls: type["_Mount"],
743
- name: str,
744
- namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
745
- client: Optional[_Client] = None,
746
- environment_name: Optional[str] = None,
747
- ) -> "_Mount":
748
- """mdmd:hidden"""
749
- deprecation_warning(
750
- (2025, 1, 27),
751
- "`modal.Mount.lookup` is deprecated and will be removed in a future release."
752
- " It can be replaced with `modal.Mount.from_name`."
753
- "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
648
+ response = await load_context.client.stub.MountGetOrCreate(req)
649
+ provider._hydrate(response.mount_id, load_context.client, response.handle_metadata)
650
+
651
+ return _Mount._from_loader(
652
+ _load,
653
+ "Mount()",
654
+ hydrate_lazily=True,
655
+ load_context_overrides=LoadContext(environment_name=environment_name, client=client),
754
656
  )
755
- obj = _Mount.from_name(name, namespace=namespace, environment_name=environment_name)
756
- if client is None:
757
- client = await _Client.from_env()
758
- resolver = Resolver(client=client)
759
- await resolver.load(obj)
760
- return obj
761
657
 
762
658
  async def _deploy(
763
659
  self: "_Mount",
@@ -769,15 +665,12 @@ class _Mount(_Object, type_prefix="mo"):
769
665
  client: Optional[_Client] = None,
770
666
  ) -> None:
771
667
  check_object_name(deployment_name, "Mount")
772
- environment_name = _get_environment_name(environment_name, resolver=None)
773
668
  self._deployment_name = deployment_name
774
669
  self._namespace = namespace
775
- self._environment_name = environment_name
776
670
  self._allow_overwrite = allow_overwrite
777
- if client is None:
778
- client = await _Client.from_env()
779
- resolver = Resolver(client=client, environment_name=environment_name)
780
- await resolver.load(self)
671
+ resolver = Resolver()
672
+ root_metadata = LoadContext(client=client, environment_name=environment_name)
673
+ await resolver.load(self, root_metadata)
781
674
 
782
675
  def _get_metadata(self) -> api_pb2.MountHandleMetadata:
783
676
  if self._content_checksum_sha256_hex is None:
@@ -865,6 +758,7 @@ async def _create_single_client_dependency_mount(
865
758
  uv_python_platform: str,
866
759
  check_if_exists: bool = True,
867
760
  allow_overwrite: bool = False,
761
+ dry_run: bool = False,
868
762
  ):
869
763
  import tempfile
870
764
 
@@ -882,7 +776,7 @@ async def _create_single_client_dependency_mount(
882
776
 
883
777
  with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmpd:
884
778
  print(f"📦 Building {mount_name}.")
885
- requirements = os.path.join(os.path.dirname(__file__), f"requirements/{builder_version}.txt")
779
+ requirements = os.path.join(os.path.dirname(__file__), f"builder/{builder_version}.txt")
886
780
  cmd = " ".join(
887
781
  [
888
782
  "uv",
@@ -929,20 +823,28 @@ async def _create_single_client_dependency_mount(
929
823
  remote_path=REMOTE_SITECUSTOMIZE_PATH,
930
824
  )
931
825
 
932
- await python_mount._deploy.aio(
933
- mount_name,
934
- api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
935
- environment_name=profile_environment,
936
- allow_overwrite=allow_overwrite,
937
- client=client,
938
- )
939
- print(f"✅ Deployed mount {mount_name} to global namespace.")
826
+ if not dry_run:
827
+ try:
828
+ await python_mount._deploy.aio(
829
+ mount_name,
830
+ api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
831
+ environment_name=profile_environment,
832
+ allow_overwrite=allow_overwrite,
833
+ client=client,
834
+ )
835
+ print(f"✅ Deployed mount {mount_name} to global namespace.")
836
+ except GRPCError as e:
837
+ print(f"⚠️ Mount creation failed with {e.status}: {e.message}")
838
+ else:
839
+ print(f"Dry run - skipping deployment of mount {mount_name}")
940
840
 
941
841
 
942
842
  async def _create_client_dependency_mounts(
943
843
  client=None,
944
844
  python_versions: list[str] = list(PYTHON_STANDALONE_VERSIONS),
845
+ builder_versions: list[str] = ["2025.06"], # Reenable "PREVIEW" during testing
945
846
  check_if_exists=True,
847
+ dry_run=False,
946
848
  ):
947
849
  arch = "x86_64"
948
850
  platform_tags = [
@@ -950,8 +852,8 @@ async def _create_client_dependency_mounts(
950
852
  ("musllinux_1_2", f"{arch}-unknown-linux-musl"), # musl >= 1.2
951
853
  ]
952
854
  coros = []
953
- for builder_version in ["2025.06", "PREVIEW"]:
954
- for python_version in python_versions:
855
+ for python_version in python_versions:
856
+ for builder_version in builder_versions:
955
857
  for platform, uv_python_platform in platform_tags:
956
858
  coros.append(
957
859
  _create_single_client_dependency_mount(
@@ -961,9 +863,12 @@ async def _create_client_dependency_mounts(
961
863
  arch,
962
864
  platform,
963
865
  uv_python_platform,
964
- # Re-enable mount overwriting for PREVIEW version while under development
965
- # check_if_exists=builder_version != "PREVIEW",
966
- # allow_overwrite=builder_version == "PREVIEW",
866
+ # This check_if_exists / allow_overwrite parameterization is very awkward
867
+ # Also it doesn't provide a hook for overwriting a non-preview version, which
868
+ # in theory we may need to do at some point (hopefully not, but...)
869
+ check_if_exists=check_if_exists and builder_version != "PREVIEW",
870
+ allow_overwrite=builder_version == "PREVIEW",
871
+ dry_run=dry_run,
967
872
  )
968
873
  )
969
874
  await TaskContext.gather(*coros)