modal 1.5.2.dev20__tar.gz → 1.5.2.dev21__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 (231) hide show
  1. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/PKG-INFO +1 -1
  2. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/container.py +10 -2
  3. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/curl.py +6 -4
  4. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/shell.py +6 -2
  5. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/client.pyi +2 -2
  6. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox.py +47 -5
  7. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox.pyi +114 -15
  8. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/types.py +7 -1
  9. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/volume.py +26 -1
  10. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/volume.pyi +26 -0
  11. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/PKG-INFO +1 -1
  12. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/mdmd.py +34 -11
  13. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_grpc.py +32 -0
  14. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2.py +800 -767
  15. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2.pyi +69 -4
  16. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2_grpc.py +66 -0
  17. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/api_pb2_grpc.pyi +20 -0
  18. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/modal_api_grpc.py +2 -0
  19. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_version/__init__.py +1 -1
  20. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/LICENSE +0 -0
  21. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/README.md +0 -0
  22. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/__init__.py +0 -0
  23. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/__main__.py +0 -0
  24. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_billing.py +0 -0
  25. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_clustered_functions.py +0 -0
  26. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_clustered_functions.pyi +0 -0
  27. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_container_entrypoint.py +0 -0
  28. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_environments.py +0 -0
  29. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_function_variants.py +0 -0
  30. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_functions.py +0 -0
  31. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_grpc_client.py +0 -0
  32. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_image.py +0 -0
  33. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_ipython.py +0 -0
  34. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_load_context.py +0 -0
  35. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_location.py +0 -0
  36. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_logs.py +0 -0
  37. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_object.py +0 -0
  38. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/__init__.py +0 -0
  39. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/manager.py +0 -0
  40. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/pty.py +0 -0
  41. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/rich.py +0 -0
  42. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_output/status.py +0 -0
  43. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_partial_function.py +0 -0
  44. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_resolver.py +0 -0
  45. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_resources.py +0 -0
  46. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/__init__.py +0 -0
  47. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/asgi.py +0 -0
  48. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/container_io_manager.py +0 -0
  49. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/container_io_manager.pyi +0 -0
  50. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/execution_context.py +0 -0
  51. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/execution_context.pyi +0 -0
  52. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  53. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/task_lifecycle_manager.py +0 -0
  54. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  55. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/telemetry.py +0 -0
  56. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/user_code_event_loop.py +0 -0
  57. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_runtime/user_code_imports.py +0 -0
  58. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_serialization.py +0 -0
  59. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_server.py +0 -0
  60. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_traceback.py +0 -0
  61. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_tunnel.py +0 -0
  62. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_tunnel.pyi +0 -0
  63. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_type_manager.py +0 -0
  64. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/__init__.py +0 -0
  65. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/app_utils.py +0 -0
  66. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/async_utils.py +0 -0
  67. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/auth_token_manager.py +0 -0
  68. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/blob_utils.py +0 -0
  69. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/browser_utils.py +0 -0
  70. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/bytes_io_segment_payload.py +0 -0
  71. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/curl_utils.py +0 -0
  72. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/deprecation.py +0 -0
  73. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/docker_utils.py +0 -0
  74. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/function_utils.py +0 -0
  75. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/git_utils.py +0 -0
  76. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/grpc_testing.py +0 -0
  77. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/grpc_utils.py +0 -0
  78. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/hash_utils.py +0 -0
  79. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/http_utils.py +0 -0
  80. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/jwt_utils.py +0 -0
  81. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/logger.py +0 -0
  82. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/mount_utils.py +0 -0
  83. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/name_utils.py +0 -0
  84. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/package_utils.py +0 -0
  85. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/pattern_utils.py +0 -0
  86. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/proxy_support.py +0 -0
  87. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/rand_pb_testing.py +0 -0
  88. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/sandbox_fs_utils.py +0 -0
  89. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/shell_utils.py +0 -0
  90. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/task_command_router_client.py +0 -0
  91. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_utils/time_utils.py +0 -0
  92. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/__init__.py +0 -0
  93. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  94. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/cloudpickle.py +0 -0
  95. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/tblib.py +0 -0
  96. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_vendor/version.py +0 -0
  97. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_watcher.py +0 -0
  98. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/_workspace.py +0 -0
  99. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/app.py +0 -0
  100. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/app.pyi +0 -0
  101. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/billing.py +0 -0
  102. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/billing.pyi +0 -0
  103. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2023.12.312.txt +0 -0
  104. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2023.12.txt +0 -0
  105. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2024.04.txt +0 -0
  106. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2024.10.txt +0 -0
  107. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/2025.06.txt +0 -0
  108. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/PREVIEW.txt +0 -0
  109. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/README.md +0 -0
  110. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/builder/base-images.json +0 -0
  111. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/call_graph.py +0 -0
  112. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/__init__.py +0 -0
  113. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_download.py +0 -0
  114. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_help.py +0 -0
  115. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/_traceback.py +0 -0
  116. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/app.py +0 -0
  117. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/billing.py +0 -0
  118. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/bootstrap.py +0 -0
  119. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/changelog.py +0 -0
  120. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/cluster.py +0 -0
  121. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/config.py +0 -0
  122. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/dashboard.py +0 -0
  123. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/dict.py +0 -0
  124. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/endpoint.py +0 -0
  125. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/entry_point.py +0 -0
  126. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/environment.py +0 -0
  127. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/image.py +0 -0
  128. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/import_refs.py +0 -0
  129. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/launch.py +0 -0
  130. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/logo.py +0 -0
  131. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/network_file_system.py +0 -0
  132. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/profile.py +0 -0
  133. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/__init__.py +0 -0
  134. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/run_jupyter.py +0 -0
  135. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/programs/vscode.py +0 -0
  136. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/queues.py +0 -0
  137. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/run.py +0 -0
  138. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/secret.py +0 -0
  139. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/selector.py +0 -0
  140. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/skills.py +0 -0
  141. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/token.py +0 -0
  142. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/utils.py +0 -0
  143. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/volume.py +0 -0
  144. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cli/workspace.py +0 -0
  145. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/client.py +0 -0
  146. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cloud_bucket_mount.py +0 -0
  147. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cloud_bucket_mount.pyi +0 -0
  148. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cls.py +0 -0
  149. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/cls.pyi +0 -0
  150. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/config.py +0 -0
  151. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/container_process.py +0 -0
  152. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/container_process.pyi +0 -0
  153. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/dict.py +0 -0
  154. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/dict.pyi +0 -0
  155. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/environments.py +0 -0
  156. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/environments.pyi +0 -0
  157. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/exception.py +0 -0
  158. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/__init__.py +0 -0
  159. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/flash.py +0 -0
  160. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/flash.pyi +0 -0
  161. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/experimental/ipython.py +0 -0
  162. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_io.py +0 -0
  163. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_io.pyi +0 -0
  164. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/file_pattern_matcher.py +0 -0
  165. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/functions.py +0 -0
  166. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/functions.pyi +0 -0
  167. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/image.py +0 -0
  168. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/image.pyi +0 -0
  169. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/io_streams.py +0 -0
  170. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/io_streams.pyi +0 -0
  171. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/mount.py +0 -0
  172. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/mount.pyi +0 -0
  173. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/network_file_system.py +0 -0
  174. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/network_file_system.pyi +0 -0
  175. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/object.py +0 -0
  176. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/object.pyi +0 -0
  177. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/output.py +0 -0
  178. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/parallel_map.py +0 -0
  179. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/parallel_map.pyi +0 -0
  180. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/partial_function.py +0 -0
  181. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/partial_function.pyi +0 -0
  182. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/proxy.py +0 -0
  183. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/proxy.pyi +0 -0
  184. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/py.typed +0 -0
  185. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/queue.py +0 -0
  186. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/queue.pyi +0 -0
  187. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/retries.py +0 -0
  188. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/runner.py +0 -0
  189. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/runner.pyi +0 -0
  190. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/running_app.py +0 -0
  191. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox_fs.py +0 -0
  192. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/sandbox_fs.pyi +0 -0
  193. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/schedule.py +0 -0
  194. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/scheduler_placement.py +0 -0
  195. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/secret.py +0 -0
  196. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/secret.pyi +0 -0
  197. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/server.py +0 -0
  198. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/server.pyi +0 -0
  199. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/serving.py +0 -0
  200. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/serving.pyi +0 -0
  201. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/skills/modal/SKILL.md +0 -0
  202. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/snapshot.py +0 -0
  203. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/snapshot.pyi +0 -0
  204. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/stream_type.py +0 -0
  205. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/token_flow.py +0 -0
  206. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/token_flow.pyi +0 -0
  207. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/workspace.py +0 -0
  208. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal/workspace.pyi +0 -0
  209. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/SOURCES.txt +0 -0
  210. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/dependency_links.txt +0 -0
  211. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/entry_points.txt +0 -0
  212. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/requires.txt +0 -0
  213. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal.egg-info/top_level.txt +0 -0
  214. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/__init__.py +0 -0
  215. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_cli_docs.py +0 -0
  216. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_cli_docs_main.py +0 -0
  217. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_reference_docs.py +0 -0
  218. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/gen_reference_docs_main.py +0 -0
  219. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/__init__.py +0 -0
  220. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/signatures.py +0 -0
  221. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_docs/mdmd/types.py +0 -0
  222. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/__init__.py +0 -0
  223. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/py.typed +0 -0
  224. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_grpc.py +0 -0
  225. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2.py +0 -0
  226. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2.pyi +0 -0
  227. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  228. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  229. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/modal_version/__main__.py +0 -0
  230. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/pyproject.toml +0 -0
  231. {modal-1.5.2.dev20 → modal-1.5.2.dev21}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.2.dev20
