modal 1.1.5.dev2__tar.gz → 1.1.5.dev3__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.dev2 → modal-1.1.5.dev3}/PKG-INFO +1 -1
  2. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/client.pyi +2 -2
  3. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/experimental/flash.py +37 -30
  4. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/experimental/flash.pyi +11 -5
  5. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/PKG-INFO +1 -1
  6. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_version/__init__.py +1 -1
  7. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/LICENSE +0 -0
  8. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/README.md +0 -0
  9. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/__init__.py +0 -0
  10. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/__main__.py +0 -0
  11. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_clustered_functions.py +0 -0
  12. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_clustered_functions.pyi +0 -0
  13. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_container_entrypoint.py +0 -0
  14. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_functions.py +0 -0
  15. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_ipython.py +0 -0
  16. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_location.py +0 -0
  17. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_object.py +0 -0
  18. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_output.py +0 -0
  19. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_partial_function.py +0 -0
  20. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_pty.py +0 -0
  21. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_resolver.py +0 -0
  22. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_resources.py +0 -0
  23. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/__init__.py +0 -0
  24. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/asgi.py +0 -0
  25. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/container_io_manager.py +0 -0
  26. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/container_io_manager.pyi +0 -0
  27. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/execution_context.py +0 -0
  28. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/execution_context.pyi +0 -0
  29. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  30. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/telemetry.py +0 -0
  31. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_runtime/user_code_imports.py +0 -0
  32. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_serialization.py +0 -0
  33. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_traceback.py +0 -0
  34. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_tunnel.py +0 -0
  35. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_tunnel.pyi +0 -0
  36. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_type_manager.py +0 -0
  37. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/__init__.py +0 -0
  38. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/app_utils.py +0 -0
  39. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/async_utils.py +0 -0
  40. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/auth_token_manager.py +0 -0
  41. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/blob_utils.py +0 -0
  42. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/bytes_io_segment_payload.py +0 -0
  43. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/deprecation.py +0 -0
  44. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/docker_utils.py +0 -0
  45. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/function_utils.py +0 -0
  46. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/git_utils.py +0 -0
  47. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/grpc_testing.py +0 -0
  48. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/grpc_utils.py +0 -0
  49. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/hash_utils.py +0 -0
  50. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/http_utils.py +0 -0
  51. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/jwt_utils.py +0 -0
  52. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/logger.py +0 -0
  53. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/mount_utils.py +0 -0
  54. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/name_utils.py +0 -0
  55. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/package_utils.py +0 -0
  56. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/pattern_utils.py +0 -0
  57. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/rand_pb_testing.py +0 -0
  58. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/shell_utils.py +0 -0
  59. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_utils/time_utils.py +0 -0
  60. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_vendor/__init__.py +0 -0
  61. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  62. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_vendor/cloudpickle.py +0 -0
  63. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_vendor/tblib.py +0 -0
  64. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/_watcher.py +0 -0
  65. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/app.py +0 -0
  66. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/app.pyi +0 -0
  67. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/2023.12.312.txt +0 -0
  68. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/2023.12.txt +0 -0
  69. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/2024.04.txt +0 -0
  70. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/2024.10.txt +0 -0
  71. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/2025.06.txt +0 -0
  72. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/PREVIEW.txt +0 -0
  73. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/README.md +0 -0
  74. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/builder/base-images.json +0 -0
  75. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/call_graph.py +0 -0
  76. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/__init__.py +0 -0
  77. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/_download.py +0 -0
  78. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/_traceback.py +0 -0
  79. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/app.py +0 -0
  80. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/cluster.py +0 -0
  81. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/config.py +0 -0
  82. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/container.py +0 -0
  83. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/dict.py +0 -0
  84. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/entry_point.py +0 -0
  85. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/environment.py +0 -0
  86. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/import_refs.py +0 -0
  87. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/launch.py +0 -0
  88. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/network_file_system.py +0 -0
  89. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/profile.py +0 -0
  90. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/programs/__init__.py +0 -0
  91. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/programs/launch_instance_ssh.py +0 -0
  92. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/programs/run_jupyter.py +0 -0
  93. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/programs/run_marimo.py +0 -0
  94. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/programs/vscode.py +0 -0
  95. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/queues.py +0 -0
  96. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/run.py +0 -0
  97. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/secret.py +0 -0
  98. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/token.py +0 -0
  99. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/utils.py +0 -0
  100. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cli/volume.py +0 -0
  101. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/client.py +0 -0
  102. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cloud_bucket_mount.py +0 -0
  103. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cloud_bucket_mount.pyi +0 -0
  104. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cls.py +0 -0
  105. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/cls.pyi +0 -0
  106. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/config.py +0 -0
  107. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/container_process.py +0 -0
  108. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/container_process.pyi +0 -0
  109. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/dict.py +0 -0
  110. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/dict.pyi +0 -0
  111. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/environments.py +0 -0
  112. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/environments.pyi +0 -0
  113. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/exception.py +0 -0
  114. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/experimental/__init__.py +0 -0
  115. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/experimental/ipython.py +0 -0
  116. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/file_io.py +0 -0
  117. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/file_io.pyi +0 -0
  118. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/file_pattern_matcher.py +0 -0
  119. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/functions.py +0 -0
  120. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/functions.pyi +0 -0
  121. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/gpu.py +0 -0
  122. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/image.py +0 -0
  123. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/image.pyi +0 -0
  124. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/io_streams.py +0 -0
  125. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/io_streams.pyi +0 -0
  126. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/mount.py +0 -0
  127. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/mount.pyi +0 -0
  128. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/network_file_system.py +0 -0
  129. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/network_file_system.pyi +0 -0
  130. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/object.py +0 -0
  131. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/object.pyi +0 -0
  132. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/output.py +0 -0
  133. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/parallel_map.py +0 -0
  134. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/parallel_map.pyi +0 -0
  135. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/partial_function.py +0 -0
  136. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/partial_function.pyi +0 -0
  137. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/proxy.py +0 -0
  138. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/proxy.pyi +0 -0
  139. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/py.typed +0 -0
  140. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/queue.py +0 -0
  141. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/queue.pyi +0 -0
  142. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/retries.py +0 -0
  143. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/runner.py +0 -0
  144. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/runner.pyi +0 -0
  145. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/running_app.py +0 -0
  146. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/sandbox.py +0 -0
  147. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/sandbox.pyi +0 -0
  148. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/schedule.py +0 -0
  149. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/scheduler_placement.py +0 -0
  150. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/secret.py +0 -0
  151. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/secret.pyi +0 -0
  152. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/serving.py +0 -0
  153. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/serving.pyi +0 -0
  154. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/snapshot.py +0 -0
  155. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/snapshot.pyi +0 -0
  156. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/stream_type.py +0 -0
  157. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/token_flow.py +0 -0
  158. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/token_flow.pyi +0 -0
  159. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/volume.py +0 -0
  160. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal/volume.pyi +0 -0
  161. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/SOURCES.txt +0 -0
  162. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/dependency_links.txt +0 -0
  163. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/entry_points.txt +0 -0
  164. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/requires.txt +0 -0
  165. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal.egg-info/top_level.txt +0 -0
  166. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/__init__.py +0 -0
  167. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/gen_cli_docs.py +0 -0
  168. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/gen_reference_docs.py +0 -0
  169. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/mdmd/__init__.py +0 -0
  170. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/mdmd/mdmd.py +0 -0
  171. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_docs/mdmd/signatures.py +0 -0
  172. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/__init__.py +0 -0
  173. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api.proto +0 -0
  174. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api_grpc.py +0 -0
  175. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api_pb2.py +0 -0
  176. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api_pb2.pyi +0 -0
  177. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api_pb2_grpc.py +0 -0
  178. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/api_pb2_grpc.pyi +0 -0
  179. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/modal_api_grpc.py +0 -0
  180. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/modal_options_grpc.py +0 -0
  181. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options.proto +0 -0
  182. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options_grpc.py +0 -0
  183. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options_pb2.py +0 -0
  184. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options_pb2.pyi +0 -0
  185. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options_pb2_grpc.py +0 -0
  186. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/options_pb2_grpc.pyi +0 -0
  187. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_proto/py.typed +0 -0
  188. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/modal_version/__main__.py +0 -0
  189. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/pyproject.toml +0 -0
  190. {modal-1.1.5.dev2 → modal-1.1.5.dev3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.5.dev2
3
+ Version: 1.1.5.dev3
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.dev2",
36
+ version: str = "1.1.5.dev3",
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.dev2",
167
+ version: str = "1.1.5.dev3",
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 = 3
25
25
 
26
26
 
27
27
  class _FlashManager:
@@ -42,7 +42,9 @@ class _FlashManager:
42
42
  self.num_failures = 0
43
43
  self.task_id = os.environ["MODAL_TASK_ID"]
44
44
 
45
- async def check_port_connection(self, process: Optional[subprocess.Popen], timeout: int = 10):
45
+ async def is_port_connection_healthy(
46
+ self, process: Optional[subprocess.Popen], timeout: int = 5
47
+ ) -> tuple[bool, Optional[Exception]]:
46
48
  import socket
47
49
 
48
50
  start_time = time.monotonic()
@@ -50,13 +52,13 @@ class _FlashManager:
50
52
  while time.monotonic() - start_time < timeout:
51
53
  try:
52
54
  if process is not None and process.poll() is not None:
53
- return Exception(f"Process {process.pid} exited with code {process.returncode}")
55
+ return False, Exception(f"Process {process.pid} exited with code {process.returncode}")
54
56
  with socket.create_connection(("localhost", self.port), timeout=1):
55
- return
57
+ return True, None
56
58
  except (ConnectionRefusedError, OSError):
57
59
  await asyncio.sleep(0.1)
58
60
 
59
- return Exception(f"Waited too long for port {self.port} to start accepting connections")
61
+ return False, Exception(f"Waited too long for port {self.port} to start accepting connections")
60
62
 
61
63
  async def _start(self):
62
64
  self.tunnel = await self.tunnel_manager.__aenter__()
@@ -74,7 +76,7 @@ class _FlashManager:
74
76
  while True:
75
77
  try:
76
78
  # Check if the container should be drained (e.g., too many failures)
77
- if self.num_failures > MAX_FAILURES:
79
+ if self.num_failures > _MAX_FAILURES:
78
80
  logger.warning(
79
81
  f"[Modal Flash] Draining task {self.task_id} on {self.tunnel.url} due to too many failures."
80
82
  )
@@ -101,35 +103,38 @@ class _FlashManager:
101
103
  first_registration = True
102
104
  while True:
103
105
  try:
104
- await self.check_port_connection(process=self.process)
105
- resp = await self.client.stub.FlashContainerRegister(
106
- api_pb2.FlashContainerRegisterRequest(
107
- priority=10,
108
- weight=5,
109
- host=host,
110
- port=port,
111
- ),
112
- timeout=10,
113
- )
114
- self.num_failures = 0
115
- if first_registration:
116
- logger.warning(
117
- f"[Modal Flash] Listening at {resp.url} over {self.tunnel.url} for task_id {self.task_id}"
106
+ port_check_resp, port_check_error = await self.is_port_connection_healthy(process=self.process)
107
+ if port_check_resp:
108
+ resp = await self.client.stub.FlashContainerRegister(
109
+ api_pb2.FlashContainerRegisterRequest(
110
+ priority=10,
111
+ weight=5,
112
+ host=host,
113
+ port=port,
114
+ ),
115
+ timeout=10,
116
+ )
117
+ self.num_failures = 0
118
+ if first_registration:
119
+ logger.warning(
120
+ f"[Modal Flash] Listening at {resp.url} over {self.tunnel.url} for task_id {self.task_id}"
121
+ )
122
+ first_registration = False
123
+ else:
124
+ logger.error(
125
+ f"[Modal Flash] Deregistering container {self.task_id} on {self.tunnel.url} "
126
+ f"due to error: {port_check_error}, num_failures: {self.num_failures}"
127
+ )
128
+ self.num_failures += 1
129
+ await retry_transient_errors(
130
+ self.client.stub.FlashContainerDeregister,
131
+ api_pb2.FlashContainerDeregisterRequest(),
118
132
  )
119
- first_registration = False
120
133
  except asyncio.CancelledError:
121
134
  logger.warning("[Modal Flash] Shutting down...")
122
135
  break
123
136
  except Exception as e:
124
137
  logger.error(f"[Modal Flash] Heartbeat failed: {e}")
125
- self.num_failures += 1
126
- logger.error(
127
- f"[Modal Flash] Deregistering container {self.tunnel.url}, num_failures: {self.num_failures}"
128
- )
129
- await retry_transient_errors(
130
- self.client.stub.FlashContainerDeregister,
131
- api_pb2.FlashContainerDeregisterRequest(),
132
- )
133
138
 
134
139
  try:
135
140
  await asyncio.sleep(1)
@@ -167,7 +172,9 @@ FlashManager = synchronize_api(_FlashManager)
167
172
 
168
173
  @synchronizer.create_blocking
169
174
  async def flash_forward(
170
- port: int, process: Optional[subprocess.Popen] = None, health_check_url: Optional[str] = None
175
+ port: int,
176
+ process: Optional[subprocess.Popen] = None,
177
+ health_check_url: Optional[str] = None,
171
178
  ) -> _FlashManager:
172
179
  """
173
180
  Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
@@ -15,7 +15,9 @@ class _FlashManager:
15
15
  """Initialize self. See help(type(self)) for accurate signature."""
16
16
  ...
17
17
 
18
- async def check_port_connection(self, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
18
+ async def is_port_connection_healthy(
19
+ self, process: typing.Optional[subprocess.Popen], timeout: int = 5
20
+ ) -> tuple[bool, typing.Optional[Exception]]: ...
19
21
  async def _start(self): ...
20
22
  async def _drain_container(self):
21
23
  """Background task that checks if we've encountered too many failures and drains the container if so."""
@@ -37,11 +39,15 @@ class FlashManager:
37
39
  health_check_url: typing.Optional[str] = None,
38
40
  ): ...
39
41
 
40
- class __check_port_connection_spec(typing_extensions.Protocol[SUPERSELF]):
41
- def __call__(self, /, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
42
- async def aio(self, /, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
42
+ class __is_port_connection_healthy_spec(typing_extensions.Protocol[SUPERSELF]):
43
+ def __call__(
44
+ self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
45
+ ) -> tuple[bool, typing.Optional[Exception]]: ...
46
+ async def aio(
47
+ self, /, process: typing.Optional[subprocess.Popen], timeout: int = 5
48
+ ) -> tuple[bool, typing.Optional[Exception]]: ...
43
49
 
44
- check_port_connection: __check_port_connection_spec[typing_extensions.Self]
50
+ is_port_connection_healthy: __is_port_connection_healthy_spec[typing_extensions.Self]
45
51
 
46
52
  class ___start_spec(typing_extensions.Protocol[SUPERSELF]):
47
53
  def __call__(self, /): ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.5.dev2
3
+ Version: 1.1.5.dev3
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.dev2"
4
+ __version__ = "1.1.5.dev3"
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