modAL 1.6.3.dev5__tar.gz → 1.6.3.dev7__tar.gz

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.
Files changed (249) hide show
  1. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/PKG-INFO +1 -1
  2. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_image.py +21 -22
  3. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/grpc_testing.py +3 -1
  4. modal-1.6.3.dev7/modal/builder/PREVIEW.txt +18 -0
  5. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/client.pyi +2 -2
  6. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/image.pyi +4 -1
  7. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/mount.py +35 -13
  8. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/mount.pyi +10 -8
  9. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_common.py +17 -2
  10. modal-1.6.3.dev7/modal/sandbox/_impl_persistent.py +470 -0
  11. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_impl_v1.py +10 -0
  12. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_impl_v2.py +10 -0
  13. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_sandbox.py +21 -3
  14. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/sandbox.pyi +38 -2
  15. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/PKG-INFO +1 -1
  16. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/SOURCES.txt +1 -0
  17. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_version/__init__.py +1 -1
  18. modal-1.6.3.dev5/modal/builder/PREVIEW.txt +0 -21
  19. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/LICENSE +0 -0
  20. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/README.md +0 -0
  21. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/__init__.py +0 -0
  22. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/__main__.py +0 -0
  23. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_billing.py +0 -0
  24. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_cluster.py +0 -0
  25. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_cluster.pyi +0 -0
  26. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_container_entrypoint.py +0 -0
  27. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_environments.py +0 -0
  28. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_function_variants.py +0 -0
  29. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_functions.py +0 -0
  30. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_grpc_client.py +0 -0
  31. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_ipython.py +0 -0
  32. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_load_context.py +0 -0
  33. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_location.py +0 -0
  34. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_logs.py +0 -0
  35. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_logs_manager.py +0 -0
  36. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_logs_manager.pyi +0 -0
  37. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_metrics.py +0 -0
  38. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_metrics.pyi +0 -0
  39. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_object.py +0 -0
  40. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_outbound_policy.py +0 -0
  41. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_outbound_policy.pyi +0 -0
  42. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_output/__init__.py +0 -0
  43. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_output/manager.py +0 -0
  44. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_output/pty.py +0 -0
  45. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_output/rich.py +0 -0
  46. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_output/status.py +0 -0
  47. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_partial_function.py +0 -0
  48. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_resolver.py +0 -0
  49. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_resources.py +0 -0
  50. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/__init__.py +0 -0
  51. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/asgi.py +0 -0
  52. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/container_io_manager.py +0 -0
  53. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/container_io_manager.pyi +0 -0
  54. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/execution_context.py +0 -0
  55. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/execution_context.pyi +0 -0
  56. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  57. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/task_lifecycle_manager.py +0 -0
  58. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  59. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/telemetry.py +0 -0
  60. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/user_code_event_loop.py +0 -0
  61. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_runtime/user_code_imports.py +0 -0
  62. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_serialization.py +0 -0
  63. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_server.py +0 -0
  64. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_server.pyi +0 -0
  65. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_supports_logs.py +0 -0
  66. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_traceback.py +0 -0
  67. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_tunnel.py +0 -0
  68. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_tunnel.pyi +0 -0
  69. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_type_manager.py +0 -0
  70. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/__init__.py +0 -0
  71. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/app_utils.py +0 -0
  72. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/async_utils.py +0 -0
  73. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/auth_token_manager.py +0 -0
  74. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/blob_utils.py +0 -0
  75. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/browser_utils.py +0 -0
  76. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/bytes_io_segment_payload.py +0 -0
  77. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/curl_utils.py +0 -0
  78. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/deprecation.py +0 -0
  79. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/display_utils.py +0 -0
  80. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/docker_utils.py +0 -0
  81. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/function_utils.py +0 -0
  82. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/git_utils.py +0 -0
  83. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/grpc_utils.py +0 -0
  84. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/hash_utils.py +0 -0
  85. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/http_utils.py +0 -0
  86. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/idle_countdown.py +0 -0
  87. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/jwt_utils.py +0 -0
  88. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/logger.py +0 -0
  89. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/mount_utils.py +0 -0
  90. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/name_utils.py +0 -0
  91. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/package_utils.py +0 -0
  92. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/pattern_utils.py +0 -0
  93. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/proxy_support.py +0 -0
  94. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/rand_pb_testing.py +0 -0
  95. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/sandbox_fs_utils.py +0 -0
  96. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/shell_utils.py +0 -0
  97. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_utils/time_utils.py +0 -0
  98. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_vendor/__init__.py +0 -0
  99. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  100. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_vendor/cloudpickle.py +0 -0
  101. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_vendor/tblib.py +0 -0
  102. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_vendor/version.py +0 -0
  103. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_watcher.py +0 -0
  104. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/_workspace.py +0 -0
  105. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/app.py +0 -0
  106. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/app.pyi +0 -0
  107. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/billing.py +0 -0
  108. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/billing.pyi +0 -0
  109. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/2023.12.312.txt +0 -0
  110. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/2023.12.txt +0 -0
  111. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/2024.04.txt +0 -0
  112. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/2024.10.txt +0 -0
  113. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/2025.06.txt +0 -0
  114. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/README.md +0 -0
  115. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/builder/base-images.json +0 -0
  116. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/call_graph.py +0 -0
  117. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/__init__.py +0 -0
  118. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/_download.py +0 -0
  119. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/_help.py +0 -0
  120. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/_logs.py +0 -0
  121. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/_stats.py +0 -0
  122. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/_traceback.py +0 -0
  123. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/app.py +0 -0
  124. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/billing.py +0 -0
  125. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/changelog.py +0 -0
  126. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/config.py +0 -0
  127. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/container.py +0 -0
  128. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/curl.py +0 -0
  129. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/dashboard.py +0 -0
  130. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/dict.py +0 -0
  131. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/endpoint.py +0 -0
  132. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/entry_point.py +0 -0
  133. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/environment.py +0 -0
  134. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/function.py +0 -0
  135. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/image.py +0 -0
  136. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/import_refs.py +0 -0
  137. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/logo.py +0 -0
  138. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/network_file_system.py +0 -0
  139. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/profile.py +0 -0
  140. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/queues.py +0 -0
  141. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/run.py +0 -0
  142. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/secret.py +0 -0
  143. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/selector.py +0 -0
  144. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/server.py +0 -0
  145. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/shell.py +0 -0
  146. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/skills.py +0 -0
  147. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/token.py +0 -0
  148. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/utils.py +0 -0
  149. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/volume.py +0 -0
  150. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cli/workspace.py +0 -0
  151. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/client.py +0 -0
  152. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cloud_bucket_mount.py +0 -0
  153. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cloud_bucket_mount.pyi +0 -0
  154. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cls.py +0 -0
  155. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cls.pyi +0 -0
  156. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cluster.py +0 -0
  157. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/cluster.pyi +0 -0
  158. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/config.py +0 -0
  159. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/container_process.py +0 -0
  160. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/container_process.pyi +0 -0
  161. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/dict.py +0 -0
  162. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/dict.pyi +0 -0
  163. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/environments.py +0 -0
  164. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/environments.pyi +0 -0
  165. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/exception.py +0 -0
  166. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/__init__.py +0 -0
  167. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/_flash_container_entry.py +0 -0
  168. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/flash.py +0 -0
  169. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/flash.pyi +0 -0
  170. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/flash_server.py +0 -0
  171. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/flash_server.pyi +0 -0
  172. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/experimental/ipython.py +0 -0
  173. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/file_pattern_matcher.py +0 -0
  174. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/functions.py +0 -0
  175. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/functions.pyi +0 -0
  176. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/image.py +0 -0
  177. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/io_streams.py +0 -0
  178. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/io_streams.pyi +0 -0
  179. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/network_file_system.py +0 -0
  180. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/network_file_system.pyi +0 -0
  181. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/object.py +0 -0
  182. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/object.pyi +0 -0
  183. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/output.py +0 -0
  184. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/parallel_map.py +0 -0
  185. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/parallel_map.pyi +0 -0
  186. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/partial_function.py +0 -0
  187. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/partial_function.pyi +0 -0
  188. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/proxy.py +0 -0
  189. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/proxy.pyi +0 -0
  190. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/py.typed +0 -0
  191. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/queue.py +0 -0
  192. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/queue.pyi +0 -0
  193. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/retries.py +0 -0
  194. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/runner.py +0 -0
  195. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/runner.pyi +0 -0
  196. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/running_app.py +0 -0
  197. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/__init__.py +0 -0
  198. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_filesystem.py +0 -0
  199. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_sidecar.py +0 -0
  200. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_snapshot.py +0 -0
  201. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/_task_command_router_client.py +0 -0
  202. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/sandbox/sandbox.py +0 -0
  203. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/schedule.py +0 -0
  204. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/scheduler_placement.py +0 -0
  205. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/secret.py +0 -0
  206. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/secret.pyi +0 -0
  207. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/server.py +0 -0
  208. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/server.pyi +0 -0
  209. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/serving.py +0 -0
  210. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/serving.pyi +0 -0
  211. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/skills/modal/SKILL.md +0 -0
  212. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/stream_type.py +0 -0
  213. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/token_flow.py +0 -0
  214. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/token_flow.pyi +0 -0
  215. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/types.py +0 -0
  216. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/volume.py +0 -0
  217. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/volume.pyi +0 -0
  218. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/workspace.py +0 -0
  219. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal/workspace.pyi +0 -0
  220. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/dependency_links.txt +0 -0
  221. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/entry_points.txt +0 -0
  222. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/requires.txt +0 -0
  223. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal.egg-info/top_level.txt +0 -0
  224. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/__init__.py +0 -0
  225. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/gen_cli_docs.py +0 -0
  226. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/gen_cli_docs_main.py +0 -0
  227. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/gen_reference_docs.py +0 -0
  228. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/gen_reference_docs_main.py +0 -0
  229. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/mdmd/__init__.py +0 -0
  230. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/mdmd/mdmd.py +0 -0
  231. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/mdmd/signatures.py +0 -0
  232. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/mdmd/types.py +0 -0
  233. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_docs/reference_intro.md +0 -0
  234. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/__init__.py +0 -0
  235. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/api_grpc.py +0 -0
  236. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/api_pb2.py +0 -0
  237. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/api_pb2.pyi +0 -0
  238. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/api_pb2_grpc.py +0 -0
  239. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/api_pb2_grpc.pyi +0 -0
  240. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/modal_api_grpc.py +0 -0
  241. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/py.typed +0 -0
  242. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/task_command_router_grpc.py +0 -0
  243. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/task_command_router_pb2.py +0 -0
  244. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/task_command_router_pb2.pyi +0 -0
  245. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  246. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  247. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/modal_version/__main__.py +0 -0
  248. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/pyproject.toml +0 -0
  249. {modal-1.6.3.dev5 → modal-1.6.3.dev7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.6.3.dev5
3
+ Version: 1.6.3.dev7
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -57,7 +57,7 @@ from .exception import (
57
57
  VersionError,
58
58
  )
59
59
  from .file_pattern_matcher import NON_PYTHON_FILES, FilePatternMatcher, _ignore_fn
60
- from .mount import _Mount, python_standalone_mount_name
60
+ from .mount import PYTHON_STANDALONE_VERSIONS, _Mount, python_standalone_mount_name
61
61
  from .network_file_system import _NetworkFileSystem
62
62
  from .output import OutputManager
63
63
  from .secret import _Secret
@@ -70,6 +70,7 @@ if typing.TYPE_CHECKING:
70
70
 
71
71
  # This is used for both type checking and runtime validation
72
72
  ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]
