modal 1.1.4.dev16__tar.gz → 1.1.4.dev18__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.4.dev16 → modal-1.1.4.dev18}/PKG-INFO +1 -1
  2. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_functions.py +8 -0
  3. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/client.pyi +2 -2
  4. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cls.py +6 -1
  5. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cls.pyi +16 -0
  6. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/experimental/flash.py +33 -12
  7. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/functions.pyi +17 -6
  8. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/PKG-INFO +1 -1
  9. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_version/__init__.py +1 -1
  10. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/LICENSE +0 -0
  11. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/README.md +0 -0
  12. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/__init__.py +0 -0
  13. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/__main__.py +0 -0
  14. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_clustered_functions.py +0 -0
  15. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_clustered_functions.pyi +0 -0
  16. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_container_entrypoint.py +0 -0
  17. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_ipython.py +0 -0
  18. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_location.py +0 -0
  19. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_object.py +0 -0
  20. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_output.py +0 -0
  21. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_partial_function.py +0 -0
  22. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_pty.py +0 -0
  23. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_resolver.py +0 -0
  24. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_resources.py +0 -0
  25. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/__init__.py +0 -0
  26. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/asgi.py +0 -0
  27. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/container_io_manager.py +0 -0
  28. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/container_io_manager.pyi +0 -0
  29. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/execution_context.py +0 -0
  30. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/execution_context.pyi +0 -0
  31. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  32. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/telemetry.py +0 -0
  33. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_runtime/user_code_imports.py +0 -0
  34. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_serialization.py +0 -0
  35. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_traceback.py +0 -0
  36. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_tunnel.py +0 -0
  37. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_tunnel.pyi +0 -0
  38. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_type_manager.py +0 -0
  39. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/__init__.py +0 -0
  40. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/app_utils.py +0 -0
  41. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/async_utils.py +0 -0
  42. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/auth_token_manager.py +0 -0
  43. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/blob_utils.py +0 -0
  44. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/bytes_io_segment_payload.py +0 -0
  45. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/deprecation.py +0 -0
  46. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/docker_utils.py +0 -0
  47. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/function_utils.py +0 -0
  48. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/git_utils.py +0 -0
  49. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/grpc_testing.py +0 -0
  50. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/grpc_utils.py +0 -0
  51. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/hash_utils.py +0 -0
  52. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/http_utils.py +0 -0
  53. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/jwt_utils.py +0 -0
  54. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/logger.py +0 -0
  55. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/mount_utils.py +0 -0
  56. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/name_utils.py +0 -0
  57. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/package_utils.py +0 -0
  58. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/pattern_utils.py +0 -0
  59. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/rand_pb_testing.py +0 -0
  60. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/shell_utils.py +0 -0
  61. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_utils/time_utils.py +0 -0
  62. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_vendor/__init__.py +0 -0
  63. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  64. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_vendor/cloudpickle.py +0 -0
  65. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_vendor/tblib.py +0 -0
  66. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/_watcher.py +0 -0
  67. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/app.py +0 -0
  68. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/app.pyi +0 -0
  69. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/2023.12.312.txt +0 -0
  70. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/2023.12.txt +0 -0
  71. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/2024.04.txt +0 -0
  72. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/2024.10.txt +0 -0
  73. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/2025.06.txt +0 -0
  74. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/PREVIEW.txt +0 -0
  75. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/README.md +0 -0
  76. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/builder/base-images.json +0 -0
  77. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/call_graph.py +0 -0
  78. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/__init__.py +0 -0
  79. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/_download.py +0 -0
  80. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/_traceback.py +0 -0
  81. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/app.py +0 -0
  82. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/cluster.py +0 -0
  83. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/config.py +0 -0
  84. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/container.py +0 -0
  85. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/dict.py +0 -0
  86. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/entry_point.py +0 -0
  87. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/environment.py +0 -0
  88. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/import_refs.py +0 -0
  89. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/launch.py +0 -0
  90. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/network_file_system.py +0 -0
  91. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/profile.py +0 -0
  92. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/programs/__init__.py +0 -0
  93. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/programs/launch_instance_ssh.py +0 -0
  94. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/programs/run_jupyter.py +0 -0
  95. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/programs/run_marimo.py +0 -0
  96. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/programs/vscode.py +0 -0
  97. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/queues.py +0 -0
  98. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/run.py +0 -0
  99. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/secret.py +0 -0
  100. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/token.py +0 -0
  101. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/utils.py +0 -0
  102. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cli/volume.py +0 -0
  103. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/client.py +0 -0
  104. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cloud_bucket_mount.py +0 -0
  105. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/cloud_bucket_mount.pyi +0 -0
  106. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/config.py +0 -0
  107. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/container_process.py +0 -0
  108. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/container_process.pyi +0 -0
  109. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/dict.py +0 -0
  110. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/dict.pyi +0 -0
  111. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/environments.py +0 -0
  112. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/environments.pyi +0 -0
  113. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/exception.py +0 -0
  114. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/experimental/__init__.py +0 -0
  115. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/experimental/flash.pyi +0 -0
  116. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/experimental/ipython.py +0 -0
  117. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/file_io.py +0 -0
  118. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/file_io.pyi +0 -0
  119. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/file_pattern_matcher.py +0 -0
  120. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/functions.py +0 -0
  121. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/gpu.py +0 -0
  122. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/image.py +0 -0
  123. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/image.pyi +0 -0
  124. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/io_streams.py +0 -0
  125. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/io_streams.pyi +0 -0
  126. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/mount.py +0 -0
  127. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/mount.pyi +0 -0
  128. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/network_file_system.py +0 -0
  129. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/network_file_system.pyi +0 -0
  130. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/object.py +0 -0
  131. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/object.pyi +0 -0
  132. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/output.py +0 -0
  133. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/parallel_map.py +0 -0
  134. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/parallel_map.pyi +0 -0
  135. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/partial_function.py +0 -0
  136. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/partial_function.pyi +0 -0
  137. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/proxy.py +0 -0
  138. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/proxy.pyi +0 -0
  139. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/py.typed +0 -0
  140. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/queue.py +0 -0
  141. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/queue.pyi +0 -0
  142. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/retries.py +0 -0
  143. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/runner.py +0 -0
  144. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/runner.pyi +0 -0
  145. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/running_app.py +0 -0
  146. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/sandbox.py +0 -0
  147. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/sandbox.pyi +0 -0
  148. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/schedule.py +0 -0
  149. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/scheduler_placement.py +0 -0
  150. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/secret.py +0 -0
  151. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/secret.pyi +0 -0
  152. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/serving.py +0 -0
  153. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/serving.pyi +0 -0
  154. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/snapshot.py +0 -0
  155. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/snapshot.pyi +0 -0
  156. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/stream_type.py +0 -0
  157. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/token_flow.py +0 -0
  158. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/token_flow.pyi +0 -0
  159. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/volume.py +0 -0
  160. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal/volume.pyi +0 -0
  161. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/SOURCES.txt +0 -0
  162. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/dependency_links.txt +0 -0
  163. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/entry_points.txt +0 -0
  164. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/requires.txt +0 -0
  165. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal.egg-info/top_level.txt +0 -0
  166. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/__init__.py +0 -0
  167. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/gen_cli_docs.py +0 -0
  168. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/gen_reference_docs.py +0 -0
  169. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/mdmd/__init__.py +0 -0
  170. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/mdmd/mdmd.py +0 -0
  171. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_docs/mdmd/signatures.py +0 -0
  172. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/__init__.py +0 -0
  173. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api.proto +0 -0
  174. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api_grpc.py +0 -0
  175. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api_pb2.py +0 -0
  176. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api_pb2.pyi +0 -0
  177. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api_pb2_grpc.py +0 -0
  178. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/api_pb2_grpc.pyi +0 -0
  179. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/modal_api_grpc.py +0 -0
  180. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/modal_options_grpc.py +0 -0
  181. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options.proto +0 -0
  182. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options_grpc.py +0 -0
  183. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options_pb2.py +0 -0
  184. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options_pb2.pyi +0 -0
  185. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options_pb2_grpc.py +0 -0
  186. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/options_pb2_grpc.pyi +0 -0
  187. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_proto/py.typed +0 -0
  188. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/modal_version/__main__.py +0 -0
  189. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/pyproject.toml +0 -0
  190. {modal-1.1.4.dev16 → modal-1.1.4.dev18}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.4.dev16
3
+ Version: 1.1.4.dev18
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1471,6 +1471,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1471
1471
  self._info = None
1472
1472
  self._serve_mounts = frozenset()
1473
1473
  self._metadata = None
1474
+ self._experimental_flash_urls = None
1474
1475
 
1475
1476
  def _hydrate_metadata(self, metadata: Optional[Message]):
1476
1477
  # Overridden concrete implementation of base class method
@@ -1498,6 +1499,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1498
1499
  self._max_object_size_bytes = (
1499
1500
  metadata.max_object_size_bytes if metadata.HasField("max_object_size_bytes") else MAX_OBJECT_SIZE_BYTES
1500
1501
  )
1502
+ self._experimental_flash_urls = metadata._experimental_flash_urls
1501
1503
 
1502
1504
  def _get_metadata(self):
1503
1505
  # Overridden concrete implementation of base class method
@@ -1515,6 +1517,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1515
1517
  input_plane_url=self._input_plane_url,
1516
1518
  input_plane_region=self._input_plane_region,
1517
1519
  max_object_size_bytes=self._max_object_size_bytes,
1520
+ _experimental_flash_urls=self._experimental_flash_urls,
1518
1521
  )
1519
1522
 
1520
1523
  def _check_no_web_url(self, fn_name: str):
@@ -1545,6 +1548,11 @@ Use the `Function.get_web_url()` method instead.
1545
1548
  """URL of a Function running as a web endpoint."""
1546
1549
  return self._web_url
1547
1550
 
1551
+ @live_method
1552
+ async def _experimental_get_flash_urls(self) -> Optional[list[str]]:
1553
+ """URL of the flash service for the function."""
1554
+ return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
1555
+
1548
1556
  @property
1549
1557
  async def is_generator(self) -> bool:
1550
1558
  """mdmd:hidden"""
@@ -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.4.dev16",
36
+ version: str = "1.1.4.dev18",
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.4.dev16",
167
+ version: str = "1.1.4.dev18",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -12,7 +12,7 @@ from grpclib import GRPCError, Status
12
12
  from modal_proto import api_pb2
13
13
 
14
14
  from ._functions import _Function, _parse_retries
15
- from ._object import _Object
15
+ from ._object import _Object, live_method
16
16
  from ._partial_function import (
17
17
  _find_callables_for_obj,
18
18
  _find_partial_methods_for_user_cls,
@@ -510,6 +510,11 @@ class _Cls(_Object, type_prefix="cs"):
510
510
  # returns method names for a *local* class only for now (used by cli)
511
511
  return self._method_partials.keys()
512
512
 
513
+ @live_method
514
+ async def _experimental_get_flash_urls(self) -> Optional[list[str]]:
515
+ """URL of the flash service for the class."""
516
+ return await self._get_class_service_function()._experimental_get_flash_urls()
517
+
513
518
  def _hydrate_metadata(self, metadata: Message):
514
519
  assert isinstance(metadata, api_pb2.ClassHandleMetadata)
515
520
  class_service_function = self._get_class_service_function()
@@ -354,6 +354,10 @@ class _Cls(modal._object._Object):
354
354
  def _get_name(self) -> str: ...
355
355
  def _get_class_service_function(self) -> modal._functions._Function: ...
356
356
  def _get_method_names(self) -> collections.abc.Collection[str]: ...
357
+ async def _experimental_get_flash_urls(self) -> typing.Optional[list[str]]:
358
+ """URL of the flash service for the class."""
359
+ ...
360
+
357
361
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
358
362
  @staticmethod
359
363
  def validate_construction_mechanism(user_cls):
@@ -520,6 +524,18 @@ class Cls(modal.object.Object):
520
524
  def _get_name(self) -> str: ...
521
525
  def _get_class_service_function(self) -> modal.functions.Function: ...
522
526
  def _get_method_names(self) -> collections.abc.Collection[str]: ...
527
+
528
+ class ___experimental_get_flash_urls_spec(typing_extensions.Protocol[SUPERSELF]):
529
+ def __call__(self, /) -> typing.Optional[list[str]]:
530
+ """URL of the flash service for the class."""
531
+ ...
532
+
533
+ async def aio(self, /) -> typing.Optional[list[str]]:
534
+ """URL of the flash service for the class."""
535
+ ...
536
+
537
+ _experimental_get_flash_urls: ___experimental_get_flash_urls_spec[typing_extensions.Self]
538
+
523
539
  def _hydrate_metadata(self, metadata: google.protobuf.message.Message): ...
524
540
  @staticmethod
525
541
  def validate_construction_mechanism(user_cls):
@@ -213,8 +213,8 @@ class _FlashPrometheusAutoscaler:
213
213
  )
214
214
 
215
215
  logger.warning(
216
- f"[Modal Flash] Scaling to {actual_target_containers} containers. Autoscaling decision "
217
- f"made in {time.time() - autoscaling_time} seconds."
216
+ f"[Modal Flash] Scaling to {actual_target_containers=} containers. "
217
+ f" Autoscaling decision made in {time.time() - autoscaling_time} seconds."
218
218
  )
219
219
 
220
220
  await self.autoscaling_decisions_dict.put(
@@ -223,9 +223,7 @@ class _FlashPrometheusAutoscaler:
223
223
  )
224
224
  await self.autoscaling_decisions_dict.put("current_replicas", actual_target_containers)
225
225
 
226
- await self.cls.update_autoscaler(
227
- min_containers=actual_target_containers,
228
- )
226
+ await self.cls.update_autoscaler(min_containers=actual_target_containers)
229
227
 
230
228
  if time.time() - autoscaling_time < self.autoscaling_interval_seconds:
231
229
  await asyncio.sleep(self.autoscaling_interval_seconds - (time.time() - autoscaling_time))
@@ -239,6 +237,8 @@ class _FlashPrometheusAutoscaler:
239
237
  await asyncio.sleep(self.autoscaling_interval_seconds)
240
238
 
241
239
  async def _compute_target_containers(self, current_replicas: int) -> int:
240
+ # current_replicas is the number of live containers + cold starting containers (not yet live)
241
+ # containers is the number of live containers that are registered in flash dns
242
242
  containers = await self._get_all_containers()
243
243
  if len(containers) > current_replicas:
244
244
  logger.info(
@@ -271,11 +271,17 @@ class _FlashPrometheusAutoscaler:
271
271
  sum_metric += container_metrics[target_metric][0].value
272
272
  containers_with_metrics += 1
273
273
 
274
+ # n_containers_missing_metric is the number of unhealthy containers + number of cold starting containers
274
275
  n_containers_missing_metric = current_replicas - containers_with_metrics
276
+ # n_containers_unhealthy is the number of live containers that are not emitting metrics i.e. unhealthy
277
+ n_containers_unhealthy = len(containers) - containers_with_metrics
278
+
279
+ # Scale up assuming that every unhealthy container is at 2x the target metric value.
280
+ scale_up_target_metric_value = (sum_metric + n_containers_unhealthy * target_metric_value) / (
281
+ (containers_with_metrics + n_containers_unhealthy) or 1
282
+ )
275
283
 
276
- # Scale up / down conservatively: Any container that is missing the metric is assumed to be at the minimum
277
- # value of the metric when scaling up and the maximum value of the metric when scaling down.
278
- scale_up_target_metric_value = sum_metric / (containers_with_metrics or 1)
284
+ # Scale down assuming that every container (including cold starting containers) are at the target metric value.
279
285
  scale_down_target_metric_value = (
280
286
  sum_metric + n_containers_missing_metric * target_metric_value
281
287
  ) / current_replicas
@@ -290,9 +296,14 @@ class _FlashPrometheusAutoscaler:
290
296
  desired_replicas = math.ceil(current_replicas * scale_down_ratio)
291
297
 
292
298
  logger.warning(
293
- f"[Modal Flash] Current replicas: {current_replicas}, target metric value: {target_metric_value}, "
294
- f"current sum of metric values: {sum_metric}, number of containers missing metric: "
295
- f"{n_containers_missing_metric}, scale up ratio: {scale_up_ratio}, scale down ratio: {scale_down_ratio}, "
299
+ f"[Modal Flash] Current replicas: {current_replicas}, "
300
+ f"target metric value: {target_metric_value}, "
301
+ f"current sum of metric values: {sum_metric}, "
302
+ f"number of containers with metrics: {containers_with_metrics}, "
303
+ f"number of containers unhealthy: {n_containers_unhealthy}, "
304
+ f"number of containers missing metric (includes unhealthy): {n_containers_missing_metric}, "
305
+ f"scale up ratio: {scale_up_ratio}, "
306
+ f"scale down ratio: {scale_down_ratio}, "
296
307
  f"desired replicas: {desired_replicas}"
297
308
  )
298
309
 
@@ -312,9 +323,19 @@ class _FlashPrometheusAutoscaler:
312
323
  logger.warning(f"[Modal Flash] Error getting metrics from {url}: {e}")
313
324
  return None
314
325
 
326
+ # Read body with timeout/error handling and parse Prometheus metrics
327
+ try:
328
+ text_body = await response.text()
329
+ except asyncio.TimeoutError:
330
+ logger.warning(f"[Modal Flash] Timeout reading metrics body from {url}")
331
+ return None
332
+ except Exception as e:
333
+ logger.warning(f"[Modal Flash] Error reading metrics body from {url}: {e}")
334
+ return None
335
+
315
336
  # Parse the text-based Prometheus metrics format
316
337
  metrics: dict[str, list[Sample]] = defaultdict(list)
317
- for family in text_string_to_metric_families(await response.text()):
338
+ for family in text_string_to_metric_families(text_body):
318
339
  for sample in family.samples:
319
340
  metrics[sample.name] += [sample]
320
341
 
@@ -359,6 +359,17 @@ class Function(
359
359
 
360
360
  get_web_url: __get_web_url_spec[typing_extensions.Self]
361
361
 
362
+ class ___experimental_get_flash_urls_spec(typing_extensions.Protocol[SUPERSELF]):
363
+ def __call__(self, /) -> typing.Optional[list[str]]:
364
+ """URL of the flash service for the function."""
365
+ ...
366
+
367
+ async def aio(self, /) -> typing.Optional[list[str]]:
368
+ """URL of the flash service for the function."""
369
+ ...
370
+
371
+ _experimental_get_flash_urls: ___experimental_get_flash_urls_spec[typing_extensions.Self]
372
+
362
373
  @property
363
374
  def is_generator(self) -> bool:
364
375
  """mdmd:hidden"""
@@ -433,7 +444,7 @@ class Function(
433
444
 
434
445
  _call_generator: ___call_generator_spec[typing_extensions.Self]
435
446
 
436
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
447
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
437
448
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
438
449
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
439
450
  ...
@@ -442,7 +453,7 @@ class Function(
442
453
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
443
454
  ...
444
455
 
445
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
456
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
446
457
 
447
458
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
448
459
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -469,7 +480,7 @@ class Function(
469
480
  """
470
481
  ...
471
482
 
472
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
483
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
473
484
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
474
485
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
475
486
 
@@ -493,7 +504,7 @@ class Function(
493
504
  ...
494
505
 
495
506
  _experimental_spawn: ___experimental_spawn_spec[
496
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
507
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
497
508
  ]
498
509
 
499
510
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -502,7 +513,7 @@ class Function(
502
513
 
503
514
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
504
515
 
505
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
516
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
506
517
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
507
518
  """Calls the function with the given arguments, without waiting for the results.
508
519
 
@@ -523,7 +534,7 @@ class Function(
523
534
  """
524
535
  ...
525
536
 
526
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
537
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
527
538
 
528
539
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
529
540
  """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.4.dev16
3
+ Version: 1.1.4.dev18
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.4.dev16"
4
+ __version__ = "1.1.4.dev18"
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