modAL 1.3.1.dev25__tar.gz → 1.3.1.dev27__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 (197) hide show
  1. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/PKG-INFO +1 -1
  2. modal-1.3.1.dev27/modal/_server.py +246 -0
  3. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/client.pyi +2 -2
  4. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/experimental/flash.py +2 -7
  5. modal-1.3.1.dev27/modal/server.py +5 -0
  6. modal-1.3.1.dev27/modal/server.pyi +147 -0
  7. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/PKG-INFO +1 -1
  8. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/SOURCES.txt +3 -0
  9. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api.proto +3 -0
  10. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api_pb2.py +668 -668
  11. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api_pb2.pyi +10 -2
  12. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_version/__init__.py +1 -1
  13. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/LICENSE +0 -0
  14. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/README.md +0 -0
  15. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/__init__.py +0 -0
  16. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/__main__.py +0 -0
  17. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_billing.py +0 -0
  18. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_clustered_functions.py +0 -0
  19. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_clustered_functions.pyi +0 -0
  20. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_container_entrypoint.py +0 -0
  21. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_functions.py +0 -0
  22. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_grpc_client.py +0 -0
  23. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_ipython.py +0 -0
  24. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_load_context.py +0 -0
  25. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_location.py +0 -0
  26. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_object.py +0 -0
  27. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_output.py +0 -0
  28. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_partial_function.py +0 -0
  29. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_pty.py +0 -0
  30. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_resolver.py +0 -0
  31. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_resources.py +0 -0
  32. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/__init__.py +0 -0
  33. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/asgi.py +0 -0
  34. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/container_io_manager.py +0 -0
  35. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/container_io_manager.pyi +0 -0
  36. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/execution_context.py +0 -0
  37. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/execution_context.pyi +0 -0
  38. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  39. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/telemetry.py +0 -0
  40. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/user_code_event_loop.py +0 -0
  41. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_runtime/user_code_imports.py +0 -0
  42. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_serialization.py +0 -0
  43. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_traceback.py +0 -0
  44. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_tunnel.py +0 -0
  45. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_tunnel.pyi +0 -0
  46. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_type_manager.py +0 -0
  47. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/__init__.py +0 -0
  48. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/app_utils.py +0 -0
  49. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/async_utils.py +0 -0
  50. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/auth_token_manager.py +0 -0
  51. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/blob_utils.py +0 -0
  52. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/bytes_io_segment_payload.py +0 -0
  53. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/deprecation.py +0 -0
  54. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/docker_utils.py +0 -0
  55. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/function_utils.py +0 -0
  56. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/git_utils.py +0 -0
  57. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/grpc_testing.py +0 -0
  58. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/grpc_utils.py +0 -0
  59. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/hash_utils.py +0 -0
  60. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/http_utils.py +0 -0
  61. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/jwt_utils.py +0 -0
  62. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/logger.py +0 -0
  63. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/mount_utils.py +0 -0
  64. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/name_utils.py +0 -0
  65. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/package_utils.py +0 -0
  66. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/pattern_utils.py +0 -0
  67. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/rand_pb_testing.py +0 -0
  68. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/shell_utils.py +0 -0
  69. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/task_command_router_client.py +0 -0
  70. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_utils/time_utils.py +0 -0
  71. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_vendor/__init__.py +0 -0
  72. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  73. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_vendor/cloudpickle.py +0 -0
  74. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_vendor/tblib.py +0 -0
  75. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/_watcher.py +0 -0
  76. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/app.py +0 -0
  77. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/app.pyi +0 -0
  78. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/billing.py +0 -0
  79. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/2023.12.312.txt +0 -0
  80. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/2023.12.txt +0 -0
  81. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/2024.04.txt +0 -0
  82. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/2024.10.txt +0 -0
  83. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/2025.06.txt +0 -0
  84. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/PREVIEW.txt +0 -0
  85. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/README.md +0 -0
  86. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/builder/base-images.json +0 -0
  87. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/call_graph.py +0 -0
  88. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/__init__.py +0 -0
  89. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/_download.py +0 -0
  90. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/_traceback.py +0 -0
  91. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/app.py +0 -0
  92. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/cluster.py +0 -0
  93. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/config.py +0 -0
  94. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/container.py +0 -0
  95. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/dict.py +0 -0
  96. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/entry_point.py +0 -0
  97. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/environment.py +0 -0
  98. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/import_refs.py +0 -0
  99. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/launch.py +0 -0
  100. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/network_file_system.py +0 -0
  101. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/profile.py +0 -0
  102. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/programs/__init__.py +0 -0
  103. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/programs/run_jupyter.py +0 -0
  104. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/programs/vscode.py +0 -0
  105. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/queues.py +0 -0
  106. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/run.py +0 -0
  107. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/secret.py +0 -0
  108. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/shell.py +0 -0
  109. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/token.py +0 -0
  110. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/utils.py +0 -0
  111. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cli/volume.py +0 -0
  112. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/client.py +0 -0
  113. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cloud_bucket_mount.py +0 -0
  114. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cloud_bucket_mount.pyi +0 -0
  115. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cls.py +0 -0
  116. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/cls.pyi +0 -0
  117. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/config.py +0 -0
  118. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/container_process.py +0 -0
  119. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/container_process.pyi +0 -0
  120. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/dict.py +0 -0
  121. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/dict.pyi +0 -0
  122. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/environments.py +0 -0
  123. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/environments.pyi +0 -0
  124. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/exception.py +0 -0
  125. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/experimental/__init__.py +0 -0
  126. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/experimental/flash.pyi +0 -0
  127. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/experimental/ipython.py +0 -0
  128. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/file_io.py +0 -0
  129. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/file_io.pyi +0 -0
  130. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/file_pattern_matcher.py +0 -0
  131. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/functions.py +0 -0
  132. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/functions.pyi +0 -0
  133. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/gpu.py +0 -0
  134. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/image.py +0 -0
  135. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/image.pyi +0 -0
  136. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/io_streams.py +0 -0
  137. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/io_streams.pyi +0 -0
  138. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/mount.py +0 -0
  139. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/mount.pyi +0 -0
  140. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/network_file_system.py +0 -0
  141. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/network_file_system.pyi +0 -0
  142. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/object.py +0 -0
  143. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/object.pyi +0 -0
  144. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/output.py +0 -0
  145. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/parallel_map.py +0 -0
  146. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/parallel_map.pyi +0 -0
  147. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/partial_function.py +0 -0
  148. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/partial_function.pyi +0 -0
  149. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/proxy.py +0 -0
  150. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/proxy.pyi +0 -0
  151. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/py.typed +0 -0
  152. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/queue.py +0 -0
  153. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/queue.pyi +0 -0
  154. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/retries.py +0 -0
  155. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/runner.py +0 -0
  156. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/runner.pyi +0 -0
  157. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/running_app.py +0 -0
  158. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/sandbox.py +0 -0
  159. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/sandbox.pyi +0 -0
  160. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/schedule.py +0 -0
  161. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/scheduler_placement.py +0 -0
  162. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/secret.py +0 -0
  163. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/secret.pyi +0 -0
  164. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/serving.py +0 -0
  165. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/serving.pyi +0 -0
  166. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/snapshot.py +0 -0
  167. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/snapshot.pyi +0 -0
  168. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/stream_type.py +0 -0
  169. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/token_flow.py +0 -0
  170. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/token_flow.pyi +0 -0
  171. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/volume.py +0 -0
  172. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal/volume.pyi +0 -0
  173. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/dependency_links.txt +0 -0
  174. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/entry_points.txt +0 -0
  175. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/requires.txt +0 -0
  176. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal.egg-info/top_level.txt +0 -0
  177. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/__init__.py +0 -0
  178. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/gen_cli_docs.py +0 -0
  179. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/gen_reference_docs.py +0 -0
  180. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/mdmd/__init__.py +0 -0
  181. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/mdmd/mdmd.py +0 -0
  182. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_docs/mdmd/signatures.py +0 -0
  183. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/__init__.py +0 -0
  184. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api_grpc.py +0 -0
  185. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api_pb2_grpc.py +0 -0
  186. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/api_pb2_grpc.pyi +0 -0
  187. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/modal_api_grpc.py +0 -0
  188. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/py.typed +0 -0
  189. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router.proto +0 -0
  190. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router_grpc.py +0 -0
  191. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router_pb2.py +0 -0
  192. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router_pb2.pyi +0 -0
  193. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  194. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  195. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/modal_version/__main__.py +0 -0
  196. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/pyproject.toml +0 -0
  197. {modal-1.3.1.dev25 → modal-1.3.1.dev27}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev25
3
+ Version: 1.3.1.dev27
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -0,0 +1,246 @@
1
+ # Copyright Modal Labs 2025
2
+ import inspect
3
+ import typing
4
+ from typing import Optional
5
+
6
+ from ._functions import _Function
7
+ from ._load_context import LoadContext
8
+ from ._object import live_method
9
+ from ._partial_function import (
10
+ _find_partial_methods_for_user_cls,
11
+ _PartialFunction,
12
+ _PartialFunctionFlags,
13
+ )
14
+ from .client import _Client
15
+ from .cls import is_parameter
16
+ from .exception import InvalidError
17
+
18
+ if typing.TYPE_CHECKING:
19
+ import modal.app
20
+
21
+
22
+ def validate_http_server_config(
23
+ port: int,
24
+ proxy_regions: list[str], # The regions to proxy the HTTP server to.
25
+ startup_timeout: int, # Maximum number of seconds to wait for the HTTP server to start.
26
+ exit_grace_period: Optional[int], # The time to wait for the HTTP server to exit gracefully.
27
+ ):
28
+ if not isinstance(port, int) or port < 1 or port > 65535:
29
+ raise InvalidError("Port must be a positive integer between 1 and 65535.")
30
+ if startup_timeout <= 0:
31
+ raise InvalidError("The `startup_timeout` argument must be positive.")
32
+ if exit_grace_period is not None and exit_grace_period < 0:
33
+ raise InvalidError("The `exit_grace_period` argument must be non-negative.")
34
+ if not proxy_regions:
35
+ raise InvalidError("The `proxy_regions` argument must be non-empty.")
36
+
37
+
38
+ class _Server:
39
+ """Server runs an HTTP server started in an `@modal.enter` method.
40
+
41
+ See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
42
+
43
+ Generally, you will not construct a Server directly.
44
+ Instead, use the [`@app.server()`](https://modal.com/docs/reference/modal.App#server) decorator.
45
+
46
+ ```python notest
47
+ @app.server(port=8000, proxy_regions=["us-east", "us-west"])
48
+ class MyServer:
49
+ @modal.enter()
50
+ def start_server(self):
51
+ self.process = subprocess.Popen(["python3", "-m", "http.server", "8080"])
52
+ ```
53
+ """
54
+
55
+ _user_cls: Optional[type] = None # None if remote
56
+ _service_function: _Function
57
+ _app: Optional["modal.app._App"] = None # None if remote
58
+
59
+ def _get_user_cls(self) -> type:
60
+ assert self._user_cls is not None
61
+ return self._user_cls
62
+
63
+ def _get_app(self) -> "modal.app._App":
64
+ assert self._app
65
+ return self._app
66
+
67
+ def _get_service_function(self) -> _Function:
68
+ return self._service_function
69
+
70
+ @staticmethod
71
+ def _extract_user_cls(wrapped_user_cls: "type | _PartialFunction") -> type:
72
+ if isinstance(wrapped_user_cls, _PartialFunction):
73
+ assert wrapped_user_cls.user_cls
74
+ return wrapped_user_cls.user_cls
75
+ else:
76
+ return wrapped_user_cls
77
+
78
+ # ============ Live Methods ============
79
+
80
+ @live_method
81
+ async def get_urls(self) -> Optional[list[str]]:
82
+ return await self._get_service_function()._experimental_get_flash_urls()
83
+
84
+ @live_method
85
+ async def update_autoscaler(
86
+ self,
87
+ *,
88
+ min_containers: Optional[int] = None,
89
+ max_containers: Optional[int] = None,
90
+ buffer_containers: Optional[int] = None,
91
+ scaledown_window: Optional[int] = None,
92
+ ) -> None:
93
+ """Override the current autoscaler behavior for this Server.
94
+
95
+ Unspecified parameters will retain their current value.
96
+
97
+ Examples:
98
+ ```python notest
99
+ server = modal.Server.from_name("my-app", "Server")
100
+
101
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
102
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
103
+
104
+ # Limit this Server to avoid spinning up more than 5 containers
105
+ server.update_autoscaler(max_containers=5)
106
+ ```
107
+
108
+ """
109
+ return await self._get_service_function().update_autoscaler(
110
+ min_containers=min_containers,
111
+ max_containers=max_containers,
112
+ scaledown_window=scaledown_window,
113
+ buffer_containers=buffer_containers,
114
+ )
115
+
116
+ # ============ Hydration ============
117
+ async def hydrate(self, client: Optional[_Client] = None) -> "_Server":
118
+ # This is required since we want to support @livemethod() decorated methods
119
+ service_function = self._get_service_function()
120
+ await service_function.hydrate(client)
121
+ return self
122
+
123
+ # ============ Construction ============
124
+ @staticmethod
125
+ def _from_local(
126
+ wrapped_user_cls: "type | _PartialFunction",
127
+ app: "modal.app._App",
128
+ service_function: _Function,
129
+ ) -> "_Server":
130
+ """Create a Server from a local class definition."""
131
+
132
+ # Note: Validation should be done by the caller (app.server()) BEFORE creating the Server.
133
+ # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
134
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
135
+
136
+ server = _Server()
137
+ server._app = app
138
+ server._user_cls = user_cls
139
+ server._service_function = service_function
140
+ return server
141
+
142
+ @classmethod
143
+ def from_name(
144
+ cls: type["_Server"],
145
+ app_name: str,
146
+ name: str,
147
+ *,
148
+ environment_name: Optional[str] = None,
149
+ client: Optional[_Client] = None,
150
+ ) -> "_Server":
151
+ """Reference a Server from a deployed App by its name.
152
+
153
+ This is a lazy method that defers hydrating the local
154
+ object with metadata from Modal servers until the first
155
+ time it is actually used.
156
+
157
+ ```python notest
158
+ server = modal.Server.from_name("other-app", "Server")
159
+ ```
160
+ """
161
+
162
+ load_context_overrides = LoadContext(client=client, environment_name=environment_name)
163
+
164
+ server = _Server()
165
+ server._service_function = _Function._from_name(
166
+ app_name,
167
+ name,
168
+ load_context_overrides=load_context_overrides,
169
+ )
170
+ return server
171
+
172
+ def _is_local(self) -> bool:
173
+ """Returns True if this Server has local source code available."""
174
+ return self._user_cls is not None
175
+
176
+ # ============ Validation ============
177
+
178
+ @staticmethod
179
+ def _validate_wrapped_user_cls_decorators(
180
+ wrapped_user_cls: "type | _PartialFunction", enable_memory_snapshot: bool
181
+ ):
182
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
183
+
184
+ if not inspect.isclass(user_cls):
185
+ raise TypeError("The @app.server() decorator must be used on a class.")
186
+
187
+ # Check for modal.parameter() - not allowed on server classes
188
+ params = {k: v for k, v in user_cls.__dict__.items() if is_parameter(v)}
189
+ if params:
190
+ raise InvalidError(
191
+ f"Server class {user_cls.__name__} cannot use modal.parameter(). "
192
+ "Servers do not support parameterization."
193
+ )
194
+
195
+ if not _find_partial_methods_for_user_cls(
196
+ user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT
197
+ ) and not _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.ENTER_POST_SNAPSHOT):
198
+ raise InvalidError("Server class must have an @modal.enter() to setup the server.")
199
+
200
+ # Check for disallowed decorators
201
+ # @modal.method() not allowed
202
+ if _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.CALLABLE_INTERFACE).values():
203
+ raise InvalidError(
204
+ f"Server class {user_cls.__name__} cannot have `@modal.method()` decorated functions. "
205
+ "Servers only expose HTTP endpoints."
206
+ )
207
+ # @enter with snap=True without enable_memory_snapshot
208
+ if (
209
+ _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.ENTER_PRE_SNAPSHOT)
210
+ and not enable_memory_snapshot
211
+ ):
212
+ raise InvalidError(
213
+ "Server must have `enable_memory_snapshot=True` to use `snap=True` on `@modal.enter` methods."
214
+ )
215
+
216
+ if isinstance(wrapped_user_cls, _PartialFunction):
217
+ # @modal.concurrent not allowed on server classes
218
+ if wrapped_user_cls.flags & _PartialFunctionFlags.CONCURRENT:
219
+ raise InvalidError(
220
+ f"Server class {user_cls.__name__} cannot be decorated with `@modal.concurrent()`. "
221
+ "Please use `target_concurrency` param instead."
222
+ )
223
+ # @modal.http_server not allowed on server classes
224
+ if wrapped_user_cls.flags & _PartialFunctionFlags.HTTP_WEB_INTERFACE:
225
+ raise InvalidError(
226
+ f"Server class {user_cls.__name__} cannot have @modal.experimental.http_server() decorator. "
227
+ "Servers already expose HTTP endpoints."
228
+ )
229
+ # @modal.web_server not allowed on server classes
230
+ if wrapped_user_cls.flags & _PartialFunctionFlags.WEB_INTERFACE:
231
+ raise InvalidError(
232
+ f"Server class {user_cls.__name__} cannot be decorated with `@modal.web_server()`. "
233
+ "Servers already expose HTTP endpoints."
234
+ )
235
+
236
+ @staticmethod
237
+ def _validate_construction_mechanism(wrapped_user_cls: "type | _PartialFunction"):
238
+ """Validate that the server class doesn't have a custom constructor."""
239
+ # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
240
+ user_cls = _Server._extract_user_cls(wrapped_user_cls)
241
+
242
+ if user_cls.__init__ != object.__init__: # type: ignore
243
+ raise InvalidError(
244
+ f"Server class {user_cls.__name__} cannot have a custom __init__ method. "
245
+ "Use @modal.enter() for initialization logic instead."
246
+ )
@@ -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.3.1.dev25",
36
+ version: str = "1.3.1.dev27",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  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.3.1.dev25",
166
+ version: str = "1.3.1.dev27",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -15,6 +15,7 @@ from modal.cls import _Cls
15
15
  from modal.dict import _Dict
16
16
  from modal_proto import api_pb2
17
17
 
18
+ from .._server import validate_http_server_config
18
19
  from .._tunnel import _forward as _forward_tunnel
19
20
  from .._utils.async_utils import synchronize_api, synchronizer
20
21
  from ..client import _Client
@@ -142,7 +143,6 @@ class _FlashManager:
142
143
  )
143
144
  self.num_failures += 1
144
145
  await self.client.stub.FlashContainerDeregister(api_pb2.FlashContainerDeregisterRequest())
145
-
146
146
  except asyncio.CancelledError:
147
147
  logger.warning("[Modal Flash] Shutting down...")
148
148
  break
@@ -670,12 +670,7 @@ def _http_server(
670
670
  raise InvalidError(
671
671
  "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.http_server()`."
672
672
  )
673
- if not isinstance(port, int) or port < 1 or port > 65535:
674
- raise InvalidError("First argument of `@http_server` must be a local port, such as `@http_server(8000)`.")
675
- if startup_timeout <= 0:
676
- raise InvalidError("The `startup_timeout` argument of `@http_server` must be positive.")
677
- if exit_grace_period is not None and exit_grace_period < 0:
678
- raise InvalidError("The `exit_grace_period` argument of `@http_server` must be non-negative.")
673
+ validate_http_server_config(port, proxy_regions, startup_timeout, exit_grace_period)
679
674
 
680
675
  from modal._partial_function import _PartialFunction, _PartialFunctionParams
681
676
 
@@ -0,0 +1,5 @@
1
+ # Copyright Modal Labs 2026
2
+ from ._server import _Server
3
+ from ._utils.async_utils import synchronize_api
4
+
5
+ Server = synchronize_api(_Server, target_module=__name__)
@@ -0,0 +1,147 @@
1
+ import modal.app
2
+ import modal.client
3
+ import modal.functions
4
+ import modal.partial_function
5
+ import typing
6
+ import typing_extensions
7
+
8
+ class Server:
9
+ """Server runs an HTTP server started in an `@modal.enter` method.
10
+
11
+ See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
12
+
13
+ Generally, you will not construct a Server directly.
14
+ Instead, use the [`@app.server()`](https://modal.com/docs/reference/modal.App#server) decorator.
15
+
16
+ ```python notest
17
+ @app.server(port=8000, proxy_regions=["us-east", "us-west"])
18
+ class MyServer:
19
+ @modal.enter()
20
+ def start_server(self):
21
+ self.process = subprocess.Popen(["python3", "-m", "http.server", "8080"])
22
+ ```
23
+ """
24
+
25
+ _user_cls: typing.Optional[type]
26
+ _service_function: modal.functions.Function
27
+ _app: typing.Optional[modal.app.App]
28
+
29
+ def __init__(self, /, *args, **kwargs):
30
+ """Initialize self. See help(type(self)) for accurate signature."""
31
+ ...
32
+
33
+ def _get_user_cls(self) -> type: ...
34
+ def _get_app(self) -> modal.app.App: ...
35
+ def _get_service_function(self) -> modal.functions.Function: ...
36
+ @staticmethod
37
+ def _extract_user_cls(wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction]) -> type: ...
38
+
39
+ class __get_urls_spec(typing_extensions.Protocol):
40
+ def __call__(self, /) -> typing.Optional[list[str]]: ...
41
+ async def aio(self, /) -> typing.Optional[list[str]]: ...
42
+
43
+ get_urls: __get_urls_spec
44
+
45
+ class __update_autoscaler_spec(typing_extensions.Protocol):
46
+ def __call__(
47
+ self,
48
+ /,
49
+ *,
50
+ min_containers: typing.Optional[int] = None,
51
+ max_containers: typing.Optional[int] = None,
52
+ buffer_containers: typing.Optional[int] = None,
53
+ scaledown_window: typing.Optional[int] = None,
54
+ ) -> None:
55
+ """Override the current autoscaler behavior for this Server.
56
+
57
+ Unspecified parameters will retain their current value.
58
+
59
+ Examples:
60
+ ```python notest
61
+ server = modal.Server.from_name("my-app", "Server")
62
+
63
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
64
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
65
+
66
+ # Limit this Server to avoid spinning up more than 5 containers
67
+ server.update_autoscaler(max_containers=5)
68
+ ```
69
+ """
70
+ ...
71
+
72
+ async def aio(
73
+ self,
74
+ /,
75
+ *,
76
+ min_containers: typing.Optional[int] = None,
77
+ max_containers: typing.Optional[int] = None,
78
+ buffer_containers: typing.Optional[int] = None,
79
+ scaledown_window: typing.Optional[int] = None,
80
+ ) -> None:
81
+ """Override the current autoscaler behavior for this Server.
82
+
83
+ Unspecified parameters will retain their current value.
84
+
85
+ Examples:
86
+ ```python notest
87
+ server = modal.Server.from_name("my-app", "Server")
88
+
89
+ # Always have at least 2 containers running, with an extra buffer of 2 containers
90
+ server.update_autoscaler(min_containers=2, buffer_containers=1)
91
+
92
+ # Limit this Server to avoid spinning up more than 5 containers
93
+ server.update_autoscaler(max_containers=5)
94
+ ```
95
+ """
96
+ ...
97
+
98
+ update_autoscaler: __update_autoscaler_spec
99
+
100
+ class __hydrate_spec(typing_extensions.Protocol):
101
+ def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> Server: ...
102
+ async def aio(self, /, client: typing.Optional[modal.client.Client] = None) -> Server: ...
103
+
104
+ hydrate: __hydrate_spec
105
+
106
+ @staticmethod
107
+ def _from_local(
108
+ wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction],
109
+ app: modal.app.App,
110
+ service_function: modal.functions.Function,
111
+ ) -> Server:
112
+ """Create a Server from a local class definition."""
113
+ ...
114
+
115
+ @classmethod
116
+ def from_name(
117
+ cls: type[Server],
118
+ app_name: str,
119
+ name: str,
120
+ *,
121
+ environment_name: typing.Optional[str] = None,
122
+ client: typing.Optional[modal.client.Client] = None,
123
+ ) -> Server:
124
+ """Reference a Server from a deployed App by its name.
125
+
126
+ This is a lazy method that defers hydrating the local
127
+ object with metadata from Modal servers until the first
128
+ time it is actually used.
129
+
130
+ ```python notest
131
+ server = modal.Server.from_name("other-app", "Server")
132
+ ```
133
+ """
134
+ ...
135
+
136
+ def _is_local(self) -> bool:
137
+ """Returns True if this Server has local source code available."""
138
+ ...
139
+
140
+ @staticmethod
141
+ def _validate_wrapped_user_cls_decorators(
142
+ wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction], enable_memory_snapshot: bool
143
+ ): ...
144
+ @staticmethod
145
+ def _validate_construction_mechanism(wrapped_user_cls: typing.Union[type, modal.partial_function.PartialFunction]):
146
+ """Validate that the server class doesn't have a custom constructor."""
147
+ ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev25
3
+ Version: 1.3.1.dev27
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -19,6 +19,7 @@ modal/_pty.py
19
19
  modal/_resolver.py