3
+ Version: 1.5.2.dev21
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -269,15 +269,23 @@ async def _exec_impl(
269
269
  pty: bool | None = None,
270
270
  container_id: str = "",
271
271
  command: tuple[str, ...] = (),
272
+ object_id_for_v2: str | None = None,
272
273
  ):
273
- """Execute a command in a container (implementation)."""
274
+ """Execute a command in a container (implementation).
275
+
276
+ For tasks belonging to V2 sandboxes, `object_id_for_v2` must be set to the
277
+ sandbox ID.
278
+ """
274
279
 
275
280
  if pty is None:
276
281
  pty = is_tty()
277
282
 
278
283
  client = await _Client.from_env()
279
284
 
280
- command_router_client = await TaskCommandRouterClient.init(client, container_id)
285
+ if object_id_for_v2 is not None:
286
+ command_router_client = await TaskCommandRouterClient.init_v2(client, object_id_for_v2, container_id)
287
+ else:
288
+ command_router_client = await TaskCommandRouterClient.init(client, container_id)
281
289
 
282
290
  process_id = str(uuid.uuid4())
283
291
 
@@ -117,10 +117,12 @@ async def curl(curl_args: tuple[str, ...]):
117
117
  All arguments are passed directly to `curl`, which must be installed locally.
118
118
 
