modal 1.4.4.dev21__tar.gz → 1.5.0__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.dev21 → modal-1.5.0}/PKG-INFO +1 -1
  2. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_environments.py +1 -1
  3. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_server.py +3 -3
  4. {modal-1.4.4.dev21 → modal-1.5.0}/modal/app.py +5 -5
  5. {modal-1.4.4.dev21 → modal-1.5.0}/modal/app.pyi +6 -6
  6. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/image.py +7 -7
  7. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/utils.py +8 -1
  8. {modal-1.4.4.dev21 → modal-1.5.0}/modal/client.pyi +2 -10
  9. {modal-1.4.4.dev21 → modal-1.5.0}/modal/sandbox.py +54 -11
  10. {modal-1.4.4.dev21 → modal-1.5.0}/modal/sandbox.pyi +12 -0
  11. {modal-1.4.4.dev21 → modal-1.5.0}/modal/server.pyi +1 -1
  12. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/PKG-INFO +1 -1
  13. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/api_pb2.py +421 -406
  14. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/api_pb2.pyi +21 -1
  15. {modal-1.4.4.dev21 → modal-1.5.0}/modal_version/__init__.py +1 -1
  16. {modal-1.4.4.dev21 → modal-1.5.0}/LICENSE +0 -0
  17. {modal-1.4.4.dev21 → modal-1.5.0}/README.md +0 -0
  18. {modal-1.4.4.dev21 → modal-1.5.0}/modal/__init__.py +0 -0
  19. {modal-1.4.4.dev21 → modal-1.5.0}/modal/__main__.py +0 -0
  20. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_billing.py +0 -0
  21. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_clustered_functions.py +0 -0
  22. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_clustered_functions.pyi +0 -0
  23. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_container_entrypoint.py +0 -0
  24. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_function_variants.py +0 -0
  25. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_functions.py +0 -0
  26. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_grpc_client.py +0 -0
  27. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_image.py +0 -0
  28. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_ipython.py +0 -0
  29. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_load_context.py +0 -0
  30. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_location.py +0 -0
  31. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_logs.py +0 -0
  32. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_object.py +0 -0
  33. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_output/__init__.py +0 -0
  34. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_output/manager.py +0 -0
  35. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_output/pty.py +0 -0
  36. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_output/rich.py +0 -0
  37. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_output/status.py +0 -0
  38. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_partial_function.py +0 -0
  39. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_resolver.py +0 -0
  40. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_resources.py +0 -0
  41. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/__init__.py +0 -0
  42. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/asgi.py +0 -0
  43. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/container_io_manager.py +0 -0
  44. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/container_io_manager.pyi +0 -0
  45. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/execution_context.py +0 -0
  46. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/execution_context.pyi +0 -0
  47. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  48. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/task_lifecycle_manager.py +0 -0
  49. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  50. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/telemetry.py +0 -0
  51. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/user_code_event_loop.py +0 -0
  52. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_runtime/user_code_imports.py +0 -0
  53. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_serialization.py +0 -0
  54. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_traceback.py +0 -0
  55. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_tunnel.py +0 -0
  56. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_tunnel.pyi +0 -0
  57. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_type_manager.py +0 -0
  58. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/__init__.py +0 -0
  59. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/app_utils.py +0 -0
  60. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/async_utils.py +0 -0
  61. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/auth_token_manager.py +0 -0
  62. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/blob_utils.py +0 -0
  63. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/browser_utils.py +0 -0
  64. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  65. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/deprecation.py +0 -0
  66. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/docker_utils.py +0 -0
  67. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/function_utils.py +0 -0
  68. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/git_utils.py +0 -0
  69. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/grpc_testing.py +0 -0
  70. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/grpc_utils.py +0 -0
  71. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/hash_utils.py +0 -0
  72. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/http_utils.py +0 -0
  73. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/jwt_utils.py +0 -0
  74. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/logger.py +0 -0
  75. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/mount_utils.py +0 -0
  76. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/name_utils.py +0 -0
  77. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/package_utils.py +0 -0
  78. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/pattern_utils.py +0 -0
  79. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/rand_pb_testing.py +0 -0
  80. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/sandbox_fs_utils.py +0 -0
  81. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/shell_utils.py +0 -0
  82. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/task_command_router_client.py +0 -0
  83. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_utils/time_utils.py +0 -0
  84. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_vendor/__init__.py +0 -0
  85. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  86. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_vendor/cloudpickle.py +0 -0
  87. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_vendor/tblib.py +0 -0
  88. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_vendor/version.py +0 -0
  89. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_watcher.py +0 -0
  90. {modal-1.4.4.dev21 → modal-1.5.0}/modal/_workspace.py +0 -0
  91. {modal-1.4.4.dev21 → modal-1.5.0}/modal/billing.py +0 -0
  92. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/2023.12.312.txt +0 -0
  93. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/2023.12.txt +0 -0
  94. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/2024.04.txt +0 -0
  95. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/2024.10.txt +0 -0
  96. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/2025.06.txt +0 -0
  97. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/PREVIEW.txt +0 -0
  98. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/README.md +0 -0
  99. {modal-1.4.4.dev21 → modal-1.5.0}/modal/builder/base-images.json +0 -0
  100. {modal-1.4.4.dev21 → modal-1.5.0}/modal/call_graph.py +0 -0
  101. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/__init__.py +0 -0
  102. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/_download.py +0 -0
  103. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/_help.py +0 -0
  104. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/_traceback.py +0 -0
  105. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/app.py +0 -0
  106. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/billing.py +0 -0
  107. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/bootstrap.py +0 -0
  108. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/changelog.py +0 -0
  109. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/cluster.py +0 -0
  110. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/config.py +0 -0
  111. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/container.py +0 -0
  112. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/dashboard.py +0 -0
  113. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/dict.py +0 -0
  114. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/entry_point.py +0 -0
  115. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/environment.py +0 -0
  116. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/import_refs.py +0 -0
  117. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/launch.py +0 -0
  118. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/logo.py +0 -0
  119. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/network_file_system.py +0 -0
  120. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/profile.py +0 -0
  121. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/programs/__init__.py +0 -0
  122. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/programs/run_jupyter.py +0 -0
  123. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/programs/vscode.py +0 -0
  124. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/queues.py +0 -0
  125. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/run.py +0 -0
  126. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/secret.py +0 -0
  127. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/selector.py +0 -0
  128. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/shell.py +0 -0
  129. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/skills.py +0 -0
  130. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/token.py +0 -0
  131. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cli/volume.py +0 -0
  132. {modal-1.4.4.dev21 → modal-1.5.0}/modal/client.py +0 -0
  133. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cloud_bucket_mount.py +0 -0
  134. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cloud_bucket_mount.pyi +0 -0
  135. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cls.py +0 -0
  136. {modal-1.4.4.dev21 → modal-1.5.0}/modal/cls.pyi +0 -0
  137. {modal-1.4.4.dev21 → modal-1.5.0}/modal/config.py +0 -0
  138. {modal-1.4.4.dev21 → modal-1.5.0}/modal/container_process.py +0 -0
  139. {modal-1.4.4.dev21 → modal-1.5.0}/modal/container_process.pyi +0 -0
  140. {modal-1.4.4.dev21 → modal-1.5.0}/modal/dict.py +0 -0
  141. {modal-1.4.4.dev21 → modal-1.5.0}/modal/dict.pyi +0 -0
  142. {modal-1.4.4.dev21 → modal-1.5.0}/modal/environments.py +0 -0
  143. {modal-1.4.4.dev21 → modal-1.5.0}/modal/environments.pyi +0 -0
  144. {modal-1.4.4.dev21 → modal-1.5.0}/modal/exception.py +0 -0
  145. {modal-1.4.4.dev21 → modal-1.5.0}/modal/experimental/__init__.py +0 -0
  146. {modal-1.4.4.dev21 → modal-1.5.0}/modal/experimental/flash.py +0 -0
  147. {modal-1.4.4.dev21 → modal-1.5.0}/modal/experimental/flash.pyi +0 -0
  148. {modal-1.4.4.dev21 → modal-1.5.0}/modal/experimental/ipython.py +0 -0
  149. {modal-1.4.4.dev21 → modal-1.5.0}/modal/file_io.py +0 -0
  150. {modal-1.4.4.dev21 → modal-1.5.0}/modal/file_io.pyi +0 -0
  151. {modal-1.4.4.dev21 → modal-1.5.0}/modal/file_pattern_matcher.py +0 -0
  152. {modal-1.4.4.dev21 → modal-1.5.0}/modal/functions.py +0 -0
  153. {modal-1.4.4.dev21 → modal-1.5.0}/modal/functions.pyi +0 -0
  154. {modal-1.4.4.dev21 → modal-1.5.0}/modal/image.py +0 -0
  155. {modal-1.4.4.dev21 → modal-1.5.0}/modal/image.pyi +0 -0
  156. {modal-1.4.4.dev21 → modal-1.5.0}/modal/io_streams.py +0 -0
  157. {modal-1.4.4.dev21 → modal-1.5.0}/modal/io_streams.pyi +0 -0
  158. {modal-1.4.4.dev21 → modal-1.5.0}/modal/mount.py +0 -0
  159. {modal-1.4.4.dev21 → modal-1.5.0}/modal/mount.pyi +0 -0
  160. {modal-1.4.4.dev21 → modal-1.5.0}/modal/network_file_system.py +0 -0
  161. {modal-1.4.4.dev21 → modal-1.5.0}/modal/network_file_system.pyi +0 -0
  162. {modal-1.4.4.dev21 → modal-1.5.0}/modal/object.py +0 -0
  163. {modal-1.4.4.dev21 → modal-1.5.0}/modal/object.pyi +0 -0
  164. {modal-1.4.4.dev21 → modal-1.5.0}/modal/output.py +0 -0
  165. {modal-1.4.4.dev21 → modal-1.5.0}/modal/parallel_map.py +0 -0
  166. {modal-1.4.4.dev21 → modal-1.5.0}/modal/parallel_map.pyi +0 -0
  167. {modal-1.4.4.dev21 → modal-1.5.0}/modal/partial_function.py +0 -0
  168. {modal-1.4.4.dev21 → modal-1.5.0}/modal/partial_function.pyi +0 -0
  169. {modal-1.4.4.dev21 → modal-1.5.0}/modal/proxy.py +0 -0
  170. {modal-1.4.4.dev21 → modal-1.5.0}/modal/proxy.pyi +0 -0
  171. {modal-1.4.4.dev21 → modal-1.5.0}/modal/py.typed +0 -0
  172. {modal-1.4.4.dev21 → modal-1.5.0}/modal/queue.py +0 -0
  173. {modal-1.4.4.dev21 → modal-1.5.0}/modal/queue.pyi +0 -0
  174. {modal-1.4.4.dev21 → modal-1.5.0}/modal/retries.py +0 -0
  175. {modal-1.4.4.dev21 → modal-1.5.0}/modal/runner.py +0 -0
  176. {modal-1.4.4.dev21 → modal-1.5.0}/modal/runner.pyi +0 -0
  177. {modal-1.4.4.dev21 → modal-1.5.0}/modal/running_app.py +0 -0
  178. {modal-1.4.4.dev21 → modal-1.5.0}/modal/sandbox_fs.py +0 -0
  179. {modal-1.4.4.dev21 → modal-1.5.0}/modal/sandbox_fs.pyi +0 -0
  180. {modal-1.4.4.dev21 → modal-1.5.0}/modal/schedule.py +0 -0
  181. {modal-1.4.4.dev21 → modal-1.5.0}/modal/scheduler_placement.py +0 -0
  182. {modal-1.4.4.dev21 → modal-1.5.0}/modal/secret.py +0 -0
  183. {modal-1.4.4.dev21 → modal-1.5.0}/modal/secret.pyi +0 -0
  184. {modal-1.4.4.dev21 → modal-1.5.0}/modal/server.py +0 -0
  185. {modal-1.4.4.dev21 → modal-1.5.0}/modal/serving.py +0 -0
  186. {modal-1.4.4.dev21 → modal-1.5.0}/modal/serving.pyi +0 -0
  187. {modal-1.4.4.dev21 → modal-1.5.0}/modal/skills/modal/SKILL.md +0 -0
  188. {modal-1.4.4.dev21 → modal-1.5.0}/modal/snapshot.py +0 -0
  189. {modal-1.4.4.dev21 → modal-1.5.0}/modal/snapshot.pyi +0 -0
  190. {modal-1.4.4.dev21 → modal-1.5.0}/modal/stream_type.py +0 -0
  191. {modal-1.4.4.dev21 → modal-1.5.0}/modal/token_flow.py +0 -0
  192. {modal-1.4.4.dev21 → modal-1.5.0}/modal/token_flow.pyi +0 -0
  193. {modal-1.4.4.dev21 → modal-1.5.0}/modal/volume.py +0 -0
  194. {modal-1.4.4.dev21 → modal-1.5.0}/modal/volume.pyi +0 -0
  195. {modal-1.4.4.dev21 → modal-1.5.0}/modal/workspace.py +0 -0
  196. {modal-1.4.4.dev21 → modal-1.5.0}/modal/workspace.pyi +0 -0
  197. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/SOURCES.txt +0 -0
  198. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/dependency_links.txt +0 -0
  199. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/entry_points.txt +0 -0
  200. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/requires.txt +0 -0
  201. {modal-1.4.4.dev21 → modal-1.5.0}/modal.egg-info/top_level.txt +0 -0
  202. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/__init__.py +0 -0
  203. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/gen_cli_docs.py +0 -0
  204. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/gen_cli_docs_main.py +0 -0
  205. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/gen_reference_docs.py +0 -0
  206. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/gen_reference_docs_main.py +0 -0
  207. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/mdmd/__init__.py +0 -0
  208. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/mdmd/mdmd.py +0 -0
  209. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/mdmd/signatures.py +0 -0
  210. {modal-1.4.4.dev21 → modal-1.5.0}/modal_docs/mdmd/types.py +0 -0
  211. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/__init__.py +0 -0
  212. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/api_grpc.py +0 -0
  213. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/api_pb2_grpc.py +0 -0
  214. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/api_pb2_grpc.pyi +0 -0
  215. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/modal_api_grpc.py +0 -0
  216. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/py.typed +0 -0
  217. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/task_command_router_grpc.py +0 -0
  218. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/task_command_router_pb2.py +0 -0
  219. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/task_command_router_pb2.pyi +0 -0
  220. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  221. {modal-1.4.4.dev21 → modal-1.5.0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  222. {modal-1.4.4.dev21 → modal-1.5.0}/modal_version/__main__.py +0 -0
  223. {modal-1.4.4.dev21 → modal-1.5.0}/pyproject.toml +0 -0
  224. {modal-1.4.4.dev21 → modal-1.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev21
3
+ Version: 1.5.0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -104,7 +104,7 @@ class _EnvironmentManager:
104
104
  MemberRole = Literal["viewer", "contributor"]
105
105
 
106
106
 
107
- def _role_to_proto(role: str) -> api_pb2.EnvironmentRole.ValueType:
107
+ def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
108
108
  match role:
109
109
  case "viewer":
110
110
  return api_pb2.ENVIRONMENT_ROLE_VIEWER
@@ -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):
@@ -1289,7 +1289,7 @@ 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
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)
@@ -1331,7 +1331,7 @@ 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
1336
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1337
1337
  cloud: Cloud provider (aws, gcp, oci, auto).
@@ -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,7 +1361,7 @@ 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,
@@ -1373,7 +1373,7 @@ class _App:
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,7 +794,7 @@ 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
799
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
800
800
  cloud: Cloud provider (aws, gcp, oci, auto).
@@ -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,7 +1790,7 @@ 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
1795
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1796
1796
  cloud: Cloud provider (aws, gcp, oci, auto).
@@ -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,12 +19,12 @@ from modal_proto import api_pb2
19
19
 
20
20
  from ._help import ModalGroup
21
21
 
22
- IMAGE_REGISTRY_LIST_PAGE_SIZE = 100
23
- IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS = 1.0
22
+ IMAGE_NAMES_LIST_PAGE_SIZE = 100
23
+ IMAGE_NAMES_LIST_PAGE_DELAY_SECONDS = 1.0
24
24
 
25
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)
26
+ image_names_cli = ModalGroup(name="names", help="Manage Modal Image names.")
27
+ image_cli.add_command(image_names_cli)
28
28
 
29
29
 
30
30
  def _print_result_summary(count: int) -> None:
@@ -60,7 +60,7 @@ async def _iter_tag_pages(
60
60
  api_pb2.ImageListTagsRequest(
61
61
  environment_name=env,
62
62
  tag_prefix=prefix,
63
- max_objects=IMAGE_REGISTRY_LIST_PAGE_SIZE,
63
+ max_objects=IMAGE_NAMES_LIST_PAGE_SIZE,
64
64
  page_token=page_token,
65
65
  )
66
66
  )
