modal 1.1.5.dev3__tar.gz → 1.1.5.dev5__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 (190) hide show
  1. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/PKG-INFO +1 -1
  2. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/client.pyi +2 -2
  3. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/experimental/flash.py +46 -26
  4. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/experimental/flash.pyi +9 -3
  5. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/functions.pyi +6 -6
  6. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/PKG-INFO +1 -1
  7. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_version/__init__.py +1 -1
  8. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/LICENSE +0 -0
  9. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/README.md +0 -0
  10. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/__init__.py +0 -0
  11. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/__main__.py +0 -0
  12. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_clustered_functions.py +0 -0
  13. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_clustered_functions.pyi +0 -0
  14. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_container_entrypoint.py +0 -0
  15. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_functions.py +0 -0
  16. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_ipython.py +0 -0
  17. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_location.py +0 -0
  18. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_object.py +0 -0
  19. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_output.py +0 -0
  20. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_partial_function.py +0 -0
  21. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_pty.py +0 -0
  22. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_resolver.py +0 -0
  23. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_resources.py +0 -0
  24. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/__init__.py +0 -0
  25. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/asgi.py +0 -0
  26. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/container_io_manager.py +0 -0
  27. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/container_io_manager.pyi +0 -0
  28. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/execution_context.py +0 -0
  29. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/execution_context.pyi +0 -0
  30. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  31. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/telemetry.py +0 -0
  32. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_runtime/user_code_imports.py +0 -0
  33. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_serialization.py +0 -0
  34. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_traceback.py +0 -0
  35. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_tunnel.py +0 -0
  36. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_tunnel.pyi +0 -0
  37. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_type_manager.py +0 -0
  38. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/__init__.py +0 -0
  39. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/app_utils.py +0 -0
  40. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/async_utils.py +0 -0
  41. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/auth_token_manager.py +0 -0
  42. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/blob_utils.py +0 -0
  43. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/bytes_io_segment_payload.py +0 -0
  44. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/deprecation.py +0 -0
  45. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/docker_utils.py +0 -0
  46. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/function_utils.py +0 -0
  47. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/git_utils.py +0 -0
  48. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/grpc_testing.py +0 -0
  49. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/grpc_utils.py +0 -0
  50. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/hash_utils.py +0 -0
  51. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/http_utils.py +0 -0
  52. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/jwt_utils.py +0 -0
  53. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/logger.py +0 -0
  54. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/mount_utils.py +0 -0
  55. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/name_utils.py +0 -0
  56. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/package_utils.py +0 -0
  57. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/pattern_utils.py +0 -0
  58. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/rand_pb_testing.py +0 -0
  59. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/shell_utils.py +0 -0
  60. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_utils/time_utils.py +0 -0
  61. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_vendor/__init__.py +0 -0
  62. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  63. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_vendor/cloudpickle.py +0 -0
  64. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_vendor/tblib.py +0 -0
  65. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/_watcher.py +0 -0
  66. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/app.py +0 -0
  67. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/app.pyi +0 -0
  68. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/2023.12.312.txt +0 -0
  69. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/2023.12.txt +0 -0
  70. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/2024.04.txt +0 -0
  71. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/2024.10.txt +0 -0
  72. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/2025.06.txt +0 -0
  73. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/PREVIEW.txt +0 -0
  74. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/README.md +0 -0
  75. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/builder/base-images.json +0 -0
  76. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/call_graph.py +0 -0
  77. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/__init__.py +0 -0
  78. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/_download.py +0 -0
  79. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/_traceback.py +0 -0
  80. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/app.py +0 -0
  81. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/cluster.py +0 -0
  82. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/config.py +0 -0
  83. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/container.py +0 -0
  84. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/dict.py +0 -0
  85. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/entry_point.py +0 -0
  86. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/environment.py +0 -0
  87. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/import_refs.py +0 -0
  88. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/launch.py +0 -0
  89. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/network_file_system.py +0 -0
  90. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/profile.py +0 -0
  91. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/programs/__init__.py +0 -0
  92. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/programs/launch_instance_ssh.py +0 -0
  93. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/programs/run_jupyter.py +0 -0
  94. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/programs/run_marimo.py +0 -0
  95. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/programs/vscode.py +0 -0
  96. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/queues.py +0 -0
  97. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/run.py +0 -0
  98. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/secret.py +0 -0
  99. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/token.py +0 -0
  100. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/utils.py +0 -0
  101. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cli/volume.py +0 -0
  102. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/client.py +0 -0
  103. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cloud_bucket_mount.py +0 -0
  104. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cloud_bucket_mount.pyi +0 -0
  105. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cls.py +0 -0
  106. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/cls.pyi +0 -0
  107. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/config.py +0 -0
  108. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/container_process.py +0 -0
  109. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/container_process.pyi +0 -0
  110. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/dict.py +0 -0
  111. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/dict.pyi +0 -0
  112. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/environments.py +0 -0
  113. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/environments.pyi +0 -0
  114. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/exception.py +0 -0
  115. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/experimental/__init__.py +0 -0
  116. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/experimental/ipython.py +0 -0
  117. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/file_io.py +0 -0
  118. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/file_io.pyi +0 -0
  119. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/file_pattern_matcher.py +0 -0
  120. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/functions.py +0 -0
  121. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/gpu.py +0 -0
  122. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/image.py +0 -0
  123. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/image.pyi +0 -0
  124. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/io_streams.py +0 -0
  125. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/io_streams.pyi +0 -0
  126. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/mount.py +0 -0
  127. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/mount.pyi +0 -0
  128. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/network_file_system.py +0 -0
  129. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/network_file_system.pyi +0 -0
  130. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/object.py +0 -0
  131. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/object.pyi +0 -0
  132. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/output.py +0 -0
  133. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/parallel_map.py +0 -0
  134. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/parallel_map.pyi +0 -0
  135. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/partial_function.py +0 -0
  136. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/partial_function.pyi +0 -0
  137. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/proxy.py +0 -0
  138. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/proxy.pyi +0 -0
  139. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/py.typed +0 -0
  140. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/queue.py +0 -0
  141. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/queue.pyi +0 -0
  142. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/retries.py +0 -0
  143. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/runner.py +0 -0
  144. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/runner.pyi +0 -0
  145. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/running_app.py +0 -0
  146. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/sandbox.py +0 -0
  147. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/sandbox.pyi +0 -0
  148. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/schedule.py +0 -0
  149. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/scheduler_placement.py +0 -0
  150. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/secret.py +0 -0
  151. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/secret.pyi +0 -0
  152. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/serving.py +0 -0
  153. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/serving.pyi +0 -0
  154. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/snapshot.py +0 -0
  155. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/snapshot.pyi +0 -0
  156. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/stream_type.py +0 -0
  157. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/token_flow.py +0 -0
  158. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/token_flow.pyi +0 -0
  159. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/volume.py +0 -0
  160. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal/volume.pyi +0 -0
  161. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/SOURCES.txt +0 -0
  162. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/dependency_links.txt +0 -0
  163. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/entry_points.txt +0 -0
  164. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/requires.txt +0 -0
  165. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal.egg-info/top_level.txt +0 -0
  166. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/__init__.py +0 -0
  167. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/gen_cli_docs.py +0 -0
  168. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/gen_reference_docs.py +0 -0
  169. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/mdmd/__init__.py +0 -0
  170. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/mdmd/mdmd.py +0 -0
  171. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_docs/mdmd/signatures.py +0 -0
  172. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/__init__.py +0 -0
  173. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api.proto +0 -0
  174. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api_grpc.py +0 -0
  175. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api_pb2.py +0 -0
  176. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api_pb2.pyi +0 -0
  177. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api_pb2_grpc.py +0 -0
  178. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/api_pb2_grpc.pyi +0 -0
  179. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/modal_api_grpc.py +0 -0
  180. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/modal_options_grpc.py +0 -0
  181. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options.proto +0 -0
  182. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options_grpc.py +0 -0
  183. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options_pb2.py +0 -0
  184. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options_pb2.pyi +0 -0
  185. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options_pb2_grpc.py +0 -0
  186. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/options_pb2_grpc.pyi +0 -0
  187. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_proto/py.typed +0 -0
  188. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/modal_version/__main__.py +0 -0
  189. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/pyproject.toml +0 -0
  190. {modal-1.1.5.dev3 → modal-1.1.5.dev5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.5.dev3
3
+ Version: 1.1.5.dev5
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.1.5.dev3",
36
+ version: str = "1.1.5.dev5",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.1.5.dev3",
167
+ version: str = "1.1.5.dev5",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -21,7 +21,7 @@ from ..client import _Client
21
21
  from ..config import logger
22
22
  from ..exception import InvalidError
23
23
 
24
- _MAX_FAILURES = 3
24
+ _MAX_FAILURES = 10
25
25
 
26
26
 
27
27
  class _FlashManager:
@@ -43,7 +43,7 @@ class _FlashManager:
43
43
  self.task_id = os.environ["MODAL_TASK_ID"]
44
44
 
45
45
  async def is_port_connection_healthy(
46
- self, process: Optional[subprocess.Popen], timeout: int = 5
46
+ self, process: Optional[subprocess.Popen], timeout: float = 0.5
47
47
  ) -> tuple[bool, Optional[Exception]]:
48
48
  import socket
49
49
 
@@ -53,7 +53,7 @@ class _FlashManager:
53
53
  try:
54
54
  if process is not None and process.poll() is not None:
55
55
  return False, Exception(f"Process {process.pid} exited with code {process.returncode}")
56
- with socket.create_connection(("localhost", self.port), timeout=1):
56
+ with socket.create_connection(("localhost", self.port), timeout=0.5):
57
57
  return True, None
58
58
  except (ConnectionRefusedError, OSError):
59
59
  await asyncio.sleep(0.1)
@@ -201,6 +201,7 @@ class _FlashPrometheusAutoscaler:
201
201
  target_metric_value: float,
202
202
  min_containers: Optional[int],
203
203
  max_containers: Optional[int],
204
+ buffer_containers: Optional[int],
204
205
  scale_up_tolerance: float,
205
206
  scale_down_tolerance: float,
206
207
  scale_up_stabilization_window_seconds: int,
@@ -228,6 +229,7 @@ class _FlashPrometheusAutoscaler:
228
229
  self.target_metric_value = target_metric_value
229
230
  self.min_containers = min_containers
230
231
  self.max_containers = max_containers
232
+ self.buffer_containers = buffer_containers
231
233
  self.scale_up_tolerance = scale_up_tolerance
232
234
  self.scale_down_tolerance = scale_down_tolerance
233
235
  self.scale_up_stabilization_window_seconds = scale_up_stabilization_window_seconds
@@ -293,6 +295,7 @@ class _FlashPrometheusAutoscaler:
293
295
  scale_down_stabilization_window_seconds=self.scale_down_stabilization_window_seconds,
294
296
  min_containers=self.min_containers,
295
297
  max_containers=self.max_containers,
298
+ buffer_containers=self.buffer_containers,
296
299
  )
297
300
 
298
301
  logger.warning(
@@ -402,6 +405,7 @@ class _FlashPrometheusAutoscaler:
402
405
  # Gets metrics from prometheus
403
406
  sum_metric = 0
404
407
  containers_with_metrics = 0
408
+ buffer_containers = self.buffer_containers or 0
405
409
  container_metrics_list = await asyncio.gather(
406
410
  *[
407
411
  self._get_metrics(f"https://{container.host}:{container.port}/{self.metrics_endpoint}")
@@ -418,29 +422,36 @@ class _FlashPrometheusAutoscaler:
418
422
  sum_metric += container_metrics[target_metric][0].value
419
423
  containers_with_metrics += 1
420
424
 
421
- # n_containers_missing_metric is the number of unhealthy containers + number of cold starting containers
425
+ # n_containers_missing = number of unhealthy containers + number of containers not registered in flash dns
422
426
  n_containers_missing_metric = current_replicas - containers_with_metrics
423
- # n_containers_unhealthy is the number of live containers that are not emitting metrics i.e. unhealthy
427
+ # n_containers_unhealthy = number of dns registered containers that are not emitting metrics
424
428
  n_containers_unhealthy = len(containers) - containers_with_metrics
425
429
 
426
- # Scale up assuming that every unhealthy container is at 2x the target metric value.
427
- scale_up_target_metric_value = (sum_metric + n_containers_unhealthy * target_metric_value) / (
428
- (containers_with_metrics + n_containers_unhealthy) or 1
429
- )
430
+ # number of total containers - buffer containers
431
+ # This is used in 1) scale ratio denominators 2) provisioning base.
432
+ # Max is used to handle case when buffer_containers are first initialized.
433
+ num_provisioned_containers = max(current_replicas - buffer_containers, 1)
434
+
435
+ # Scale up assuming that every unhealthy container is at (1 + scale_up_tolerance)x the target metric value.
436
+ # This way if all containers are unhealthy, we will increase our number of containers.
437
+ scale_up_target_metric_value = (
438
+ sum_metric + (1 + self.scale_up_tolerance) * n_containers_unhealthy * target_metric_value
439
+ ) / (num_provisioned_containers)
430
440
 
431
441
  # Scale down assuming that every container (including cold starting containers) are at the target metric value.
442
+ # The denominator is just num_provisioned_containers because we don't want to account for the buffer containers.
432
443
  scale_down_target_metric_value = (sum_metric + n_containers_missing_metric * target_metric_value) / (
433
- current_replicas or 1
444
+ num_provisioned_containers
434
445
  )
435
446
 
436
447
  scale_up_ratio = scale_up_target_metric_value / target_metric_value
437
448
  scale_down_ratio = scale_down_target_metric_value / target_metric_value
438
449
 
439
- desired_replicas = current_replicas
450
+ desired_replicas = num_provisioned_containers
440
451
  if scale_up_ratio > 1 + self.scale_up_tolerance:
441
- desired_replicas = math.ceil(current_replicas * scale_up_ratio)
452
+ desired_replicas = math.ceil(desired_replicas * scale_up_ratio)
442
453
  elif scale_down_ratio < 1 - self.scale_down_tolerance:
443
- desired_replicas = math.ceil(current_replicas * scale_down_ratio)
454
+ desired_replicas = math.ceil(desired_replicas * scale_down_ratio)
444
455
 
445
456
  logger.warning(
446
457
  f"[Modal Flash] Current replicas: {current_replicas}, "
@@ -449,6 +460,7 @@ class _FlashPrometheusAutoscaler:
449
460
  f"number of containers with metrics: {containers_with_metrics}, "
450
461
  f"number of containers unhealthy: {n_containers_unhealthy}, "
451
462
  f"number of containers missing metric (includes unhealthy): {n_containers_missing_metric}, "
463
+ f"number of provisioned containers: {num_provisioned_containers}, "
452
464
  f"scale up ratio: {scale_up_ratio}, "
453
465
  f"scale down ratio: {scale_down_ratio}, "
454
466
  f"desired replicas: {desired_replicas}"
@@ -510,6 +522,7 @@ class _FlashPrometheusAutoscaler:
510
522
  scale_down_stabilization_window_seconds: int = 60 * 5,
511
523
  min_containers: Optional[int] = None,
512
524
  max_containers: Optional[int] = None,
525
+ buffer_containers: Optional[int] = None,
513
526
  ) -> int:
514
527
  """
515
528
  Return the target number of containers following (simplified) Kubernetes HPA
@@ -560,6 +573,10 @@ class _FlashPrometheusAutoscaler:
560
573
  new_replicas = max(min_containers, new_replicas)
561
574
  if max_containers is not None:
562
575
  new_replicas = min(max_containers, new_replicas)
576
+
577
+ if buffer_containers is not None:
578
+ new_replicas += buffer_containers
579
+
563
580
  return new_replicas
564
581
 
565
582
  async def stop(self):
@@ -597,6 +614,8 @@ async def flash_prometheus_autoscaler(
597
614
  # How often to make autoscaling decisions.
598
615
  # Corresponds to --horizontal-pod-autoscaler-sync-period in Kubernetes.
599
616
  autoscaling_interval_seconds: int = 15,
617
+ # Whether to include overprovisioned containers in the scale up calculation.
618
+ buffer_containers: Optional[int] = None,
600
619
  ) -> _FlashPrometheusAutoscaler:
601
620
  """
602
621
  Autoscale a Flash service based on containers' Prometheus metrics.
@@ -614,19 +633,20 @@ async def flash_prometheus_autoscaler(
614
633
 
615
634
  client = await _Client.from_env()
616
635
  autoscaler = _FlashPrometheusAutoscaler(
617
- client,
618
- app_name,
619
- cls_name,
620
- metrics_endpoint,
621
- target_metric,
622
- target_metric_value,
623
- min_containers,
624
- max_containers,
625
- scale_up_tolerance,
626
- scale_down_tolerance,
627
- scale_up_stabilization_window_seconds,
628
- scale_down_stabilization_window_seconds,
629
- autoscaling_interval_seconds,
636
+ client=client,
637
+ app_name=app_name,
638
+ cls_name=cls_name,
639
+ metrics_endpoint=metrics_endpoint,
640
+ target_metric=target_metric,
641
+ target_metric_value=target_metric_value,
642
+ min_containers=min_containers,
643
+ max_containers=max_containers,
644
+ buffer_containers=buffer_containers,
645
+ scale_up_tolerance=scale_up_tolerance,
646
+ scale_down_tolerance=scale_down_tolerance,
647
+ scale_up_stabilization_window_seconds=scale_up_stabilization_window_seconds,
648
+ scale_down_stabilization_window_seconds=scale_down_stabilization_window_seconds,
649
+ autoscaling_interval_seconds=autoscaling_interval_seconds,
630
650
  )
631
651
  await autoscaler.start()
632
652
  return autoscaler
@@ -16,7 +16,7 @@ class _FlashManager:
16
16
  ...
17
17
 
18
18
  async def is_port_connection_healthy(
19
- self, process: typing.Optional[subprocess.Popen], timeout: int = 5
19
+ self, process: typing.Optional[subprocess.Popen], timeout: float = 0.5
20
20
  ) -> tuple[bool, typing.Optional[Exception]]: ...
21
21
  async def _start(self): ...
22
22
  async def _drain_container(self):
@@ -41,10 +41,10 @@ class FlashManager:
41
41
 
42
42
  class __is_port_connection_healthy_spec(typing_extensions.Protocol[SUPERSELF]):
43
43
  def __call__(
44
- self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
44
+ self, /, process: typing.Optional[subprocess.Popen], timeout: float = 0.5
45
45
  ) -> tuple[bool, typing.Optional[Exception]]: ...
46
46
  async def aio(
47
- self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
47
+ self, /, process: typing.Optional[subprocess.Popen], timeout: float = 0.5
48
48
  ) -> tuple[bool, typing.Optional[Exception]]: ...
49
49
 
50
50
  is_port_connection_healthy: __is_port_connection_healthy_spec[typing_extensions.Self]
@@ -126,6 +126,7 @@ class _FlashPrometheusAutoscaler:
126
126
  target_metric_value: float,
127
127
  min_containers: typing.Optional[int],
128
128
  max_containers: typing.Optional[int],
129
+ buffer_containers: typing.Optional[int],
129
130
  scale_up_tolerance: float,
130
131
  scale_down_tolerance: float,
131
132
  scale_up_stabilization_window_seconds: int,
@@ -155,6 +156,7 @@ class _FlashPrometheusAutoscaler:
155
156
  scale_down_stabilization_window_seconds: int = 300,
156
157
  min_containers: typing.Optional[int] = None,
157
158
  max_containers: typing.Optional[int] = None,
159
+ buffer_containers: typing.Optional[int] = None,
158
160
  ) -> int:
159
161
  """Return the target number of containers following (simplified) Kubernetes HPA
160
162
  stabilization-window semantics.
@@ -187,6 +189,7 @@ class FlashPrometheusAutoscaler:
187
189
  target_metric_value: float,
188
190
  min_containers: typing.Optional[int],
189
191
  max_containers: typing.Optional[int],
192
+ buffer_containers: typing.Optional[int],
190
193
  scale_up_tolerance: float,
191
194
  scale_down_tolerance: float,
192
195
  scale_up_stabilization_window_seconds: int,
@@ -253,6 +256,7 @@ class FlashPrometheusAutoscaler:
253
256
  scale_down_stabilization_window_seconds: int = 300,
254
257
  min_containers: typing.Optional[int] = None,
255
258
  max_containers: typing.Optional[int] = None,
259
+ buffer_containers: typing.Optional[int] = None,
256
260
  ) -> int:
257
261
  """Return the target number of containers following (simplified) Kubernetes HPA
258
262
  stabilization-window semantics.
@@ -294,6 +298,7 @@ class __flash_prometheus_autoscaler_spec(typing_extensions.Protocol):
294
298
  scale_up_stabilization_window_seconds: int = 0,
295
299
  scale_down_stabilization_window_seconds: int = 300,
296
300
  autoscaling_interval_seconds: int = 15,
301
+ buffer_containers: typing.Optional[int] = None,
297
302
  ) -> FlashPrometheusAutoscaler:
298
303
  """Autoscale a Flash service based on containers' Prometheus metrics.
299
304
 
@@ -319,6 +324,7 @@ class __flash_prometheus_autoscaler_spec(typing_extensions.Protocol):
319
324
  scale_up_stabilization_window_seconds: int = 0,
320
325
  scale_down_stabilization_window_seconds: int = 300,
321
326
  autoscaling_interval_seconds: int = 15,
327
+ buffer_containers: typing.Optional[int] = None,
322
328
  ) -> FlashPrometheusAutoscaler:
323
329
  """Autoscale a Flash service based on containers' Prometheus metrics.
324
330
 
@@ -445,7 +445,7 @@ class Function(
445
445
 
446
446
  _call_generator: ___call_generator_spec[typing_extensions.Self]
447
447
 
448
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
448
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
450
450
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
451
451
  ...
@@ -454,7 +454,7 @@ class Function(
454
454
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
455
455
  ...
456
456
 
457
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
457
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
458
458
 
459
459
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
460
460
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -481,7 +481,7 @@ class Function(
481
481
  """
482
482
  ...
483
483
 
484
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
484
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
485
485
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
486
486
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
487
487
 
@@ -505,7 +505,7 @@ class Function(
505
505
  ...
506
506
 
507
507
  _experimental_spawn: ___experimental_spawn_spec[
508
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
508
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
509
509
  ]
510
510
 
511
511
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -514,7 +514,7 @@ class Function(
514
514
 
515
515
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
516
516
 
517
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
517
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
518
518
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
519
519
  """Calls the function with the given arguments, without waiting for the results.
520
520
 
@@ -535,7 +535,7 @@ class Function(
535
535
  """
536
536
  ...
537
537
 
538
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
538
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
539
539
 
540
540
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
541
541
  """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.1.5.dev3
3
+ Version: 1.1.5.dev5
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.1.5.dev3"
4
+ __version__ = "1.1.5.dev5"
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
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