20
20
  modal/_resources.py
21
21
  modal/_serialization.py
22
+ modal/_server.py
22
23
  modal/_traceback.py
23
24
  modal/_tunnel.py
24
25
  modal/_tunnel.pyi
@@ -78,6 +79,8 @@ modal/schedule.py
78
79
  modal/scheduler_placement.py
79
80
  modal/secret.py
80
81
  modal/secret.pyi
82
+ modal/server.py
83
+ modal/server.pyi
81
84
  modal/serving.py
82
85
  modal/serving.pyi
83
86
  modal/snapshot.py
@@ -1561,6 +1561,7 @@ message Function {
1561
1561
 
1562
1562
  bool single_use_containers = 91; // When True, containers will shut down after handling a single input
1563
1563
 
1564
+ bool is_server = 92; // When True, the function is a server-side function.
1564
1565
  }
1565
1566
 
1566
1567
  message FunctionAsyncInvokeRequest {
@@ -1744,6 +1745,8 @@ message FunctionData {
1744
1745
  // Attribute on the module with the implementation, which may differ from function_name
1745
1746
  // when the user provided a custom name= for the Function inside the Application namespace
1746
1747
  string implementation_name = 40;
1748
+
1749
+ bool is_server = 41; // When True, the function is a server-side function.
1747
1750
  }
1748
1751
 
1749
1752
  message FunctionExtended {