modal 1.4.4.dev20__tar.gz → 1.4.4.dev22__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 (224) hide show
  1. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/PKG-INFO +1 -1
  2. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_functions.py +28 -9
  3. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_server.py +13 -5
  4. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/app.py +9 -9
  5. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/app.pyi +8 -8
  6. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/entry_point.py +2 -0
  7. modal-1.4.4.dev22/modal/cli/image.py +110 -0
  8. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/client.pyi +2 -2
  9. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/functions.pyi +26 -4
  10. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/server.pyi +17 -3
  11. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/PKG-INFO +1 -1
  12. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/SOURCES.txt +1 -0
  13. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/gen_cli_docs.py +11 -5
  14. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/api_grpc.py +32 -0
  15. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/api_pb2.py +879 -810
  16. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/api_pb2.pyi +173 -8
  17. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/api_pb2_grpc.py +66 -0
  18. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/api_pb2_grpc.pyi +20 -0
  19. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/modal_api_grpc.py +2 -0
  20. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_version/__init__.py +1 -1
  21. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/LICENSE +0 -0
  22. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/README.md +0 -0
  23. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/__init__.py +0 -0
  24. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/__main__.py +0 -0
  25. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_billing.py +0 -0
  26. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_clustered_functions.py +0 -0
  27. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_clustered_functions.pyi +0 -0
  28. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_container_entrypoint.py +0 -0
  29. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_environments.py +0 -0
  30. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_function_variants.py +0 -0
  31. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_grpc_client.py +0 -0
  32. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_image.py +0 -0
  33. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_ipython.py +0 -0
  34. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_load_context.py +0 -0
  35. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_location.py +0 -0
  36. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_logs.py +0 -0
  37. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_object.py +0 -0
  38. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_output/__init__.py +0 -0
  39. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_output/manager.py +0 -0
  40. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_output/pty.py +0 -0
  41. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_output/rich.py +0 -0
  42. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_output/status.py +0 -0
  43. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_partial_function.py +0 -0
  44. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_resolver.py +0 -0
  45. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_resources.py +0 -0
  46. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/__init__.py +0 -0
  47. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/asgi.py +0 -0
  48. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/container_io_manager.py +0 -0
  49. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/container_io_manager.pyi +0 -0
  50. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/execution_context.py +0 -0
  51. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/execution_context.pyi +0 -0
  52. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  53. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/task_lifecycle_manager.py +0 -0
  54. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  55. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/telemetry.py +0 -0
  56. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/user_code_event_loop.py +0 -0
  57. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_runtime/user_code_imports.py +0 -0
  58. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_serialization.py +0 -0
  59. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_traceback.py +0 -0
  60. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_tunnel.py +0 -0
  61. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_tunnel.pyi +0 -0
  62. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_type_manager.py +0 -0
  63. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/__init__.py +0 -0
  64. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/app_utils.py +0 -0
  65. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/async_utils.py +0 -0
  66. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/auth_token_manager.py +0 -0
  67. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/blob_utils.py +0 -0
  68. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/browser_utils.py +0 -0
  69. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/bytes_io_segment_payload.py +0 -0
  70. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/deprecation.py +0 -0
  71. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/docker_utils.py +0 -0
  72. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/function_utils.py +0 -0
  73. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/git_utils.py +0 -0
  74. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/grpc_testing.py +0 -0
  75. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/grpc_utils.py +0 -0
  76. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/hash_utils.py +0 -0
  77. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/http_utils.py +0 -0
  78. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/jwt_utils.py +0 -0
  79. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/logger.py +0 -0
  80. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/mount_utils.py +0 -0
  81. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/name_utils.py +0 -0
  82. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/package_utils.py +0 -0
  83. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/pattern_utils.py +0 -0
  84. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/rand_pb_testing.py +0 -0
  85. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/sandbox_fs_utils.py +0 -0
  86. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/shell_utils.py +0 -0
  87. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/task_command_router_client.py +0 -0
  88. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_utils/time_utils.py +0 -0
  89. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_vendor/__init__.py +0 -0
  90. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  91. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_vendor/cloudpickle.py +0 -0
  92. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_vendor/tblib.py +0 -0
  93. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_vendor/version.py +0 -0
  94. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_watcher.py +0 -0
  95. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/_workspace.py +0 -0
  96. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/billing.py +0 -0
  97. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/2023.12.312.txt +0 -0
  98. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/2023.12.txt +0 -0
  99. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/2024.04.txt +0 -0
  100. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/2024.10.txt +0 -0
  101. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/2025.06.txt +0 -0
  102. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/PREVIEW.txt +0 -0
  103. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/README.md +0 -0
  104. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/builder/base-images.json +0 -0
  105. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/call_graph.py +0 -0
  106. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/__init__.py +0 -0
  107. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/_download.py +0 -0
  108. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/_help.py +0 -0
  109. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/_traceback.py +0 -0
  110. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/app.py +0 -0
  111. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/billing.py +0 -0
  112. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/bootstrap.py +0 -0
  113. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/changelog.py +0 -0
  114. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/cluster.py +0 -0
  115. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/config.py +0 -0
  116. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/container.py +0 -0
  117. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/dashboard.py +0 -0
  118. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/dict.py +0 -0
  119. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/environment.py +0 -0
  120. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/import_refs.py +0 -0
  121. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/launch.py +0 -0
  122. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/logo.py +0 -0
  123. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/network_file_system.py +0 -0
  124. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/profile.py +0 -0
  125. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/programs/__init__.py +0 -0
  126. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/programs/run_jupyter.py +0 -0
  127. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/programs/vscode.py +0 -0
  128. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/queues.py +0 -0
  129. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/run.py +0 -0
  130. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/secret.py +0 -0
  131. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/selector.py +0 -0
  132. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/shell.py +0 -0
  133. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/skills.py +0 -0
  134. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/token.py +0 -0
  135. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/utils.py +0 -0
  136. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cli/volume.py +0 -0
  137. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/client.py +0 -0
  138. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cloud_bucket_mount.py +0 -0
  139. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cloud_bucket_mount.pyi +0 -0
  140. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cls.py +0 -0
  141. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/cls.pyi +0 -0
  142. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/config.py +0 -0
  143. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/container_process.py +0 -0
  144. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/container_process.pyi +0 -0
  145. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/dict.py +0 -0
  146. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/dict.pyi +0 -0
  147. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/environments.py +0 -0
  148. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/environments.pyi +0 -0
  149. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/exception.py +0 -0
  150. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/experimental/__init__.py +0 -0
  151. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/experimental/flash.py +0 -0
  152. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/experimental/flash.pyi +0 -0
  153. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/experimental/ipython.py +0 -0
  154. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/file_io.py +0 -0
  155. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/file_io.pyi +0 -0
  156. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/file_pattern_matcher.py +0 -0
  157. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/functions.py +0 -0
  158. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/image.py +0 -0
  159. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/image.pyi +0 -0
  160. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/io_streams.py +0 -0
  161. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/io_streams.pyi +0 -0
  162. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/mount.py +0 -0
  163. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/mount.pyi +0 -0
  164. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/network_file_system.py +0 -0
  165. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/network_file_system.pyi +0 -0
  166. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/object.py +0 -0
  167. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/object.pyi +0 -0
  168. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/output.py +0 -0
  169. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/parallel_map.py +0 -0
  170. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/parallel_map.pyi +0 -0
  171. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/partial_function.py +0 -0
  172. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/partial_function.pyi +0 -0
  173. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/proxy.py +0 -0
  174. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/proxy.pyi +0 -0
  175. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/py.typed +0 -0
  176. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/queue.py +0 -0
  177. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/queue.pyi +0 -0
  178. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/retries.py +0 -0
  179. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/runner.py +0 -0
  180. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/runner.pyi +0 -0
  181. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/running_app.py +0 -0
  182. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/sandbox.py +0 -0
  183. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/sandbox.pyi +0 -0
  184. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/sandbox_fs.py +0 -0
  185. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/sandbox_fs.pyi +0 -0
  186. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/schedule.py +0 -0
  187. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/scheduler_placement.py +0 -0
  188. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/secret.py +0 -0
  189. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/secret.pyi +0 -0
  190. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/server.py +0 -0
  191. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/serving.py +0 -0
  192. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/serving.pyi +0 -0
  193. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/skills/modal/SKILL.md +0 -0
  194. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/snapshot.py +0 -0
  195. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/snapshot.pyi +0 -0
  196. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/stream_type.py +0 -0
  197. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/token_flow.py +0 -0
  198. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/token_flow.pyi +0 -0
  199. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/volume.py +0 -0
  200. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/volume.pyi +0 -0
  201. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/workspace.py +0 -0
  202. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal/workspace.pyi +0 -0
  203. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/dependency_links.txt +0 -0
  204. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/entry_points.txt +0 -0
  205. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/requires.txt +0 -0
  206. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal.egg-info/top_level.txt +0 -0
  207. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/__init__.py +0 -0
  208. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/gen_cli_docs_main.py +0 -0
  209. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/gen_reference_docs.py +0 -0
  210. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/gen_reference_docs_main.py +0 -0
  211. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/mdmd/__init__.py +0 -0
  212. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/mdmd/mdmd.py +0 -0
  213. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/mdmd/signatures.py +0 -0
  214. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_docs/mdmd/types.py +0 -0
  215. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/__init__.py +0 -0
  216. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/py.typed +0 -0
  217. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/task_command_router_grpc.py +0 -0
  218. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/task_command_router_pb2.py +0 -0
  219. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/task_command_router_pb2.pyi +0 -0
  220. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  221. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  222. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/modal_version/__main__.py +0 -0
  223. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/pyproject.toml +0 -0
  224. {modal-1.4.4.dev20 → modal-1.4.4.dev22}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev20
3
+ Version: 1.4.4.dev22
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -934,7 +934,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
934
934
  if app and app.name:
935
935
  app_name = app.name
936
936
 
937
- # on builder > 2024.10 we mount client dependencies at runtime
937
+ # TODO: client-side `mount_client_dependencies` can be removed after 2026-06-15
938
+ # Kept so that the function definition proto stays identical across deploys
939
+ # (removing it would change the proto, creating a new app version)
938
940
  mount_client_dependencies = False
939
941
  if image._metadata is not None:
940
942
  mount_client_dependencies = image._metadata.image_builder_version > "2024.10"
@@ -1149,6 +1151,28 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1149
1151
 
1150
1152
  return obj
1151
1153
 
1154
+ async def _update_autoscaler(
1155
+ self,
1156
+ *,
1157
+ min_containers: int | None = None,
1158
+ max_containers: int | None = None,
1159
+ buffer_containers: int | None = None,
1160
+ scaledown_window: int | None = None,
1161
+ target_concurrency: int | None = None,
1162
+ ) -> None:
1163
+ settings = api_pb2.AutoscalerSettings(
1164
+ min_containers=min_containers,
1165
+ max_containers=max_containers,
1166
+ buffer_containers=buffer_containers,
1167
+ scaledown_window=scaledown_window,
1168
+ target_concurrency=target_concurrency,
1169
+ )
1170
+ request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
1171
+ await self.client.stub.FunctionUpdateSchedulingParams(request)
1172
+
1173
+ # One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
1174
+ # and then we could return them here (would need some ad hoc dataclass, which I don't love)
1175
+
1152
1176
  @live_method
