modal 1.5.1.dev14__tar.gz → 1.5.2__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 (236) hide show
  1. {modal-1.5.1.dev14 → modal-1.5.2}/PKG-INFO +1 -1
  2. {modal-1.5.1.dev14 → modal-1.5.2}/modal/__init__.py +2 -1
  3. modal-1.5.2/modal/_billing.py +83 -0
  4. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_environments.py +33 -29
  5. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_function_variants.py +4 -0
  6. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_functions.py +8 -16
  7. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_image.py +41 -13
  8. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/container_io_manager.py +1 -1
  9. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/gpu_memory_snapshot.py +5 -0
  10. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/task_lifecycle_manager.py +9 -4
  11. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_server.py +31 -4
  12. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/blob_utils.py +7 -0
  13. modal-1.5.2/modal/_utils/curl_utils.py +115 -0
  14. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/grpc_utils.py +80 -17
  15. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/name_utils.py +8 -2
  16. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/task_command_router_client.py +24 -8
  17. modal-1.5.2/modal/_workspace.py +388 -0
  18. {modal-1.5.1.dev14 → modal-1.5.2}/modal/app.py +15 -16
  19. {modal-1.5.1.dev14 → modal-1.5.2}/modal/app.pyi +30 -28
  20. {modal-1.5.1.dev14 → modal-1.5.2}/modal/billing.py +2 -1
  21. {modal-1.5.1.dev14 → modal-1.5.2}/modal/billing.pyi +27 -36
  22. {modal-1.5.1.dev14 → modal-1.5.2}/modal/call_graph.py +1 -30
  23. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/_download.py +2 -1
  24. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/_help.py +7 -1
  25. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/app.py +24 -3
  26. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/billing.py +9 -9
  27. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/changelog.py +7 -7
  28. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/cluster.py +1 -1
  29. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/config.py +2 -2
  30. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/container.py +33 -14
  31. modal-1.5.2/modal/cli/curl.py +144 -0
  32. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/dict.py +5 -5
  33. modal-1.5.2/modal/cli/endpoint.py +352 -0
  34. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/entry_point.py +8 -2
  35. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/environment.py +17 -18
  36. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/image.py +104 -0
  37. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/network_file_system.py +12 -6
  38. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/profile.py +21 -6
  39. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/queues.py +5 -5
  40. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/secret.py +2 -2
  41. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/shell.py +6 -2
  42. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/utils.py +8 -3
  43. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/volume.py +17 -9
  44. modal-1.5.2/modal/cli/workspace.py +175 -0
  45. {modal-1.5.1.dev14 → modal-1.5.2}/modal/client.pyi +2 -10
  46. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cls.py +5 -4
  47. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cls.pyi +4 -0
  48. {modal-1.5.1.dev14 → modal-1.5.2}/modal/config.py +4 -2
  49. {modal-1.5.1.dev14 → modal-1.5.2}/modal/dict.py +1 -13
  50. {modal-1.5.1.dev14 → modal-1.5.2}/modal/dict.pyi +4 -24
  51. {modal-1.5.1.dev14 → modal-1.5.2}/modal/environments.pyi +64 -17
  52. {modal-1.5.1.dev14 → modal-1.5.2}/modal/file_io.py +1 -1
  53. {modal-1.5.1.dev14 → modal-1.5.2}/modal/file_io.pyi +9 -9
  54. {modal-1.5.1.dev14 → modal-1.5.2}/modal/functions.pyi +16 -15
  55. {modal-1.5.1.dev14 → modal-1.5.2}/modal/network_file_system.pyi +7 -7
  56. {modal-1.5.1.dev14 → modal-1.5.2}/modal/queue.py +1 -13
  57. {modal-1.5.1.dev14 → modal-1.5.2}/modal/queue.pyi +4 -24
  58. {modal-1.5.1.dev14 → modal-1.5.2}/modal/sandbox.py +228 -73
  59. {modal-1.5.1.dev14 → modal-1.5.2}/modal/sandbox.pyi +322 -69
  60. {modal-1.5.1.dev14 → modal-1.5.2}/modal/sandbox_fs.py +1 -64
  61. {modal-1.5.1.dev14 → modal-1.5.2}/modal/sandbox_fs.pyi +14 -117
  62. {modal-1.5.1.dev14 → modal-1.5.2}/modal/secret.py +40 -22
  63. {modal-1.5.1.dev14 → modal-1.5.2}/modal/secret.pyi +23 -24
  64. {modal-1.5.1.dev14 → modal-1.5.2}/modal/server.pyi +65 -12
  65. modal-1.5.2/modal/types.py +289 -0
  66. {modal-1.5.1.dev14 → modal-1.5.2}/modal/volume.py +22 -39
  67. {modal-1.5.1.dev14 → modal-1.5.2}/modal/volume.pyi +34 -73
  68. modal-1.5.2/modal/workspace.py +30 -0
  69. modal-1.5.2/modal/workspace.pyi +578 -0
  70. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/PKG-INFO +1 -1
  71. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/SOURCES.txt +5 -0
  72. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/gen_cli_docs.py +22 -7
  73. modal-1.5.2/modal_docs/gen_reference_docs.py +361 -0
  74. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/mdmd/mdmd.py +139 -23
  75. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/mdmd/signatures.py +28 -0
  76. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/mdmd/types.py +1 -0
  77. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/api_grpc.py +176 -0
  78. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/api_pb2.py +1149 -966
  79. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/api_pb2.pyi +430 -44
  80. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/api_pb2_grpc.py +367 -3
  81. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/api_pb2_grpc.pyi +120 -8
  82. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/modal_api_grpc.py +11 -0
  83. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/task_command_router_grpc.py +16 -0
  84. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/task_command_router_pb2.py +71 -41
  85. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/task_command_router_pb2.pyi +40 -3
  86. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/task_command_router_pb2_grpc.py +34 -0
  87. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/task_command_router_pb2_grpc.pyi +12 -0
  88. {modal-1.5.1.dev14 → modal-1.5.2}/modal_version/__init__.py +1 -1
  89. modal-1.5.1.dev14/modal/_billing.py +0 -155
  90. modal-1.5.1.dev14/modal/_workspace.py +0 -191
  91. modal-1.5.1.dev14/modal/workspace.py +0 -13
  92. modal-1.5.1.dev14/modal/workspace.pyi +0 -139
  93. modal-1.5.1.dev14/modal_docs/gen_reference_docs.py +0 -177
  94. {modal-1.5.1.dev14 → modal-1.5.2}/LICENSE +0 -0
  95. {modal-1.5.1.dev14 → modal-1.5.2}/README.md +0 -0
  96. {modal-1.5.1.dev14 → modal-1.5.2}/modal/__main__.py +0 -0
  97. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_clustered_functions.py +0 -0
  98. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_clustered_functions.pyi +0 -0
  99. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_container_entrypoint.py +0 -0
  100. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_grpc_client.py +0 -0
  101. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_ipython.py +0 -0
  102. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_load_context.py +0 -0
  103. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_location.py +0 -0
  104. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_logs.py +0 -0
  105. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_object.py +0 -0
  106. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_output/__init__.py +0 -0
  107. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_output/manager.py +0 -0
  108. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_output/pty.py +0 -0
  109. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_output/rich.py +0 -0
  110. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_output/status.py +0 -0
  111. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_partial_function.py +0 -0
  112. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_resolver.py +0 -0
  113. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_resources.py +0 -0
  114. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/__init__.py +0 -0
  115. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/asgi.py +0 -0
  116. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/container_io_manager.pyi +0 -0
  117. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/execution_context.py +0 -0
  118. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/execution_context.pyi +0 -0
  119. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  120. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/telemetry.py +0 -0
  121. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/user_code_event_loop.py +0 -0
  122. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_runtime/user_code_imports.py +0 -0
  123. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_serialization.py +0 -0
  124. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_traceback.py +0 -0
  125. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_tunnel.py +0 -0
  126. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_tunnel.pyi +0 -0
  127. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_type_manager.py +0 -0
  128. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/__init__.py +0 -0
  129. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/app_utils.py +0 -0
  130. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/async_utils.py +0 -0
  131. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/auth_token_manager.py +0 -0
  132. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/browser_utils.py +0 -0
  133. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/bytes_io_segment_payload.py +0 -0
  134. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/deprecation.py +0 -0
  135. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/docker_utils.py +0 -0
  136. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/function_utils.py +0 -0
  137. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/git_utils.py +0 -0
  138. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/grpc_testing.py +0 -0
  139. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/hash_utils.py +0 -0
  140. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/http_utils.py +0 -0
  141. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/jwt_utils.py +0 -0
  142. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/logger.py +0 -0
  143. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/mount_utils.py +0 -0
  144. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/package_utils.py +0 -0
  145. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/pattern_utils.py +0 -0
  146. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/proxy_support.py +0 -0
  147. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/rand_pb_testing.py +0 -0
  148. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/sandbox_fs_utils.py +0 -0
  149. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/shell_utils.py +0 -0
  150. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_utils/time_utils.py +0 -0
  151. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_vendor/__init__.py +0 -0
  152. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  153. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_vendor/cloudpickle.py +0 -0
  154. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_vendor/tblib.py +0 -0
  155. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_vendor/version.py +0 -0
  156. {modal-1.5.1.dev14 → modal-1.5.2}/modal/_watcher.py +0 -0
  157. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/2023.12.312.txt +0 -0
  158. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/2023.12.txt +0 -0
  159. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/2024.04.txt +0 -0
  160. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/2024.10.txt +0 -0
  161. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/2025.06.txt +0 -0
  162. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/PREVIEW.txt +0 -0
  163. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/README.md +0 -0
  164. {modal-1.5.1.dev14 → modal-1.5.2}/modal/builder/base-images.json +0 -0
  165. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/__init__.py +0 -0
  166. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/_traceback.py +0 -0
  167. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/bootstrap.py +0 -0
  168. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/dashboard.py +0 -0
  169. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/import_refs.py +0 -0
  170. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/launch.py +0 -0
  171. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/logo.py +0 -0
  172. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/programs/__init__.py +0 -0
  173. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/programs/run_jupyter.py +0 -0
  174. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/programs/vscode.py +0 -0
  175. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/run.py +0 -0
  176. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/selector.py +0 -0
  177. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/skills.py +0 -0
  178. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cli/token.py +0 -0
  179. {modal-1.5.1.dev14 → modal-1.5.2}/modal/client.py +0 -0
  180. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cloud_bucket_mount.py +0 -0
  181. {modal-1.5.1.dev14 → modal-1.5.2}/modal/cloud_bucket_mount.pyi +0 -0
  182. {modal-1.5.1.dev14 → modal-1.5.2}/modal/container_process.py +0 -0
  183. {modal-1.5.1.dev14 → modal-1.5.2}/modal/container_process.pyi +0 -0
  184. {modal-1.5.1.dev14 → modal-1.5.2}/modal/environments.py +0 -0
  185. {modal-1.5.1.dev14 → modal-1.5.2}/modal/exception.py +0 -0
  186. {modal-1.5.1.dev14 → modal-1.5.2}/modal/experimental/__init__.py +0 -0
  187. {modal-1.5.1.dev14 → modal-1.5.2}/modal/experimental/flash.py +0 -0
  188. {modal-1.5.1.dev14 → modal-1.5.2}/modal/experimental/flash.pyi +0 -0
  189. {modal-1.5.1.dev14 → modal-1.5.2}/modal/experimental/ipython.py +0 -0
  190. {modal-1.5.1.dev14 → modal-1.5.2}/modal/file_pattern_matcher.py +0 -0
  191. {modal-1.5.1.dev14 → modal-1.5.2}/modal/functions.py +0 -0
  192. {modal-1.5.1.dev14 → modal-1.5.2}/modal/image.py +0 -0
  193. {modal-1.5.1.dev14 → modal-1.5.2}/modal/image.pyi +0 -0
  194. {modal-1.5.1.dev14 → modal-1.5.2}/modal/io_streams.py +0 -0
  195. {modal-1.5.1.dev14 → modal-1.5.2}/modal/io_streams.pyi +0 -0
  196. {modal-1.5.1.dev14 → modal-1.5.2}/modal/mount.py +0 -0
  197. {modal-1.5.1.dev14 → modal-1.5.2}/modal/mount.pyi +0 -0
  198. {modal-1.5.1.dev14 → modal-1.5.2}/modal/network_file_system.py +0 -0
  199. {modal-1.5.1.dev14 → modal-1.5.2}/modal/object.py +0 -0
  200. {modal-1.5.1.dev14 → modal-1.5.2}/modal/object.pyi +0 -0
  201. {modal-1.5.1.dev14 → modal-1.5.2}/modal/output.py +0 -0
  202. {modal-1.5.1.dev14 → modal-1.5.2}/modal/parallel_map.py +0 -0
  203. {modal-1.5.1.dev14 → modal-1.5.2}/modal/parallel_map.pyi +0 -0
  204. {modal-1.5.1.dev14 → modal-1.5.2}/modal/partial_function.py +0 -0
  205. {modal-1.5.1.dev14 → modal-1.5.2}/modal/partial_function.pyi +0 -0
  206. {modal-1.5.1.dev14 → modal-1.5.2}/modal/proxy.py +0 -0
  207. {modal-1.5.1.dev14 → modal-1.5.2}/modal/proxy.pyi +0 -0
  208. {modal-1.5.1.dev14 → modal-1.5.2}/modal/py.typed +0 -0
  209. {modal-1.5.1.dev14 → modal-1.5.2}/modal/retries.py +0 -0
  210. {modal-1.5.1.dev14 → modal-1.5.2}/modal/runner.py +0 -0
  211. {modal-1.5.1.dev14 → modal-1.5.2}/modal/runner.pyi +0 -0
  212. {modal-1.5.1.dev14 → modal-1.5.2}/modal/running_app.py +0 -0
  213. {modal-1.5.1.dev14 → modal-1.5.2}/modal/schedule.py +0 -0
  214. {modal-1.5.1.dev14 → modal-1.5.2}/modal/scheduler_placement.py +0 -0
  215. {modal-1.5.1.dev14 → modal-1.5.2}/modal/server.py +0 -0
  216. {modal-1.5.1.dev14 → modal-1.5.2}/modal/serving.py +0 -0
  217. {modal-1.5.1.dev14 → modal-1.5.2}/modal/serving.pyi +0 -0
  218. {modal-1.5.1.dev14 → modal-1.5.2}/modal/skills/modal/SKILL.md +0 -0
  219. {modal-1.5.1.dev14 → modal-1.5.2}/modal/snapshot.py +0 -0
  220. {modal-1.5.1.dev14 → modal-1.5.2}/modal/snapshot.pyi +0 -0
  221. {modal-1.5.1.dev14 → modal-1.5.2}/modal/stream_type.py +0 -0
  222. {modal-1.5.1.dev14 → modal-1.5.2}/modal/token_flow.py +0 -0
  223. {modal-1.5.1.dev14 → modal-1.5.2}/modal/token_flow.pyi +0 -0
  224. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/dependency_links.txt +0 -0
  225. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/entry_points.txt +0 -0
  226. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/requires.txt +0 -0
  227. {modal-1.5.1.dev14 → modal-1.5.2}/modal.egg-info/top_level.txt +0 -0
  228. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/__init__.py +0 -0
  229. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/gen_cli_docs_main.py +0 -0
  230. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/gen_reference_docs_main.py +0 -0
  231. {modal-1.5.1.dev14 → modal-1.5.2}/modal_docs/mdmd/__init__.py +0 -0
  232. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/__init__.py +0 -0
  233. {modal-1.5.1.dev14 → modal-1.5.2}/modal_proto/py.typed +0 -0
  234. {modal-1.5.1.dev14 → modal-1.5.2}/modal_version/__main__.py +0 -0
  235. {modal-1.5.1.dev14 → modal-1.5.2}/pyproject.toml +0 -0
  236. {modal-1.5.1.dev14 → modal-1.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.1.dev14
3
+ Version: 1.5.2
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -9,7 +9,7 @@ if sys.version_info[:2] >= (3, 15):
9
9
  from modal_version import __version__
10
10
 
11
11
  try:
12
- from . import billing
12
+ from . import billing, types
13
13
  from ._runtime.execution_context import current_function_call_id, current_input_id, interact, is_local
14
14
  from ._tunnel import Tunnel, forward
15
15
  from .app import App
@@ -100,6 +100,7 @@ __all__ = [
100
100
  "interact",
101
101
  "method",
102
102
  "parameter",
103
+ "types",
103
104
  "web_endpoint",
104
105
  "web_server",
105
106
  "wsgi_app",
@@ -0,0 +1,83 @@
1
+ # Copyright Modal Labs 2025
2
+ from datetime import datetime
3
+ from decimal import Decimal
4
+ from typing import TypedDict
5
+
6
+ from ._utils.deprecation import deprecation_warning
7
+ from .client import _Client
8
+
9
+
10
+ class WorkspaceBillingReportItem(TypedDict):
11
+ object_id: str
12
+ description: str
13
+ environment_name: str
14
+ interval_start: datetime
15
+ cost: Decimal
16
+ tags: dict[str, str]
17
+
18
+
19
+ async def _workspace_billing_report(
20
+ *,
21
+ start: datetime, # Start of the report, inclusive
22
+ end: datetime | None = None, # End of the report, exclusive
23
+ resolution: str = "d", # Resolution, e.g. "d" for daily or "h" for hourly
24
+ tag_names: list[str] | None = None, # Optional additional metadata to include
25
+ client: _Client | None = None,
26
+ ) -> list[WorkspaceBillingReportItem]:
27
+ """Generate a tabular report of workspace usage by object and time.
28
+
29
+ The result will be a list of dictionaries for each interval (determined by `resolution`)
30
+ between the `start` and `end` limits. The dictionary represents a single Modal object
31
+ that billing can be attributed to (e.g., an App) along with metadata (including user-defined
32
+ tags) for identifying that object. The dictionary also contains a breakdown of the cost value
33
+ attributed to individual resources (for an App, this can be CPU, Memory, specific GPU types,
34
+ etc.). The specific resource types included in the breakdown are subject to change as
35
+ Modal's billing model evolves.
36
+
37
+ The `start` and `end` parameters are required to either have a UTC timezone or to be
38
+ timezone-naive (which will be interpreted as UTC times). The timestamps in the result will
39
+ be in UTC. Cost will be reported for full intervals, even if the provided `start` or `end`
40
+ parameters are partial: `start` will be rounded to the beginning of its interval, while
41
+ partial `end` intervals will be excluded.
42
+
43
+ Additional user-provided metadata can be included in the report if the objects have tags
44
+ and `tag_names` (i.e., keys) are specified in the request. Alternatively, pass `tag_names=["*"]`
45
+ to include all tags in the report. Note that tags will be attributed to the entire interval even
46
+ if they were added or removed at some point within it. If the tag name was not in use during an
47
+ interval, it will be absent from the tags dictionary in that output row.
48
+
49
+ It's also possible to generate reports using the
50
+ [`modal billing report`](https://modal.com/docs/cli/latest/billing) CLI command. The CLI
51
+ has a few convenience features for generating reports across relative time ranges.
52
+
53
+ """
54
+
55
+ deprecation_warning(
56
+ (2026, 6, 18),
57
+ "The `modal.billing.workspace_billing_report()` function is deprecated. "
58
+ "Use the `workspace.billing.report()` method on the `modal.Workspace` object instead.",
59
+ )
60
+
61
+ from ._workspace import _Workspace
62
+
63
+ data = await _Workspace.from_context(client=client).billing.report(
64
+ start=start,
65
+ end=end,
66
+ resolution=resolution,
67
+ tag_names=tag_names,
68
+ )
69
+
70
+ res: list[WorkspaceBillingReportItem] = []
71
+ for datum in data:
72
+ item: WorkspaceBillingReportItem = {
73
+ "object_id": datum.object_id,
74
+ "description": datum.description,
75
+ "environment_name": datum.environment_name,
76
+ "interval_start": datum.interval_start,
77
+ "cost": Decimal(datum.cost),
78
+ "tags": dict(datum.tags),
79
+ }
80
+
81
+ res.append(item)
82
+
83
+ return res
@@ -13,7 +13,6 @@ from synchronicity import classproperty
13
13
 
14
14
  from modal_proto import api_pb2
15
15
 
16
- from ._billing import BILLING_DOCSTRING, BillingReportItem
17
16
  from ._load_context import LoadContext
18
17
  from ._object import _Object
19
18
  from ._resolver import Resolver
@@ -21,6 +20,7 @@ from ._utils.name_utils import check_environment_name
21
20
  from .client import _Client
22
21
  from .config import config, logger
23
22
  from .exception import InvalidError, WorkspaceManagementError
23
+ from .types import BillingReportItem
24
24
 
25
25
 
26
26
  @dataclass(frozen=True)
@@ -103,9 +103,6 @@ class _EnvironmentManager:
103
103
  await client.stub.EnvironmentDelete(api_pb2.EnvironmentDeleteRequest(name=name))
104
104
 
105
105
 
106
- MemberRole = Literal["viewer", "contributor"]
107
-
108
-
109
106
  def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
110
107
  match role:
111
108
  case "viewer":
@@ -116,7 +113,7 @@ def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
116
113
  raise InvalidError(f"Invalid Environment role: {role!r} (expected 'viewer' or 'contributor')")
117
114
 
118
115
 
119
- def _role_from_proto(proto_value: int) -> MemberRole:
116
+ def _role_from_proto(proto_value: int) -> str:
120
117
  match proto_value:
121
118
  case api_pb2.ENVIRONMENT_ROLE_VIEWER:
122
119
  return "viewer"
@@ -137,7 +134,7 @@ class _EnvironmentMembersManager:
137
134
  """mdmd:hidden"""
138
135
  self._environment = environment
139
136
 
140
- async def list(self) -> dict[Literal["users", "service_users"], dict[str, MemberRole]]:
137
+ async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
141
138
  """Return the members of a restricted Environment with their roles.
142
139
 
143
140
  **Examples:**
@@ -155,8 +152,8 @@ class _EnvironmentMembersManager:
155
152
  req = api_pb2.EnvironmentGetManagedRequest(environment_id=self._environment.object_id)
156
153
  resp = await self._environment.client.stub.EnvironmentGetManaged(req)
157
154
 
158
- users: dict[str, MemberRole] = {}
159
- service_users: dict[str, MemberRole] = {}
155
+ users: dict[str, str] = {}
156
+ service_users: dict[str, str] = {}
160
157
  for principal in resp.principal_roles:
161
158
  role = _role_from_proto(principal.role)
162
159
  if principal.user_id:
@@ -169,8 +166,8 @@ class _EnvironmentMembersManager:
169
166
  async def update(
170
167
  self,
171
168
  *,
172
- users: Mapping[str, MemberRole] | None = None,
173
- service_users: Mapping[str, MemberRole] | None = None,
169
+ users: Mapping[str, str] | None = None,
170
+ service_users: Mapping[str, str] | None = None,
174
171
  ) -> None:
175
172
  """Add or modify roles for members of a restricted Environment.
176
173
 
@@ -416,26 +413,33 @@ class _EnvironmentBillingManager:
416
413
  resolution: str = "d", # Resolution, e.g. "d" for daily or "h" for hourly
417
414
  tag_names: list[str] | None = None, # Optional additional metadata to include
418
415
  ) -> list[BillingReportItem]:
419
- (
420
- """Return a report of workspace usage by object and time, scoped to the calling Environment.
421
-
422
- The result will be a list of dataclasses for each interval (determined by `resolution`)
423
- between the `start` and `end` limits. Each item represents a single (Modal object, time interval)
424
- pair that billing can be attributed to (e.g., an App) along with metadata (including user-defined
425
- tags) to identify that object. The dataclass also contains a breakdown of the cost value
426
- attributed to individual resources (for an App, this can be CPU, Memory, specific GPU types,
427
- etc.). The specific resource types included in the breakdown are subject to change as
428
- Modal's billing model evolves.
429
-
430
- It's also possible to generate reports using the
431
- [`modal environment billing report`](https://modal.com/docs/cli/latest/environment#modal-environment-billing-report)
432
- CLI command. The CLI has a few convenience features for generating
433
- reports across relative time ranges.
434
-
435
- """
436
- + BILLING_DOCSTRING
437
- )
416
+ """Return a cost report for Environment usage, broken down by object and time.
417
+
418
+ Args:
419
+ start: Start of the report, inclusive and rounded to the beginning of the interval.
420
+ Must be in UTC or timezone-naive (interpreted as UTC).
421
+ end: End of the report, exclusive. Must be in UTC or timezone-naive. Partial final
422
+ intervals will be excluded from the report.
423
+ resolution: Resolution, e.g. "d" for daily or "h" for hourly.
424
+ tag_names: List of tag names; each row will include the tag name and value in use
425
+ for that object during the relevant time interval. Pass `["*"]` to include all
426
+ tags in the report.
427
+
428
+ Returns:
429
+ A list of `BillingReportItem` dataclasses. Each item reports the cost attributed to
430
+ a specific Modal object during a given time interval. Cost is further broken down by
431
+ the resource type that generated it (e.g. CPU, Memory, specific GPU usage).
432
+ Note that the specific resource types included in the breakdown are subject to change
433
+ as Modal's billing model evolves.
434
+
435
+ See also:
436
+ - [`modal environment billing report`](https://modal.com/docs/cli/latest/environment#modal-environment-billing-report):
437
+ An environment report CLI that has convenience features around relative time range queries
438
+ and JSON/CSV output.
439
+ - [`Workspace.billing.report()`](https://modal.com/docs/sdk/py/latest/modal.Workspace#billingreport):
440
+ An analogous report API for the entire Workspace.
438
441
 
442
+ """
439
443
  if tag_names is None:
440
444
  tag_names = []
441
445
 
@@ -51,6 +51,7 @@ class _FunctionOptions:
51
51
  target_concurrent_inputs: int | None = None
52
52
  batch_max_size: int | None = None
53
53
  batch_wait_ms: int | None = None
54
+ routing_region: str | None = None
54
55
 
55
56
  @classmethod
56
57
  def new(
@@ -73,6 +74,7 @@ class _FunctionOptions:
73
74
  target_concurrent_inputs: int | None = None,
74
75
  batch_max_size: int | None = None,
75
76
  batch_wait_ms: int | None = None,
77
+ routing_region: str | None = None,
76
78
  ) -> "_FunctionOptions":
77
79
  """Internal constructor that validates and normalizes public parameters."""
78
80
  retry_policy = _parse_retries(retries)
@@ -119,6 +121,7 @@ class _FunctionOptions:
119
121
  target_concurrent_inputs=target_concurrent_inputs,
120
122
  batch_max_size=batch_max_size,
121
123
  batch_wait_ms=batch_wait_ms,
124
+ routing_region=routing_region,
122
125
  )
123
126
 
124
127
  def merge_options(self, new_options: "_FunctionOptions") -> "_FunctionOptions":
@@ -182,6 +185,7 @@ class _FunctionOptions:
182
185
  batch_linger_ms=self.batch_wait_ms,
183
186
  scheduler_placement=self.scheduler_placement,
184
187
  cloud_provider_str=self.cloud,
188
+ routing_region=self.routing_region,
185
189
  )
186
190
 
187
191
 
@@ -90,6 +90,7 @@ from .proxy import _Proxy
90
90
  from .retries import Retries, RetryManager
91
91
  from .schedule import Schedule
92
92
  from .secret import _Secret
93
+ from .types import FunctionStats
93
94
  from .volume import _Volume, _volume_to_mount_proto
94
95
 
95
96
  if TYPE_CHECKING:
@@ -541,17 +542,6 @@ class _InputPlaneInvocation:
541
542
  return [("x-modal-input-plane-region", input_plane_region), ("x-modal-auth-token", token)]
542
543
 
543
544
 
544
- # Wrapper type for api_pb2.FunctionStats
545
- @dataclass(frozen=True)
546
- class FunctionStats:
547
- """Simple data structure storing stats for a running function."""
548
-
549
- backlog: int
550
- num_total_runners: int
551
- num_running_inputs: int
552
- input_headroom: int
553
-
554
-
555
545
  @dataclass
556
546
  class _FunctionSpec:
557
547
  """
@@ -1195,10 +1185,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1195
1185
  its static configuration.
1196
1186
 
1197
1187
  Args:
1198
- min_containers: Minimum number of containers to keep running, or `None` to leave unchanged.
1199
- max_containers: Maximum concurrent containers, or `None` to leave unchanged.
1200
- buffer_containers: Extra containers to keep warm beyond demand, or `None` to leave unchanged.
1201
- scaledown_window: Seconds idle containers wait before scaling down, or `None` to leave unchanged.
1188
+ min_containers: Minimum number of containers to keep running.
1189
+ max_containers: Maximum concurrent containers.
1190
+ buffer_containers: Extra containers to keep warm beyond current demand.
1191
+ scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
1202
1192
 
1203
1193
  Examples:
1204
1194
  ```python notest
@@ -1212,7 +1202,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1212
1202
 
1213
1203
  # Extend the scaledown window to increase the amount of time that idle containers stay alive
1214
1204
  f.update_autoscaler(scaledown_window=300)
1215
- ```
1205
+ ```
1216
1206
 
1217
1207
  """
1218
1208
  # Assert .update_autoscaler() is not called on a method as opposed to the Object. Applicable for Cls only.
@@ -1519,6 +1509,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1519
1509
  timeout: int | None = None,
1520
1510
  region: str | Sequence[str] | None = None,
1521
1511
  cloud: str | None = None,
1512
+ routing_region: str | None = None,
1522
1513
  ) -> "_Function[P, ReturnType, OriginalReturnType]":
1523
1514
  """Dynamically override the static Function configuration with invocation-specific values.
1524
1515
 
@@ -1567,6 +1558,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1567
1558
  timeout=timeout,
1568
1559
  region=region,
1569
1560
  cloud=cloud,
1561
+ routing_region=routing_region,
1570
1562
  )
1571
1563
 
1572
1564
  return self._apply_dynamic_config(options, "with_options")
@@ -117,13 +117,34 @@ def _validate_image_tag(tag: str) -> None:
117
117
  check_object_name(tag, "Image tag")
118
118
 
119
119
 
120
- def _parse_named_image_ref(name: str) -> str:
120
+ def _parse_named_image_ref(name: str) -> tuple[str, str]:
121
+ """Parse an image reference, returning (namespace_prefix, name_tag).
122
+
123
+ If the name contains a '/', the part before the last '/' is extracted as
124
+ a namespace prefix (intended for environment/name or workspace/env/name
125
+ syntax). The actual image name (after the last '/') is validated as a
126
+ standard object name.
127
+
128
+ Returns a tuple of (prefix, "full_name:tag") where prefix is empty string
129
+ if no '/' is present.
130
+ """
121
131
  image_name, sep, tag = name.partition(":")
122
132
  if not sep:
123
133
  tag = "latest"
134
+
135
+ prefix = ""
136
+ if "/" in image_name:
137
+ prefix, image_name = image_name.rsplit("/", 1)
138
+ if not prefix:
139
+ raise InvalidError("Invalid Image name: '/' prefix must be non-empty.")
140
+ if not image_name:
141
+ raise InvalidError("Invalid Image name: name after '/' must be non-empty.")
142
+
124
143
  _validate_image_name(image_name)
125
144
  _validate_image_tag(tag)
126
- return f"{image_name}:{tag}"
145
+
146
+ full_name = f"{prefix}/{image_name}" if prefix else image_name
147
+ return prefix, f"{full_name}:{tag}"
127
148
 
128
149
 
129
150
  def _validate_python_version(
@@ -1022,7 +1043,7 @@ class _Image(_Object, type_prefix="im"):
1022
1043
  ```
1023
1044
  """