@@ -70,10 +70,10 @@ async def _iter_tag_pages(
70
70
  page_token = response.next_page_token
71
71
  if not page_token:
72
72
  return
73
- await asyncio.sleep(IMAGE_REGISTRY_LIST_PAGE_DELAY_SECONDS)
73
+ await asyncio.sleep(IMAGE_NAMES_LIST_PAGE_DELAY_SECONDS)
74
74
 
75
75
 
76
- @image_registry_cli.command("list", help="List named Images.")
76
+ @image_names_cli.command("list", help="List named Images.")
77
77
  @env_option
78
78
  @click.option("--prefix", default="", help="Only include named image tags that start with this prefix.")
79
79
  @click.option("--json", is_flag=True, default=False)
@@ -1,6 +1,7 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  import io
4
+ import re
4
5
  import sys
5
6
  from collections.abc import Sequence
6
7
  from contextlib import nullcontext
@@ -126,6 +127,11 @@ def _plain(text: Text | str) -> str:
126
127
  return text.plain if isinstance(text, Text) else text
127
128
 
128
129
 
130
+ def _col_name_to_json_key(name: str) -> str:
131
+ """Convert a display column name like "App ID" to a snake_case JSON key like "app_id"."""
132
+ return re.sub(r"[^a-zA-Z0-9]+", "_", name).lower().strip("_")
133
+
134
+
129
135
  def is_tty() -> bool:
130
136
  return OutputManager.get().is_terminal
131
137
 
@@ -145,7 +151,8 @@ def display_table(
145
151
 
146
152
  output = OutputManager.get()
147
153
  if json:
148
- json_data = [{col_to_str(col): _plain(row[i]) for i, col in enumerate(columns)} for row in rows]
154
+ json_keys = [_col_name_to_json_key(col_to_str(col)) for col in columns]
155
+ json_data = [{json_keys[i]: _plain(row[i]) for i in range(len(columns))} for row in rows]
149
156
  output.print_json(dumps(json_data))
150
157
  elif csv:
151
158
  csv_buffer = io.StringIO()
@@ -31,11 +31,7 @@ class _Client:
31
31
  client_type: int
32
32
 
33
33
  def __init__(
34
- self,
35
- server_url: str,
36
- client_type: int,
37
- credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.4.4.dev21",
34
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.5.0"
39
35
  ):
40
36
  """mdmd:hidden
41
37
  The Modal client object is not intended to be instantiated directly by users.
@@ -201,11 +197,7 @@ class Client:
201
197
  client_type: int
202
198
 
203
199
  def __init__(
204
- self,
205
- server_url: str,
206
- client_type: int,
207
- credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.4.4.dev21",
200
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.5.0"
209
201
  ):
210
202
  """mdmd:hidden
211
203
  The Modal client object is not intended to be instantiated directly by users.
@@ -3,12 +3,13 @@ import asyncio
3
3
  import builtins
4
4
  import enum
5
5
  import json
6
+ import logging
6
7
  import os
7
8
  import time
8
9
  import typing
9
10
  import uuid
10
11
  import weakref
11
- from collections.abc import AsyncGenerator, AsyncIterator, Collection, Sequence
12
+ from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Collection, Sequence
12
13
  from dataclasses import dataclass
13
14
  from pathlib import PurePosixPath
14
15
  from typing import TYPE_CHECKING, Any, Literal, Union, overload
@@ -71,6 +72,42 @@ from .stream_type import StreamType
71
72
  _default_image: _Image = _Image.debian_slim()
72
73
 
73
74
 
75
+ async def _gather_load_with_timings(
76
+ load_coros: Sequence[Awaitable[Any]],
77
+ ) -> list[tuple[str, float]]:
78
+ """Await all loader coroutines concurrently and return [(object_id, elapsed_seconds)] per load."""
79
+ timings: list[tuple[str, float]] = []
80
+
81
+ async def timed(coro: Awaitable[Any]) -> None:
82
+ start = time.monotonic()
83
+ obj = await coro
84
+ timings.append((obj.object_id, time.monotonic() - start))
85
+
86
+ await asyncio.gather(*(timed(c) for c in load_coros))
87
+ return timings
88
+
89
+
90
+ def _format_sandbox_create_timing_log(
91
+ sandbox_id: str,
92
+ total_seconds: float,
93
+ rpc_seconds: float,
94
+ dep_timings: Sequence[tuple[str, float]],
95
+ ) -> str:
96
+ """Format the Sandbox create debug log line, listing the slowest deps first."""
97
+ if dep_timings:
98
+ deps_sorted = sorted(dep_timings, key=lambda t: t[1], reverse=True)
99
+ shown = deps_sorted[:10]
100
+ dep_summary = ", ".join(f"{label}: {elapsed:.2f}s" for label, elapsed in shown)
101
+ if len(deps_sorted) > 10:
102
+ dep_summary += f", +{len(deps_sorted) - 10} more"
103
+ else:
104
+ dep_summary = "none"
105
+ return (
106
+ f"Sandbox {sandbox_id} created in {total_seconds:.2f}s "
107
+ f"(create rpc: {rpc_seconds:.2f}s; dependencies: {dep_summary})"
108
+ )
109
+
110
+
74
111
  # The maximum number of bytes that can be passed to an exec on Linux.
75
112
  # Though this is technically a 'server side' limit, it is unlikely to change.
76
113
  # getconf ARG_MAX will show this value on a host.
@@ -349,6 +386,7 @@ class _Sandbox(_Object, type_prefix="sb"):
349
386
  pty_info = _Sandbox._default_pty_info()
350
387
 
351
388
  async def _load(self: _Sandbox, resolver: Resolver, load_context: LoadContext, _existing_object_id: str | None):
389
+ load_start = time.monotonic()
352
390
  # An already-hydrated image (e.g. one returned by
353
391
  # `Sandbox.snapshot_directory`) is skipped — there's nothing to load.
354
392
  dep_tasks: list = []
@@ -365,8 +403,7 @@ class _Sandbox(_Object, type_prefix="sb"):
365
403
  dep_tasks.append(resolver.load(cloud_bucket_mount.secret, load_context))
366
404
  if proxy:
367
405
  dep_tasks.append(resolver.load(proxy, load_context))
368
- if dep_tasks:
369
- await asyncio.gather(*dep_tasks)
406
+ dep_timings = await _gather_load_with_timings(dep_tasks) if dep_tasks else []
370
407
 
371
408
  # Validate that the same volume (by object_id) isn't mounted at multiple paths
372
409
  validate_volumes_by_object_id(validated_volumes)
@@ -444,10 +481,16 @@ class _Sandbox(_Object, type_prefix="sb"):
444
481
  create_req = api_pb2.SandboxCreateRequest(
445
482
  app_id=load_context.app_id, definition=definition, tags=tag_protos
446
483
  )
484
+ rpc_start = time.monotonic()
447
485
  create_resp = await load_context.client.stub.SandboxCreate(create_req)
486
+ rpc_elapsed = time.monotonic() - rpc_start
448
487
  sandbox_id = create_resp.sandbox_id
449
488
  self._hydrate(sandbox_id, load_context.client, None)
450
489
 
490
+ if logger.isEnabledFor(logging.DEBUG):
491
+ total_elapsed = time.monotonic() - load_start
492
+ logger.debug(_format_sandbox_create_timing_log(sandbox_id, total_elapsed, rpc_elapsed, dep_timings))
493
+
451
494
  return _Sandbox._from_loader(_load, "Sandbox()", load_context_overrides=LoadContext.empty())
452
495
 
453
496
  @staticmethod
@@ -864,6 +907,7 @@ class _Sandbox(_Object, type_prefix="sb"):
864
907
  )
865
908
 
866
909
  async def _load(self: _Sandbox, resolver: Resolver, load_context: LoadContext, _existing_object_id: str | None):
910
+ load_start = time.monotonic()
867
911
  dep_tasks: list = []
868
912
  if not image._is_hydrated:
869
913
  dep_tasks.append(resolver.load(image, load_context))
@@ -874,8 +918,7 @@ class _Sandbox(_Object, type_prefix="sb"):
874
918
  for _, cloud_bucket_mount in cloud_bucket_mounts:
875
919
  if cloud_bucket_mount.secret:
876
920
  dep_tasks.append(resolver.load(cloud_bucket_mount.secret, load_context))
877
- if dep_tasks:
878
- await asyncio.gather(*dep_tasks)
921
+ dep_timings = await _gather_load_with_timings(dep_tasks) if dep_tasks else []
879
922
 
880
923
  validate_volumes_by_object_id(validated_volumes)
881
924
 
@@ -909,9 +952,11 @@ class _Sandbox(_Object, type_prefix="sb"):
909
952
  create_req = api_pb2.SandboxCreateV2Request(app_id=load_context.app_id, definition=definition)
910
953
  assert load_context.client._auth_token_manager
911
954
  auth_token = await load_context.client._auth_token_manager.get_token()
955
+ rpc_start = time.monotonic()
912
956
  create_resp = await load_context.client.stub.SandboxCreateV2(
913
957
  create_req, metadata=[("x-modal-auth-token", auth_token)]
914
958
  )
959
+ rpc_elapsed = time.monotonic() - rpc_start
915
960
  sandbox_id = create_resp.sandbox_id
916
961
  self._hydrate(sandbox_id, load_context.client, None)
917
962
  self._is_v2 = True
@@ -922,6 +967,10 @@ class _Sandbox(_Object, type_prefix="sb"):
922
967
  for t in create_resp.tunnels
923
968
  }
924
969
 
970
+ if logger.isEnabledFor(logging.DEBUG):
971
+ total_elapsed = time.monotonic() - load_start
972
+ logger.debug(_format_sandbox_create_timing_log(sandbox_id, total_elapsed, rpc_elapsed, dep_timings))
973
+
925
974
  obj = _Sandbox._from_loader(_load, "Sandbox()", load_context_overrides=LoadContext.empty())
926
975
 
927
976
  app_id: str | None = None
@@ -1247,8 +1296,6 @@ class _Sandbox(_Object, type_prefix="sb"):
1247
1296
  sandbox_session_2.filesystem.list_files("/user_project")
1248
1297
  ```
1249
1298
  """
1250
- self._ensure_v1("mount_image")
1251
-
1252
1299
  if not isinstance(image, _Image):
1253
1300
  raise TypeError(f"Sandbox.mount_image(image=...) expects an Image object, got {image!r}")
1254
1301
 
@@ -1299,8 +1346,6 @@ class _Sandbox(_Object, type_prefix="sb"):
1299
1346
  path: Absolute mount point directory to unmount.
1300
1347
 
1301
1348
  """
1302
- self._ensure_v1("unmount_image")
1303
-
1304
1349
  task_id = await self._get_task_id()
1305
1350
  command_router_client = await self._get_command_router_client(task_id)
1306
1351
 
@@ -1344,8 +1389,6 @@ class _Sandbox(_Object, type_prefix="sb"):
1344
1389
  sandbox_session_2.filesystem.list_files("/user_project")
1345
1390
  ```
1346
1391
  """
1347
- self._ensure_v1("snapshot_directory")
1348
-
1349
1392
  wire_ttl_seconds = _ttl_to_wire_ttl(ttl)
1350
1393
 
1351
1394
  task_id = await self._get_task_id()
@@ -29,6 +29,18 @@ import pathlib
29
29
  import typing
30
30
  import typing_extensions
31
31
 
32
+ async def _gather_load_with_timings(
33
+ load_coros: collections.abc.Sequence[collections.abc.Awaitable[typing.Any]],
34
+ ) -> list[tuple[str, float]]:
35
+ """Await all loader coroutines concurrently and return [(object_id, elapsed_seconds)] per load."""
36
+ ...
37
+
38
+ def _format_sandbox_create_timing_log(
39
+ sandbox_id: str, total_seconds: float, rpc_seconds: float, dep_timings: collections.abc.Sequence[tuple[str, float]]
40
+ ) -> str:
41
+ """Format the Sandbox create debug log line, listing the slowest deps first."""
42
+ ...
43
+
32
44
  def _ttl_to_wire_ttl(ttl: typing.Optional[int]) -> int:
33
45
  """Convert a TTL value to the wire format, validating the input."""
34
46
  ...
@@ -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):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev21
3
+ Version: 1.5.0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0