1153
1177
  async def update_autoscaler(
1154
1178
  self,
@@ -1184,24 +1208,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1184
1208
 
1185
1209
  # Extend the scaledown window to increase the amount of time that idle containers stay alive
1186
1210
  f.update_autoscaler(scaledown_window=300)
1187
-
1188
- ```
1211
+ ```
1189
1212
 
1190
1213
  """
1214
+ # Assert .update_autoscaler() is not called on a method as opposed to the Object. Applicable for Cls only.
1191
1215
  if self._is_method:
1192
1216
  raise InvalidError("Cannot call .update_autoscaler() on a method. Call it on the class instance instead.")
1193
1217
 
1194
- settings = api_pb2.AutoscalerSettings(
1218
+ await self._update_autoscaler(
1195
1219
  min_containers=min_containers,
1196
1220
  max_containers=max_containers,
1197
1221
  buffer_containers=buffer_containers,
1198
1222
  scaledown_window=scaledown_window,
1199
1223
  )
1200
- request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
1201
- await self.client.stub.FunctionUpdateSchedulingParams(request)
1202
-
1203
- # One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
1204
- # and then we could return them here (would need some ad hoc dataclass, which I don't love)
1205
1224
 
1206
1225
  @classmethod
1207
1226
  def _from_name(
@@ -34,9 +34,9 @@ def validate_http_server_config(
34
34
  if not is_server and exit_grace_period is not None and exit_grace_period > 25:
35
35
  raise InvalidError("The `exit_grace_period` argument must not exceed 25 seconds.")
36
36
 
37
- if not proxy_regions:
37
+ if not proxy_regions or not proxy_regions[0]:
38
38
  if is_server:
39
- raise InvalidError("The `routing_regions` argument must be non-empty.")
39
+ raise InvalidError("The `routing_region` argument must be passed.")
40
40
  raise InvalidError("The `proxy_regions` argument must be non-empty.")
41
41
 
42
42
 
@@ -49,7 +49,7 @@ class _Server:
49
49
  Instead, use the [`@app._experimental_server()`](https://modal.com/docs/reference/modal.App#server) decorator.
50
50
 
51
51
  ```python notest
52
- @app._experimental_server(port=8000, routing_regions=["us-east", "us-west"])
52
+ @app._experimental_server(port=8000, routing_region="us-east")
53
53
  class MyServer:
54
54
  @modal.enter()
55
55
  def start_server(self):
@@ -100,6 +100,7 @@ class _Server:
100
100
  max_containers: int | None = None,
101
101
  buffer_containers: int | None = None,
102
102
  scaledown_window: int | None = None,
103
+ target_concurrency: int | None = None,
103
104
  ) -> None:
104
105
  """Override the current autoscaler behavior for this Server.
105
106
 
@@ -114,14 +115,21 @@ class _Server:
114
115
 
115
116
  # Limit this Server to avoid spinning up more than 5 containers
116
117
  server.update_autoscaler(max_containers=5)
117
- ```
118
+
119
+ # Adjust Server autoscaling to target 20 concurrent requests per replica
120
+ server.update_autoscaler(target_concurrency=20)
121
+
122
+ # Disable the Server autoscaling by setting target_concurrency to 0
123
+ server.update_autoscaler(target_concurrency=0)
124
+ ```
118
125
 
119
126
  """
120
- return await self._get_service_function().update_autoscaler(
127
+ return await self._get_service_function()._update_autoscaler(
121
128
  min_containers=min_containers,
122
129
  max_containers=max_containers,
123
130
  scaledown_window=scaledown_window,
124
131
  buffer_containers=buffer_containers,
132
+ target_concurrency=target_concurrency,
125
133
  )
126
134
 
127
135
  # ============ Hydration ============
@@ -1289,9 +1289,9 @@ class _App:
1289
1289
  port: int = 8000, # Port the HTTP server listens on
1290
1290
  startup_timeout: int = 30, # Maximum startup time in seconds
1291
1291
  exit_grace_period: int = 0, # Grace period for in-flight requests on shutdown
1292
- routing_regions: list[str] = ["us-east"], # Required: Regions to deploy proxy endpoints
1292
+ routing_region: str = "us-east", # Region to route Server requests through
1293
1293
  h2_enabled: bool = False, # Enable HTTP/2
1294
- target_concurrency: int | None = None, # Target concurrency for the server
1294
+ target_concurrency: int | None = None, # Target concurrency for the server; 0 disables autoscaling
1295
1295
  cloud: str | None = None, # Cloud provider (aws, gcp, oci, auto)
1296
1296
  region: str | Sequence[str] | None = None, # Region(s) to run on
1297
1297
  nonpreemptible: bool = False, # Whether to use non-preemptible instances
@@ -1331,9 +1331,9 @@ class _App:
1331
1331
  port: Port the HTTP server listens on.
1332
1332
  startup_timeout: Maximum startup time in seconds.
1333
1333
  exit_grace_period: Grace period for in-flight requests on shutdown.
1334
- routing_regions: Regions to deploy proxy endpoints.
1334
+ routing_region: Region to route Server requests through.
1335
1335
  h2_enabled: Enable HTTP/2.
1336
- target_concurrency: Target concurrency for the server.
1336
+ target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1337
1337
  cloud: Cloud provider (aws, gcp, oci, auto).
1338
1338
  region: Region(s) to run on.
1339
1339
  nonpreemptible: Whether to use non-preemptible instances.
@@ -1344,7 +1344,7 @@ class _App:
1344
1344
 
1345
1345
  Examples:
1346
1346
  ```python
1347
- @app._experimental_server(port=8000, routing_regions=["us-east"])
1347
+ @app._experimental_server(port=8000, routing_region="us-east")
1348
1348
  class MyServer:
1349
1349
  @modal.enter()
1350
1350
  def start(self):
@@ -1361,19 +1361,19 @@ class _App:
1361
1361
  # Validate HTTP server config
1362
1362
  validate_http_server_config(
1363
1363
  port=port,
1364
- proxy_regions=routing_regions,
1364
+ proxy_regions=[routing_region],
1365
1365
  startup_timeout=startup_timeout,
1366
1366
  exit_grace_period=exit_grace_period,
1367
1367
  is_server=True,
1368
1368
  )
1369
1369
 
1370
1370
  if target_concurrency is not None:
1371
- if not isinstance(target_concurrency, int) or target_concurrency < 1:
1372
- raise InvalidError("The `target_concurrency` argument must be a positive integer.")
1371
+ if not isinstance(target_concurrency, int) or target_concurrency < 0:
1372
+ raise InvalidError("The `target_concurrency` argument must be a non-negative integer.")
1373
1373
 
1374
1374
  http_config = api_pb2.HTTPConfig(
1375
1375
  port=port,
1376
- proxy_regions=routing_regions,
1376
+ proxy_regions=[routing_region],
1377
1377
  startup_timeout=startup_timeout,
1378
1378
  exit_grace_period=exit_grace_period,
1379
1379
  h2_enabled=h2_enabled,
@@ -752,7 +752,7 @@ class _App:
752
752
  port: int = 8000,
753
753
  startup_timeout: int = 30,
754
754
  exit_grace_period: int = 0,
755
- routing_regions: list[str] = ["us-east"],
755
+ routing_region: str = "us-east",
756
756
  h2_enabled: bool = False,
757
757
  target_concurrency: typing.Optional[int] = None,
758
758
  cloud: typing.Optional[str] = None,
@@ -794,9 +794,9 @@ class _App:
794
794
  port: Port the HTTP server listens on.
795
795
  startup_timeout: Maximum startup time in seconds.
796
796
  exit_grace_period: Grace period for in-flight requests on shutdown.
797
- routing_regions: Regions to deploy proxy endpoints.
797
+ routing_region: Region to route Server requests through.
798
798
  h2_enabled: Enable HTTP/2.
799
- target_concurrency: Target concurrency for the server.
799
+ target_concurrency: Target concurrency for the server; 0 disables autoscaling.
800
800
  cloud: Cloud provider (aws, gcp, oci, auto).
801
801
  region: Region(s) to run on.
802
802
  nonpreemptible: Whether to use non-preemptible instances.
@@ -807,7 +807,7 @@ class _App:
807
807
 
808
808
  Examples:
809
809
  ```python
810
- @app._experimental_server(port=8000, routing_regions=["us-east"])
810
+ @app._experimental_server(port=8000, routing_region="us-east")
811
811
  class MyServer:
812
812
  @modal.enter()
813
813
  def start(self):
@@ -1750,7 +1750,7 @@ class App:
1750
1750
  port: int = 8000,
1751
1751
  startup_timeout: int = 30,
1752
1752
  exit_grace_period: int = 0,
1753
- routing_regions: list[str] = ["us-east"],
1753
+ routing_region: str = "us-east",
1754
1754
  h2_enabled: bool = False,
1755
1755
  target_concurrency: typing.Optional[int] = None,
1756
1756
  cloud: typing.Optional[str] = None,
@@ -1790,9 +1790,9 @@ class App:
1790
1790
  port: Port the HTTP server listens on.
1791
1791
  startup_timeout: Maximum startup time in seconds.
1792
1792
  exit_grace_period: Grace period for in-flight requests on shutdown.
1793
- routing_regions: Regions to deploy proxy endpoints.
1793
+ routing_region: Region to route Server requests through.
1794
1794
  h2_enabled: Enable HTTP/2.
1795
- target_concurrency: Target concurrency for the server.
1795
+ target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1796
1796
  cloud: Cloud provider (aws, gcp, oci, auto).
1797
1797
  region: Region(s) to run on.
1798
1798
  nonpreemptible: Whether to use non-preemptible instances.
@@ -1803,7 +1803,7 @@ class App:
1803
1803
 
1804
1804
  Examples:
1805
1805
  ```python
1806
- @app._experimental_server(port=8000, routing_regions=["us-east"])
1806
+ @app._experimental_server(port=8000, routing_region="us-east")
1807
1807
  class MyServer:
1808
1808
  @modal.enter()
1809
1809
  def start(self):
@@ -19,6 +19,7 @@ from .container import container_cli
19
19
  from .dashboard import dashboard
20
20
  from .dict import dict_cli
21
21
  from .environment import environment_cli
22
+ from .image import image_cli
22
23
  from .launch import launch_cli
23
24
  from .logo import print_logo
24
25
  from .network_file_system import nfs_cli
@@ -106,6 +107,7 @@ entrypoint_cli.add_command(container_cli, panel="Deployments")
106
107
  # cluster is hidden while multi-node is in beta/experimental
107
108
  entrypoint_cli.add_command(cluster_cli, panel="Deployments", hidden=True)
108
109
 
110
+ entrypoint_cli.add_command(image_cli, panel="Storage")
109
111
  entrypoint_cli.add_command(dict_cli, panel="Storage")
110
112
  entrypoint_cli.add_command(nfs_cli, panel="Storage")
111
113
  entrypoint_cli.add_command(secret_cli, panel="Storage")
@@ -0,0 +1,110 @@
1
+ # Copyright Modal Labs 2026
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json as json_module
7
+ from collections.abc import AsyncIterator
8
+ from typing import Optional
9
+
10
+ import click
11
+
12
+ from modal._environments import ensure_env
13
+ from modal._utils.async_utils import synchronizer
14
+ from modal._utils.time_utils import timestamp_to_localized_str
15
+ from modal.cli.utils import display_table, env_option
16
+ from modal.client import _Client
17
+ from modal.output import OutputManager
18
+ from modal_proto import api_pb2
19
+
20
+ from ._help import ModalGroup
21
+
22
+ IMAGE_REGISTRY_LIST_PAGE_SIZE = 100
23
+ IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS = 1.0
24
+
25
+ image_cli = ModalGroup(name="image", help="Manage Images.")
26
+ image_registry_cli = ModalGroup(name="registry", help="Manage the Modal Image name/tag registry.")
27
+ image_cli.add_command(image_registry_cli)
28
+
29
+
30
+ def _print_result_summary(count: int) -> None:
31
+ result_label = "result" if count == 1 else "results"
32
+ OutputManager.get().print(f"Showing {count} {result_label}")
33
+
34
+
35
+ def _tag_item_json(item: api_pb2.ImageListTagsItem) -> dict[str, str]:
36
+ return {
37
+ "tag": item.tag,
38
+ "image_id": item.image_id,
39
+ "created_at": timestamp_to_localized_str(item.created_at, True) or "-",
40
+ "updated_at": timestamp_to_localized_str(item.updated_at, True) or "-",
41
+ }
42
+
43
+
44
+ def _tag_item_row(item: api_pb2.ImageListTagsItem) -> tuple[str, str, str]:
45
+ return (
46
+ item.tag,
47
+ item.image_id,
48
+ timestamp_to_localized_str(item.updated_at, False) or "-",
49
+ )
50
+
51
+
52
+ async def _iter_tag_pages(
53
+ client: _Client, env: str, prefix: str
54
+ ) -> AsyncIterator[tuple[str, list[api_pb2.ImageListTagsItem]]]:
55
+ page_token = ""
56
+ environment_name = env
57
+
58
+ while True:
59
+ response = await client.stub.ImageListTags(
60
+ api_pb2.ImageListTagsRequest(
61
+ environment_name=env,
62
+ tag_prefix=prefix,
63
+ max_objects=IMAGE_REGISTRY_LIST_PAGE_SIZE,
64
+ page_token=page_token,
65
+ )
66
+ )
67
+ environment_name = response.environment_name
68
+ yield environment_name, list(response.items)
69
+
70
+ page_token = response.next_page_token
71
+ if not page_token:
72
+ return
73
+ await asyncio.sleep(IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS)
74
+
75
+
76
+ @image_registry_cli.command("list", help="List named Images.")
77
+ @env_option
78
+ @click.option("--prefix", default="", help="Only include named image tags that start with this prefix.")
79
+ @click.option("--json", is_flag=True, default=False)
80
+ @synchronizer.create_blocking
81
+ async def list_(
82
+ env: Optional[str] = None,
83
+ prefix: str = "",
84
+ json: bool = False,
85
+ ):
86
+ env = ensure_env(env)
87
+ client = await _Client.from_env()
88
+
89
+ if json:
90
+ count = 0
91
+ click.echo("[", nl=False)
92
+ async for _, page_items in _iter_tag_pages(client, env, prefix):
93
+ for item in page_items:
94
+ if count:
95
+ click.echo(",", nl=False)
96
+ click.echo(json_module.dumps(_tag_item_json(item)), nl=False)
97
+ count += 1
98
+ click.echo("]")
99
+ return
100
+
101
+ count = 0
102
+ first_page = True
103
+ async for environment_name, page_items in _iter_tag_pages(client, env, prefix):
104
+ if first_page or page_items:
105
+ title = f"Named Images in environment '{environment_name}'" if first_page else ""
106
+ display_table(["Tag", "Image ID", "Updated at"], [_tag_item_row(item) for item in page_items], title=title)
107
+ first_page = False
108
+ count += len(page_items)
109
+
110
+ _print_result_summary(count)
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.4.4.dev20",
38
+ version: str = "1.4.4.dev22",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -205,7 +205,7 @@ class Client:
205
205
  server_url: str,
206
206
  client_type: int,
207
207
  credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.4.4.dev20",
208
+ version: str = "1.4.4.dev22",
209
209
  ):
210
210
  """mdmd:hidden
211
211
  The Modal client object is not intended to be instantiated directly by users.
@@ -121,6 +121,30 @@ class Function(
121
121
  """
122
122
  ...
123
123
 
124
+ class ___update_autoscaler_spec(typing_extensions.Protocol):
125
+ def __call__(
126
+ self,
127
+ /,
128
+ *,
129
+ min_containers: typing.Optional[int] = None,
130
+ max_containers: typing.Optional[int] = None,
131
+ buffer_containers: typing.Optional[int] = None,
132
+ scaledown_window: typing.Optional[int] = None,
133
+ target_concurrency: typing.Optional[int] = None,
134
+ ) -> None: ...
135
+ async def aio(
136
+ self,
137
+ /,
138
+ *,
139
+ min_containers: typing.Optional[int] = None,
140
+ max_containers: typing.Optional[int] = None,
141
+ buffer_containers: typing.Optional[int] = None,
142
+ scaledown_window: typing.Optional[int] = None,
143
+ target_concurrency: typing.Optional[int] = None,
144
+ ) -> None: ...
145
+
146
+ _update_autoscaler: ___update_autoscaler_spec
147
+
124
148
  class __update_autoscaler_spec(typing_extensions.Protocol):
125
149
  def __call__(
126
150
  self,
@@ -157,8 +181,7 @@ class Function(
157
181
 
158
182
  # Extend the scaledown window to increase the amount of time that idle containers stay alive
159
183
  f.update_autoscaler(scaledown_window=300)
160
-
161
- ```
184
+ ```
162
185
  """
163
186
  ...
164
187
 
@@ -197,8 +220,7 @@ class Function(
197
220
 
198
221
  # Extend the scaledown window to increase the amount of time that idle containers stay alive
199
222
  f.update_autoscaler(scaledown_window=300)
200
-
201
- ```
223
+ ```
202
224
  """
203
225
  ...
204
226
 
@@ -14,7 +14,7 @@ class Server:
14
14
  Instead, use the [`@app._experimental_server()`](https://modal.com/docs/reference/modal.App#server) decorator.
15
15
 
16
16
  ```python notest
17
- @app._experimental_server(port=8000, routing_regions=["us-east", "us-west"])
17
+ @app._experimental_server(port=8000, routing_region="us-east")
18
18
  class MyServer:
19
19
  @modal.enter()
20
20
  def start_server(self):
@@ -51,6 +51,7 @@ class Server:
51
51
  max_containers: typing.Optional[int] = None,
52
52
  buffer_containers: typing.Optional[int] = None,
53
53
  scaledown_window: typing.Optional[int] = None,
54
+ target_concurrency: typing.Optional[int] = None,
54
55
  ) -> None:
55
56
  """Override the current autoscaler behavior for this Server.
56
57
 
@@ -65,7 +66,13 @@ class Server:
65
66
 
66
67
  # Limit this Server to avoid spinning up more than 5 containers
67
68
  server.update_autoscaler(max_containers=5)
68
- ```
69
+
70
+ # Adjust Server autoscaling to target 20 concurrent requests per replica
71
+ server.update_autoscaler(target_concurrency=20)
72
+
73
+ # Disable the Server autoscaling by setting target_concurrency to 0
74
+ server.update_autoscaler(target_concurrency=0)
75
+ ```
69
76
  """
70
77
  ...
71
78
 
@@ -77,6 +84,7 @@ class Server:
77
84
  max_containers: typing.Optional[int] = None,
78
85
  buffer_containers: typing.Optional[int] = None,
79
86
  scaledown_window: typing.Optional[int] = None,
87
+ target_concurrency: typing.Optional[int] = None,
80
88
  ) -> None:
81
89
  """Override the current autoscaler behavior for this Server.
82
90
 
@@ -91,7 +99,13 @@ class Server:
91
99
 
92
100
  # Limit this Server to avoid spinning up more than 5 containers
93
101
  server.update_autoscaler(max_containers=5)
94
- ```
102
+
103
+ # Adjust Server autoscaling to target 20 concurrent requests per replica
104
+ server.update_autoscaler(target_concurrency=20)
105
+
106
+ # Disable the Server autoscaling by setting target_concurrency to 0
107
+ server.update_autoscaler(target_concurrency=0)
108
+ ```
95
109
  """
96
110
  ...
97
111
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev20
3
+ Version: 1.4.4.dev22
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -173,6 +173,7 @@ modal/cli/dashboard.py
173
173
  modal/cli/dict.py
174
174
  modal/cli/entry_point.py
175
175
  modal/cli/environment.py
176
+ modal/cli/image.py
176
177
  modal/cli/import_refs.py
177
178
  modal/cli/launch.py
178
179
  modal/cli/logo.py
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2023
2
2
  import inspect
3
+ import re
3
4
  import sys
4
5
  from pathlib import Path
5
6
  from typing import cast
@@ -9,6 +10,11 @@ from click import Command, Context, Group
9
10
  from modal.cli.entry_point import entrypoint_cli
10
11
 
11
12
 
13
+ def _escape_svelte(text: str) -> str:
14
+ """Wrap {placeholder} patterns in backticks so mdsvex/Svelte won't interpret them as expressions."""
15
+ return re.sub(r"(?<!`)\{([a-z_]+)\}(?!`)", r"`{\1}`", text)
16
+
17
+
12
18
  # Adapted from typer_cli for generating CLI docs from click commands
13
19
  # (see https://github.com/tiangolo/typer-cli/issues/50)
14
20
  def get_docs_for_click(
@@ -26,7 +32,7 @@ def get_docs_for_click(
26
32
  title = f"`{command_name}`" if command_name else "CLI"
27
33
  docs += f" {title}\n\n"
28
34
  if obj.help:
29
- docs += f"{inspect.cleandoc(obj.help)}\n\n"
35
+ docs += f"{_escape_svelte(inspect.cleandoc(obj.help))}\n\n"
30
36
  usage_pieces = obj.collect_usage_pieces(ctx)
31
37
  if usage_pieces:
32
38
  docs += "**Usage**:\n\n"
@@ -51,7 +57,7 @@ def get_docs_for_click(
51
57
  for arg_name, arg_help in args:
52
58
  docs += f"* `{arg_name}`"
53
59
  if arg_help:
54
- docs += f": {arg_help}"
60
+ docs += f": {_escape_svelte(arg_help)}"
55
61
  docs += "\n"
56
62
  docs += "\n"
57
63
  if opts:
@@ -59,11 +65,11 @@ def get_docs_for_click(
59
65
  for opt_name, opt_help in opts:
60
66
  docs += f"* `{opt_name}`"
61
67
  if opt_help:
62
- docs += f": {opt_help}"
68
+ docs += f": {_escape_svelte(opt_help)}"
63
69
  docs += "\n"
64
70
  docs += "\n"
65
71
  if obj.epilog:
66
- docs += f"{obj.epilog}\n\n"
72
+ docs += f"{_escape_svelte(obj.epilog)}\n\n"
67
73
  if isinstance(obj, Group):
68
74
  group: Group = cast(Group, obj)
69
75
  commands = group.list_commands(ctx)
@@ -77,7 +83,7 @@ def get_docs_for_click(
77
83
  docs += f"* `{command_obj.name}`"
78
84
  command_help = command_obj.get_short_help_str(limit=250)
79
85
  if command_help:
80
- docs += f": {command_help}"
86
+ docs += f": {_escape_svelte(command_help)}"
81
87
  docs += "\n"
82
88
  docs += "\n"
83
89
  for command in commands:
@@ -435,10 +435,18 @@ class ModalClientBase(abc.ABC):
435
435
  async def ImageJoinStreaming(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageJoinStreamingRequest, modal_proto.api_pb2.ImageJoinStreamingResponse]') -> None:
436
436
  pass
437
437
 
438
+ @abc.abstractmethod
439
+ async def ImageListTags(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageListTagsRequest, modal_proto.api_pb2.ImageListTagsResponse]') -> None:
440
+ pass
441
+
438
442
  @abc.abstractmethod
439
443
  async def ImagePublish(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImagePublishRequest, modal_proto.api_pb2.ImagePublishResponse]') -> None:
440
444
  pass
441
445
 
446
+ @abc.abstractmethod
447
+ async def ImageTagRevisions(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ImageTagRevisionsRequest, modal_proto.api_pb2.ImageTagRevisionsResponse]') -> None:
448
+ pass
449
+
442
450
  @abc.abstractmethod
443
451
  async def MapAwait(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.MapAwaitRequest, modal_proto.api_pb2.MapAwaitResponse]') -> None:
444
452
  pass
@@ -1461,12 +1469,24 @@ class ModalClientBase(abc.ABC):
1461
1469
  modal_proto.api_pb2.ImageJoinStreamingRequest,
1462
1470
  modal_proto.api_pb2.ImageJoinStreamingResponse,
1463
1471
  ),
1472
+ '/modal.client.ModalClient/ImageListTags': grpclib.const.Handler(
1473
+ self.ImageListTags,
1474
+ grpclib.const.Cardinality.UNARY_UNARY,
1475
+ modal_proto.api_pb2.ImageListTagsRequest,
1476
+ modal_proto.api_pb2.ImageListTagsResponse,
1477
+ ),
1464
1478
  '/modal.client.ModalClient/ImagePublish': grpclib.const.Handler(
1465
1479
  self.ImagePublish,
1466
1480
  grpclib.const.Cardinality.UNARY_UNARY,
1467
1481
  modal_proto.api_pb2.ImagePublishRequest,
1468
1482
  modal_proto.api_pb2.ImagePublishResponse,
1469
1483
  ),
1484
+ '/modal.client.ModalClient/ImageTagRevisions': grpclib.const.Handler(
1485
+ self.ImageTagRevisions,
1486
+ grpclib.const.Cardinality.UNARY_UNARY,
1487
+ modal_proto.api_pb2.ImageTagRevisionsRequest,
1488
+ modal_proto.api_pb2.ImageTagRevisionsResponse,
1489
+ ),
1470
1490
  '/modal.client.ModalClient/MapAwait': grpclib.const.Handler(
1471
1491
  self.MapAwait,
1472
1492
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -2691,12 +2711,24 @@ class ModalClientStub:
2691
2711
  modal_proto.api_pb2.ImageJoinStreamingRequest,
2692
2712
  modal_proto.api_pb2.ImageJoinStreamingResponse,
2693
2713
  )
2714
+ self.ImageListTags = grpclib.client.UnaryUnaryMethod(
2715
+ channel,
2716
+ '/modal.client.ModalClient/ImageListTags',
2717
+ modal_proto.api_pb2.ImageListTagsRequest,
2718
+ modal_proto.api_pb2.ImageListTagsResponse,
2719
+ )
2694
2720
  self.ImagePublish = grpclib.client.UnaryUnaryMethod(
2695
2721
  channel,
2696
2722
  '/modal.client.ModalClient/ImagePublish',
2697
2723
  modal_proto.api_pb2.ImagePublishRequest,
2698
2724
  modal_proto.api_pb2.ImagePublishResponse,
2699
2725
  )
2726
+ self.ImageTagRevisions = grpclib.client.UnaryUnaryMethod(
2727
+ channel,
2728
+ '/modal.client.ModalClient/ImageTagRevisions',
2729
+ modal_proto.api_pb2.ImageTagRevisionsRequest,
2730
+ modal_proto.api_pb2.ImageTagRevisionsResponse,
2731
+ )
2700
2732
  self.MapAwait = grpclib.client.UnaryUnaryMethod(
2701
2733
  channel,
2702
2734
  '/modal.client.ModalClient/MapAwait',