1024
1045
  if app.app_id is None:
1025
- raise InvalidError("App has not been initialized yet. Use the content manager `app.run()` or `App.lookup`")
1046
+ raise InvalidError("App has not been initialized yet. Use the context manager `app.run()` or `App.lookup`")
1026
1047
 
1027
1048
  resolver = Resolver()
1028
1049
  async with TaskContext() as tc:
@@ -2923,12 +2944,15 @@ class _Image(_Object, type_prefix="im"):
2923
2944
  ...
2924
2945
  ```
2925
2946
  """
2926
- tag = _parse_named_image_ref(name)
2947
+ namespace_prefix, tag = _parse_named_image_ref(name)
2948
+
2949
+ if namespace_prefix and environment_name:
2950
+ raise InvalidError("Cannot specify 'environment_name' when the image name contains a '/'.")
2927
2951
 
2928
2952
  async def _load(self: _Image, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
2929
2953
  req = api_pb2.ImageGetByTagRequest(
2930
2954
  tag=tag,
2931
- environment_name=load_context.environment_name,
2955
+ environment_name="" if namespace_prefix else load_context.environment_name,
2932
2956
  )
2933
2957
  response = await load_context.client.stub.ImageGetByTag(req)
2934
2958
  self._hydrate(response.image_id, load_context.client, None)
@@ -2963,21 +2987,25 @@ class _Image(_Object, type_prefix="im"):
2963
2987
  image.publish("my-image-with-numpy:v1")
2964
2988
  ```
2965
2989
  """
2966
- tag = _parse_named_image_ref(name)
2990
+ namespace_prefix, tag = _parse_named_image_ref(name)
2967
2991
 
2968
- if not self.is_hydrated:
2969
- raise InvalidError("Cannot publish an image that has not been created yet. Call `.build()` first.")
2992
+ if namespace_prefix:
2993
+ if environment_name:
2994
+ raise InvalidError("Cannot specify 'environment_name' when the image name contains a '/'.")
2995
+ resolved_env = ""
2996
+ else:
2997
+ resolved_env = environment_name or config.get("environment") or ""
2970
2998
 
2971
- _client = client or self._client
2972
- assert _client is not None
2999
+ if self._object_id is None:
3000
+ raise InvalidError("Cannot publish an image that has not been created yet. Call `.build()` first.")
2973
3001
 
2974
- resolved_env = environment_name or config.get("environment") or ""
3002
+ _client = client or await _Client.from_env()
2975
3003
 
2976
3004
  await _client.stub.ImagePublish(
2977
3005
  api_pb2.ImagePublishRequest(
2978
- image_id=self.object_id,
3006
+ image_id=self._object_id,
2979
3007
  environment_name=resolved_env,
2980
- is_public=False,
3008
+ allow_public=False,
2981
3009
  tag=tag,
2982
3010
  )
2983
3011
  )
@@ -264,7 +264,7 @@ class IOContext:
264
264
  if not inspect.isasyncgen(expected_async_gen):
265
265
  raise InvalidError(f"Async generator function returned value of type {type(expected_async_gen)}")
266
266
 
267
- async with aclosing(expected_async_gen) as gen:
267
+ async with aclosing(cast(AsyncGenerator[Any, None], expected_async_gen)) as gen:
268
268
  async for result in gen:
269
269
  yield result
270
270
  logger.debug(f"Finished generator input {self.input_ids}")
@@ -17,6 +17,11 @@ from modal.config import config, logger
17
17
 
18
18
  CUDA_CHECKPOINT_PATH: str = config.get("cuda_checkpoint_path")
19
19
 
20
+ # Exit code used to signal to modal-runtime that a GPU memory snapshot restore failed
21
+ # and the task should be retried without a snapshot. This is a sentinel value that must
22
+ # stay in sync with `GPU_SNAPSHOT_RESTORE_FAILED_EXIT_CODE` in runner.rs.
23
+ SNAPSHOT_RESTORE_FAILED_EXIT_CODE: int = 222
24
+
20
25
  # Maximum total duration for each individual `cuda-checkpoint` invocation.
21
26
  CUDA_CHECKPOINT_TIMEOUT: float = 3 * 60.0
22
27
 
@@ -180,10 +180,15 @@ class _TaskLifecycleManager:
180
180
  if self.function_def._experimental_enable_gpu_snapshot and self.function_def.resources.gpu_config.gpu_type:
181
181
  logger.debug("GPU memory snapshot enabled. Attempting to restore GPU memory.")
182
182
 
183
- assert self._cuda_checkpoint_session, (
184
- "CudaCheckpointSession not found when attempting to restore GPU memory"
185
- )
186
- self._cuda_checkpoint_session.restore()
183
+ try:
184
+ if self._cuda_checkpoint_session is None:
185
+ raise gpu_memory_snapshot.CudaCheckpointException("CudaCheckpointSession not found")
186
+ self._cuda_checkpoint_session.restore()
187
+ except gpu_memory_snapshot.CudaCheckpointException as exc:
188
+ logger.warning(f"GPU memory snapshot restore failed; retrying task without snapshot. Error: {exc}")
189
+ sys.stderr.flush()
190
+ # Exit with a sentinel code that the runtime will use to retry the task without a snapshot.
191
+ os._exit(gpu_memory_snapshot.SNAPSHOT_RESTORE_FAILED_EXIT_CODE)
187
192
 
188
193
  self._client = await _Client.from_env()
189
194
 
@@ -46,7 +46,7 @@ def validate_http_server_config(
46
46
  class _Server:
47
47
  """Server runs an HTTP server started in an `@modal.enter` method.
48
48
 
49
- See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
49
+ See the [guide](https://modal.com/docs/guide/servers) for more information.
50
50
 
51
51
  Generally, you will not construct a Server directly.
52
52
  Instead, use the [`@app.server()`](https://modal.com/docs/sdk/py/latest/modal.App#server) decorator.
@@ -77,6 +77,7 @@ class _Server:
77
77
 
78
78
  @property
79
79
  def object_id(self) -> str:
80
+ """Modal's internal ID for this Server instance."""
80
81
  return self._service_function.object_id
81
82
 
82
83
  @staticmethod
@@ -91,6 +92,7 @@ class _Server:
91
92
 
92
93
  @live_method
93
94
  async def get_url(self) -> str | None:
95
+ """The URL for making requests to this Server."""
94
96
  urls = await self._get_service_function()._experimental_get_flash_urls()
95
97
  # Return the first URL if it exists. Servers should only have one URL
96
98
  # since they only have one region.
@@ -101,16 +103,28 @@ class _Server:
101
103
  async def update_autoscaler(
102
104
  self,
103
105
  *,
106
+ target_concurrency: int | None = None,
104
107
  min_containers: int | None = None,
105
108
  max_containers: int | None = None,
106
109
  buffer_containers: int | None = None,
107
110
  scaleup_window: int | None = None,
108
111
  scaledown_window: int | None = None,
109
- target_concurrency: int | None = None,
110
112
  ) -> None:
111
113
  """Override the current autoscaler behavior for this Server.
112
114
 
113
- Unspecified parameters will retain their current value.
115
+ Unspecified parameters will retain their current value, i.e. either the static value
116
+ from the `@app.server()` decorator, or an override value from a previous call to this method.
117
+
118
+ Subsequent deployments of the App containing this Server will reset the autoscaler back to
119
+ its static configuration.
120
+
121
+ Args:
122
+ target_concurrency: Target number of concurrent requests per container.
123
+ min_containers: Minimum number of containers to keep running regardless of demand.
124
+ max_containers: Limit on the number of containers that can be concurrently running.
125
+ buffer_containers: Extra containers to scale up beyond current demand.
126
+ scaleup_window: Seconds of sustained demand required before scaling up new containers.
127
+ scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
114
128
 
115
129
  Examples:
116
130
  ```python notest
@@ -130,7 +144,7 @@ class _Server:
130
144
 
131
145
  # Disable the Server autoscaling by setting target_concurrency to 0
132
146
  server.update_autoscaler(target_concurrency=0)
133
- ```
147
+ ```
134
148
 
135
149
  """
136
150
  return await self._get_service_function()._update_autoscaler(
@@ -144,6 +158,13 @@ class _Server:
144
158
 
145
159
  # ============ Hydration ============
146
160
  async def hydrate(self, client: _Client | None = None) -> "_Server":
161
+ """Synchronize the local object with its identity on the Modal server.
162
+
163
+ It is rarely necessary to call this method explicitly, as most operations will
164
+ lazily hydrate when needed. The main use case is when you need to access object
165
+ metadata, such as its ID.
166
+
167
+ """
147
168
  # This is required since we want to support @livemethod() decorated methods
148
169
  service_function = self._get_service_function()
149
170
  await service_function.hydrate(client)
@@ -183,6 +204,12 @@ class _Server:
183
204
  object with metadata from Modal servers until the first
184
205
  time it is actually used.
185
206
 
207
+ Args:
208
+ app_name: Name of the App containing the Server.
209
+ name: Name of the Server within the App.
210
+ environment_name: Name of the Environment where the App is deployed.
211
+ client: Modal client instance for this session.
212
+
186
213
  ```python notest
187
214
  server = modal.Server.from_name("other-app", "Server")
188
215
  ```
@@ -138,6 +138,13 @@ async def _upload_to_s3_url(
138
138
  text = await resp.text()
139
139
  except Exception:
140
140
  text = "<no body>"
141
+
142
+ if "The Content-MD5 you specified did not match what we received." in text:
143
+ raise ExecutionError(
144
+ "Hash mismatch during upload. This may be caused by files changing while an upload is in "
145
+ "progress. Please try again."
146
+ )
147
+
141
148
  raise ExecutionError(f"Put to url {upload_url} failed with status {resp.status}: {text}")
142
149
 
143
150
  # client side ETag checksum verification