modal 1.2.5.dev13__tar.gz → 1.2.5.dev15__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 (202) hide show
  1. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/PKG-INFO +1 -1
  2. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_partial_function.py +1 -2
  3. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/user_code_imports.py +5 -0
  4. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/app.py +7 -0
  5. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/client.pyi +2 -2
  6. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/flash.py +43 -4
  7. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/flash.pyi +20 -0
  8. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/PKG-INFO +1 -1
  9. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api.proto +5 -0
  10. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2.py +338 -338
  11. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2.pyi +8 -1
  12. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_version/__init__.py +1 -1
  13. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/LICENSE +0 -0
  14. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/README.md +0 -0
  15. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/__init__.py +0 -0
  16. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/__main__.py +0 -0
  17. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_billing.py +0 -0
  18. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_clustered_functions.py +0 -0
  19. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_clustered_functions.pyi +0 -0
  20. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_container_entrypoint.py +0 -0
  21. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_functions.py +0 -0
  22. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_grpc_client.py +0 -0
  23. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_ipython.py +0 -0
  24. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_load_context.py +0 -0
  25. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_location.py +0 -0
  26. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_object.py +0 -0
  27. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_output.py +0 -0
  28. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_pty.py +0 -0
  29. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_resolver.py +0 -0
  30. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_resources.py +0 -0
  31. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/__init__.py +0 -0
  32. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/asgi.py +0 -0
  33. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.py +0 -0
  34. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/container_io_manager.pyi +0 -0
  35. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/execution_context.py +0 -0
  36. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/execution_context.pyi +0 -0
  37. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  38. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/telemetry.py +0 -0
  39. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_runtime/user_code_event_loop.py +0 -0
  40. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_serialization.py +0 -0
  41. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_traceback.py +0 -0
  42. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_tunnel.py +0 -0
  43. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_tunnel.pyi +0 -0
  44. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_type_manager.py +0 -0
  45. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/__init__.py +0 -0
  46. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/app_utils.py +0 -0
  47. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/async_utils.py +0 -0
  48. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/auth_token_manager.py +0 -0
  49. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/blob_utils.py +0 -0
  50. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/bytes_io_segment_payload.py +0 -0
  51. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/deprecation.py +0 -0
  52. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/docker_utils.py +0 -0
  53. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/function_utils.py +0 -0
  54. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/git_utils.py +0 -0
  55. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/grpc_testing.py +0 -0
  56. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/grpc_utils.py +0 -0
  57. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/hash_utils.py +0 -0
  58. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/http_utils.py +0 -0
  59. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/jwt_utils.py +0 -0
  60. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/logger.py +0 -0
  61. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/mount_utils.py +0 -0
  62. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/name_utils.py +0 -0
  63. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/package_utils.py +0 -0
  64. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/pattern_utils.py +0 -0
  65. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/rand_pb_testing.py +0 -0
  66. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/shell_utils.py +0 -0
  67. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/task_command_router_client.py +0 -0
  68. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_utils/time_utils.py +0 -0
  69. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/__init__.py +0 -0
  70. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  71. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/cloudpickle.py +0 -0
  72. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_vendor/tblib.py +0 -0
  73. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/_watcher.py +0 -0
  74. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/app.pyi +0 -0
  75. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/billing.py +0 -0
  76. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2023.12.312.txt +0 -0
  77. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2023.12.txt +0 -0
  78. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2024.04.txt +0 -0
  79. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2024.10.txt +0 -0
  80. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/2025.06.txt +0 -0
  81. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/PREVIEW.txt +0 -0
  82. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/README.md +0 -0
  83. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/builder/base-images.json +0 -0
  84. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/call_graph.py +0 -0
  85. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/__init__.py +0 -0
  86. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/_download.py +0 -0
  87. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/_traceback.py +0 -0
  88. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/app.py +0 -0
  89. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/cluster.py +0 -0
  90. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/config.py +0 -0
  91. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/container.py +0 -0
  92. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/dict.py +0 -0
  93. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/entry_point.py +0 -0
  94. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/environment.py +0 -0
  95. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/import_refs.py +0 -0
  96. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/launch.py +0 -0
  97. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/network_file_system.py +0 -0
  98. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/profile.py +0 -0
  99. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/__init__.py +0 -0
  100. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/launch_instance_ssh.py +0 -0
  101. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/run_jupyter.py +0 -0
  102. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/run_marimo.py +0 -0
  103. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/programs/vscode.py +0 -0
  104. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/queues.py +0 -0
  105. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/run.py +0 -0
  106. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/secret.py +0 -0
  107. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/shell.py +0 -0
  108. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/token.py +0 -0
  109. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/utils.py +0 -0
  110. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cli/volume.py +0 -0
  111. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/client.py +0 -0
  112. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.py +0 -0
  113. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cloud_bucket_mount.pyi +0 -0
  114. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cls.py +0 -0
  115. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/cls.pyi +0 -0
  116. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/config.py +0 -0
  117. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/container_process.py +0 -0
  118. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/container_process.pyi +0 -0
  119. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/dict.py +0 -0
  120. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/dict.pyi +0 -0
  121. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/environments.py +0 -0
  122. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/environments.pyi +0 -0
  123. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/exception.py +0 -0
  124. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/__init__.py +0 -0
  125. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/experimental/ipython.py +0 -0
  126. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_io.py +0 -0
  127. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_io.pyi +0 -0
  128. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/file_pattern_matcher.py +0 -0
  129. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/functions.py +0 -0
  130. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/functions.pyi +0 -0
  131. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/gpu.py +0 -0
  132. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/image.py +0 -0
  133. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/image.pyi +0 -0
  134. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/io_streams.py +0 -0
  135. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/io_streams.pyi +0 -0
  136. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/mount.py +0 -0
  137. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/mount.pyi +0 -0
  138. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/network_file_system.py +0 -0
  139. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/network_file_system.pyi +0 -0
  140. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/object.py +0 -0
  141. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/object.pyi +0 -0
  142. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/output.py +0 -0
  143. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/parallel_map.py +0 -0
  144. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/parallel_map.pyi +0 -0
  145. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/partial_function.py +0 -0
  146. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/partial_function.pyi +0 -0
  147. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/proxy.py +0 -0
  148. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/proxy.pyi +0 -0
  149. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/py.typed +0 -0
  150. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/queue.py +0 -0
  151. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/queue.pyi +0 -0
  152. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/retries.py +0 -0
  153. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/runner.py +0 -0
  154. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/runner.pyi +0 -0
  155. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/running_app.py +0 -0
  156. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/sandbox.py +0 -0
  157. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/sandbox.pyi +0 -0
  158. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/schedule.py +0 -0
  159. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/scheduler_placement.py +0 -0
  160. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/secret.py +0 -0
  161. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/secret.pyi +0 -0
  162. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/serving.py +0 -0
  163. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/serving.pyi +0 -0
  164. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/snapshot.py +0 -0
  165. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/snapshot.pyi +0 -0
  166. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/stream_type.py +0 -0
  167. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/token_flow.py +0 -0
  168. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/token_flow.pyi +0 -0
  169. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/volume.py +0 -0
  170. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal/volume.pyi +0 -0
  171. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/SOURCES.txt +0 -0
  172. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/dependency_links.txt +0 -0
  173. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/entry_points.txt +0 -0
  174. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/requires.txt +0 -0
  175. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal.egg-info/top_level.txt +0 -0
  176. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/__init__.py +0 -0
  177. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/gen_cli_docs.py +0 -0
  178. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/gen_reference_docs.py +0 -0
  179. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/__init__.py +0 -0
  180. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/mdmd.py +0 -0
  181. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_docs/mdmd/signatures.py +0 -0
  182. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/__init__.py +0 -0
  183. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_grpc.py +0 -0
  184. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.py +0 -0
  185. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/api_pb2_grpc.pyi +0 -0
  186. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/modal_api_grpc.py +0 -0
  187. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/py.typed +0 -0
  188. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router.proto +0 -0
  189. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_grpc.py +0 -0
  190. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.py +0 -0
  191. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2.pyi +0 -0
  192. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
  193. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
  194. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router.proto +0 -0
  195. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_grpc.py +0 -0
  196. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.py +0 -0
  197. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2.pyi +0 -0
  198. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  199. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  200. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/modal_version/__main__.py +0 -0
  201. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/pyproject.toml +0 -0
  202. {modal-1.2.5.dev13 → modal-1.2.5.dev15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev13
3
+ Version: 1.2.5.dev15
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -160,9 +160,8 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
160
160
  raise InvalidError("Interface decorators cannot be combined with lifecycle decorators.")
161
161
 
162
162
  has_web_interface = self.flags & _PartialFunctionFlags.WEB_INTERFACE
163
- has_http_web_interface = self.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE
164
163
  has_callable_interface = self.flags & _PartialFunctionFlags.CALLABLE_INTERFACE
165
- if (has_web_interface or has_http_web_interface) and has_callable_interface:
164
+ if has_web_interface and has_callable_interface:
166
165
  self.registered = True # Hacky, avoid false-positive warning
167
166
  raise InvalidError("Callable decorators cannot be combined with web interface decorators.")
168
167
 
@@ -30,6 +30,7 @@ from modal._utils.function_utils import (
30
30
  from modal.app import _App
31
31
  from modal.config import logger
32
32
  from modal.exception import ExecutionError, InvalidError
33
+ from modal.experimental.flash import _FlashContainerEntry
33
34
  from modal_proto import api_pb2
34
35
 
35
36
  if typing.TYPE_CHECKING:
@@ -392,18 +393,22 @@ class ImportedClass(Service):
392
393
  event_loop: UserCodeEventLoop,
393
394
  container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
394
395
  ):
396
+ flash_entry = _FlashContainerEntry(self.function_def.http_config)
395
397
  # Identify the "enter" methods to run after resuming from a snapshot.
396
398
  if not self.function_def.is_auto_snapshot:
397
399
  post_snapshot_methods = _find_callables_for_obj(
398
400
  self.user_cls_instance, _PartialFunctionFlags.ENTER_POST_SNAPSHOT
399
401
  )
400
402
  call_lifecycle_functions(event_loop, container_io_manager, list(post_snapshot_methods.values()))
403
+ flash_entry.enter()
401
404
  try:
402
405
  yield
403
406
  finally:
404
407
  if not self.function_def.is_auto_snapshot:
408
+ flash_entry.stop()
405
409
  exit_methods = _find_callables_for_obj(self.user_cls_instance, _PartialFunctionFlags.EXIT)
406
410
  call_lifecycle_functions(event_loop, container_io_manager, list(exit_methods.values()))
411
+ flash_entry.close()
407
412
 
408
413
 
409
414
  def get_user_class_instance(_cls: modal.cls._Cls, args: tuple[Any, ...], kwargs: dict[str, Any]) -> typing.Any:
@@ -1098,6 +1098,13 @@ class _App:
1098
1098
  "The `@modal.http_server` decorator cannot be used on methods; decorate the class instead."
1099
1099
  )
1100
1100
 
1101
+ if http_config is not None:
1102
+ for method in _find_partial_methods_for_user_cls(
1103
+ user_cls, _PartialFunctionFlags.CALLABLE_INTERFACE
1104
+ ).values():
1105
+ method.registered = True # Avoid warning about not registering the method (hacky!)
1106
+ raise InvalidError("Callable decorators cannot be combined with web interface decorators.")
1107
+
1101
1108
  info = FunctionInfo(None, serialized=serialized, user_cls=user_cls)
1102
1109
 
1103
1110
  i6pn_enabled = i6pn or cluster_size is not None
@@ -32,7 +32,7 @@ class _Client:
32
32
  server_url: str,
33
33
  client_type: int,
34
34
  credentials: typing.Optional[tuple[str, str]],
35
- version: str = "1.2.5.dev13",
35
+ version: str = "1.2.5.dev15",
36
36
  ):
37
37
  """mdmd:hidden
38
38
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.2.5.dev13",
166
+ version: str = "1.2.5.dev15",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -32,6 +32,7 @@ class _FlashManager:
32
32
  process: Optional[subprocess.Popen] = None, # to be deprecated
33
33
  health_check_url: Optional[str] = None,
34
34
  startup_timeout: int = 30,
35
+ exit_grace_period: int = 0,
35
36
  h2_enabled: bool = False,
36
37
  ):
37
38
  self.client = client
@@ -40,6 +41,7 @@ class _FlashManager:
40
41
  # Health check is not currently being used
41
42
  self.health_check_url = health_check_url
42
43
  self.startup_timeout = startup_timeout
44
+ self.exit_grace_period = exit_grace_period
43
45
  self.tunnel_manager = _forward_tunnel(port, h2_enabled=h2_enabled, client=client)
44
46
  self.stopped = False
45
47
  self.num_failures = 0
@@ -158,9 +160,12 @@ class _FlashManager:
158
160
  return self.tunnel.url
159
161
 
160
162
  async def stop(self):
161
- self.heartbeat_task.cancel()
162
- await self.client.stub.FlashContainerDeregister(api_pb2.FlashContainerDeregisterRequest())
163
+ try:
164
+ self.heartbeat_task.cancel()
165
+ except Exception as e:
166
+ logger.error(f"[Modal Flash] Error stopping: {e}")
163
167
 
168
+ await self.client.stub.FlashContainerDeregister(api_pb2.FlashContainerDeregisterRequest())
164
169
  self.stopped = True
165
170
  logger.warning(f"[Modal Flash] No longer accepting new requests on {self.tunnel.url}.")
166
171
 
@@ -171,19 +176,22 @@ class _FlashManager:
171
176
  if not self.stopped:
172
177
  await self.stop()
173
178
 
179
+ await asyncio.sleep(self.exit_grace_period)
180
+
174
181
  logger.warning(f"[Modal Flash] Closing tunnel on {self.tunnel.url}.")
175
182
  await self.tunnel_manager.__aexit__(*sys.exc_info())
176
183
 
177
184
 
178
- FlashManager = synchronize_api(_FlashManager)
185
+ FlashManager = synchronize_api(_FlashManager, target_module=__name__)
179
186
 
180
187
 
181
188
  @synchronizer.create_blocking
182
189
  async def flash_forward(
183
190
  port: int,
184
- process: Optional[subprocess.Popen] = None,
191
+ process: Optional[subprocess.Popen] = None, # to be deprecated
185
192
  health_check_url: Optional[str] = None,
186
193
  startup_timeout: int = 30,
194
+ exit_grace_period: int = 0,
187
195
  h2_enabled: bool = False,
188
196
  ) -> _FlashManager:
189
197
  """
@@ -199,6 +207,7 @@ async def flash_forward(
199
207
  process=process,
200
208
  health_check_url=health_check_url,
201
209
  startup_timeout=startup_timeout,
210
+ exit_grace_period=exit_grace_period,
202
211
  h2_enabled=h2_enabled,
203
212
  )
204
213
  await manager._start()
@@ -692,3 +701,33 @@ def _http_server(
692
701
 
693
702
 
694
703
  http_server = synchronize_api(_http_server, target_module=__name__)
704
+
705
+
706
+ class _FlashContainerEntry:
707
+ """
708
+ A class that manages the lifecycle of Flash manager for Flash containers.
709
+
710
+ It is intentional that stop() runs before exit handlers and close().
711
+ This ensures the container is deregistered first, preventing new requests from being routed to it
712
+ while exit handlers execute and the exit grace period elapses, before finally closing the tunnel.
713
+ """
714
+
715
+ def __init__(self, http_config: api_pb2.HTTPConfig):
716
+ self.http_config: api_pb2.HTTPConfig = http_config
717
+ self.flash_manager: Optional[FlashManager] = None # type: ignore
718
+
719
+ def enter(self):
720
+ if self.http_config != api_pb2.HTTPConfig():
721
+ self.flash_manager = flash_forward(
722
+ self.http_config.port,
723
+ startup_timeout=self.http_config.startup_timeout,
724
+ exit_grace_period=self.http_config.exit_grace_period,
725
+ )
726
+
727
+ def stop(self):
728
+ if self.flash_manager:
729
+ self.flash_manager.stop()
730
+
731
+ def close(self):
732
+ if self.flash_manager:
733
+ self.flash_manager.close()
@@ -1,4 +1,5 @@
1
1
  import modal.client
2
+ import modal_proto.api_pb2
2
3
  import subprocess
3
4
  import typing
4
5
  import typing_extensions
@@ -11,6 +12,7 @@ class _FlashManager:
11
12
  process: typing.Optional[subprocess.Popen] = None,
12
13
  health_check_url: typing.Optional[str] = None,
13
14
  startup_timeout: int = 30,
15
+ exit_grace_period: int = 0,
14
16
  h2_enabled: bool = False,
15
17
  ):
16
18
  """Initialize self. See help(type(self)) for accurate signature."""
@@ -39,6 +41,7 @@ class FlashManager:
39
41
  process: typing.Optional[subprocess.Popen] = None,
40
42
  health_check_url: typing.Optional[str] = None,
41
43
  startup_timeout: int = 30,
44
+ exit_grace_period: int = 0,
42
45
  h2_enabled: bool = False,
43
46
  ): ...
44
47
 
@@ -97,6 +100,7 @@ class __flash_forward_spec(typing_extensions.Protocol):
97
100
  process: typing.Optional[subprocess.Popen] = None,
98
101
  health_check_url: typing.Optional[str] = None,
99
102
  startup_timeout: int = 30,
103
+ exit_grace_period: int = 0,
100
104
  h2_enabled: bool = False,
101
105
  ) -> FlashManager:
102
106
  """Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
@@ -112,6 +116,7 @@ class __flash_forward_spec(typing_extensions.Protocol):
112
116
  process: typing.Optional[subprocess.Popen] = None,
113
117
  health_check_url: typing.Optional[str] = None,
114
118
  startup_timeout: int = 30,
119
+ exit_grace_period: int = 0,
115
120
  h2_enabled: bool = False,
116
121
  ) -> FlashManager:
117
122
  """Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
