modal 1.2.5.dev14__tar.gz → 1.2.5.dev16__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.dev14 → modal-1.2.5.dev16}/PKG-INFO +1 -1
  2. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_functions.py +2 -0
  3. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_partial_function.py +1 -2
  4. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/user_code_imports.py +6 -1
  5. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/function_utils.py +7 -10
  6. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/app.py +7 -0
  7. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/client.pyi +2 -2
  8. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/experimental/flash.py +43 -4
  9. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/experimental/flash.pyi +20 -0
  10. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/functions.pyi +6 -6
  11. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/PKG-INFO +1 -1
  12. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_version/__init__.py +1 -1
  13. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/LICENSE +0 -0
  14. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/README.md +0 -0
  15. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/__init__.py +0 -0
  16. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/__main__.py +0 -0
  17. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_billing.py +0 -0
  18. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_clustered_functions.py +0 -0
  19. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_clustered_functions.pyi +0 -0
  20. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_container_entrypoint.py +0 -0
  21. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_grpc_client.py +0 -0
  22. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_ipython.py +0 -0
  23. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_load_context.py +0 -0
  24. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_location.py +0 -0
  25. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_object.py +0 -0
  26. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_output.py +0 -0
  27. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_pty.py +0 -0
  28. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_resolver.py +0 -0
  29. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_resources.py +0 -0
  30. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/__init__.py +0 -0
  31. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/asgi.py +0 -0
  32. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/container_io_manager.py +0 -0
  33. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/container_io_manager.pyi +0 -0
  34. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/execution_context.py +0 -0
  35. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/execution_context.pyi +0 -0
  36. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  37. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/telemetry.py +0 -0
  38. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_runtime/user_code_event_loop.py +0 -0
  39. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_serialization.py +0 -0
  40. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_traceback.py +0 -0
  41. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_tunnel.py +0 -0
  42. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_tunnel.pyi +0 -0
  43. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_type_manager.py +0 -0
  44. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/__init__.py +0 -0
  45. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/app_utils.py +0 -0
  46. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/async_utils.py +0 -0
  47. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/auth_token_manager.py +0 -0
  48. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/blob_utils.py +0 -0
  49. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/bytes_io_segment_payload.py +0 -0
  50. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/deprecation.py +0 -0
  51. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/docker_utils.py +0 -0
  52. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/git_utils.py +0 -0
  53. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/grpc_testing.py +0 -0
  54. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/grpc_utils.py +0 -0
  55. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/hash_utils.py +0 -0
  56. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/http_utils.py +0 -0
  57. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/jwt_utils.py +0 -0
  58. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/logger.py +0 -0
  59. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/mount_utils.py +0 -0
  60. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/name_utils.py +0 -0
  61. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/package_utils.py +0 -0
  62. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/pattern_utils.py +0 -0
  63. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/rand_pb_testing.py +0 -0
  64. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/shell_utils.py +0 -0
  65. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/task_command_router_client.py +0 -0
  66. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_utils/time_utils.py +0 -0
  67. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_vendor/__init__.py +0 -0
  68. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  69. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_vendor/cloudpickle.py +0 -0
  70. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_vendor/tblib.py +0 -0
  71. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/_watcher.py +0 -0
  72. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/app.pyi +0 -0
  73. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/billing.py +0 -0
  74. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/2023.12.312.txt +0 -0
  75. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/2023.12.txt +0 -0
  76. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/2024.04.txt +0 -0
  77. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/2024.10.txt +0 -0
  78. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/2025.06.txt +0 -0
  79. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/PREVIEW.txt +0 -0
  80. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/README.md +0 -0
  81. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/builder/base-images.json +0 -0
  82. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/call_graph.py +0 -0
  83. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/__init__.py +0 -0
  84. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/_download.py +0 -0
  85. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/_traceback.py +0 -0
  86. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/app.py +0 -0
  87. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/cluster.py +0 -0
  88. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/config.py +0 -0
  89. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/container.py +0 -0
  90. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/dict.py +0 -0
  91. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/entry_point.py +0 -0
  92. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/environment.py +0 -0
  93. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/import_refs.py +0 -0
  94. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/launch.py +0 -0
  95. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/network_file_system.py +0 -0
  96. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/profile.py +0 -0
  97. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/programs/__init__.py +0 -0
  98. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/programs/launch_instance_ssh.py +0 -0
  99. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/programs/run_jupyter.py +0 -0
  100. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/programs/run_marimo.py +0 -0
  101. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/programs/vscode.py +0 -0
  102. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/queues.py +0 -0
  103. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/run.py +0 -0
  104. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/secret.py +0 -0
  105. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/shell.py +0 -0
  106. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/token.py +0 -0
  107. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/utils.py +0 -0
  108. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cli/volume.py +0 -0
  109. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/client.py +0 -0
  110. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cloud_bucket_mount.py +0 -0
  111. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cloud_bucket_mount.pyi +0 -0
  112. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cls.py +0 -0
  113. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/cls.pyi +0 -0
  114. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/config.py +0 -0
  115. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/container_process.py +0 -0
  116. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/container_process.pyi +0 -0
  117. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/dict.py +0 -0
  118. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/dict.pyi +0 -0
  119. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/environments.py +0 -0
  120. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/environments.pyi +0 -0
  121. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/exception.py +0 -0
  122. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/experimental/__init__.py +0 -0
  123. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/experimental/ipython.py +0 -0
  124. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/file_io.py +0 -0
  125. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/file_io.pyi +0 -0
  126. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/file_pattern_matcher.py +0 -0
  127. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/functions.py +0 -0
  128. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/gpu.py +0 -0
  129. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/image.py +0 -0
  130. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/image.pyi +0 -0
  131. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/io_streams.py +0 -0
  132. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/io_streams.pyi +0 -0
  133. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/mount.py +0 -0
  134. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/mount.pyi +0 -0
  135. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/network_file_system.py +0 -0
  136. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/network_file_system.pyi +0 -0
  137. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/object.py +0 -0
  138. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/object.pyi +0 -0
  139. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/output.py +0 -0
  140. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/parallel_map.py +0 -0
  141. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/parallel_map.pyi +0 -0
  142. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/partial_function.py +0 -0
  143. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/partial_function.pyi +0 -0
  144. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/proxy.py +0 -0
  145. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/proxy.pyi +0 -0
  146. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/py.typed +0 -0
  147. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/queue.py +0 -0
  148. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/queue.pyi +0 -0
  149. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/retries.py +0 -0
  150. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/runner.py +0 -0
  151. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/runner.pyi +0 -0
  152. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/running_app.py +0 -0
  153. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/sandbox.py +0 -0
  154. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/sandbox.pyi +0 -0
  155. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/schedule.py +0 -0
  156. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/scheduler_placement.py +0 -0
  157. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/secret.py +0 -0
  158. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/secret.pyi +0 -0
  159. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/serving.py +0 -0
  160. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/serving.pyi +0 -0
  161. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/snapshot.py +0 -0
  162. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/snapshot.pyi +0 -0
  163. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/stream_type.py +0 -0
  164. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/token_flow.py +0 -0
  165. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/token_flow.pyi +0 -0
  166. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/volume.py +0 -0
  167. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal/volume.pyi +0 -0
  168. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/SOURCES.txt +0 -0
  169. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/dependency_links.txt +0 -0
  170. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/entry_points.txt +0 -0
  171. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/requires.txt +0 -0
  172. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal.egg-info/top_level.txt +0 -0
  173. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/__init__.py +0 -0
  174. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/gen_cli_docs.py +0 -0
  175. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/gen_reference_docs.py +0 -0
  176. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/mdmd/__init__.py +0 -0
  177. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/mdmd/mdmd.py +0 -0
  178. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_docs/mdmd/signatures.py +0 -0
  179. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/__init__.py +0 -0
  180. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api.proto +0 -0
  181. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api_grpc.py +0 -0
  182. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api_pb2.py +0 -0
  183. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api_pb2.pyi +0 -0
  184. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api_pb2_grpc.py +0 -0
  185. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/api_pb2_grpc.pyi +0 -0
  186. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/modal_api_grpc.py +0 -0
  187. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/py.typed +0 -0
  188. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router.proto +0 -0
  189. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router_grpc.py +0 -0
  190. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router_pb2.py +0 -0
  191. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router_pb2.pyi +0 -0
  192. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
  193. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
  194. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router.proto +0 -0
  195. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router_grpc.py +0 -0
  196. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router_pb2.py +0 -0
  197. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router_pb2.pyi +0 -0
  198. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  199. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  200. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/modal_version/__main__.py +0 -0
  201. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/pyproject.toml +0 -0
  202. {modal-1.2.5.dev14 → modal-1.2.5.dev16}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev14
3
+ Version: 1.2.5.dev16
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -988,6 +988,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
988
988
  function_definition = api_pb2.Function(
989
989
  module_name=info.module_name or "",
990
990
  function_name=info.function_name,
991
+ implementation_name=info.implementation_name,
991
992
  mount_ids=loaded_mount_ids,
992
993
  secret_ids=[secret.object_id for secret in secrets],
993
994
  image_id=(image.object_id if image else ""),
@@ -1054,6 +1055,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1054
1055
  function_data = api_pb2.FunctionData(
1055
1056
  module_name=function_definition.module_name,
1056
1057
  function_name=function_definition.function_name,
1058
+ implementation_name=function_definition.implementation_name,
1057
1059
  function_type=function_definition.function_type,
1058
1060
  warm_pool_size=function_definition.warm_pool_size,
1059
1061
  concurrency_limit=function_definition.concurrency_limit,
@@ -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:
@@ -460,7 +465,7 @@ def import_single_function_service(
460
465
  else:
461
466
  # Load the module dynamically
462
467
  module = importlib.import_module(function_def.module_name)
463
- qual_name: str = function_def.function_name
468
+ qual_name: str = function_def.implementation_name
464
469
 
465
470
  if not is_global_object(qual_name):
466
471
  raise LocalFunctionError("Attempted to load a function defined in a function scope")
@@ -129,6 +129,7 @@ class FunctionInfo:
129
129
 
130
130
  raw_f: Optional[Callable[..., Any]] # if None - this is a "class service function"
131
131
  function_name: str
132
+ implementation_name: str
132
133
  user_cls: Optional[type[Any]]
133
134
  module_name: Optional[str]
134
135
 
@@ -160,20 +161,16 @@ class FunctionInfo:
160
161
  self.raw_f = f
161
162
  self.user_cls = user_cls
162
163
 
163
- if name_override is not None:
164
- if not serialized:
165
- # We may relax this constraint in the future, but currently we don't track the distinction between
166
- # the Function's name inside modal and the name of the object that we need to import in a container.
167
- raise InvalidError("Setting a custom `name=` also requires setting `serialized=True`")
168
- self.function_name = name_override
169
- elif f is None and user_cls:
164
+ if f is None and user_cls:
170
165
  # "service function" for running all methods of a class
171
- self.function_name = f"{user_cls.__name__}.*"
166
+ self.implementation_name = f"{user_cls.__name__}.*"
172
167
  elif f and user_cls:
173
168
  # Method may be defined on superclass of the wrapped class
174
- self.function_name = f"{user_cls.__name__}.{f.__name__}"
169
+ self.implementation_name = f"{user_cls.__name__}.{f.__name__}"
175
170
  else:
176
- self.function_name = f.__qualname__
171
+ self.implementation_name = f.__qualname__
172
+
173
+ self.function_name = name_override or self.implementation_name
177
174
 
178
175
  # If it's a cls, the @method could be defined in a base class in a different file.
179
176
  if user_cls is not None:
@@ -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.dev14",
35
+ version: str = "1.2.5.dev16",
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.dev14",
166
+ version: str = "1.2.5.dev16",
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): ...
@@ -409,7 +409,7 @@ class Function(
409
409
 
410
410
  _call_generator: ___call_generator_spec[typing_extensions.Self]
411
411
 
412
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
412
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
413
413
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
414
414
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
415
415
  ...
@@ -418,7 +418,7 @@ class Function(
418
418
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
419
419
  ...
420
420
 
421
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
421
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
422
422
 
423
423
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
424
424
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -445,7 +445,7 @@ class Function(
445
445
  """
446
446
  ...
447
447
 
448
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
448
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
450
450
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
451
451
 
@@ -469,7 +469,7 @@ class Function(
469
469
  ...
470
470
 
471
471
  _experimental_spawn: ___experimental_spawn_spec[
472
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
472
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
473
473
  ]
474
474
 
475
475
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -478,7 +478,7 @@ class Function(
478
478
 
479
479
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
480
480
 
481
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
481
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
482
482
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
483
483
  """Calls the function with the given arguments, without waiting for the results.
484
484
 
@@ -499,7 +499,7 @@ class Function(
499
499
  """
500
500
  ...
501
501
 
502
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
502
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
503
503
 
504
504
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
505
505
  """Return the inner Python object wrapped by this Modal Function."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev14
3
+ Version: 1.2.5.dev16
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.5.dev14"
4
+ __version__ = "1.2.5.dev16"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes