modal 1.5.6.dev27__tar.gz → 1.5.6.dev29__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 (241) hide show
  1. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/PKG-INFO +1 -1
  2. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_partial_function.py +2 -58
  3. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_server.py +20 -5
  4. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_server.pyi +12 -0
  5. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/app.py +5 -21
  6. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/app.pyi +12 -12
  7. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/app.py +51 -16
  8. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/function.py +343 -8
  9. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/server.py +315 -6
  10. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/utils.py +4 -10
  11. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/client.pyi +2 -2
  12. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/partial_function.pyi +7 -19
  13. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/sandbox.py +64 -7
  14. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/sandbox.pyi +24 -3
  15. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/secret.py +5 -2
  16. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/secret.pyi +13 -2
  17. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/server.pyi +12 -0
  18. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/types.py +28 -1
  19. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/PKG-INFO +1 -1
  20. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/gen_reference_docs.py +10 -2
  21. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/mdmd/mdmd.py +82 -8
  22. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/api_pb2.py +1216 -1218
  23. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/api_pb2.pyi +6 -29
  24. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/task_command_router_pb2.py +96 -96
  25. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/task_command_router_pb2.pyi +11 -2
  26. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_version/__init__.py +1 -1
  27. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/LICENSE +0 -0
  28. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/README.md +0 -0
  29. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/__init__.py +0 -0
  30. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/__main__.py +0 -0
  31. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_billing.py +0 -0
  32. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_cluster.py +0 -0
  33. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_cluster.pyi +0 -0
  34. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_container_entrypoint.py +0 -0
  35. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_environments.py +0 -0
  36. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_function_variants.py +0 -0
  37. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_functions.py +0 -0
  38. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_grpc_client.py +0 -0
  39. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_image.py +0 -0
  40. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_ipython.py +0 -0
  41. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_load_context.py +0 -0
  42. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_location.py +0 -0
  43. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_logs.py +0 -0
  44. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_logs_manager.py +0 -0
  45. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_logs_manager.pyi +0 -0
  46. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_object.py +0 -0
  47. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_outbound_policy.py +0 -0
  48. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_outbound_policy.pyi +0 -0
  49. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_output/__init__.py +0 -0
  50. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_output/manager.py +0 -0
  51. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_output/pty.py +0 -0
  52. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_output/rich.py +0 -0
  53. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_output/status.py +0 -0
  54. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_resolver.py +0 -0
  55. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_resources.py +0 -0
  56. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/__init__.py +0 -0
  57. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/asgi.py +0 -0
  58. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/container_io_manager.py +0 -0
  59. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/container_io_manager.pyi +0 -0
  60. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/execution_context.py +0 -0
  61. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/execution_context.pyi +0 -0
  62. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  63. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/task_lifecycle_manager.py +0 -0
  64. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  65. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/telemetry.py +0 -0
  66. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/user_code_event_loop.py +0 -0
  67. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_runtime/user_code_imports.py +0 -0
  68. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_serialization.py +0 -0
  69. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_supports_logs.py +0 -0
  70. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_traceback.py +0 -0
  71. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_tunnel.py +0 -0
  72. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_tunnel.pyi +0 -0
  73. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_type_manager.py +0 -0
  74. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/__init__.py +0 -0
  75. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/app_utils.py +0 -0
  76. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/async_utils.py +0 -0
  77. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/auth_token_manager.py +0 -0
  78. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/blob_utils.py +0 -0
  79. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/browser_utils.py +0 -0
  80. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/bytes_io_segment_payload.py +0 -0
  81. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/curl_utils.py +0 -0
  82. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/deprecation.py +0 -0
  83. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/display_utils.py +0 -0
  84. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/docker_utils.py +0 -0
  85. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/function_utils.py +0 -0
  86. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/git_utils.py +0 -0
  87. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/grpc_testing.py +0 -0
  88. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/grpc_utils.py +0 -0
  89. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/hash_utils.py +0 -0
  90. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/http_utils.py +0 -0
  91. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/idle_countdown.py +0 -0
  92. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/jwt_utils.py +0 -0
  93. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/logger.py +0 -0
  94. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/mount_utils.py +0 -0
  95. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/name_utils.py +0 -0
  96. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/package_utils.py +0 -0
  97. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/pattern_utils.py +0 -0
  98. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/proxy_support.py +0 -0
  99. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/rand_pb_testing.py +0 -0
  100. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/sandbox_fs_utils.py +0 -0
  101. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/shell_utils.py +0 -0
  102. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/task_command_router_client.py +0 -0
  103. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_utils/time_utils.py +0 -0
  104. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_vendor/__init__.py +0 -0
  105. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  106. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_vendor/cloudpickle.py +0 -0
  107. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_vendor/tblib.py +0 -0
  108. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_vendor/version.py +0 -0
  109. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_watcher.py +0 -0
  110. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/_workspace.py +0 -0
  111. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/billing.py +0 -0
  112. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/billing.pyi +0 -0
  113. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/2023.12.312.txt +0 -0
  114. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/2023.12.txt +0 -0
  115. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/2024.04.txt +0 -0
  116. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/2024.10.txt +0 -0
  117. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/2025.06.txt +0 -0
  118. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/PREVIEW.txt +0 -0
  119. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/README.md +0 -0
  120. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/builder/base-images.json +0 -0
  121. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/call_graph.py +0 -0
  122. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/__init__.py +0 -0
  123. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/_download.py +0 -0
  124. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/_help.py +0 -0
  125. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/_logs.py +0 -0
  126. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/_stats.py +0 -0
  127. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/_traceback.py +0 -0
  128. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/billing.py +0 -0
  129. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/changelog.py +0 -0
  130. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/config.py +0 -0
  131. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/container.py +0 -0
  132. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/curl.py +0 -0
  133. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/dashboard.py +0 -0
  134. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/dict.py +0 -0
  135. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/endpoint.py +0 -0
  136. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/entry_point.py +0 -0
  137. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/environment.py +0 -0
  138. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/image.py +0 -0
  139. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/import_refs.py +0 -0
  140. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/logo.py +0 -0
  141. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/network_file_system.py +0 -0
  142. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/profile.py +0 -0
  143. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/queues.py +0 -0
  144. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/run.py +0 -0
  145. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/secret.py +0 -0
  146. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/selector.py +0 -0
  147. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/shell.py +0 -0
  148. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/skills.py +0 -0
  149. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/token.py +0 -0
  150. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/volume.py +0 -0
  151. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cli/workspace.py +0 -0
  152. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/client.py +0 -0
  153. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cloud_bucket_mount.py +0 -0
  154. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cloud_bucket_mount.pyi +0 -0
  155. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cls.py +0 -0
  156. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cls.pyi +0 -0
  157. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cluster.py +0 -0
  158. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/cluster.pyi +0 -0
  159. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/config.py +0 -0
  160. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/container_process.py +0 -0
  161. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/container_process.pyi +0 -0
  162. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/dict.py +0 -0
  163. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/dict.pyi +0 -0
  164. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/environments.py +0 -0
  165. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/environments.pyi +0 -0
  166. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/exception.py +0 -0
  167. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/__init__.py +0 -0
  168. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/_flash_container_entry.py +0 -0
  169. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/flash.py +0 -0
  170. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/flash.pyi +0 -0
  171. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/flash_server.py +0 -0
  172. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/flash_server.pyi +0 -0
  173. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/experimental/ipython.py +0 -0
  174. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/file_pattern_matcher.py +0 -0
  175. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/functions.py +0 -0
  176. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/functions.pyi +0 -0
  177. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/image.py +0 -0
  178. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/image.pyi +0 -0
  179. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/io_streams.py +0 -0
  180. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/io_streams.pyi +0 -0
  181. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/mount.py +0 -0
  182. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/mount.pyi +0 -0
  183. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/network_file_system.py +0 -0
  184. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/network_file_system.pyi +0 -0
  185. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/object.py +0 -0
  186. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/object.pyi +0 -0
  187. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/output.py +0 -0
  188. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/parallel_map.py +0 -0
  189. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/parallel_map.pyi +0 -0
  190. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/partial_function.py +0 -0
  191. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/proxy.py +0 -0
  192. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/proxy.pyi +0 -0
  193. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/py.typed +0 -0
  194. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/queue.py +0 -0
  195. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/queue.pyi +0 -0
  196. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/retries.py +0 -0
  197. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/runner.py +0 -0
  198. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/runner.pyi +0 -0
  199. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/running_app.py +0 -0
  200. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/sandbox_fs.py +0 -0
  201. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/sandbox_fs.pyi +0 -0
  202. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/schedule.py +0 -0
  203. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/scheduler_placement.py +0 -0
  204. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/server.py +0 -0
  205. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/serving.py +0 -0
  206. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/serving.pyi +0 -0
  207. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/skills/modal/SKILL.md +0 -0
  208. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/snapshot.py +0 -0
  209. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/snapshot.pyi +0 -0
  210. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/stream_type.py +0 -0
  211. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/token_flow.py +0 -0
  212. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/token_flow.pyi +0 -0
  213. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/volume.py +0 -0
  214. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/volume.pyi +0 -0
  215. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/workspace.py +0 -0
  216. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal/workspace.pyi +0 -0
  217. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/SOURCES.txt +0 -0
  218. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/dependency_links.txt +0 -0
  219. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/entry_points.txt +0 -0
  220. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/requires.txt +0 -0
  221. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal.egg-info/top_level.txt +0 -0
  222. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/__init__.py +0 -0
  223. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/gen_cli_docs.py +0 -0
  224. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/gen_cli_docs_main.py +0 -0
  225. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/gen_reference_docs_main.py +0 -0
  226. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/mdmd/__init__.py +0 -0
  227. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/mdmd/signatures.py +0 -0
  228. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/mdmd/types.py +0 -0
  229. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_docs/reference_intro.md +0 -0
  230. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/__init__.py +0 -0
  231. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/api_grpc.py +0 -0
  232. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/api_pb2_grpc.py +0 -0
  233. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/api_pb2_grpc.pyi +0 -0
  234. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/modal_api_grpc.py +0 -0
  235. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/py.typed +0 -0
  236. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/task_command_router_grpc.py +0 -0
  237. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  238. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  239. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/modal_version/__main__.py +0 -0
  240. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/pyproject.toml +0 -0
  241. {modal-1.5.6.dev27 → modal-1.5.6.dev29}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev27