@@ -420,3 +425,18 @@ def http_server(
420
425
  exit_grace_period: The time to wait for the HTTP server to exit gracefully.
421
426
  """
422
427
  ...
428
+
429
+ class _FlashContainerEntry:
430
+ """A class that manages the lifecycle of Flash manager for Flash containers.
431
+
432
+ It is intentional that stop() runs before exit handlers and close().
433
+ This ensures the container is deregistered first, preventing new requests from being routed to it
434
+ while exit handlers execute and the exit grace period elapses, before finally closing the tunnel.
435
+ """
436
+ def __init__(self, http_config: modal_proto.api_pb2.HTTPConfig):
437
+ """Initialize self. See help(type(self)) for accurate signature."""
438
+ ...
439
+
440
+ def enter(self): ...
441
+ def stop(self): ...
442
+ def close(self): ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev13
3
+ Version: 1.2.5.dev15
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -2793,6 +2793,11 @@ message Sandbox {
2793
2793
  // Exec commands for the sandbox will be issued directly to the sandbox
2794
2794
  // command router running on the Modal worker.
2795
2795
  bool direct_sandbox_commands_enabled = 34;
2796
+
2797
+ // Internal: restricts sandbox to run on this specific instance type.
2798
+ // Set by server during SandboxRestore to ensure the restored sandbox runs
2799
+ // on the same instance type as the original snapshot.
2800
+ string _restore_instance_type = 35;
2796
2801
  }
2797
2802
 
2798
2803
  message SandboxCreateConnectTokenRequest {