modal 1.6.3.dev12__tar.gz → 1.6.3.dev13__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 (250) hide show
  1. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/PKG-INFO +1 -1
  2. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_image.py +37 -11
  3. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/docker_utils.py +10 -1
  4. modal-1.6.3.dev13/modal/builder/PREVIEW.txt +17 -0
  5. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/base-images.json +3 -0
  6. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/endpoint.py +49 -7
  7. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/profile.py +1 -1
  8. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/workspace.py +1 -1
  9. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/client.pyi +2 -2
  10. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/image.pyi +24 -12
  11. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/io_streams.py +26 -14
  12. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/io_streams.pyi +2 -0
  13. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/mount.py +9 -0
  14. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/PKG-INFO +1 -1
  15. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/SOURCES.txt +1 -0
  16. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_grpc.py +16 -0
  17. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2.py +1205 -1175
  18. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2.pyi +52 -1
  19. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2_grpc.py +33 -0
  20. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/api_pb2_grpc.pyi +10 -0
  21. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/modal_api_grpc.py +1 -0
  22. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_version/__init__.py +1 -1
  23. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/LICENSE +0 -0
  24. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/README.md +0 -0
  25. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/__init__.py +0 -0
  26. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/__main__.py +0 -0
  27. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_billing.py +0 -0
  28. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_cluster.py +0 -0
  29. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_cluster.pyi +0 -0
  30. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_container_entrypoint.py +0 -0
  31. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_environments.py +0 -0
  32. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_function_variants.py +0 -0
  33. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_functions.py +0 -0
  34. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_grpc_client.py +0 -0
  35. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_ipython.py +0 -0
  36. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_load_context.py +0 -0
  37. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_location.py +0 -0
  38. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs.py +0 -0
  39. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs_manager.py +0 -0
  40. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_logs_manager.pyi +0 -0
  41. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_metrics.py +0 -0
  42. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_metrics.pyi +0 -0
  43. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_object.py +0 -0
  44. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_outbound_policy.py +0 -0
  45. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_outbound_policy.pyi +0 -0
  46. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/__init__.py +0 -0
  47. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/manager.py +0 -0
  48. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/pty.py +0 -0
  49. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/rich.py +0 -0
  50. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_output/status.py +0 -0
  51. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_partial_function.py +0 -0
  52. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_resolver.py +0 -0
  53. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_resources.py +0 -0
  54. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/__init__.py +0 -0
  55. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/asgi.py +0 -0
  56. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/container_io_manager.py +0 -0
  57. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/container_io_manager.pyi +0 -0
  58. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/execution_context.py +0 -0
  59. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/execution_context.pyi +0 -0
  60. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  61. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/task_lifecycle_manager.py +0 -0
  62. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  63. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/telemetry.py +0 -0
  64. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/user_code_event_loop.py +0 -0
  65. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_runtime/user_code_imports.py +0 -0
  66. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_serialization.py +0 -0
  67. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_server.py +0 -0
  68. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_server.pyi +0 -0
  69. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_supports_logs.py +0 -0
  70. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_traceback.py +0 -0
  71. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_tunnel.py +0 -0
  72. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_tunnel.pyi +0 -0
  73. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_type_manager.py +0 -0
  74. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/__init__.py +0 -0
  75. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/app_utils.py +0 -0
  76. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/async_utils.py +0 -0
  77. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/auth_token_manager.py +0 -0
  78. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/blob_utils.py +0 -0
  79. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/browser_utils.py +0 -0
  80. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/bytes_io_segment_payload.py +0 -0
  81. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/curl_utils.py +0 -0
  82. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/deprecation.py +0 -0
  83. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/display_utils.py +0 -0
  84. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/function_utils.py +0 -0
  85. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/git_utils.py +0 -0
  86. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/grpc_testing.py +0 -0
  87. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/grpc_utils.py +0 -0
  88. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/hash_utils.py +0 -0
  89. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/http_utils.py +0 -0
  90. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/idle_countdown.py +0 -0
  91. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/jwt_utils.py +0 -0
  92. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/logger.py +0 -0
  93. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/mount_utils.py +0 -0
  94. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/name_utils.py +0 -0
  95. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/oauth.py +0 -0
  96. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/package_utils.py +0 -0
  97. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/pattern_utils.py +0 -0
  98. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/proxy_support.py +0 -0
  99. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/rand_pb_testing.py +0 -0
  100. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/sandbox_fs_utils.py +0 -0
  101. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/shell_utils.py +0 -0
  102. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_utils/time_utils.py +0 -0
  103. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/__init__.py +0 -0
  104. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  105. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/cloudpickle.py +0 -0
  106. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/tblib.py +0 -0
  107. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_vendor/version.py +0 -0
  108. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_watcher.py +0 -0
  109. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/_workspace.py +0 -0
  110. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/app.py +0 -0
  111. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/app.pyi +0 -0
  112. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/billing.py +0 -0
  113. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/billing.pyi +0 -0
  114. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2023.12.312.txt +0 -0
  115. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2023.12.txt +0 -0
  116. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2024.04.txt +0 -0
  117. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2024.10.txt +0 -0
  118. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/2025.06.txt +0 -0
  119. /modal-1.6.3.dev12/modal/builder/PREVIEW.txt → /modal-1.6.3.dev13/modal/builder/2026.10.txt +0 -0
  120. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/builder/README.md +0 -0
  121. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/call_graph.py +0 -0
  122. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/__init__.py +0 -0
  123. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_download.py +0 -0
  124. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_help.py +0 -0
  125. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_logs.py +0 -0
  126. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_stats.py +0 -0
  127. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/_traceback.py +0 -0
  128. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/app.py +0 -0
  129. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/billing.py +0 -0
  130. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/changelog.py +0 -0
  131. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/config.py +0 -0
  132. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/container.py +0 -0
  133. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/curl.py +0 -0
  134. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/dashboard.py +0 -0
  135. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/dict.py +0 -0
  136. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/entry_point.py +0 -0
  137. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/environment.py +0 -0
  138. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/function.py +0 -0
  139. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/image.py +0 -0
  140. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/import_refs.py +0 -0
  141. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/logo.py +0 -0
  142. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/network_file_system.py +0 -0
  143. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/queues.py +0 -0
  144. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/run.py +0 -0
  145. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/secret.py +0 -0
  146. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/selector.py +0 -0
  147. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/server.py +0 -0
  148. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/shell.py +0 -0
  149. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/skills.py +0 -0
  150. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/token.py +0 -0
  151. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/utils.py +0 -0
  152. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cli/volume.py +0 -0
  153. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/client.py +0 -0
  154. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cloud_bucket_mount.py +0 -0
  155. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cloud_bucket_mount.pyi +0 -0
  156. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cls.py +0 -0
  157. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cls.pyi +0 -0
  158. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cluster.py +0 -0
  159. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/cluster.pyi +0 -0
  160. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/config.py +0 -0
  161. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/container_process.py +0 -0
  162. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/container_process.pyi +0 -0
  163. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/dict.py +0 -0
  164. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/dict.pyi +0 -0
  165. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/environments.py +0 -0
  166. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/environments.pyi +0 -0
  167. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/exception.py +0 -0
  168. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/__init__.py +0 -0
  169. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/_flash_container_entry.py +0 -0
  170. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash.py +0 -0
  171. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash.pyi +0 -0
  172. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash_server.py +0 -0
  173. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/flash_server.pyi +0 -0
  174. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/experimental/ipython.py +0 -0
  175. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/file_pattern_matcher.py +0 -0
  176. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/functions.py +0 -0
  177. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/functions.pyi +0 -0
  178. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/image.py +0 -0
  179. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/mount.pyi +0 -0
  180. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/network_file_system.py +0 -0
  181. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/network_file_system.pyi +0 -0
  182. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/object.py +0 -0
  183. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/object.pyi +0 -0
  184. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/output.py +0 -0
  185. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/parallel_map.py +0 -0
  186. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/parallel_map.pyi +0 -0
  187. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/partial_function.py +0 -0
  188. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/partial_function.pyi +0 -0
  189. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/proxy.py +0 -0
  190. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/proxy.pyi +0 -0
  191. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/py.typed +0 -0
  192. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/queue.py +0 -0
  193. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/queue.pyi +0 -0
  194. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/retries.py +0 -0
  195. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/runner.py +0 -0
  196. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/runner.pyi +0 -0
  197. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/running_app.py +0 -0
  198. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/__init__.py +0 -0
  199. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_common.py +0 -0
  200. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_filesystem.py +0 -0
  201. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_persistent.py +0 -0
  202. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_v1.py +0 -0
  203. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_impl_v2.py +0 -0
  204. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_sandbox.py +0 -0
  205. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_sidecar.py +0 -0
  206. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_snapshot.py +0 -0
  207. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/_task_command_router_client.py +0 -0
  208. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/sandbox.py +0 -0
  209. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/sandbox/sandbox.pyi +0 -0
  210. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/schedule.py +0 -0
  211. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/scheduler_placement.py +0 -0
  212. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/secret.py +0 -0
  213. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/secret.pyi +0 -0
  214. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/server.py +0 -0
  215. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/server.pyi +0 -0
  216. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/serving.py +0 -0
  217. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/serving.pyi +0 -0
  218. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/skills/modal/SKILL.md +0 -0
  219. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/stream_type.py +0 -0
  220. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/token_flow.py +0 -0
  221. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/token_flow.pyi +0 -0
  222. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/types.py +0 -0
  223. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/volume.py +0 -0
  224. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/volume.pyi +0 -0
  225. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/workspace.py +0 -0
  226. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal/workspace.pyi +0 -0
  227. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/dependency_links.txt +0 -0
  228. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/entry_points.txt +0 -0
  229. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/requires.txt +0 -0
  230. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal.egg-info/top_level.txt +0 -0
  231. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/__init__.py +0 -0
  232. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_cli_docs.py +0 -0
  233. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_cli_docs_main.py +0 -0
  234. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_reference_docs.py +0 -0
  235. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/gen_reference_docs_main.py +0 -0
  236. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/__init__.py +0 -0
  237. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/mdmd.py +0 -0
  238. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/signatures.py +0 -0
  239. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/mdmd/types.py +0 -0
  240. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_docs/reference_intro.md +0 -0
  241. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/__init__.py +0 -0
  242. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/py.typed +0 -0
  243. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_grpc.py +0 -0
  244. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2.py +0 -0
  245. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2.pyi +0 -0
  246. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  247. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  248. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/modal_version/__main__.py +0 -0
  249. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/pyproject.toml +0 -0
  250. {modal-1.6.3.dev12 → modal-1.6.3.dev13}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.6.3.dev12
3
+ Version: 1.6.3.dev13
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -40,6 +40,7 @@ from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
40
40
  from ._utils.docker_utils import (
41
41
  extract_copy_command_patterns,
42
42
  find_dockerignore_file,
43
+ is_multi_stage_dockerfile,
43
44
  splittable_from_line_index,
44
45
  )
45
46
  from ._utils.function_utils import FunctionSourceInfo, parse_gpu_config
@@ -70,8 +71,8 @@ if typing.TYPE_CHECKING:
70
71
  import modal.client
71
72
 
72
73
  # This is used for both type checking and runtime validation
73
- ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]
74
- LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2025.06"
74
+ ImageBuilderVersion = Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]
75
+ LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2026.10"
75
76
 
76
77
  # Note: we also define supported Python versions via logic at the top of the package __init__.py
77
78
  # so that we fail fast / clearly in unsupported containers. Additionally, we enumerate the supported
@@ -79,6 +80,7 @@ LATEST_IMAGE_BUILDER_VERSION: ImageBuilderVersion = "2025.06"
79
80
  # Consider consolidating these multiple sources of truth?
80
81
  SUPPORTED_PYTHON_SERIES: dict[ImageBuilderVersion, list[str]] = {
81
82
  "PREVIEW": ["3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
83
+ "2026.10": ["3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
82
84
  "2025.06": ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15", "3.15t"],
83
85
  "2024.10": ["3.10", "3.11", "3.12", "3.13"],
84
86
  "2024.04": ["3.10", "3.11", "3.12"],
@@ -2523,28 +2525,33 @@ class _Image(_Object, type_prefix="im"):
2523
2525
  *,
2524
2526
  force_build: bool = False,
2525
2527
  context_dir: Path | str | None = None,
2526
- env: dict[str, str | None] | None = None,
2528
+ ignore: Sequence[str] | Callable[[Path], bool] = AUTO_DOCKERIGNORE,
2529
+ build_args: dict[str, str] = {},
2530
+ registry_secret: _Secret | None = None,
2527
2531
  secrets: Collection[_Secret] | None = None,
2532
+ env: dict[str, str | None] | None = None,
2528
2533
  gpu: str | None = None,
2529
2534
  add_python: str | None = None,
2530
- build_args: dict[str, str] = {},
2531
- ignore: Sequence[str] | Callable[[Path], bool] = AUTO_DOCKERIGNORE,
2532
2535
  ) -> "_Image":
2533
2536
  """Build a Modal image from a local Dockerfile.
2534
2537
 
2535
2538
  If your Dockerfile does not have Python installed, you can use the `add_python` parameter
2536
2539
  to specify a version of Python to add to the image.
2537
2540
 
2541
+ A `registry_secret` can be provided to pull the base image from a private registry.
2542
+ Currently this is supported only for single-stage Dockerfiles.
2543
+
2538
2544
  Args:
2539
2545
  path: Path to the Dockerfile on the local machine.
2540
2546
  force_build: If True, skip cached image builds.
2541
2547
  context_dir: Build context directory for resolving relative COPY paths.
2548
+ ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
2549
+ build_args: Dockerfile ``ARG`` values forwarded to the build.
2550
+ registry_secret: Optional Secret with credentials for pulling the base image from a private registry.
2551
+ secrets: Secrets injected as environment variables in the build container.
2542
2552
  env: Environment variables set in the build container.
2543
- secrets: Secrets injected as environment variables during the build.
2544
- gpu: GPU type to attach to the builder container.
2553
+ gpu: GPU type to attach to the build container.
2545
2554
  add_python: Standalone Python version to add when the Dockerfile does not install Python.
2546
- build_args: Dockerfile ``ARG`` values forwarded to the build.
2547
- ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
2548
2555
 
2549
2556
  Returns:
2550
2557
  An `Image` built from the Dockerfile plus Modal runtime dependencies.
@@ -2590,6 +2597,12 @@ class _Image(_Object, type_prefix="im"):
2590
2597
  add_python="3.12",
2591
2598
  ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
2592
2599
  )
2600
+
2601
+ # Pull the Dockerfile's base image from a private registry.
2602
+ image = modal.Image.from_dockerfile(
2603
+ "./Dockerfile",
2604
+ registry_secret=modal.Secret.from_name("my-registry-secret"),
2605
+ )
2593
2606
  ```
2594
2607
  """
2595
2608
 
@@ -2602,7 +2615,15 @@ class _Image(_Object, type_prefix="im"):
2602
2615
  ignore=ignore, dockerfile_path=Path(path), context_dir=context_dir
2603
2616
  )
2604
2617
 
2605
- def dockerfile_image(commands: list[str], base_images: dict[str, _Image] | None = None) -> _Image:
2618
+ registry_config = (
2619
+ _ImageRegistryConfig(secret=registry_secret, infer_auth_type=True) if registry_secret is not None else None
2620
+ )
2621
+
2622
+ def dockerfile_image(
2623
+ commands: list[str],
2624
+ base_images: dict[str, _Image] | None = None,
2625
+ image_registry_config: _ImageRegistryConfig | None = None,
2626
+ ) -> _Image:
2606
2627
  return _Image._from_args(
2607
2628
  base_images=base_images,
2608
2629
  dockerfile_function=lambda version: DockerfileSpec(commands=commands, context_files={}),
@@ -2610,6 +2631,7 @@ class _Image(_Object, type_prefix="im"):
2610
2631
  gpu_config=gpu_config,
2611
2632
  secrets=secrets,
2612
2633
  build_args=build_args,
2634
+ image_registry_config=image_registry_config,
2613
2635
  force_build=force_build,
2614
2636
  )
2615
2637
 
@@ -2648,19 +2670,23 @@ class _Image(_Object, type_prefix="im"):
2648
2670
  )
2649
2671
  with open(os.path.expanduser(path)) as f:
2650
2672
  dockerfile_lines = f.read().split("\n")
2673
+ if registry_secret is not None and is_multi_stage_dockerfile(dockerfile_lines):
2674
+ raise InvalidError("`registry_secret` is not supported with multi-stage Dockerfiles.")
2651
2675
 
2652
2676
  # Builder versions after 2025.06 put the FROM line of a single-stage Dockerfile in its own image.
2653
2677
  # Dockerfiles the split could change (see splittable_from_line_index) stay in one layer.
2654
2678
  from_index = splittable_from_line_index(dockerfile_lines) if builder_version > "2025.06" else None
2655
2679
  if from_index is None:
2656
- image = dockerfile_image(dockerfile_lines)
2680
+ image = dockerfile_image(dockerfile_lines, image_registry_config=registry_config)
2657
2681
  else:
2658
2682
  # Drop the lines above FROM so Dockerfiles that differ only in header comments or blank lines
2659
2683
  # share the cached image. Those lines can only be comments or blank lines, since
2660
2684
  # splittable_from_line_index rejects parser directives.
2661
2685
  from_lines = [dockerfile_lines[from_index]]
2686
+ # Only this layer pulls from the registry; the layers above it build on top of it.
2662
2687
  from_image = _Image._from_args(
2663
2688
  dockerfile_function=lambda version: DockerfileSpec(commands=from_lines, context_files={}),
2689
+ image_registry_config=registry_config,
2664
2690
  force_build=force_build,
2665
2691
  )
2666
2692
  rest = dockerfile_lines[from_index + 1 :]
@@ -79,6 +79,15 @@ _FROM_ALIAS_PATTERN = re.compile(r"\sAS\s+\S+\s*$", re.IGNORECASE)
79
79
  _PARSER_DIRECTIVE_PATTERN = re.compile(r"^#\s*[a-zA-Z][a-zA-Z0-9]*\s*=")
80
80
 
81
81
 
82
+ def is_multi_stage_dockerfile(dockerfile_lines: Sequence[str]) -> bool:
83
+ """Return whether more than one line starts with a FROM instruction.
84
+
85
+ Can have false-positives for Dockerfiles with a Heredoc bodies of continuation lines
86
+ that contain "from" tokens.
87
+ """
88
+ return sum(1 for line in dockerfile_lines if _FROM_PATTERN.match(line)) > 1
89
+
90
+
82
91
  def splittable_from_line_index(dockerfile_lines: Sequence[str]) -> int | None:
83
92
  """Return the line index of the FROM instruction if it can be built as its own image.
84
93
 
@@ -96,7 +105,7 @@ def splittable_from_line_index(dockerfile_lines: Sequence[str]) -> int | None:
96
105
  return None
97
106
  if any(_PARSER_DIRECTIVE_PATTERN.match(line.strip()) for line in dockerfile_lines[:start]):
98
107
  return None
99
- if any(_FROM_PATTERN.match(line) for line in dockerfile_lines[start + 1 :]):
108
+ if is_multi_stage_dockerfile(dockerfile_lines):
100
109
  return None
101
110
  return start
102
111
 
@@ -0,0 +1,17 @@
1
+ aiohappyeyeballs==2.7.1
2
+ aiohttp==3.14.5
3
+ aiosignal==1.4.0
4
+ attrs==26.1.0
5
+ cbor2==5.9.0
6
+ certifi==2026.10.19
7
+ frozenlist==1.8.0
8
+ grpclib==0.4.9
9
+ h2==4.4.1
10
+ hpack==4.2.0
11
+ hyperframe==6.1.0
12
+ idna==3.20
13
+ multidict==7.1.0
14
+ propcache==0.5.4
15
+ protobuf==7.36.2
16
+ typing_extensions==4.16.0
17
+ yarl==1.25.1
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "debian": {
3
3
  "PREVIEW": "trixie",
4
+ "2026.10": "trixie",
4
5
  "2025.06": "bookworm",
5
6
  "2024.10": "bookworm",
6
7
  "2024.04": "bookworm",
@@ -38,6 +39,7 @@
38
39
  },
39
40
  "micromamba": {
40
41
  "PREVIEW": "2.9.0-debian13-slim",
42
+ "2026.10": "2.9.0-debian13-slim",
41
43
  "2025.06": "2.1.1-debian12-slim",
42
44
  "2024.10": "1.5.10-bookworm-slim",
43
45
  "2024.04": "1.5.8-bookworm-slim",
@@ -45,6 +47,7 @@
45
47
  },
46
48
  "package_tools": {
47
49
  "PREVIEW": "pip wheel uv",
50
+ "2026.10": "pip wheel uv",
48
51
  "2025.06": "pip wheel uv",
49
52
  "2024.10": "pip wheel uv",
50
53
  "2024.04": "pip wheel uv",
@@ -260,26 +260,35 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
260
260
  @click.option(
261
261
  "--name",
262
262
  default=None,
263
- help="Endpoint name. Defaults to inference-{number} if not provided.",
263
+ help="Endpoint name. If omitted, Modal chooses a name.",
264
264
  )
265
265
  @click.option(
266
266
  "--model",
267
267
  required=True,
268
268
  help="Hugging Face repo ID for the base model architecture (e.g., 'Qwen/Qwen3.6-27B-FP8').",
269
269
  )
270
+ @click.option(
271
+ "--shared",
272
+ is_flag=True,
273
+ default=False,
274
+ help="Create a Shared Endpoint. Defaults to a Dedicated Endpoint.",
275
+ )
270
276
  @click.option(
271
277
  "--routing-region",
272
278
  "routing_region",
273
279
  multiple=True,
274
280
  callback=_single_routing_region_callback,
275
- help=f"Region to route inference requests through. Defaults to {_DEFAULT_ROUTING_REGION}.",
281
+ help=(
282
+ f"Region to route inference requests through. Defaults to {_DEFAULT_ROUTING_REGION}. "
283
+ "Shared Endpoint routing cannot currently be customized."
284
+ ),
276
285
  )
277
286
  @click.option(
278
287
  "--compute-region",
279
288
  "compute_regions",
280
289
  multiple=True,
281
290
  help=(
282
- "Region to run Endpoint containers in. May be specified multiple times. "
291
+ "Region to run Dedicated Endpoint containers in. May be specified multiple times. "
283
292
  "This incurs a region selection price multiplier."
284
293
  ),
285
294
  )
@@ -287,13 +296,16 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
287
296
  "--colocate-compute",
288
297
  is_flag=True,
289
298
  default=False,
290
- help="Run all containers within the routing region. This incurs a region selection price multiplier.",
299
+ help=(
300
+ "Run all Dedicated Endpoint containers within the routing region. "
301
+ "This incurs a region selection price multiplier."
302
+ ),
291
303
  )
292
304
  @click.option(
293
305
  "--unauthenticated",
294
306
  is_flag=True,
295
307
  default=False,
296
- help="Allow unauthenticated HTTP requests to the endpoint.",
308
+ help="Allow unauthenticated HTTP requests to a Dedicated Endpoint.",
297
309
  )
298
310
  @click.option("--custom-hf-repo", default=None, help="Hugging Face repo ID for fine-tuned model weights.")
299
311
  @click.option("--custom-hf-revision", default=None, help="Git revision for --custom-hf-repo.")
@@ -304,6 +316,7 @@ async def list_(*, json: bool = False, env: Optional[str] = None):
304
316
  async def create(
305
317
  name: Optional[str],
306
318
  model: Optional[str] = None,
319
+ shared: bool = False,
307
320
  routing_region: str = _DEFAULT_ROUTING_REGION,
308
321
  compute_regions: tuple[str, ...] = (),
309
322
  colocate_compute: bool = False,
@@ -317,13 +330,21 @@ async def create(
317
330
  ):
318
331
  """Deploy a new Endpoint.
319
332
 
333
+ Creates a Dedicated Endpoint by default, with support for custom weights and compute placement.
334
+ Use --shared to serve a supported base model with Modal-managed capacity.
335
+
320
336
  Examples:
321
337
 
322
- Create an Endpoint from a base model:
338
+ Create a Dedicated Endpoint from a base model:
323
339
  ```bash
324
340
  modal endpoint create --model Qwen/Qwen3.6-27B-FP8
325
341
  ```
326
342
 
343
+ Create a Shared Endpoint from a supported base model:
344
+ ```bash
345
+ modal endpoint create --shared --model moonshotai/Kimi-K3
346
+ ```
347
+
327
348
  Create an Endpoint with an explicit name:
328
349
  ```bash
329
350
  modal endpoint create --name qwen-chat --model Qwen/Qwen3.6-27B-FP8
@@ -348,6 +369,22 @@ async def create(
348
369
  ```
349
370
 
350
371
  """
372
+ if shared:
373
+ for option, provided in (
374
+ ("--compute-region", bool(compute_regions)),
375
+ ("--colocate-compute", colocate_compute),
376
+ ("--unauthenticated", unauthenticated),
377
+ ("--custom-hf-repo", custom_hf_repo is not None),
378
+ ("--custom-hf-revision", custom_hf_revision is not None),
379
+ ("--custom-hf-token", custom_hf_token is not None),
380
+ ("--custom-volume-name", custom_volume_name is not None),
381
+ ("--custom-volume-path", custom_volume_path is not None),
382
+ ):
383
+ if provided:
384
+ raise click.UsageError(f"{option} cannot be used with --shared.")
385
+ if routing_region != _DEFAULT_ROUTING_REGION:
386
+ raise click.UsageError(f"Shared Endpoints require --routing-region {_DEFAULT_ROUTING_REGION}.")
387
+
351
388
  if compute_regions and colocate_compute:
352
389
  raise click.UsageError("--compute-region and --colocate-compute are mutually exclusive.")
353
390
  if custom_hf_repo and custom_volume_name:
@@ -403,13 +440,18 @@ async def create(
403
440
  model=model_source,
404
441
  environment_name=environment_name,
405
442
  unauthenticated=unauthenticated,
443
+ serving_mode=(api_pb2.ENDPOINT_SERVING_MODE_SHARED if shared else api_pb2.ENDPOINT_SERVING_MODE_DEDICATED),
406
444
  )
407
445
  if name:
408
446
  req.name = name
409
447
  resp = await client._stub.EndpointCreate(req)
410
448
 
411
449
  output = OutputManager.get()
412
- output.print(f"[green]✓[/green] Endpoint '{resp.name}' ({resp.endpoint_id}) was created and started provisioning.")
450
+ serving_mode = "Shared" if shared else "Dedicated"
451
+ output.print(
452
+ f"[green]✓[/green] {serving_mode} Endpoint '{resp.name}' ({resp.endpoint_id}) "
453
+ "was created and started provisioning."
454
+ )
413
455
  if resp.model_name:
414
456
  output.print(f' → Send [cyan]"model": "{resp.model_name}"[/cyan] in requests to the Endpoint.')
415
457
  if resp.endpoint_page_url:
@@ -33,7 +33,7 @@ async def list_(json: bool | None = False):
33
33
  profiles = config_profiles()
34
34
  lookup_coros = [
35
35
  _lookup_workspace(
36
- config.get("server_url", profile=profile),
36
+ config.get("server_url", profile=profile, use_env=False),
37
37
  token_id=config.get("token_id", profile=profile, use_env=False),
38
38
  token_secret=config.get("token_secret", profile=profile, use_env=False),
39
39
  oauth_refresh_token=config.get("oauth_refresh_token", profile=profile, use_env=False),
@@ -471,7 +471,7 @@ def service_users_list(json: bool):
471
471
 
472
472
  @service_users_cli.command("delete", no_args_is_help=True)
473
473
  @click.argument("name")
474
- @click.option("--allow-missing", is_flag=True, default=False, help="Don't error if the Volume doesn't exist.")
474
+ @click.option("--allow-missing", is_flag=True, default=False, help="Don't error if the Service User doesn't exist.")
475
475
  @yes_option
476
476
  def service_users_delete(name: str, *, allow_missing: bool, yes: bool):
477
477
  """Delete a Service User in this Workspace.
@@ -87,7 +87,7 @@ class _Client:
87
87
  server_url: str,
88
88
  client_type: int,
89
89
  credentials: typing.Optional[tuple[str, str]],
90
- version: str = "1.6.3.dev12",
90
+ version: str = "1.6.3.dev13",
91
91
  *,
92
92
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
93
93
  ):
@@ -304,7 +304,7 @@ class Client:
304
304
  server_url: str,
305
305
  client_type: int,
306
306
  credentials: typing.Optional[tuple[str, str]],
307
- version: str = "1.6.3.dev12",
307
+ version: str = "1.6.3.dev13",
308
308
  *,
309
309
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
310
310
  ):
@@ -65,7 +65,8 @@ class Image(modal.object.Object):
65
65
  base_images: typing.Optional[dict[str, Image]] = None,
66
66
  dockerfile_function: typing.Optional[
67
67
  collections.abc.Callable[
68
- [typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]], modal._image.DockerfileSpec
68
+ [typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]],
69
+ modal._image.DockerfileSpec,
69
70
  ]
70
71
  ] = None,
71
72
  secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
@@ -75,7 +76,7 @@ class Image(modal.object.Object):
75
76
  image_registry_config: typing.Optional[modal._image._ImageRegistryConfig] = None,
76
77
  context_mount_function: typing.Optional[
77
78
  collections.abc.Callable[
78
- [typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"]],
79
+ [typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"]],
79
80
  typing.Optional[modal.mount.Mount],
80
81
  ]
81
82
  ] = None,
@@ -873,7 +874,7 @@ class Image(modal.object.Object):
873
874
  @staticmethod
874
875
  def _registry_setup_commands(
875
876
  tag: str,
876
- builder_version: typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "PREVIEW"],
877
+ builder_version: typing.Literal["2023.12", "2024.04", "2024.10", "2025.06", "2026.10", "PREVIEW"],
877
878
  setup_commands: list[str],
878
879
  add_python: typing.Optional[str] = None,
879
880
  ) -> list[str]: ...
@@ -1043,30 +1044,35 @@ class Image(modal.object.Object):
1043
1044
  *,
1044
1045
  force_build: bool = False,
1045
1046
  context_dir: typing.Union[pathlib.Path, str, None] = None,
1046
- env: typing.Optional[dict[str, typing.Optional[str]]] = None,
1047
- secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
1048
- gpu: typing.Optional[str] = None,
1049
- add_python: typing.Optional[str] = None,
1050
- build_args: dict[str, str] = {},
1051
1047
  ignore: typing.Union[
1052
1048
  collections.abc.Sequence[str], collections.abc.Callable[[pathlib.Path], bool]
1053
1049
  ] = modal._image.AUTO_DOCKERIGNORE,
1050
+ build_args: dict[str, str] = {},
1051
+ registry_secret: typing.Optional[modal.secret.Secret] = None,
1052
+ secrets: typing.Optional[collections.abc.Collection[modal.secret.Secret]] = None,
1053
+ env: typing.Optional[dict[str, typing.Optional[str]]] = None,
1054
+ gpu: typing.Optional[str] = None,
1055
+ add_python: typing.Optional[str] = None,
1054
1056
  ) -> Image:
1055
1057
  """Build a Modal image from a local Dockerfile.
1056
1058
 
1057
1059
  If your Dockerfile does not have Python installed, you can use the `add_python` parameter
1058
1060
  to specify a version of Python to add to the image.
1059
1061
 
1062
+ A `registry_secret` can be provided to pull the base image from a private registry.
1063
+ Currently this is supported only for single-stage Dockerfiles.
1064
+
1060
1065
  Args:
1061
1066
  path: Path to the Dockerfile on the local machine.
1062
1067
  force_build: If True, skip cached image builds.
1063
1068
  context_dir: Build context directory for resolving relative COPY paths.
1069
+ ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
1070
+ build_args: Dockerfile ``ARG`` values forwarded to the build.
1071
+ registry_secret: Optional Secret with credentials for pulling the base image from a private registry.
1072
+ secrets: Secrets injected as environment variables in the build container.
1064
1073
  env: Environment variables set in the build container.
1065
- secrets: Secrets injected as environment variables during the build.
1066
- gpu: GPU type to attach to the builder container.
1074
+ gpu: GPU type to attach to the build container.
1067
1075
  add_python: Standalone Python version to add when the Dockerfile does not install Python.
1068
- build_args: Dockerfile ``ARG`` values forwarded to the build.
1069
- ignore: Ignore rules for the implicit context mount (defaults to auto ``.dockerignore`` behavior).
1070
1076
 
1071
1077
  Returns:
1072
1078
  An `Image` built from the Dockerfile plus Modal runtime dependencies.
@@ -1112,6 +1118,12 @@ class Image(modal.object.Object):
1112
1118
  add_python="3.12",
1113
1119
  ignore=FilePatternMatcher.from_file("/path/to/dockerignore"),
1114
1120
  )
1121
+
1122
+ # Pull the Dockerfile's base image from a private registry.
1123
+ image = modal.Image.from_dockerfile(
1124
+ "./Dockerfile",
1125
+ registry_secret=modal.Secret.from_name("my-registry-secret"),
1126
+ )
1115
1127
  ```
1116
1128
  """
1117
1129
  ...
@@ -369,36 +369,48 @@ class _StreamReaderThroughCommandRouterBase(_StreamReaderImpl[T]):
369
369
  """Open the underlying stream at the current offset."""
370
370
  raise NotImplementedError
371
371
 
372
+ def _current_stream(self) -> AsyncGenerator[T, None]:
373
+ if self._stream is None:
374
+ self._stream = self._iterate_stream()
375
+ return self._stream
376
+
372
377
  async def __anext__(self) -> T:
373
378
  async with self._lock:
374
- if self._stream is None:
375
- self._stream = self._iterate_stream()
376
- return await self._stream.__anext__()
379
+ return await self._current_stream().__anext__()
377
380
 
378
381
  async def _consume_stream(self) -> AsyncGenerator[T, None]:
382
+ # An iterator abandoned mid-loop is finalized at an arbitrary later
383
+ # time, so it must only close the stream it pulled from, never one a
384
+ # later iterator has since reopened.
385
+ stream: AsyncGenerator[T, None] | None = None
379
386
  try:
380
387
  while True:
381
- try:
382
- item = await self.__anext__()
383
- except StopAsyncIteration:
384
- return
388
+ async with self._lock:
389
+ stream = self._current_stream()
390
+ try:
391
+ item = await stream.__anext__()
392
+ except StopAsyncIteration:
393
+ return
385
394
  yield item
386
395
  finally:
387
- await self.aclose()
396
+ await self._close_stream(stream)
388
397
 
389
398
  def __aiter__(self) -> AsyncGenerator[T, None]:
390
399
  return self._consume_stream()
391
400
 
392
- async def aclose(self) -> None:
393
- """Close the underlying stream. A stream another reader is currently
394
- pulling from is left open for that reader to close."""
395
- if self._lock.locked():
401
+ async def _close_stream(self, stream: AsyncGenerator[T, None] | None) -> None:
402
+ if stream is None or self._lock.locked():
396
403
  return
397
404
  async with self._lock:
398
- stream, self._stream = self._stream, None
399
- if stream is not None:
405
+ if self._stream is stream:
406
+ self._stream = None
400
407
  await stream.aclose()
401
408
 
409
+ async def aclose(self) -> None:
410
+ """Close the underlying stream. A stream another reader is currently
411
+ pulling from is left open for that reader to close."""
412
+ await self._close_stream(self._stream)
413
+
402
414
 
403
415
  class _BytesStreamReaderThroughCommandRouter(_StreamReaderThroughCommandRouterBase[bytes]):
404
416
  """StreamReader that yields raw bytes from the router-backed stdio source
@@ -243,9 +243,11 @@ class _StreamReaderThroughCommandRouterBase(_StreamReaderImpl[T]):
243
243
  """Open the underlying stream at the current offset."""
244
244
  ...
245
245
 
246
+ def _current_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
246
247
  async def __anext__(self) -> T: ...
247
248
  def _consume_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
248
249
  def __aiter__(self) -> collections.abc.AsyncGenerator[T, None]: ...
250
+ async def _close_stream(self, stream: typing.Optional[collections.abc.AsyncGenerator[T, None]]) -> None: ...
249
251
  async def aclose(self) -> None:
250
252
  """Close the underlying stream. A stream another reader is currently
251
253
  pulling from is left open for that reader to close.
@@ -63,6 +63,15 @@ PYTHON_STANDALONE_VERSIONS: dict[str, dict[str, tuple[str, str]]] = {
63
63
  "3.15": ("20261021", "3.15.1"),
64
64
  "3.15t": ("20261021", "3.15.1t"),
65
65
  },
66
+ "2026.10": {
67
+ "3.11": ("20261021", "3.11.17"),
68
+ "3.12": ("20261021", "3.12.15"),
69
+ "3.13": ("20261021", "3.13.17"),
70
+ "3.14": ("20261021", "3.14.9"),
71
+ "3.14t": ("20261021", "3.14.9t"),
72
+ "3.15": ("20261021", "3.15.1"),
73
+ "3.15t": ("20261021", "3.15.1t"),
74
+ },
66
75
  "2025.06": _PYTHON_STANDALONE_VERSIONS_2025_06,
67
76
  "2024.10": _PYTHON_STANDALONE_VERSIONS_2025_06,
68
77
  "2024.04": _PYTHON_STANDALONE_VERSIONS_2025_06,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.6.3.dev12
3
+ Version: 1.6.3.dev13
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -163,6 +163,7 @@ modal/builder/2023.12.txt
163
163
  modal/builder/2024.04.txt
164
164
  modal/builder/2024.10.txt
165
165
  modal/builder/2025.06.txt
166
+ modal/builder/2026.10.txt
166
167
  modal/builder/PREVIEW.txt
167
168
  modal/builder/README.md
168
169
  modal/builder/base-images.json
@@ -303,6 +303,10 @@ class ModalClientBase(abc.ABC):
303
303
  async def EndpointGetInfo(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetInfoRequest, modal_proto.api_pb2.EndpointGetInfoResponse]') -> None:
304
304
  pass
305
305
 
306
+ @abc.abstractmethod
307
+ async def EndpointGetLibrary(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetLibraryRequest, modal_proto.api_pb2.EndpointGetLibraryResponse]') -> None:
308
+ pass
309
+
306
310
  @abc.abstractmethod
307
311
  async def EndpointGetLifecycle(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointGetLifecycleRequest, modal_proto.api_pb2.EndpointGetLifecycleResponse]') -> None:
308
312
  pass
@@ -1547,6 +1551,12 @@ class ModalClientBase(abc.ABC):
1547
1551
  modal_proto.api_pb2.EndpointGetInfoRequest,
1548
1552
  modal_proto.api_pb2.EndpointGetInfoResponse,
1549
1553
  ),
1554
+ '/modal.client.ModalClient/EndpointGetLibrary': grpclib.const.Handler(
1555
+ self.EndpointGetLibrary,
1556
+ grpclib.const.Cardinality.UNARY_UNARY,
1557
+ modal_proto.api_pb2.EndpointGetLibraryRequest,
1558
+ modal_proto.api_pb2.EndpointGetLibraryResponse,
1559
+ ),
1550
1560
  '/modal.client.ModalClient/EndpointGetLifecycle': grpclib.const.Handler(
1551
1561
  self.EndpointGetLifecycle,
1552
1562
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -3203,6 +3213,12 @@ class ModalClientStub:
3203
3213
  modal_proto.api_pb2.EndpointGetInfoRequest,
3204
3214
  modal_proto.api_pb2.EndpointGetInfoResponse,
3205
3215
  )
3216
+ self.EndpointGetLibrary = grpclib.client.UnaryUnaryMethod(
3217
+ channel,
3218
+ '/modal.client.ModalClient/EndpointGetLibrary',
3219
+ modal_proto.api_pb2.EndpointGetLibraryRequest,
3220
+ modal_proto.api_pb2.EndpointGetLibraryResponse,
3221
+ )
3206
3222
  self.EndpointGetLifecycle = grpclib.client.UnaryUnaryMethod(
3207
3223
  channel,
3208
3224
  '/modal.client.ModalClient/EndpointGetLifecycle',