73
+ LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2025.06"
73
74
 
74
75
  # Note: we also define supported Python versions via logic at the top of the package __init__.py
75
76
  # so that we fail fast / clearly in unsupported containers. Additionally, we enumerate the supported
@@ -402,7 +403,7 @@ def _create_context_mount_function(
402
403
 
403
404
  if ignore is AUTO_DOCKERIGNORE:
404
405
 
405
- def auto_created_context_mount_fn() -> _Mount | None:
406
+ def auto_created_context_mount_fn(builder_version: ImageBuilderVersion) -> _Mount | None:
406
407
  nonlocal context_dir
407
408
  context_dir = Path.cwd() if context_dir is None else Path(context_dir).absolute()
408
409
  dockerignore_file = find_dockerignore_file(context_dir, dockerfile_path)
@@ -419,7 +420,7 @@ def _create_context_mount_function(
419
420
 
420
421
  else:
421
422
 
422
- def auto_created_context_mount_fn() -> _Mount | None:
423
+ def auto_created_context_mount_fn(builder_version: ImageBuilderVersion) -> _Mount | None:
423
424
  # use COPY commands and ignore patterns to construct implicit context mount
424
425
  nonlocal context_dir
425
426
  context_dir = Path.cwd() if context_dir is None else Path(context_dir).absolute()
@@ -622,7 +623,7 @@ class _Image(_Object, type_prefix="im"):
622
623
  build_function: "modal._functions._Function | None" = None,
623
624
  build_function_input: api_pb2.FunctionInput | None = None,
624
625
  image_registry_config: _ImageRegistryConfig | None = None,
625
- context_mount_function: Callable[[], _Mount | None] | None = None,
626
+ context_mount_function: Callable[[ImageBuilderVersion], _Mount | None] | None = None,
626
627
  force_build: bool = False,
627
628
  build_args: dict[str, str] = {},
628
629
  validated_volumes: Sequence[tuple[str, _Volume]] | None = None,
@@ -661,7 +662,13 @@ class _Image(_Object, type_prefix="im"):
661
662
  return deps
662
663
 
663
664
  async def _load(self: _Image, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
664
- context_mount = context_mount_function() if context_mount_function else None
665
+ assert load_context.app_id # type narrowing
666
+ environment = await _get_environment_cached(load_context.environment_name or "", load_context.client)
667
+ # A bit hacky,but assume that the environment provides a valid builder version
668
+ image_builder_version = cast(ImageBuilderVersion, environment._settings.image_builder_version)
669
+ builder_version = _get_image_builder_version(image_builder_version)
670
+
671
+ context_mount = context_mount_function(builder_version) if context_mount_function else None
665
672
  if context_mount:
666
673
  await resolver.load(context_mount, load_context)
667
674
 
@@ -670,12 +677,6 @@ class _Image(_Object, type_prefix="im"):
670
677
  # base images can't have
671
678
  image._assert_no_mount_layers()
672
679
 
673
- assert load_context.app_id # type narrowing
674
- environment = await _get_environment_cached(load_context.environment_name or "", load_context.client)
675
- # A bit hacky,but assume that the environment provides a valid builder version
676
- image_builder_version = cast(ImageBuilderVersion, environment._settings.image_builder_version)
677
- builder_version = _get_image_builder_version(image_builder_version)
678
-
679
680
  if dockerfile_function is None:
680
681
  dockerfile = DockerfileSpec(commands=[], context_files={})
681
682
  else:
@@ -847,7 +848,7 @@ class _Image(_Object, type_prefix="im"):
847
848
  return _Image._from_args(
848
849
  base_images={"base": self},
849
850
  dockerfile_function=build_dockerfile,
850
- context_mount_function=lambda: mount,
851
+ context_mount_function=lambda builder_version: mount,
851
852
  )
852
853
 
853
854
  @_requires_image_instance
@@ -2191,12 +2192,9 @@ class _Image(_Object, type_prefix="im"):
2191
2192
  "COPY /python/. /usr/local",
2192
2193
  "ENV TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo:/usr/lib/terminfo",
2193
2194
  ]
2194
- python_minor = add_python.split(".")[1]
2195
- if python_minor.endswith("t"):
2196
- python_minor = python_minor[:-1]
2197
- if int(python_minor) < 13:
2198
- # Previous versions did not include the `python` binary, but later ones do.
2199
- # (The important factor is not the Python version itself, but the standalone dist version.)
2195
+ release, _ = PYTHON_STANDALONE_VERSIONS[builder_version][add_python]
2196
+ if release < "20241008":
2197
+ # Older standalone releases do not include the `python` binary.
2200
2198
  # We insert the command in the list at the position it was previously always added
2201
2199
  # for backwards compatibility with existing images.
2202
2200
  add_python_commands.insert(1, "RUN ln -s /usr/local/bin/python3 /usr/local/bin/python")
@@ -2271,10 +2269,10 @@ class _Image(_Object, type_prefix="im"):
2271
2269
  ```
2272
2270
  """
2273
2271
 
2274
- def context_mount_function() -> _Mount | None:
2272
+ def context_mount_function(builder_version: ImageBuilderVersion) -> _Mount | None:
2275
2273
  return (
2276
2274
  _Mount.from_name(
2277
- python_standalone_mount_name(add_python),
2275
+ python_standalone_mount_name(add_python, builder_version),
2278
2276
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
2279
2277
  )
2280
2278
  if add_python
@@ -2516,16 +2514,17 @@ class _Image(_Object, type_prefix="im"):
2516
2514
  gpu_config=gpu_config,
2517
2515
  secrets=secrets,
2518
2516
  build_args=build_args,
2517
+ force_build=force_build,
2519
2518
  )
2520
2519
 
2521
2520
  # --- Now add in the modal dependencies, and, optionally a Python distribution
2522
2521
  # This happening in two steps is probably a vestigial consequence of previous limitations,
2523
2522
  # but it will be difficult to merge them without forcing rebuilds of images.
2524
2523
 
2525
- def add_python_mount():
2524
+ def add_python_mount(builder_version: ImageBuilderVersion) -> _Mount | None:
2526
2525
  return (
2527
2526
  _Mount.from_name(
2528
- python_standalone_mount_name(add_python),
2527
+ python_standalone_mount_name(add_python, builder_version),
2529
2528
  namespace=api_pb2.DEPLOYMENT_NAMESPACE_GLOBAL,
2530
2529
  )
2531
2530
  if add_python
@@ -116,7 +116,7 @@ class InterceptionContext:
116
116
  """Adds one response payload to an expected queue of responses for a method.
117
117
 
118
118
  These responses will be used once each instead of calling the MockServicer's
119
- implementation of the method.
119
+ implementation of the method. An exception payload is raised instead of being sent.
120
120
 
121
121
  The interception context will throw an exception on exit if not all of the added
122
122
  responses have been consumed.
@@ -184,6 +184,8 @@ class InterceptionContext:
184
184
  async def responder(servicer_self, stream):
185
185
  await stream.recv_message() # get the input message so we can track that
186
186
  for msg in next_response_messages:
187
+ if isinstance(msg, Exception):
188
+ raise msg
187
189
  await stream.send_message(msg)
188
190
 
189
191
  return responder
@@ -0,0 +1,18 @@
1
+ aiohappyeyeballs==2.7.1
2
+ aiohttp==3.14.5
3
+ aiosignal==1.4.0
4
+ async-timeout==5.0.1 ; python_version < "3.11"
5
+ attrs==26.1.0
6
+ cbor2==5.9.0
7
+ certifi==2026.10.19
8
+ frozenlist==1.8.0
9
+ grpclib==0.4.9
10
+ h2==4.4.1
11
+ hpack==4.2.0
12
+ hyperframe==6.1.0
13
+ idna==3.20
14
+ multidict==7.1.0
15
+ propcache==0.5.4
16
+ protobuf==7.36.2
17
+ typing_extensions==4.16.0
18
+ yarl==1.25.1
@@ -80,7 +80,7 @@ class _Client:
80
80
  server_url: str,
81
81
  client_type: int,
82
82
  credentials: typing.Optional[tuple[str, str]],
83
- version: str = "1.6.3.dev5",
83
+ version: str = "1.6.3.dev7",
84
84
  *,
85
85
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
86
86
  ):
@@ -290,7 +290,7 @@ class Client:
290
290
  server_url: str,
291
291
  client_type: int,
292
292
  credentials: typing.Optional[tuple[str, str]],
293
- version: str = "1.6.3.dev5",
293
+ version: str = "1.6.3.dev7",
294
294
  *,
295
295
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
296
296
  ):
@@ -74,7 +74,10 @@ class Image(modal.object.Object):
74
74
  build_function_input: typing.Optional[modal_proto.api_pb2.FunctionInput] = None,
75
75
  image_registry_config: typing.Optional[modal._image._ImageRegistryConfig] = None,
76
76
  context_mount_function: typing.Optional[
77
- collections.abc.Callable[[], typing.Optional[modal.mount.Mount]]
77
+ collections.abc.Callable[
78
+ [typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]],
79
+ typing.Optional[modal.mount.Mount],
80
+ ]
78
81
  ] = None,
79
82
  force_build: bool = False,
80
83
  build_args: dict[str, str] = {},
@@ -39,11 +39,11 @@ from .file_pattern_matcher import FilePatternMatcher
39
39
  ROOT_DIR: PurePosixPath = PurePosixPath("/root")
40
40
  MOUNT_PUT_FILE_CLIENT_TIMEOUT = 10 * 60 # 10 min max for transferring files
41
41
 
42
- # Supported releases and versions for python-build-standalone.
42
+ # Supported releases and versions for python-build-standalone, keyed by image builder version.
43
43
  #
44
- # These can be updated safely, but changes will trigger a rebuild for all images
45
- # that rely on `add_python()` in their constructor.
46
- PYTHON_STANDALONE_VERSIONS: dict[str, tuple[str, str]] = {
44
+ # Changing the versions for a builder version triggers a rebuild for all images on that builder
45
+ # version that rely on `add_python()` in their constructor, so only "PREVIEW" should be updated.
46
+ _PYTHON_STANDALONE_VERSIONS_2025_06: dict[str, tuple[str, str]] = {
47
47
  "3.10": ("20230826", "3.10.13"),
48
48
  "3.11": ("20230826", "3.11.5"),
49
49
  "3.12": ("20240107", "3.12.1"),
@@ -53,6 +53,22 @@ PYTHON_STANDALONE_VERSIONS: dict[str, tuple[str, str]] = {
53
53
  "3.15": ("20261009", "3.15.0"),
54
54
  "3.15t": ("20261009", "3.15.0t"),
55
55
  }
56
+ PYTHON_STANDALONE_VERSIONS: dict[str, dict[str, tuple[str, str]]] = {
57
+ "PREVIEW": {
58
+ "3.10": ("20261009", "3.10.22"),
59
+ "3.11": ("20261021", "3.11.17"),
60
+ "3.12": ("20261021", "3.12.15"),
61
+ "3.13": ("20261021", "3.13.17"),
62
+ "3.14": ("20261021", "3.14.9"),
63
+ "3.14t": ("20261021", "3.14.9t"),
64
+ "3.15": ("20261021", "3.15.1"),
65
+ "3.15t": ("20261021", "3.15.1t"),
66
+ },
67
+ "2025.06": _PYTHON_STANDALONE_VERSIONS_2025_06,
68
+ "2024.10": _PYTHON_STANDALONE_VERSIONS_2025_06,
69
+ "2024.04": _PYTHON_STANDALONE_VERSIONS_2025_06,
70
+ "2023.12": _PYTHON_STANDALONE_VERSIONS_2025_06,
71
+ }
56
72
 
57
73
  MOUNT_DEPRECATION_MESSAGE_PATTERN = """modal.Mount usage will soon be deprecated.
58
74
 
@@ -72,20 +88,20 @@ def client_mount_name() -> str:
72
88
  return f"modal-client-mount-{version}"
73
89
 
74
90
 
75
- def python_standalone_mount_name(version: str) -> str:
91
+ def python_standalone_mount_name(version: str, builder_version: str) -> str:
76
92
  """Get the deployed name of the python-build-standalone mount."""
77
93
  if "-" in version: # default to glibc
78
94
  version, libc = version.split("-")
79
95
  else:
80
96
  libc = "gnu"
81
- if version not in PYTHON_STANDALONE_VERSIONS:
97
+ standalone_versions = PYTHON_STANDALONE_VERSIONS[builder_version]
98
+ if version not in standalone_versions:
82
99
  raise modal.exception.InvalidError(
83
- f"Unsupported standalone python version: {version!r}, supported values are "
84
- f"{list(PYTHON_STANDALONE_VERSIONS)}"
100
+ f"Unsupported standalone python version: {version!r}, supported values are {list(standalone_versions)}"
85
101
  )
86
102
  if libc != "gnu":
87
103
  raise modal.exception.InvalidError(f"Unsupported libc identifier: {libc}")
88
- release, full_version = PYTHON_STANDALONE_VERSIONS[version]
104
+ release, full_version = standalone_versions[version]
89
105
  return f"python-build-standalone.{release}.{full_version}-{libc}"
90
106
 
91
107
 
@@ -950,19 +966,25 @@ async def _create_single_client_dependency_mount(
950
966
 
951
967
  async def _create_client_dependency_mounts(
952
968
  client=None,
953
- python_versions: list[str] = list(PYTHON_STANDALONE_VERSIONS),
954
- builder_versions: list[str] = ["2025.06"], # Reenable "PREVIEW" during testing
969
+ python_versions: list[str] | None = None,
970
+ builder_versions: list[str] | None = None, # Pass "PREVIEW" during testing
955
971
  check_if_exists=True,
956
972
  dry_run=False,
957
973
  ):
974
+ from ._image import LATEST_IMAGE_BUILDER_VERSION, SUPPORTED_PYTHON_SERIES
975
+
958
976
  arch = "x86_64"
959
977
  platform_tags = [
960
978
  ("manylinux_2_17", f"{arch}-manylinux_2_17"), # glibc >= 2.17
961
979
  ("musllinux_1_2", f"{arch}-unknown-linux-musl"), # musl >= 1.2
962
980
  ]
963
981
  coros = []
964
- for python_version in python_versions:
965
- for builder_version in builder_versions:
982
+ for builder_version in builder_versions or [LATEST_IMAGE_BUILDER_VERSION]:
983
+ if python_versions is None:
984
+ builder_python_versions = SUPPORTED_PYTHON_SERIES[builder_version]
985
+ else:
986
+ builder_python_versions = python_versions
987
+ for python_version in builder_python_versions:
966
988
  for platform, uv_python_platform in platform_tags:
967
989
  coros.append(
968
990
  _create_single_client_dependency_mount(
@@ -16,7 +16,7 @@ def client_mount_name() -> str:
16
16
  """Get the deployed name of the client package mount."""
17
17
  ...
18
18
 
19
- def python_standalone_mount_name(version: str) -> str:
19
+ def python_standalone_mount_name(version: str, builder_version: str) -> str:
20
20
  """Get the deployed name of the python-build-standalone mount."""
21
21
  ...
22
22
 
@@ -444,8 +444,8 @@ async def _create_single_client_dependency_mount(
444
444
  ): ...
445
445
  async def _create_client_dependency_mounts(
446
446
  client=None,
447
- python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
448
- builder_versions: list[str] = ["2025.06"],
447
+ python_versions: typing.Optional[list[str]] = None,
448
+ builder_versions: typing.Optional[list[str]] = None,
449
449
  check_if_exists=True,
450
450
  dry_run=False,
451
451
  ): ...
@@ -455,8 +455,8 @@ class __create_client_dependency_mounts_spec(typing_extensions.Protocol):
455
455
  self,
456
456
  /,
457
457
  client=None,
458
- python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
459
- builder_versions: list[str] = ["2025.06"],
458
+ python_versions: typing.Optional[list[str]] = None,
459
+ builder_versions: typing.Optional[list[str]] = None,
460
460
  check_if_exists=True,
461
461
  dry_run=False,
462
462
  ): ...
@@ -464,8 +464,8 @@ class __create_client_dependency_mounts_spec(typing_extensions.Protocol):
464
464
  self,
465
465
  /,
466
466
  client=None,
467
- python_versions: list[str] = ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
468
- builder_versions: list[str] = ["2025.06"],
467
+ python_versions: typing.Optional[list[str]] = None,
468
+ builder_versions: typing.Optional[list[str]] = None,
469
469
  check_if_exists=True,
470
470
  dry_run=False,
471
471
  ): ...
@@ -474,4 +474,6 @@ create_client_dependency_mounts: __create_client_dependency_mounts_spec
474
474
 
475
475
  ROOT_DIR: pathlib.PurePosixPath
476
476
 
477
- PYTHON_STANDALONE_VERSIONS: dict[str, tuple[str, str]]
477
+ _PYTHON_STANDALONE_VERSIONS_2025_06: dict[str, tuple[str, str]]
478
+
479
+ PYTHON_STANDALONE_VERSIONS: dict[str, dict[str, tuple[str, str]]]
@@ -386,7 +386,7 @@ class SandboxCreateArgs:
386
386
  secrets: Collection[_Secret] | None = None
387
387
  env: dict[str, str | None] | None = None
388
388
  name: str | None = None
389
- timeout: int = 300
389
+ timeout: int | None = 300
390
390
  idle_timeout: int | None = None
391
391
  workdir: str | None = None
392
392
  gpu: str | None = None
@@ -417,6 +417,7 @@ class SandboxCreateArgs:
417
417
  custom_domain: str | None = None
418
418
  include_oidc_identity_token: bool = False
419
419
  runtime: SandboxRuntime | None = None
420
+ persistent: bool = False
420
421
 
421
422
 
422
423
  class SandboxCreateCommon:
@@ -428,6 +429,8 @@ class SandboxCreateCommon:
428
429
 
429
430
  def __init__(self, create_args: SandboxCreateArgs, *, secrets: Sequence[_Secret]) -> None:
430
431
  a = create_args
432
+ if a.timeout is None:
433
+ raise InvalidError("`timeout=None` is only supported for experimental persistent Sandboxes.")
431
434
  _validate_exec_args(a.args)
432
435
  if a.name is not None:
433
436
  check_object_name(a.name, "Sandbox")
@@ -452,6 +455,7 @@ class SandboxCreateCommon:
452
455
  raise InvalidError("`inbound_cidr_allowlist` cannot be used when `block_network` is enabled")
453
456
 
454
457
  self.create_args = a
458
+ self.timeout = a.timeout
455
459
  self.secrets = secrets
456
460
  self.image = a.image or _default_image
457
461
  self.network_file_systems = validate_network_file_systems(a.network_file_systems)
@@ -503,7 +507,7 @@ class SandboxCreateCommon:
503
507
  secret_ids=[secret.object_id for secret in self.secrets],
504
508
  # Ephemeral disk requests are not supported on Sandboxes.
505
509
  resources=convert_fn_config_to_resources_config(cpu=a.cpu, memory=a.memory, gpu=a.gpu, ephemeral_disk=None),
506
- timeout_secs=a.timeout,
510
+ timeout_secs=self.timeout,
507
511
  idle_timeout_secs=a.idle_timeout,
508
512
  workdir=a.workdir,
509
513
  cloud_provider_str=a.cloud if a.cloud else None, # pyright: ignore[reportArgumentType]
@@ -637,6 +641,10 @@ class SandboxImpl(Protocol):
637
641
  """
638
642
  ...
639
643
 
644
+ def clone(self) -> "SandboxImpl":
645
+ """Create an implementation of the same type for another handle to the same Sandbox."""
646
+ ...
647
+
640
648
  @classmethod
641
649
  async def lookup_by_name(
642
650
  cls, client: _Client, req: api_pb2.SandboxGetFromNameRequest
@@ -674,6 +682,10 @@ class SandboxImpl(Protocol):
674
682
 
675
683
  async def terminate(self) -> None: ...
676
684
 
685
+ async def pause(self) -> None: ...
686
+
687
+ async def resume(self) -> None: ...
688
+
677
689
  async def close(self) -> None:
678
690
  """Release connections held for the Sandbox once in-flight operations complete."""
679
691
  ...
@@ -907,6 +919,9 @@ async def snapshot_filesystem_via_tcr(
907
919
  return _Image._new_hydrated(res.image_id, client, None)
908
920
 
909
921
 
922
+ _PERSISTENT_ONLY_MESSAGE = "`{}` requires a persistent Sandbox, created with `_experimental_persistent=True`."
923
+
924
+
910
925
  async def mount_image_via_tcr(
911
926
  impl: SandboxImpl,
912
927
  path: PurePosixPath | str,