119
119
  Examples:
120
- ```bash
121
- modal curl https://user--my-app.us-west.modal.direct
122
- modal curl -X GET https://user--my-app.us-west.modal.direct
123
- ```
120
+
121
+ ```bash
122
+ modal curl https://user--my-app.us-west.modal.direct
123
+ modal curl -X GET https://user--my-app.us-west.modal.direct
124
+ ```
125
+
124
126
  """
125
127
  url = find_url(curl_args)
126
128
  if url is None:
@@ -17,7 +17,7 @@ from ..functions import Function
17
17
  from ..image import Image
18
18
  from ..mount import _Mount
19
19
  from ..runner import interactive_shell
20
- from ..sandbox import _MAIN_CONTAINER_NAME, Sandbox
20
+ from ..sandbox import _MAIN_CONTAINER_NAME, Sandbox, _is_v2_sandbox_id
21
21
  from ..secret import Secret
22
22
  from ..stream_type import StreamType
23
23
  from ..volume import Volume
@@ -135,6 +135,8 @@ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
135
135
  if container_name is not None and container_name != _MAIN_CONTAINER_NAME:
136
136
  return _start_shell_in_sidecar_container(sandbox_id, container_name, cmd, pty)
137
137
 
138
+ object_id_for_v2: str | None = None
139
+
138
140
  if _is_valid_modal_id(sandbox_id, "sb-"):
139
141
  try:
140
142
  sandbox = Sandbox.from_id(sandbox_id)
@@ -143,10 +145,12 @@ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
143
145
  raise ClickException(f"Sandbox '{sandbox_id}' not found (is it still running?)")
144
146
  except Exception as e:
145
147
  raise ClickException(f"Error connecting to Sandbox '{sandbox_id}': {str(e)}")
148
+ if _is_v2_sandbox_id(sandbox_id):
149
+ object_id_for_v2 = sandbox_id
146
150
 
147
151
  assert _is_valid_modal_id(ref, "ta-")
148
152
  try:
149
- _exec_impl(container_id=ref, command=shlex.split(cmd), pty=pty)
153
+ _exec_impl(container_id=ref, command=shlex.split(cmd), pty=pty, object_id_for_v2=object_id_for_v2)
150
154
  except NotFoundError:
151
155
  raise ClickException(f"Container '{ref}' not found (is it still running?)")
152
156
  except Exception as e:
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.5.2.dev20",
38
+ version: str = "1.5.2.dev21",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -205,7 +205,7 @@ class Client:
205
205
  server_url: str,
206
206
  client_type: int,
207
207
  credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.5.2.dev20",
208
+ version: str = "1.5.2.dev21",
209
209
  ):
210
210
  """mdmd:hidden
211
211
  The Modal client object is not intended to be instantiated directly by users.
@@ -861,9 +861,10 @@ class _Sandbox(_Object, type_prefix="sb"):
861
861
  (e.g. `region="us-east-1"`).
862
862
 
863
863
  V2 sandboxes created with this method are not currently returned by
864
- `Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
865
- Store `sandbox.object_id` if you need to retrieve the sandbox later, and
866
- use `Sandbox.from_id(sandbox.object_id)` to reattach.
864
+ `Sandbox.list()`. A named sandbox can be looked up with
865
+ `Sandbox._experimental_from_name(app_name, name)`; otherwise store
866
+ `sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
867
+ reattach.
867
868
  """
868
869
  from .app import _App
869
870
 
@@ -1186,6 +1187,44 @@ class _Sandbox(_Object, type_prefix="sb"):
1186
1187
  resp = await client.stub.SandboxGetFromName(req)
1187
1188
  return _Sandbox._new_hydrated(resp.sandbox_id, client, None)
1188
1189
 
1190
+ @staticmethod
1191
+ async def _experimental_from_name(
1192
+ app_name: str,
1193
+ name: str,
1194
+ *,
1195
+ environment_name: str | None = None,
1196
+ client: _Client | None = None,
1197
+ ) -> "_Sandbox":
1198
+ """Get a running V2 Sandbox by name from a deployed App.
1199
+
1200
+ This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
1201
+
1202
+ Args:
1203
+ app_name: Name of the deployed app to look up the sandbox under.
1204
+ name: Sandbox name to resolve.
1205
+ environment_name: Optional environment name for the lookup; defaults to the configured environment.
1206
+ client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
1207
+
1208
+ Returns:
1209
+ A `Sandbox` handle for the running sandbox.
1210
+
1211
+ Raises:
1212
+ NotFoundError: If no running sandbox exists with the given name.
1213
+ """
1214
+ if client is None:
1215
+ client = await _Client.from_env()
1216
+ env_name = _get_environment_name(environment_name)
1217
+
1218
+ req = api_pb2.SandboxGetFromNameRequest(sandbox_name=name, app_name=app_name, environment_name=env_name)
1219
+ assert client._auth_token_manager
1220
+ auth_token = await client._auth_token_manager.get_token()
1221
+ resp = await client.stub.SandboxGetFromNameV2(req, metadata=[("x-modal-auth-token", auth_token)])
1222
+
1223
+ obj = _Sandbox._new_hydrated(resp.sandbox_id, client, None)
1224
+ obj._is_v2 = True
1225
+ obj._hydrate_metadata_v2()
1226
+ return obj
1227
+
1189
1228
  @staticmethod
1190
1229
  async def from_id(sandbox_id: str, client: _Client | None = None) -> "_Sandbox":
1191
1230
  """Construct a Sandbox from an id and look up the Sandbox result.
@@ -2279,15 +2318,15 @@ class _Sandbox(_Object, type_prefix="sb"):
2279
2318
 
2280
2319
  @staticmethod
2281
2320
  async def _experimental_list(
2282
- *, app_id: str | None = None, client: _Client | None = None
2321
+ *, app_id: str | None = None, tags: dict[str, str] | None = None, client: _Client | None = None
2283
2322
  ) -> AsyncGenerator["_Sandbox", None]:
2284
2323
  """List v2 Sandboxes in an App.
2285
2324
 
2286
2325
  This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
2287
- Filtering based on tags is not yet supported.
2288
2326
 
2289
2327
  Args:
2290
2328
  app_id: The App to list Sandboxes under.
2329
+ tags: If set, only sandboxes containing at least these tags are returned.
2291
2330
  client: Optional client to use for the session.
2292
2331
 
2293
2332
  Yields:
@@ -2304,12 +2343,15 @@ class _Sandbox(_Object, type_prefix="sb"):
2304
2343
  if client is None:
2305
2344
  client = await _Client.from_env()
2306
2345
 
2346
+ tags_list = [api_pb2.SandboxTag(tag_name=name, tag_value=value) for name, value in tags.items()] if tags else []
2347
+
2307
2348
  assert client._auth_token_manager
2308
2349
  while True:
2309
2350
  req = api_pb2.SandboxListRequest(
2310
2351
  app_id=app_id,
2311
2352
  before_timestamp=before_timestamp,
2312
2353
  include_finished=False,
2354
+ tags=tags_list,
2313
2355
  )
2314
2356
 
2315
2357
  # Fetches a batch of sandboxes. SandboxListV2 authenticates via the
@@ -409,9 +409,10 @@ class _Sandbox(modal._object._Object):
409
409
  (e.g. `region="us-east-1"`).
410
410
 
411
411
  V2 sandboxes created with this method are not currently returned by
412
- `Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
413
- Store `sandbox.object_id` if you need to retrieve the sandbox later, and
414
- use `Sandbox.from_id(sandbox.object_id)` to reattach.
412
+ `Sandbox.list()`. A named sandbox can be looked up with
413
+ `Sandbox._experimental_from_name(app_name, name)`; otherwise store
414
+ `sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
415
+ reattach.
415
416
  """
416
417
  ...
417
418
 
@@ -465,6 +466,32 @@ class _Sandbox(modal._object._Object):
465
466
  """
466
467
  ...
467
468
 
469
+ @staticmethod
470
+ async def _experimental_from_name(
471
+ app_name: str,
472
+ name: str,
473
+ *,
474
+ environment_name: typing.Optional[str] = None,
475
+ client: typing.Optional[modal.client._Client] = None,
476
+ ) -> _Sandbox:
477
+ """Get a running V2 Sandbox by name from a deployed App.
478
+
479
+ This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
480
+
481
+ Args:
482
+ app_name: Name of the deployed app to look up the sandbox under.
483
+ name: Sandbox name to resolve.
484
+ environment_name: Optional environment name for the lookup; defaults to the configured environment.
485
+ client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
486
+
487
+ Returns:
488
+ A `Sandbox` handle for the running sandbox.
489
+
490
+ Raises:
491
+ NotFoundError: If no running sandbox exists with the given name.
492
+ """
493
+ ...
494
+
468
495
  @staticmethod
469
496
  async def from_id(sandbox_id: str, client: typing.Optional[modal.client._Client] = None) -> _Sandbox:
470
497
  """Construct a Sandbox from an id and look up the Sandbox result.
@@ -922,15 +949,18 @@ class _Sandbox(modal._object._Object):
922
949
 
923
950
  @staticmethod
924
951
  def _experimental_list(
925
- *, app_id: typing.Optional[str] = None, client: typing.Optional[modal.client._Client] = None
952
+ *,
953
+ app_id: typing.Optional[str] = None,
954
+ tags: typing.Optional[dict[str, str]] = None,
955
+ client: typing.Optional[modal.client._Client] = None,
926
956
  ) -> collections.abc.AsyncGenerator[_Sandbox, None]:
927
957
  """List v2 Sandboxes in an App.
928
958
 
929
959
  This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
930
- Filtering based on tags is not yet supported.
931
960
 
932
961
  Args:
933
962
  app_id: The App to list Sandboxes under.
963
+ tags: If set, only sandboxes containing at least these tags are returned.
934
964
  client: Optional client to use for the session.
935
965
 
936
966
  Yields:
@@ -1729,9 +1759,10 @@ class Sandbox(modal.object.Object):
1729
1759
  (e.g. `region="us-east-1"`).
1730
1760
 
1731
1761
  V2 sandboxes created with this method are not currently returned by
1732
- `Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
1733
- Store `sandbox.object_id` if you need to retrieve the sandbox later, and
1734
- use `Sandbox.from_id(sandbox.object_id)` to reattach.
1762
+ `Sandbox.list()`. A named sandbox can be looked up with
1763
+ `Sandbox._experimental_from_name(app_name, name)`; otherwise store
1764
+ `sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
1765
+ reattach.
1735
1766
  """
1736
1767
  ...
1737
1768
 
@@ -1789,9 +1820,10 @@ class Sandbox(modal.object.Object):
1789
1820
  (e.g. `region="us-east-1"`).
1790
1821
 
1791
1822
  V2 sandboxes created with this method are not currently returned by
1792
- `Sandbox.list()` and cannot be looked up with `Sandbox.from_name()`.
1793
- Store `sandbox.object_id` if you need to retrieve the sandbox later, and
1794
- use `Sandbox.from_id(sandbox.object_id)` to reattach.
1823
+ `Sandbox.list()`. A named sandbox can be looked up with
1824
+ `Sandbox._experimental_from_name(app_name, name)`; otherwise store
1825
+ `sandbox.object_id` and use `Sandbox.from_id(sandbox.object_id)` to
1826
+ reattach.
1795
1827
  """
1796
1828
  ...
1797
1829
 
@@ -1892,6 +1924,63 @@ class Sandbox(modal.object.Object):
1892
1924
 
1893
1925
  from_name: typing.ClassVar[__from_name_spec]
1894
1926
 
1927
+ class ___experimental_from_name_spec(typing_extensions.Protocol):
1928
+ def __call__(
1929
+ self,
1930
+ /,
1931
+ app_name: str,
1932
+ name: str,
1933
+ *,
1934
+ environment_name: typing.Optional[str] = None,
1935
+ client: typing.Optional[modal.client.Client] = None,
1936
+ ) -> Sandbox:
1937
+ """Get a running V2 Sandbox by name from a deployed App.
1938
+
1939
+ This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
1940
+
1941
+ Args:
1942
+ app_name: Name of the deployed app to look up the sandbox under.
1943
+ name: Sandbox name to resolve.
1944
+ environment_name: Optional environment name for the lookup; defaults to the configured environment.
1945
+ client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
1946
+
1947
+ Returns:
1948
+ A `Sandbox` handle for the running sandbox.
1949
+
1950
+ Raises:
1951
+ NotFoundError: If no running sandbox exists with the given name.
1952
+ """
1953
+ ...
1954
+
1955
+ async def aio(
1956
+ self,
1957
+ /,
1958
+ app_name: str,
1959
+ name: str,
1960
+ *,
1961
+ environment_name: typing.Optional[str] = None,
1962
+ client: typing.Optional[modal.client.Client] = None,
1963
+ ) -> Sandbox:
1964
+ """Get a running V2 Sandbox by name from a deployed App.
1965
+
1966
+ This looks up V2 sandboxes, ie sandboxes created via `modal.Sandbox._experimental_create`.
1967
+
1968
+ Args:
1969
+ app_name: Name of the deployed app to look up the sandbox under.
1970
+ name: Sandbox name to resolve.
1971
+ environment_name: Optional environment name for the lookup; defaults to the configured environment.
1972
+ client: Modal client to use for the RPC; defaults to `Client.from_env()` when omitted.
1973
+
1974
+ Returns:
1975
+ A `Sandbox` handle for the running sandbox.
1976
+
1977
+ Raises:
1978
+ NotFoundError: If no running sandbox exists with the given name.
1979
+ """
1980
+ ...
1981
+
1982
+ _experimental_from_name: typing.ClassVar[___experimental_from_name_spec]
1983
+
1895
1984
  class __from_id_spec(typing_extensions.Protocol):
1896
1985
  def __call__(self, /, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox:
1897
1986
  """Construct a Sandbox from an id and look up the Sandbox result.
@@ -2864,15 +2953,20 @@ class Sandbox(modal.object.Object):
2864
2953
 
2865
2954
  class ___experimental_list_spec(typing_extensions.Protocol):
2866
2955
  def __call__(
2867
- self, /, *, app_id: typing.Optional[str] = None, client: typing.Optional[modal.client.Client] = None
2956
+ self,
2957
+ /,
2958
+ *,
2959
+ app_id: typing.Optional[str] = None,
2960
+ tags: typing.Optional[dict[str, str]] = None,
2961
+ client: typing.Optional[modal.client.Client] = None,
2868
2962
  ) -> typing.Generator[Sandbox, None, None]:
2869
2963
  """List v2 Sandboxes in an App.
2870
2964
 
2871
2965
  This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
2872
- Filtering based on tags is not yet supported.
2873
2966
 
2874
2967
  Args:
2875
2968
  app_id: The App to list Sandboxes under.
2969
+ tags: If set, only sandboxes containing at least these tags are returned.
2876
2970
  client: Optional client to use for the session.
2877
2971
 
2878
2972
  Yields:
@@ -2881,15 +2975,20 @@ class Sandbox(modal.object.Object):
2881
2975
  ...
2882
2976
 
2883
2977
  def aio(
2884
- self, /, *, app_id: typing.Optional[str] = None, client: typing.Optional[modal.client.Client] = None
2978
+ self,
2979
+ /,
2980
+ *,
2981
+ app_id: typing.Optional[str] = None,
2982
+ tags: typing.Optional[dict[str, str]] = None,
2983
+ client: typing.Optional[modal.client.Client] = None,
2885
2984
  ) -> collections.abc.AsyncGenerator[Sandbox, None]:
2886
2985
  """List v2 Sandboxes in an App.
2887
2986
 
2888
2987
  This function lists v2 sandboxes, ie sandboxes created via modal.Sandbox._experimental_create.
2889
- Filtering based on tags is not yet supported.
2890
2988
 
2891
2989
  Args:
2892
2990
  app_id: The App to list Sandboxes under.
2991
+ tags: If set, only sandboxes containing at least these tags are returned.
2893
2992
  client: Optional client to use for the session.
2894
2993
 
2895
2994
  Yields:
@@ -5,7 +5,7 @@ import enum
5
5
  from dataclasses import FrozenInstanceError, dataclass
6
6
  from datetime import datetime, timezone
7
7
  from decimal import Decimal
8
- from typing import Any, Iterable, Optional
8
+ from typing import Any, Iterable, Optional, TypedDict
9
9
 
10
10
  from modal_proto import api_pb2
11
11
 
@@ -90,6 +90,12 @@ class SecretInfo:
90
90
  created_by: str | None
91
91
 
92
92
 
93
+ class VolumeCreateOptions(TypedDict, total=False):
94
+ """Options used when creating a Volume."""
95
+
96
+ experimental_options: dict[str, Any]
97
+
98
+
93
99
  @dataclass
94
100
  class VolumeInfo:
95
101
  """Information about a Volume object."""
@@ -71,7 +71,7 @@ from ._utils.name_utils import check_object_name
71
71
  from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
72
72
  from .client import _Client
73
73
  from .config import logger
74
- from .types import FileEntry, FileEntryType as FileEntryType, VolumeInfo
74
+ from .types import FileEntry, FileEntryType as FileEntryType, VolumeCreateOptions, VolumeInfo
75
75
 
76
76
  # Max duration for uploading to volumes files
77
77
  # As a guide, files >40GiB will take >10 minutes to upload.
@@ -120,6 +120,20 @@ def _validate_volume_version(
120
120
  )
121
121
 
122
122
 
123
+ def _experimental_options_to_string_map(experimental_options: dict[str, Any]) -> dict[str, str]:
124
+ """Coerce experimental option values to strings for the proto's string map."""
125
+ return {str(key): str(value) for key, value in experimental_options.items()}
126
+
127
+
128
+ def _volume_create_options_to_proto(create_options: "VolumeCreateOptions | None") -> api_pb2.VolumeCreateOptions:
129
+ """Build a VolumeCreateOptions proto message from the public TypedDict."""
130
+ if not create_options:
131
+ return api_pb2.VolumeCreateOptions()
132
+ return api_pb2.VolumeCreateOptions(
133
+ experimental_options=_experimental_options_to_string_map(create_options.get("experimental_options", {})),
134
+ )
135
+
136
+
123
137
  class _VolumeManager:
124
138
  """Namespace with methods for managing named Volume objects."""
125
139
 
@@ -131,6 +145,7 @@ class _VolumeManager:
131
145
  allow_existing: bool = False,
132
146
  environment_name: str | None = None,
133
147
  client: _Client | None = None,
148
+ experimental_options: dict[str, Any] | None = None,
134
149
  ) -> None:
135
150
  """Create a new named Volume in the workspace environment.
136
151
 
@@ -144,6 +159,7 @@ class _VolumeManager:
144
159
  allow_existing: If True, do nothing when a Volume with this name already exists.
145
160
  environment_name: Environment to create in; defaults to the active environment.
146
161
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
162
+ experimental_options: Experimental options to create Volume with.
147
163
 
148
164
  Examples:
149
165
  ```python notest
@@ -181,6 +197,9 @@ class _VolumeManager:
181
197
  environment_name=_get_environment_name(environment_name),
182
198
  object_creation_type=object_creation_type,
183
199
  version=version,
200
+ create_options=_volume_create_options_to_proto(
201
+ VolumeCreateOptions(experimental_options=experimental_options or {})
202
+ ),
184
203
  )
185
204
  try:
186
205
  response = await client.stub.VolumeGetOrCreate(req)
@@ -545,6 +564,7 @@ class _Volume(_Object, type_prefix="vo"):
545
564
  environment_name: str | None = None,
546
565
  create_if_missing: bool = False,
547
566
  version: "modal_proto.api_pb2.VolumeFsVersion.ValueType | None" = None,
567
+ create_options: "VolumeCreateOptions | None" = None,
548
568
  client: _Client | None = None,
549
569
  ) -> "_Volume":
550
570
  """Reference a Volume by name, optionally creating it on the server first.
@@ -556,6 +576,7 @@ class _Volume(_Object, type_prefix="vo"):
556
576
  environment_name: Environment to resolve the name in; defaults to the active environment.
557
577
  create_if_missing: If True, create the Volume when it does not already exist.
558
578
  version: Optional VolumeFS backend version; must match an existing Volume when set.
579
+ create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
559
580
  client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
560
581
 
561
582
  Returns:
@@ -580,6 +601,7 @@ class _Volume(_Object, type_prefix="vo"):
580
601
  environment_name=load_context.environment_name,
581
602
  object_creation_type=(api_pb2.OBJECT_CREATION_TYPE_CREATE_IF_MISSING if create_if_missing else None),
582
603
  version=version,
604
+ create_options=_volume_create_options_to_proto(create_options),
583
605
  )
584
606
  response = await load_context.client.stub.VolumeGetOrCreate(req)
585
607
  if version is not None:
@@ -650,6 +672,7 @@ class _Volume(_Object, type_prefix="vo"):
650
672
  client: _Client | None = None,
651
673
  environment_name: str | None = None,
652
674
  version: "modal_proto.api_pb2.VolumeFsVersion.ValueType | None" = None,
675
+ create_options: "VolumeCreateOptions | None" = None,
653
676
  _heartbeat_sleep: float = EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, # mdmd:line-hidden
654
677
  ) -> AsyncGenerator["_Volume", None]:
655
678
  """Create an anonymous Volume that exists for the duration of the context manager.
@@ -658,6 +681,7 @@ class _Volume(_Object, type_prefix="vo"):
658
681
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
659
682
  environment_name: Environment for the ephemeral Volume; defaults to the active environment.
660
683
  version: Optional VolumeFS backend version for the ephemeral Volume.
684
+ create_options: Options applied when creating the ephemeral Volume.
661
685
 
662
686
  Examples:
663
687
  ```python
@@ -678,6 +702,7 @@ class _Volume(_Object, type_prefix="vo"):
678
702
  object_creation_type=api_pb2.OBJECT_CREATION_TYPE_EPHEMERAL,
679
703
  environment_name=_get_environment_name(environment_name),
680
704
  version=version,
705
+ create_options=_volume_create_options_to_proto(create_options),
681
706
  )
682
707
  response = await client.stub.VolumeGetOrCreate(request)
683
708
  async with TaskContext() as tc:
@@ -28,6 +28,16 @@ def _validate_volume_version(requested_version: int, actual_version: int, volume
28
28
  """Validate that the returned volume version matches the requested version."""
29
29
  ...
30
30
 
31
+ def _experimental_options_to_string_map(experimental_options: dict[str, typing.Any]) -> dict[str, str]:
32
+ """Coerce experimental option values to strings for the proto's string map."""
33
+ ...
34
+
35
+ def _volume_create_options_to_proto(
36
+ create_options: typing.Optional[modal.types.VolumeCreateOptions],
37
+ ) -> modal_proto.api_pb2.VolumeCreateOptions:
38
+ """Build a VolumeCreateOptions proto message from the public TypedDict."""
39
+ ...
40
+
31
41
  class _VolumeManager:
32
42
  """Namespace with methods for managing named Volume objects."""
33
43
  async def create(
@@ -38,6 +48,7 @@ class _VolumeManager:
38
48
  allow_existing: bool = False,
39
49
  environment_name: typing.Optional[str] = None,
40
50
  client: typing.Optional[modal.client._Client] = None,
51
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
41
52
  ) -> None:
42
53
  """Create a new named Volume in the workspace environment.
43
54
 
@@ -51,6 +62,7 @@ class _VolumeManager:
51
62
  allow_existing: If True, do nothing when a Volume with this name already exists.
52
63
  environment_name: Environment to create in; defaults to the active environment.
53
64
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
65
+ experimental_options: Experimental options to create Volume with.
54
66
 
55
67
  Examples:
56
68
  ```python notest
@@ -167,6 +179,7 @@ class VolumeManager:
167
179
  allow_existing: bool = False,
168
180
  environment_name: typing.Optional[str] = None,
169
181
  client: typing.Optional[modal.client.Client] = None,
182
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
170
183
  ) -> None:
171
184
  """Create a new named Volume in the workspace environment.
172
185
 
@@ -180,6 +193,7 @@ class VolumeManager:
180
193
  allow_existing: If True, do nothing when a Volume with this name already exists.
181
194
  environment_name: Environment to create in; defaults to the active environment.
182
195
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
196
+ experimental_options: Experimental options to create Volume with.
183
197
 
184
198
  Examples:
185
199
  ```python notest
@@ -212,6 +226,7 @@ class VolumeManager:
212
226
  allow_existing: bool = False,
213
227
  environment_name: typing.Optional[str] = None,
214
228
  client: typing.Optional[modal.client.Client] = None,
229
+ experimental_options: typing.Optional[dict[str, typing.Any]] = None,
215
230
  ) -> None:
216
231
  """Create a new named Volume in the workspace environment.
217
232
 
@@ -225,6 +240,7 @@ class VolumeManager:
225
240
  allow_existing: If True, do nothing when a Volume with this name already exists.
226
241
  environment_name: Environment to create in; defaults to the active environment.
227
242
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
243
+ experimental_options: Experimental options to create Volume with.
228
244
 
229
245
  Examples:
230
246
  ```python notest
@@ -564,6 +580,7 @@ class _Volume(modal._object._Object):
564
580
  environment_name: typing.Optional[str] = None,
565
581
  create_if_missing: bool = False,
566
582
  version: typing.Optional[int] = None,
583
+ create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
567
584
  client: typing.Optional[modal.client._Client] = None,
568
585
  ) -> _Volume:
569
586
  """Reference a Volume by name, optionally creating it on the server first.
@@ -575,6 +592,7 @@ class _Volume(modal._object._Object):
575
592
  environment_name: Environment to resolve the name in; defaults to the active environment.
576
593
  create_if_missing: If True, create the Volume when it does not already exist.
577
594
  version: Optional VolumeFS backend version; must match an existing Volume when set.
595
+ create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
578
596
  client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
579
597
 
580
598
  Returns:
@@ -630,6 +648,7 @@ class _Volume(modal._object._Object):
630
648
  client: typing.Optional[modal.client._Client] = None,
631
649
  environment_name: typing.Optional[str] = None,
632
650
  version: typing.Optional[int] = None,
651
+ create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
633
652
  _heartbeat_sleep: float = 300,
634
653
  ) -> typing.AsyncContextManager[_Volume]:
635
654
  """Create an anonymous Volume that exists for the duration of the context manager.
@@ -638,6 +657,7 @@ class _Volume(modal._object._Object):
638
657
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
639
658
  environment_name: Environment for the ephemeral Volume; defaults to the active environment.
640
659
  version: Optional VolumeFS backend version for the ephemeral Volume.
660
+ create_options: Options applied when creating the ephemeral Volume.
641
661
 
642
662
  Examples:
643
663
  ```python
@@ -928,6 +948,7 @@ class Volume(modal.object.Object):
928
948
  environment_name: typing.Optional[str] = None,
929
949
  create_if_missing: bool = False,
930
950
  version: typing.Optional[int] = None,
951
+ create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
931
952
  client: typing.Optional[modal.client.Client] = None,
932
953
  ) -> Volume:
933
954
  """Reference a Volume by name, optionally creating it on the server first.
@@ -939,6 +960,7 @@ class Volume(modal.object.Object):
939
960
  environment_name: Environment to resolve the name in; defaults to the active environment.
940
961
  create_if_missing: If True, create the Volume when it does not already exist.
941
962
  version: Optional VolumeFS backend version; must match an existing Volume when set.
963
+ create_options: Applied when creating the Volume. If an existing Volume, validates options are consistent.
942
964
  client: Modal client to use for loading; defaults to `Client.from_env()` when omitted.
943
965
 
944
966
  Returns:
@@ -995,6 +1017,7 @@ class Volume(modal.object.Object):
995
1017
  client: typing.Optional[modal.client.Client] = None,
996
1018
  environment_name: typing.Optional[str] = None,
997
1019
  version: typing.Optional[int] = None,
1020
+ create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
998
1021
  _heartbeat_sleep: float = 300,
999
1022
  ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Volume]:
1000
1023
  """Create an anonymous Volume that exists for the duration of the context manager.
@@ -1003,6 +1026,7 @@ class Volume(modal.object.Object):
1003
1026
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
1004
1027
  environment_name: Environment for the ephemeral Volume; defaults to the active environment.
1005
1028
  version: Optional VolumeFS backend version for the ephemeral Volume.
1029
+ create_options: Options applied when creating the ephemeral Volume.
1006
1030
 
1007
1031
  Examples:
1008
1032
  ```python
@@ -1025,6 +1049,7 @@ class Volume(modal.object.Object):
1025
1049
  client: typing.Optional[modal.client.Client] = None,
1026
1050
  environment_name: typing.Optional[str] = None,
1027
1051
  version: typing.Optional[int] = None,
1052
+ create_options: typing.Optional[modal.types.VolumeCreateOptions] = None,
1028
1053
  _heartbeat_sleep: float = 300,
1029
1054
  ) -> typing.AsyncContextManager[Volume]:
1030
1055
  """Create an anonymous Volume that exists for the duration of the context manager.
@@ -1033,6 +1058,7 @@ class Volume(modal.object.Object):
1033
1058
  client: Modal client to use; defaults to `Client.from_env()` when omitted.
1034
1059
  environment_name: Environment for the ephemeral Volume; defaults to the active environment.
1035
1060
  version: Optional VolumeFS backend version for the ephemeral Volume.
1061
+ create_options: Options applied when creating the ephemeral Volume.
1036
1062
 
1037
1063
  Examples:
1038
1064
  ```python