3
+ Version: 1.5.6.dev29
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -282,7 +282,6 @@ class _MethodDecoratorType:
282
282
 
283
283
  # TODO(elias): fix support for coroutine type unwrapping for methods (static typing)
284
284
  def _method(
285
- _warn_parentheses_missing=None, # mdmd:line-hidden
286
285
  *,
287
286
  # Set this to True if it's a non-generator function returning
288
287
  # a [sync/async] generator object
@@ -300,10 +299,6 @@ def _method(
300
299
  ...
301
300
  ```
302
301
  """
303
- if _warn_parentheses_missing is not None:
304
- raise InvalidError(
305
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.method()`."
306
- )
307
302
 
308
303
  def wrapper(obj: Callable[..., Any] | _PartialFunction) -> _PartialFunction:
309
304
  flags = _PartialFunctionFlags.CALLABLE_INTERFACE
@@ -336,7 +331,6 @@ def _parse_custom_domains(custom_domains: Iterable[str] | None = None) -> list[a
336
331
 
337
332
 
338
333
  def _fastapi_endpoint(
339
- _warn_parentheses_missing=None, # mdmd:line-hidden
340
334
  *,
341
335
  method: str = "GET", # REST method for the created endpoint.
342
336
  label: str | None = None, # Label for created endpoint. Final subdomain will be <workspace>--<label>.modal.run.
@@ -362,15 +356,6 @@ def _fastapi_endpoint(
362
356
 
363
357
  *Added in v0.73.82*: This function replaces the deprecated `@web_endpoint` decorator.
364
358
  """
365
- if isinstance(_warn_parentheses_missing, str):
366
- # Probably passing the method string as a positional argument.
367
- raise InvalidError(
368
- f'Positional arguments are not allowed. Suggestion: `@modal.fastapi_endpoint(method="{method}")`.'
369
- )
370
- elif _warn_parentheses_missing is not None:
371
- raise InvalidError(
372
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.fastapi_endpoint()`."
373
- )
374
359
 
375
360
  webhook_config = api_pb2.WebhookConfig(
376
361
  type=api_pb2.WEBHOOK_TYPE_FUNCTION,
@@ -400,7 +385,6 @@ def _fastapi_endpoint(
400
385
 
401
386
 
402
387
  def _web_endpoint(
403
- _warn_parentheses_missing=None, # mdmd:line-hidden
404
388
  *args,
405
389
  **kwargs,
406
390
  ) -> None:
@@ -413,7 +397,6 @@ def _web_endpoint(
413
397
 
414
398
 
415
399
  def _asgi_app(
416
- _warn_parentheses_missing=None, # mdmd:line-hidden
417
400
  *,
418
401
  label: str | None = None, # Label for created endpoint. Final subdomain will be <workspace>--<label>.modal.run.
419
402
  custom_domains: Iterable[str] | None = None, # Deploy this endpoint on a custom domain.
@@ -437,12 +420,6 @@ def _asgi_app(
437
420
  To learn how to use Modal with popular web frameworks, see the
438
421
  [guide on Web Functions](https://modal.com/docs/guide/webhooks).
439
422
  """
440
- if isinstance(_warn_parentheses_missing, str):
441
- raise InvalidError(f'Positional arguments are not allowed. Suggestion: `@modal.asgi_app(label="{label}")`.')
442
- elif _warn_parentheses_missing is not None:
443
- raise InvalidError(
444
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.asgi_app()`."
445
- )
446
423
 
447
424
  webhook_config = api_pb2.WebhookConfig(
448
425
  type=api_pb2.WEBHOOK_TYPE_ASGI_APP,
@@ -468,7 +445,6 @@ def _asgi_app(
468
445
 
469
446
 
470
447
  def _wsgi_app(
471
- _warn_parentheses_missing=None, # mdmd:line-hidden
472
448
  *,
473
449
  label: str | None = None, # Label for created endpoint. Final subdomain will be <workspace>--<label>.modal.run.
474
450
  custom_domains: Iterable[str] | None = None, # Deploy this endpoint on a custom domain.
@@ -494,12 +470,6 @@ def _wsgi_app(
494
470
  To learn how to use this decorator with popular web frameworks, see the
495
471
  [guide on Web Functions](https://modal.com/docs/guide/webhooks).
496
472
  """
497
- if isinstance(_warn_parentheses_missing, str):
498
- raise InvalidError(f'Positional arguments are not allowed. Suggestion: `@modal.wsgi_app(label="{label}")`.')
499
- elif _warn_parentheses_missing is not None:
500
- raise InvalidError(
501
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.wsgi_app()`."
502
- )
503
473
 
504
474
  webhook_config = api_pb2.WebhookConfig(
505
475
  type=api_pb2.WEBHOOK_TYPE_WSGI_APP,
@@ -588,17 +558,12 @@ def _web_server(
588
558
 
589
559
 
590
560
  def _enter(
591
- _warn_parentheses_missing=None, # mdmd:line-hidden
592
561
  *,
593
562
  snap: bool = False,
594
563
  ) -> Callable[[_PartialFunction | NullaryMethod], _PartialFunction]:
595
564
  """Decorator for methods which should be executed when a new container is started.
596
565
 
597
566
  See the [lifeycle function guide](https://modal.com/docs/guide/lifecycle-functions#enter) for more information."""
598
- if _warn_parentheses_missing is not None:
599
- raise InvalidError(
600
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.enter()`."
601
- )
602
567
 
603
568
  flags = _PartialFunctionFlags.ENTER_PRE_SNAPSHOT if snap else _PartialFunctionFlags.ENTER_POST_SNAPSHOT
604
569
  params = _PartialFunctionParams()
@@ -615,14 +580,10 @@ def _enter(
615
580
  return wrapper
616
581
 
617
582
 
618
- def _exit(_warn_parentheses_missing=None) -> Callable[[NullaryMethod], _PartialFunction]:
583
+ def _exit() -> Callable[[NullaryMethod], _PartialFunction]:
619
584
  """Decorator for methods which should be executed when a container is about to exit.
620
585
 
621
586
  See the [lifeycle function guide](https://modal.com/docs/guide/lifecycle-functions#exit) for more information."""
622
- if _warn_parentheses_missing is not None:
623
- raise InvalidError(
624
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.exit()`."
625
- )
626
587
 
627
588
  flags = _PartialFunctionFlags.EXIT
628
589
  params = _PartialFunctionParams()
@@ -639,7 +600,6 @@ def _exit(_warn_parentheses_missing=None) -> Callable[[NullaryMethod], _PartialF
639
600
 
640
601
 
641
602
  def _batched(
642
- _warn_parentheses_missing=None, # mdmd:line-hidden
643
603
  *,
644
604
  max_batch_size: int,
645
605
  wait_ms: int,
@@ -670,10 +630,6 @@ def _batched(
670
630
 
671
631
  See the [dynamic batching guide](https://modal.com/docs/guide/dynamic-batching) for more information.
672
632
  """
673
- if _warn_parentheses_missing is not None:
674
- raise InvalidError(
675
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.batched()`."
676
- )
677
633
  if max_batch_size < 1:
678
634
  raise InvalidError("max_batch_size must be a positive integer.")
679
635
  if max_batch_size > MAX_MAX_BATCH_SIZE:
@@ -700,7 +656,6 @@ def _batched(
700
656
 
701
657
 
702
658
  def _concurrent(
703
- _warn_parentheses_missing=None, # mdmd:line-hidden
704
659
  *,
705
660
  max_inputs: int | None = None, # Hard limit on each container's input concurrency
706
661
  target_inputs: int | None = None, # Input concurrency that Modal's autoscaler should target
@@ -749,10 +704,6 @@ def _concurrent(
749
704
  in `@app.function()` and `@app.cls()`.
750
705
 
751
706
  """
752
- if _warn_parentheses_missing is not None:
753
- raise InvalidError(
754
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.concurrent()`."
755
- )
756
707
 
757
708
  if max_inputs is not None and target_inputs is not None and target_inputs > max_inputs:
758
709
  raise InvalidError("`target_inputs` parameter cannot be greater than `max_inputs`.")
@@ -777,9 +728,7 @@ def _concurrent(
777
728
  return wrapper
778
729
 
779
730
 
780
- def _sessioned(
781
- _warn_parentheses_missing=None, # mdmd:line-hidden
782
- ) -> Callable[
731
+ def _sessioned() -> Callable[
783
732
  [Callable[P, ReturnType] | _PartialFunction[P, ReturnType, ReturnType]],
784
733
  _PartialFunction[P, ReturnType, ReturnType],
785
734
  ]:
@@ -822,11 +771,6 @@ def _sessioned(
822
771
  server.sessions.terminate(session.token)
823
772
  ```
824
773
  """
825
- if _warn_parentheses_missing is not None:
826
- raise InvalidError(
827
- "Positional arguments are not allowed. Did you forget parentheses? Suggestion: `@modal.sessioned()`."
828
- )
829
-
830
774
  flags = _PartialFunctionFlags.SESSIONED
831
775
  params = _PartialFunctionParams()
832
776
 
@@ -4,6 +4,8 @@ import json
4
4
  import typing
5
5
  from datetime import datetime, timedelta, timezone
6
6
 
7
+ from typing_extensions import Self
8
+
7
9
  from modal_proto import api_pb2
8
10
 
9
11
  from ._functions import _Function
@@ -73,7 +75,7 @@ class _Server:
73
75
  _user_cls: type | None = None # None if remote
74
76
  _service_function: _Function
75
77
  _app: "modal.app._App | None" = None # None if remote
76
- _is_sessioned: bool | None = None # None if remote
78
+ _is_sessioned: bool | None = None # None until known (via local set or remote hydration)
77
79
 
78
80
  def _get_user_cls(self) -> type:
79
81
  assert self._user_cls is not None
@@ -248,8 +250,22 @@ class _Server:
248
250
  # This is required since we want to support @livemethod() decorated methods
249
251
  service_function = self._get_service_function()
250
252
  await service_function.hydrate(client)
253
+ if service_function._function_info is not None:
254
+ self._is_sessioned = service_function._function_info._sessioned
251
255
  return self
252
256
 
257
+ @classmethod
258
+ def _new_from_function(cls, object_id: str, client: _Client, metadata: api_pb2.FunctionHandleMetadata) -> Self:
259
+ """mdmd:hidden
260
+
261
+ Callers which already have handle metadata for a Server service function can use this method to
262
+ create a hydrated handle without having to do an unnecessary RPC.
263
+ """
264
+
265
+ obj = cls()
266
+ obj._service_function = _Function._new_hydrated(object_id, client, metadata)
267
+ return obj
268
+
253
269
  # ============ Construction ============
254
270
  @staticmethod
255
271
  def _from_local(
@@ -478,7 +494,7 @@ class _ServerSessionsManager:
478
494
  self._server = server
479
495
 
480
496
  def _validate(self) -> None:
481
- if self._server._is_local() and self._server._is_sessioned is False:
497
+ if self._server._is_sessioned is False:
482
498
  raise InvalidError("`sessions` requires `@modal.sessioned()` on the Server.")
483
499
 
484
500
  async def start(self, idle_timeout: int = 600) -> ServerSessionCredentials:
@@ -504,12 +520,12 @@ class _ServerSessionsManager:
504
520
  server.sessions.terminate(session.token)
505
521
  ```
506
522
  """
507
- self._validate()
508
523
  if not isinstance(idle_timeout, int) or idle_timeout <= 0:
509
524
  raise InvalidError("`idle_timeout` must be a positive integer.")
510
525
 
511
526
  fn = self._server._get_service_function()
512
527
  url = await self._server.get_url()
528
+ self._validate()
513
529
  assert url is not None, "Server has no URL."
514
530
 
515
531
  headers = {
@@ -545,10 +561,9 @@ class _ServerSessionsManager:
545
561
  server.sessions.terminate(session.token)
546
562
  ```
547
563
  """
548
- self._validate()
549
-
550
564
  fn = self._server._get_service_function()
551
565
  url = await self._server.get_url()
566
+ self._validate()
552
567
  assert url is not None, "Server has no URL."
553
568
 
554
569
  headers = {
@@ -7,6 +7,7 @@ import modal.app
7
7
  import modal.client
8
8
  import modal.server
9
9
  import modal.types
10
+ import modal_proto.api_pb2
10
11
  import typing
11
12
  import typing_extensions
12
13
 
@@ -165,6 +166,17 @@ class _Server:
165
166
  """
166
167
  ...
167
168
 
169
+ @classmethod
170
+ def _new_from_function(
171
+ cls, object_id: str, client: modal.client._Client, metadata: modal_proto.api_pb2.FunctionHandleMetadata
172
+ ) -> typing_extensions.Self:
173
+ """mdmd:hidden
174
+
175
+ Callers which already have handle metadata for a Server service function can use this method to
176
+ create a hydrated handle without having to do an unnecessary RPC.
177
+ """
178
+ ...
179
+
168
180
  @staticmethod
169
181
  def _from_local(
170
182
  wrapped_user_cls: typing.Union[type, modal._partial_function._PartialFunction],
@@ -727,9 +727,7 @@ class _App:
727
727
  """
728
728
  return self._local_state.web_endpoints
729
729
 
730
- def local_entrypoint(
731
- self, _warn_parentheses_missing: Any = None, *, name: str | None = None
732
- ) -> Callable[[Callable[..., Any]], _LocalEntrypoint]:
730
+ def local_entrypoint(self, *, name: str | None = None) -> Callable[[Callable[..., Any]], _LocalEntrypoint]:
733
731
  """Decorate a function to be used as a CLI entrypoint for a Modal App.
734
732
 
735
733
  These functions can be used to define code that runs locally to set up the app,
@@ -784,8 +782,6 @@ class _App:
784
782
  Currently, `str`, `int`, `float`, `bool`, and `datetime.datetime` are supported.
785
783
  Use `modal run app_module.py --help` for more information on usage.
786
784
  """
787
- if _warn_parentheses_missing:
788
- raise InvalidError("Did you forget parentheses? Suggestion: `@app.local_entrypoint()`.")
789
785
  if name is not None and not isinstance(name, str):
790
786
  raise InvalidError("Invalid value for `name`: Must be string.")
791
787
 
@@ -804,7 +800,6 @@ class _App:
804
800
  @handle_deprecated_parameters
805
801
  def function(
806
802
  self,
807
- _warn_parentheses_missing=None, # mdmd:line-hidden
808
803
  *,
809
804
  image: _Image | None = None,
810
805
  schedule: Schedule | None = None,
@@ -890,11 +885,6 @@ class _App:
890
885
  Returns:
891
886
  A decorator that registers the wrapped callable or partial as a Modal `Function`.
892
887
  """
893
- if isinstance(_warn_parentheses_missing, _Image):
894
- # Handle edge case where maybe (?) some users passed image as a positional arg
895
- raise InvalidError("`image` needs to be a keyword argument: `@app.function(image=image)`.")
896
- if _warn_parentheses_missing:
897
- raise InvalidError("Did you forget parentheses? Suggestion: `@app.function()`.")
898
888
 
899
889
  if image is None:
900
890
  image = self._get_default_image()
@@ -1063,7 +1053,6 @@ class _App:
1063
1053
  @handle_deprecated_parameters
1064
1054
  def cls(
1065
1055
  self,
1066
- _warn_parentheses_missing=None, # mdmd:line-hidden
1067
1056
  *,
1068
1057
  image: _Image | None = None,
1069
1058
  env: dict[str, str | None] | None = None,
@@ -1096,7 +1085,7 @@ class _App:
1096
1085
  experimental_options: dict[str, Any] | None = None,
1097
1086
  _experimental_restrict_output: bool = False,
1098
1087
  max_inputs: int | None = None,
1099
- ) -> Callable[[CLS_T | _PartialFunction], CLS_T]:
1088
+ ) -> Callable[[CLS_T | _PartialFunction[..., Any, Any]], CLS_T]:
1100
1089
  """
1101
1090
  Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/Cls) with this App.
1102
1091
 
@@ -1141,8 +1130,6 @@ class _App:
1141
1130
  Returns:
1142
1131
  A decorator that registers the wrapped class or partial as a Modal `Cls`.
1143
1132
  """
1144
- if _warn_parentheses_missing:
1145
- raise InvalidError("Did you forget parentheses? Suggestion: `@app.cls()`.")
1146
1133
 
1147
1134
  if max_inputs is not None:
1148
1135
  if not isinstance(max_inputs, int):
@@ -1161,7 +1148,7 @@ class _App:
1161
1148
  if env:
1162
1149
  secrets = [*secrets, _Secret.from_dict(env)]
1163
1150
 
1164
- def wrapper(wrapped_cls: CLS_T | _PartialFunction) -> CLS_T:
1151
+ def wrapper(wrapped_cls: CLS_T | _PartialFunction[..., Any, Any]) -> CLS_T:
1165
1152
  local_state = self._local_state
1166
1153
  # Check if the decorated object is a class
1167
1154
  http_config = None
@@ -1306,7 +1293,6 @@ class _App:
1306
1293
 
1307
1294
  def server(
1308
1295
  self,
1309
- _warn_parentheses_missing=None, # mdmd:line-hidden
1310
1296
  *,
1311
1297
  image: _Image | None = None, # The image to run as the container for the server
1312
1298
  env: dict[str, str | None] | None = None, # Environment variables to set in the container
@@ -1342,7 +1328,7 @@ class _App:
1342
1328
  include_source: bool | None = None, # Whether to add source to container
1343
1329
  # Experimental options
1344
1330
  experimental_options: dict[str, Any] | None = None,
1345
- ) -> Callable[[type[Any] | _PartialFunction], _Server]:
1331
+ ) -> Callable[[type[Any] | _PartialFunction[..., Any, Any]], _Server]:
1346
1332
  """
1347
1333
  Decorator to register a new Modal Server with this App.
1348
1334
 
@@ -1407,8 +1393,6 @@ class _App:
1407
1393
  self.proc.terminate()
1408
1394
  ```
1409
1395
  """
1410
- if _warn_parentheses_missing:
1411
- raise InvalidError("Did you forget parentheses? Suggestion: `@app.server()`.")
1412
1396
 
1413
1397
  # Validate HTTP server config
1414
1398
  validate_http_server_config(
@@ -1455,7 +1439,7 @@ class _App:
1455
1439
  if env:
1456
1440
  secrets_list.append(_Secret.from_dict(env))
1457
1441
 
1458
- def wrapper(wrapped_user_cls: type[Any] | _PartialFunction | Callable) -> _Server:
1442
+ def wrapper(wrapped_user_cls: type[Any] | _PartialFunction[..., Any, Any] | Callable[..., Any]) -> _Server:
1459
1443
  _Server._validate_wrapped_user_cls_decorators(wrapped_user_cls, enable_memory_snapshot)
1460
1444
 
1461
1445
  # Validate the server class
@@ -487,7 +487,7 @@ class _App:
487
487
  ...
488
488
 
489
489
  def local_entrypoint(
490
- self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
490
+ self, *, name: typing.Optional[str] = None
491
491
  ) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], _LocalEntrypoint]:
492
492
  """Decorate a function to be used as a CLI entrypoint for a Modal App.
493
493
 
@@ -547,7 +547,6 @@ class _App:
547
547
 
548
548
  def function(
549
549
  self,
550
- _warn_parentheses_missing=None,
551
550
  *,
552
551
  image: typing.Optional[modal._image._Image] = None,
553
552
  schedule: typing.Optional[modal.schedule.Schedule] = None,
@@ -646,7 +645,6 @@ class _App:
646
645
  )
647
646
  def cls(
648
647
  self,
649
- _warn_parentheses_missing=None,
650
648
  *,
651
649
  image: typing.Optional[modal._image._Image] = None,
652
650
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
@@ -684,7 +682,9 @@ class _App:
684
682
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
685
683
  _experimental_restrict_output: bool = False,
686
684
  max_inputs: typing.Optional[int] = None,
687
- ) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
685
+ ) -> collections.abc.Callable[
686
+ [typing.Union[CLS_T, modal._partial_function._PartialFunction[..., typing.Any, typing.Any]]], CLS_T
687
+ ]:
688
688
  """Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/Cls) with this App.
689
689
 
690
690
  Args:
@@ -732,7 +732,6 @@ class _App:
732
732
 
733
733
  def server(
734
734
  self,
735
- _warn_parentheses_missing=None,
736
735
  *,
737
736
  image: typing.Optional[modal._image._Image] = None,
738
737
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
@@ -769,7 +768,8 @@ class _App:
769
768
  include_source: typing.Optional[bool] = None,
770
769
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
771
770
  ) -> collections.abc.Callable[
772
- [typing.Union[type[typing.Any], modal._partial_function._PartialFunction]], modal._server._Server
771
+ [typing.Union[type[typing.Any], modal._partial_function._PartialFunction[..., typing.Any, typing.Any]]],
772
+ modal._server._Server,
773
773
  ]:
774
774
  """Decorator to register a new Modal Server with this App.
775
775
 
@@ -1525,7 +1525,7 @@ class App:
1525
1525
  ...
1526
1526
 
1527
1527
  def local_entrypoint(
1528
- self, _warn_parentheses_missing: typing.Any = None, *, name: typing.Optional[str] = None
1528
+ self, *, name: typing.Optional[str] = None
1529
1529
  ) -> collections.abc.Callable[[collections.abc.Callable[..., typing.Any]], LocalEntrypoint]:
1530
1530
  """Decorate a function to be used as a CLI entrypoint for a Modal App.
1531
1531
 
@@ -1585,7 +1585,6 @@ class App:
1585
1585
 
1586
1586
  def function(
1587
1587
  self,
1588
- _warn_parentheses_missing=None,
1589
1588
  *,
1590
1589
  image: typing.Optional[modal.image.Image] = None,
1591
1590
  schedule: typing.Optional[modal.schedule.Schedule] = None,
@@ -1684,7 +1683,6 @@ class App:
1684
1683
  )
1685
1684
  def cls(
1686
1685
  self,
1687
- _warn_parentheses_missing=None,
1688
1686
  *,
1689
1687
  image: typing.Optional[modal.image.Image] = None,
1690
1688
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
@@ -1722,7 +1720,9 @@ class App:
1722
1720
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
1723
1721
  _experimental_restrict_output: bool = False,
1724
1722
  max_inputs: typing.Optional[int] = None,
1725
- ) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
1723
+ ) -> collections.abc.Callable[
1724
+ [typing.Union[CLS_T, modal.partial_function.PartialFunction[..., typing.Any, typing.Any]]], CLS_T
1725
+ ]:
1726
1726
  """Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/Cls) with this App.
1727
1727
 
1728
1728
  Args:
@@ -1770,7 +1770,6 @@ class App:
1770
1770
 
1771
1771
  def server(
1772
1772
  self,
1773
- _warn_parentheses_missing=None,
1774
1773
  *,
1775
1774
  image: typing.Optional[modal.image.Image] = None,
1776
1775
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
@@ -1807,7 +1806,8 @@ class App:
1807
1806
  include_source: typing.Optional[bool] = None,
1808
1807
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
1809
1808
  ) -> collections.abc.Callable[
1810
- [typing.Union[type[typing.Any], modal.partial_function.PartialFunction]], modal.server.Server
1809
+ [typing.Union[type[typing.Any], modal.partial_function.PartialFunction[..., typing.Any, typing.Any]]],
1810
+ modal.server.Server,
1811
1811
  ]:
1812
1812
  """Decorator to register a new Modal Server with this App.
1813
1813
 
@@ -9,7 +9,7 @@ from typing import Literal, get_args
9
9
 
10
10
  import click
11
11
  import rich
12
- from click import UsageError
12
+ from click import ClickException, UsageError
13
13
  from google.protobuf.json_format import MessageToDict
14
14
  from rich.table import Column, Table
15
15
  from rich.text import Text
@@ -29,6 +29,7 @@ from .._utils.time_utils import timestamp_to_localized_str
29
29
  from ._help import ModalGroup
30
30
  from ._logs import _run_logs_command, _validate_logs_args
31
31
  from .utils import (
32
+ HEADER_ONLY,
32
33
  confirm_or_suggest_yes,
33
34
  display_table,
34
35
  env_option,
@@ -97,10 +98,34 @@ async def resolve_app_identifier(
97
98
 
98
99
  @app_cli.command("list")
99
100
  @env_option
100
- @click.option("--json", is_flag=True, default=False)
101
+ @click.option("--json", help="Output as JSON.", is_flag=True, default=False)
102
+ @click.option(
103
+ "--limit",
104
+ default=0,
105
+ help="Show at most N results. Use 0 to list every result. Default is 0.",
106
+ )
101
107
  @synchronizer.create_blocking
102
- async def list_(env: str | None = None, json: bool = False):
103
- """List Apps that are running, deployed or recently stopped."""
108
+ async def list_(env: str | None = None, json: bool = False, limit: int = 0):
109
+ """List Apps that are running, deployed or recently stopped.
110
+
111
+ Examples:
112
+
113
+ Limit output to 30 apps:
114
+
115
+ ```
116
+ modal app list --limit 30
117
+ ```
118
+
119
+ Display output in JSON:
120
+
121
+ ```
122
+ modal app list --json
123
+ ```
124
+
125
+ """
126
+ if not isinstance(limit, int) or limit < 0:
127
+ raise ClickException("--limit must be an integer value greater than or equal to 0.")
128
+
104
129
  env = ensure_env(env)
105
130
  client = await _Client.from_env()
106
131
 
@@ -108,14 +133,6 @@ async def list_(env: str | None = None, json: bool = False):
108
133
  api_pb2.AppListRequest(environment_name=_get_environment_name(env))
109
134
  )
110
135
 
111
- columns: list[Column | str] = [
112
- Column("App ID", min_width=25), # Ensure that App ID is not truncated in slim terminals
113
- "Description",
114
- "State",
115
- "Tasks",
116
- "Created at",
117
- "Stopped at",
118
- ]
119
136
  rows: list[list[Text | str | None]] = []
120
137
  for app_stats in resp.apps:
121
138
  state = APP_STATE_TO_MESSAGE.get(app_stats.state, Text("unknown", style="gray"))
@@ -129,9 +146,24 @@ async def list_(env: str | None = None, json: bool = False):
129
146
  timestamp_to_localized_str(app_stats.stopped_at, json),
130
147
  ]
131
148
  )
149
+ rows.sort(key=lambda s: (int(s[3]), s[4]), reverse=True) # type: ignore
150
+ rows_trunc = rows
151
+ if limit:
152
+ rows_trunc = rows[:limit]
153
+
154
+ app_id_width = max([len(r[0]) for r in rows]) if len(rows) else 25 # type: ignore
155
+ columns: list[Column | str] = [
156
+ Column("App ID", min_width=app_id_width, no_wrap=True),
157
+ Column("Description", min_width=1, overflow="fold"),
158
+ Column("State", min_width=1, overflow="fold"),
159
+ Column("Tasks", min_width=1, overflow="fold"),
160
+ Column("Created at", min_width=1, overflow="fold"),
161
+ Column("Stopped at", min_width=1, overflow="fold"),
162
+ ]
132
163
 
133
164
  env_part = f" in environment '{env}'" if env else ""
134
- display_table(columns, rows, json, title=f"Apps{env_part}")
165
+ app_count_part = f" ({len(rows_trunc)} of {len(rows)})" if len(rows_trunc) < len(rows) else ""
166
+ display_table(columns, rows_trunc, json, title=f"Apps{env_part}{app_count_part}", table_box=HEADER_ONLY)
135
167
 
136
168
 
137
169
  @app_cli.command("logs", no_args_is_help=True)
@@ -688,7 +720,11 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
688
720
  )
689
721
  return
690
722
 
723
+ name_width = max((Text(name).cell_len for name in [*app_info.functions, *app_info.servers]), default=0)
724
+ label_width = max(len("Deployment:"), name_width + 2)
691
725
  header = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
726
+ header.add_column(width=label_width)
727
+ header.add_column(overflow="fold")
692
728
  header.add_row(Text("App:"), Text(app_info.description))
693
729
  header.add_row(Text("App ID:"), Text(app_info.app_id))
694
730
  header.add_row(Text("State:"), Text(state.plain))
@@ -706,7 +742,6 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
706
742
  event("Created", lifecycle.created_at, lifecycle.created_by)
707
743
  output.print(header)
708
744
 
709
- name_width = max((len(name) for name in [*app_info.functions, *app_info.servers]), default=0)
710
745
  for title, entries in (("Functions", app_info.functions), ("Servers", app_info.servers)):
711
746
  if not entries:
712
747
  continue
@@ -716,8 +751,8 @@ async def info(app_identifier: str, *, env: str | None = None, json: bool = Fals
716
751
  if index:
717
752
  output.print("")
718
753
  identity = Table(show_header=False, box=None, pad_edge=False, padding=(0, 1))
719
- identity.add_column(width=name_width + 2)
720
- identity.add_column(no_wrap=True)
754
+ identity.add_column(width=label_width)
755
+ identity.add_column(width=26, overflow="fold")
721
756
  identity.add_row(Text(f" {name}"), Text(function_id))
722
757
  output.print(identity)
723
758
  if function_id not in resp.function_info_summaries: