modal 1.5.6.dev30__tar.gz → 1.6.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 (241) hide show
  1. {modal-1.5.6.dev30 → modal-1.6.0}/PKG-INFO +1 -1
  2. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_functions.py +9 -5
  3. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_grpc_client.py +9 -4
  4. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_image.py +16 -1
  5. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_partial_function.py +3 -3
  6. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_server.py +16 -11
  7. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_server.pyi +23 -17
  8. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/grpc_utils.py +43 -8
  9. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/app.py +310 -308
  10. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/entry_point.py +8 -8
  11. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/environment.py +134 -132
  12. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/function.py +788 -784
  13. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/image.py +4 -2
  14. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/queues.py +42 -42
  15. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/server.py +727 -736
  16. {modal-1.5.6.dev30 → modal-1.6.0}/modal/client.py +10 -5
  17. {modal-1.5.6.dev30 → modal-1.6.0}/modal/client.pyi +11 -11
  18. {modal-1.5.6.dev30 → modal-1.6.0}/modal/config.py +1 -1
  19. {modal-1.5.6.dev30 → modal-1.6.0}/modal/mount.py +9 -1
  20. {modal-1.5.6.dev30 → modal-1.6.0}/modal/mount.pyi +5 -1
  21. {modal-1.5.6.dev30 → modal-1.6.0}/modal/partial_function.pyi +3 -3
  22. {modal-1.5.6.dev30 → modal-1.6.0}/modal/queue.py +16 -2
  23. {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox.py +85 -72
  24. {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox.pyi +31 -8
  25. {modal-1.5.6.dev30 → modal-1.6.0}/modal/secret.py +12 -9
  26. {modal-1.5.6.dev30 → modal-1.6.0}/modal/secret.pyi +5 -5
  27. {modal-1.5.6.dev30 → modal-1.6.0}/modal/server.pyi +1 -1
  28. {modal-1.5.6.dev30 → modal-1.6.0}/modal/types.py +94 -67
  29. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/PKG-INFO +1 -1
  30. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_grpc.py +16 -0
  31. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2.py +1106 -1061
  32. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2.pyi +118 -0
  33. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2_grpc.py +33 -0
  34. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/api_pb2_grpc.pyi +10 -0
  35. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/modal_api_grpc.py +1 -0
  36. {modal-1.5.6.dev30 → modal-1.6.0}/modal_version/__init__.py +1 -1
  37. {modal-1.5.6.dev30 → modal-1.6.0}/LICENSE +0 -0
  38. {modal-1.5.6.dev30 → modal-1.6.0}/README.md +0 -0
  39. {modal-1.5.6.dev30 → modal-1.6.0}/modal/__init__.py +0 -0
  40. {modal-1.5.6.dev30 → modal-1.6.0}/modal/__main__.py +0 -0
  41. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_billing.py +0 -0
  42. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_cluster.py +0 -0
  43. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_cluster.pyi +0 -0
  44. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_container_entrypoint.py +0 -0
  45. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_environments.py +0 -0
  46. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_function_variants.py +0 -0
  47. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_ipython.py +0 -0
  48. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_load_context.py +0 -0
  49. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_location.py +0 -0
  50. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs.py +0 -0
  51. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs_manager.py +0 -0
  52. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_logs_manager.pyi +0 -0
  53. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_object.py +0 -0
  54. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_outbound_policy.py +0 -0
  55. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_outbound_policy.pyi +0 -0
  56. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/__init__.py +0 -0
  57. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/manager.py +0 -0
  58. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/pty.py +0 -0
  59. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/rich.py +0 -0
  60. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_output/status.py +0 -0
  61. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_resolver.py +0 -0
  62. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_resources.py +0 -0
  63. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/__init__.py +0 -0
  64. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/asgi.py +0 -0
  65. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/container_io_manager.py +0 -0
  66. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/container_io_manager.pyi +0 -0
  67. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/execution_context.py +0 -0
  68. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/execution_context.pyi +0 -0
  69. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  70. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/task_lifecycle_manager.py +0 -0
  71. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  72. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/telemetry.py +0 -0
  73. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/user_code_event_loop.py +0 -0
  74. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_runtime/user_code_imports.py +0 -0
  75. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_serialization.py +0 -0
  76. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_supports_logs.py +0 -0
  77. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_traceback.py +0 -0
  78. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_tunnel.py +0 -0
  79. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_tunnel.pyi +0 -0
  80. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_type_manager.py +0 -0
  81. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/__init__.py +0 -0
  82. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/app_utils.py +0 -0
  83. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/async_utils.py +0 -0
  84. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/auth_token_manager.py +0 -0
  85. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/blob_utils.py +0 -0
  86. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/browser_utils.py +0 -0
  87. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  88. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/curl_utils.py +0 -0
  89. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/deprecation.py +0 -0
  90. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/display_utils.py +0 -0
  91. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/docker_utils.py +0 -0
  92. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/function_utils.py +0 -0
  93. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/git_utils.py +0 -0
  94. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/grpc_testing.py +0 -0
  95. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/hash_utils.py +0 -0
  96. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/http_utils.py +0 -0
  97. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/idle_countdown.py +0 -0
  98. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/jwt_utils.py +0 -0
  99. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/logger.py +0 -0
  100. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/mount_utils.py +0 -0
  101. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/name_utils.py +0 -0
  102. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/package_utils.py +0 -0
  103. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/pattern_utils.py +0 -0
  104. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/proxy_support.py +0 -0
  105. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/rand_pb_testing.py +0 -0
  106. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/sandbox_fs_utils.py +0 -0
  107. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/shell_utils.py +0 -0
  108. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/task_command_router_client.py +0 -0
  109. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_utils/time_utils.py +0 -0
  110. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/__init__.py +0 -0
  111. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  112. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/cloudpickle.py +0 -0
  113. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/tblib.py +0 -0
  114. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_vendor/version.py +0 -0
  115. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_watcher.py +0 -0
  116. {modal-1.5.6.dev30 → modal-1.6.0}/modal/_workspace.py +0 -0
  117. {modal-1.5.6.dev30 → modal-1.6.0}/modal/app.py +0 -0
  118. {modal-1.5.6.dev30 → modal-1.6.0}/modal/app.pyi +0 -0
  119. {modal-1.5.6.dev30 → modal-1.6.0}/modal/billing.py +0 -0
  120. {modal-1.5.6.dev30 → modal-1.6.0}/modal/billing.pyi +0 -0
  121. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2023.12.312.txt +0 -0
  122. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2023.12.txt +0 -0
  123. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2024.04.txt +0 -0
  124. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2024.10.txt +0 -0
  125. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/2025.06.txt +0 -0
  126. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/PREVIEW.txt +0 -0
  127. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/README.md +0 -0
  128. {modal-1.5.6.dev30 → modal-1.6.0}/modal/builder/base-images.json +0 -0
  129. {modal-1.5.6.dev30 → modal-1.6.0}/modal/call_graph.py +0 -0
  130. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/__init__.py +0 -0
  131. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_download.py +0 -0
  132. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_help.py +0 -0
  133. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_logs.py +0 -0
  134. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_stats.py +0 -0
  135. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/_traceback.py +0 -0
  136. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/billing.py +85 -85
  137. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/changelog.py +0 -0
  138. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/config.py +0 -0
  139. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/container.py +0 -0
  140. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/curl.py +0 -0
  141. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/dashboard.py +0 -0
  142. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/dict.py +16 -16
  143. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/endpoint.py +67 -67
  144. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/import_refs.py +0 -0
  145. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/logo.py +0 -0
  146. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/network_file_system.py +64 -64
  147. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/profile.py +19 -19
  148. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/run.py +0 -0
  149. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/secret.py +0 -0
  150. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/selector.py +0 -0
  151. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/shell.py +0 -0
  152. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/skills.py +0 -0
  153. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/token.py +50 -50
  154. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/utils.py +0 -0
  155. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/volume.py +121 -121
  156. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cli/workspace.py +34 -34
  157. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cloud_bucket_mount.py +0 -0
  158. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cloud_bucket_mount.pyi +0 -0
  159. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cls.py +0 -0
  160. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cls.pyi +0 -0
  161. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cluster.py +0 -0
  162. {modal-1.5.6.dev30 → modal-1.6.0}/modal/cluster.pyi +0 -0
  163. {modal-1.5.6.dev30 → modal-1.6.0}/modal/container_process.py +0 -0
  164. {modal-1.5.6.dev30 → modal-1.6.0}/modal/container_process.pyi +0 -0
  165. {modal-1.5.6.dev30 → modal-1.6.0}/modal/dict.py +0 -0
  166. {modal-1.5.6.dev30 → modal-1.6.0}/modal/dict.pyi +0 -0
  167. {modal-1.5.6.dev30 → modal-1.6.0}/modal/environments.py +0 -0
  168. {modal-1.5.6.dev30 → modal-1.6.0}/modal/environments.pyi +0 -0
  169. {modal-1.5.6.dev30 → modal-1.6.0}/modal/exception.py +0 -0
  170. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/__init__.py +0 -0
  171. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/_flash_container_entry.py +0 -0
  172. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash.py +0 -0
  173. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash.pyi +0 -0
  174. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash_server.py +0 -0
  175. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/flash_server.pyi +0 -0
  176. {modal-1.5.6.dev30 → modal-1.6.0}/modal/experimental/ipython.py +0 -0
  177. {modal-1.5.6.dev30 → modal-1.6.0}/modal/file_pattern_matcher.py +0 -0
  178. {modal-1.5.6.dev30 → modal-1.6.0}/modal/functions.py +0 -0
  179. {modal-1.5.6.dev30 → modal-1.6.0}/modal/functions.pyi +0 -0
  180. {modal-1.5.6.dev30 → modal-1.6.0}/modal/image.py +0 -0
  181. {modal-1.5.6.dev30 → modal-1.6.0}/modal/image.pyi +0 -0
  182. {modal-1.5.6.dev30 → modal-1.6.0}/modal/io_streams.py +0 -0
  183. {modal-1.5.6.dev30 → modal-1.6.0}/modal/io_streams.pyi +0 -0
  184. {modal-1.5.6.dev30 → modal-1.6.0}/modal/network_file_system.py +0 -0
  185. {modal-1.5.6.dev30 → modal-1.6.0}/modal/network_file_system.pyi +0 -0
  186. {modal-1.5.6.dev30 → modal-1.6.0}/modal/object.py +0 -0
  187. {modal-1.5.6.dev30 → modal-1.6.0}/modal/object.pyi +0 -0
  188. {modal-1.5.6.dev30 → modal-1.6.0}/modal/output.py +0 -0
  189. {modal-1.5.6.dev30 → modal-1.6.0}/modal/parallel_map.py +0 -0
  190. {modal-1.5.6.dev30 → modal-1.6.0}/modal/parallel_map.pyi +0 -0
  191. {modal-1.5.6.dev30 → modal-1.6.0}/modal/partial_function.py +0 -0
  192. {modal-1.5.6.dev30 → modal-1.6.0}/modal/proxy.py +0 -0
  193. {modal-1.5.6.dev30 → modal-1.6.0}/modal/proxy.pyi +0 -0
  194. {modal-1.5.6.dev30 → modal-1.6.0}/modal/py.typed +0 -0
  195. {modal-1.5.6.dev30 → modal-1.6.0}/modal/queue.pyi +0 -0
  196. {modal-1.5.6.dev30 → modal-1.6.0}/modal/retries.py +0 -0
  197. {modal-1.5.6.dev30 → modal-1.6.0}/modal/runner.py +0 -0
  198. {modal-1.5.6.dev30 → modal-1.6.0}/modal/runner.pyi +0 -0
  199. {modal-1.5.6.dev30 → modal-1.6.0}/modal/running_app.py +0 -0
  200. {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox_fs.py +0 -0
  201. {modal-1.5.6.dev30 → modal-1.6.0}/modal/sandbox_fs.pyi +0 -0
  202. {modal-1.5.6.dev30 → modal-1.6.0}/modal/schedule.py +0 -0
  203. {modal-1.5.6.dev30 → modal-1.6.0}/modal/scheduler_placement.py +0 -0
  204. {modal-1.5.6.dev30 → modal-1.6.0}/modal/server.py +0 -0
  205. {modal-1.5.6.dev30 → modal-1.6.0}/modal/serving.py +0 -0
  206. {modal-1.5.6.dev30 → modal-1.6.0}/modal/serving.pyi +0 -0
  207. {modal-1.5.6.dev30 → modal-1.6.0}/modal/skills/modal/SKILL.md +0 -0
  208. {modal-1.5.6.dev30 → modal-1.6.0}/modal/snapshot.py +0 -0
  209. {modal-1.5.6.dev30 → modal-1.6.0}/modal/snapshot.pyi +0 -0
  210. {modal-1.5.6.dev30 → modal-1.6.0}/modal/stream_type.py +0 -0
  211. {modal-1.5.6.dev30 → modal-1.6.0}/modal/token_flow.py +0 -0
  212. {modal-1.5.6.dev30 → modal-1.6.0}/modal/token_flow.pyi +0 -0
  213. {modal-1.5.6.dev30 → modal-1.6.0}/modal/volume.py +0 -0
  214. {modal-1.5.6.dev30 → modal-1.6.0}/modal/volume.pyi +0 -0
  215. {modal-1.5.6.dev30 → modal-1.6.0}/modal/workspace.py +0 -0
  216. {modal-1.5.6.dev30 → modal-1.6.0}/modal/workspace.pyi +0 -0
  217. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/SOURCES.txt +0 -0
  218. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/dependency_links.txt +0 -0
  219. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/entry_points.txt +0 -0
  220. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/requires.txt +0 -0
  221. {modal-1.5.6.dev30 → modal-1.6.0}/modal.egg-info/top_level.txt +0 -0
  222. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/__init__.py +0 -0
  223. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_cli_docs.py +0 -0
  224. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_cli_docs_main.py +0 -0
  225. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_reference_docs.py +0 -0
  226. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/gen_reference_docs_main.py +0 -0
  227. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/__init__.py +0 -0
  228. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/mdmd.py +0 -0
  229. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/signatures.py +0 -0
  230. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/mdmd/types.py +0 -0
  231. {modal-1.5.6.dev30 → modal-1.6.0}/modal_docs/reference_intro.md +0 -0
  232. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/__init__.py +0 -0
  233. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/py.typed +0 -0
  234. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_grpc.py +0 -0
  235. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2.py +0 -0
  236. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2.pyi +0 -0
  237. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  238. {modal-1.5.6.dev30 → modal-1.6.0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  239. {modal-1.5.6.dev30 → modal-1.6.0}/modal_version/__main__.py +0 -0
  240. {modal-1.5.6.dev30 → modal-1.6.0}/pyproject.toml +0 -0
  241. {modal-1.5.6.dev30 → modal-1.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev30
3
+ Version: 1.6.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
@@ -1262,15 +1262,19 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1262
1262
  },
1263
1263
  # We don't have any secret IDs yet as we are dehydrated, so we return the reprs instead
1264
1264
  secrets=[repr(s) for s in secrets],
1265
- _http_info=FunctionInfo.HttpInfo(
1265
+ startup_timeout=startup_timeout,
1266
+ _inner_server_info=FunctionInfo._InnerServerInfo(
1267
+ server_url="",
1266
1268
  port=http_config.port,
1267
1269
  unauthenticated=http_config.unauthenticated,
1268
1270
  h2_enabled=http_config.h2_enabled,
1269
- proxy_regions=list(http_config.proxy_regions),
1271
+ routing_region=http_config.proxy_regions[0] if http_config.proxy_regions else None,
1272
+ sessioned=is_sessioned,
1273
+ startup_timeout=http_config.startup_timeout,
1274
+ exit_grace_period=http_config.exit_grace_period,
1270
1275
  )
1271
1276
  if http_config
1272
1277
  else None,
1273
- _sessioned=is_sessioned,
1274
1278
  web_info=FunctionInfo.WebInfo._from_proto("", webhook_config) if webhook_config else None,
1275
1279
  method_names=list(method_definitions.keys()) if method_definitions is not None else None,
1276
1280
  method_details={
@@ -1745,14 +1749,14 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1745
1749
  return list(self._experimental_flash_urls) if self._experimental_flash_urls else None
1746
1750
 
1747
1751
  async def _fetch_flash_auth_token(self, retry: Retry) -> str:
1748
- resp = await self.client.stub.FunctionGetFlashAuthToken(
1752
+ resp = await self.client._stub.FunctionGetFlashAuthToken(
1749
1753
  api_pb2.FunctionGetFlashAuthTokenRequest(function_id=self.object_id), retry=retry
1750
1754
  )
1751
1755
  return resp.token
1752
1756
 
1753
1757
  async def _get_flash_auth_token(self) -> str:
1754
1758
  if self._flash_token_manager is None:
1755
- self._flash_token_manager = _AuthTokenManager(self.client.stub, fetch=self._fetch_flash_auth_token)
1759
+ self._flash_token_manager = _AuthTokenManager(self.client._stub, fetch=self._fetch_flash_auth_token)
1756
1760
  return await self._flash_token_manager.get_token()
1757
1761
 
1758
1762
  def _apply_dynamic_config(
@@ -44,6 +44,14 @@ _STATUS_TO_EXCEPTION: dict[Status, type[exception._GRPCErrorWrapper]] = {
44
44
  }
45
45
 
46
46
 
47
+ def grpc_error_to_modal_exception(exc: GRPCError) -> exception._GRPCErrorWrapper:
48
+ modal_exc = _STATUS_TO_EXCEPTION[exc.status](exc.message)
49
+ modal_exc._grpc_message = exc.message or ""
50
+ modal_exc._grpc_status = exc.status
51
+ modal_exc._grpc_details = exc.details
52
+ return modal_exc
53
+
54
+
47
55
  class grpc_error_converter:
48
56
  def __enter__(self):
49
57
  pass
@@ -53,10 +61,7 @@ class grpc_error_converter:
53
61
  use_full_traceback = config.get("traceback")
54
62
  with suppress_tb_frame():
55
63
  if isinstance(exc, GRPCError):
56
- modal_exc = _STATUS_TO_EXCEPTION[exc.status](exc.message)
57
- modal_exc._grpc_message = exc.message or ""
58
- modal_exc._grpc_status = exc.status
59
- modal_exc._grpc_details = exc.details
64
+ modal_exc = grpc_error_to_modal_exception(exc)
60
65
  if use_full_traceback:
61
66
  raise modal_exc
62
67
  else:
@@ -354,6 +354,16 @@ def _create_context_mount(
354
354
  copy_patterns = extract_copy_command_patterns(docker_commands)
355
355
  if not copy_patterns:
356
356
  return None # no mount needed
357
+
358
+ resolved_context_dir = context_dir.resolve()
359
+ for copy_pattern in copy_patterns:
360
+ # Docker treats leading slashes as relative to the build context root.
361
+ resolved_source = (resolved_context_dir / copy_pattern.lstrip("/")).resolve()
362
+ if not resolved_source.is_relative_to(resolved_context_dir):
363
+ raise InvalidError(
364
+ f"COPY source {copy_pattern!r} is outside the build context directory ({str(context_dir)!r})."
365
+ )
366
+
357
367
  include_fn = FilePatternMatcher(*copy_patterns)
358
368
 
359
369
  def ignore_with_include(source: Path) -> bool:
@@ -364,7 +374,12 @@ def _create_context_mount(
364
374
 
365
375
  return False
366
376
 
367
- return _Mount._add_local_dir(context_dir, PurePosixPath("/"), ignore=ignore_with_include)
377
+ return _Mount._add_local_dir(
378
+ context_dir,
379
+ PurePosixPath("/"),
380
+ ignore=ignore_with_include,
381
+ filter_external_links=True,
382
+ )
368
383
 
369
384
 
370
385
  def _create_context_mount_function(
@@ -732,7 +732,7 @@ def _sessioned() -> Callable[
732
732
  [Callable[P, ReturnType] | _PartialFunction[P, ReturnType, ReturnType]],
733
733
  _PartialFunction[P, ReturnType, ReturnType],
734
734
  ]:
735
- """Decorator that enables sessions on a Server.
735
+ """Decorator that enables sticky sessions on a Server.
736
736
 
737
737
  Every request must carry a session token obtained from a session start request; requests with the same token are
738
738
  routed to the same container until the session is idle for `idle_timeout` seconds or explicitly terminated. A
@@ -741,7 +741,7 @@ def _sessioned() -> Callable[
741
741
  Only valid with `@app.server()`.
742
742
 
743
743
  Examples:
744
- Define a sessioned Server:
744
+ Define a Server with the `@modal.sessioned()` decorator:
745
745
 
746
746
  ```python
747
747
  app = modal.App("my-app")
@@ -828,7 +828,7 @@ def _clustered(*, size: int, rdma: bool = False) -> _ClusteredDecorator:
828
828
  containers and must be multiples of `size`. For example, `size=4` with
829
829
  `min_containers=8` keeps two clusters warm.
830
830
 
831
- See the [multi-node clusters guide](https://modal.com/docs/guide/multi-node-training)
831
+ See the [multi-node clusters guide](https://modal.com/docs/guide/multi-node-clusters)
832
832
  for hardware requirements and networking details.
833
833
 
834
834
  Parameters:
@@ -113,7 +113,7 @@ class _Server:
113
113
 
114
114
  @property
115
115
  def sessions(self) -> "_ServerSessionsManager":
116
- """Start and terminate sessions on a Server decorated with `@modal.sessioned()`."""
116
+ """Start and terminate sticky sessions on a Server decorated with `@modal.sessioned()`."""
117
117
  return _ServerSessionsManager(self)
118
118
 
119
119
  async def info(self, *, refresh: bool = False) -> ServerInfo:
@@ -160,7 +160,7 @@ class _Server:
160
160
  This interface is experimental and may change or be removed without warning.
161
161
  """
162
162
  service_function = self._get_service_function()
163
- response = await service_function.client.stub.FlashContainerList(
163
+ response = await service_function.client._stub.FlashContainerList(
164
164
  api_pb2.FlashContainerListRequest(function_id=service_function.object_id)
165
165
  )
166
166
  return [
@@ -250,8 +250,11 @@ class _Server:
250
250
  # This is required since we want to support @livemethod() decorated methods
251
251
  service_function = self._get_service_function()
252
252
  await service_function.hydrate(client)
253
- if service_function._function_info is not None:
254
- self._is_sessioned = service_function._function_info._sessioned
253
+ if (
254
+ service_function._function_info is not None
255
+ and service_function._function_info._inner_server_info is not None
256
+ ):
257
+ self._is_sessioned = service_function._function_info._inner_server_info.sessioned
255
258
  return self
256
259
 
257
260
  @classmethod
@@ -470,13 +473,13 @@ class _Server:
470
473
  request.until.FromDatetime(until)
471
474
  if container:
472
475
  request.container_id = container
473
- stats = await self._get_service_function().client.stub.ServerGetTimeRangeStats(request)
476
+ stats = await self._get_service_function().client._stub.ServerGetTimeRangeStats(request)
474
477
  return ServerStats._from_proto(stats)
475
478
 
476
479
 
477
480
  @retry(n_attempts=5, base_delay=0.5, attempt_timeout=65, total_timeout=200)
478
481
  async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
479
- """POST to a session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
482
+ """POST to a sticky session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
480
483
  async with ClientSessionRegistry.get_session().post(url, headers=headers) as resp:
481
484
  body = await resp.text()
482
485
  if resp.status >= 400:
@@ -498,11 +501,11 @@ class _ServerSessionsManager:
498
501
  raise InvalidError("`sessions` requires `@modal.sessioned()` on the Server.")
499
502
 
500
503
  async def start(self, idle_timeout: int = 600) -> ServerSessionCredentials:
501
- """Start a session and return its ID and token.
504
+ """Start a sticky session and return its ID and token.
502
505
 
503
- Requests to the server URL that carry the returned token are routed to the same container until the session
504
- has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down for as long
505
- as it holds a live session.
506
+ Requests to the server URL that carry the returned token are routed to the same container until the
507
+ session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
508
+ for as long as it holds a live session.
506
509
 
507
510
  Args:
508
511
  idle_timeout: Seconds without an in-flight request before the session ends.
@@ -547,7 +550,9 @@ class _ServerSessionsManager:
547
550
  raise ExecutionError("Failed to start session: unexpected response from server") from None
548
551
 
549
552
  async def terminate(self, token: str) -> None:
550
- """Terminate a session. New requests to it will be rejected. Container will continue serving other sessions.
553
+ """Terminate a sticky session.
554
+
555
+ New requests to it will be rejected. The container continues serving other sessions.
551
556
 
552
557
  Args:
553
558
  token: The `token` of the `ServerSessionCredentials` to terminate.
@@ -67,7 +67,7 @@ class _Server:
67
67
 
68
68
  @property
69
69
  def sessions(self) -> _ServerSessionsManager:
70
- """Start and terminate sessions on a Server decorated with `@modal.sessioned()`."""
70
+ """Start and terminate sticky sessions on a Server decorated with `@modal.sessioned()`."""
71
71
  ...
72
72
 
73
73
  async def info(self, *, refresh: bool = False) -> modal.types.ServerInfo:
@@ -272,7 +272,7 @@ class _Server:
272
272
  ...
273
273
 
274
274
  async def _post_session_control(url: str, headers: dict[str, str]) -> tuple[int, str, str]:
275
- """POST to a session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
275
+ """POST to a sticky session control endpoint, retrying connection errors and 5xx. Returns (status, reason, body)."""
276
276
  ...
277
277
 
278
278
  class _ServerSessionsManager:
@@ -283,11 +283,11 @@ class _ServerSessionsManager:
283
283
 
284
284
  def _validate(self) -> None: ...
285
285
  async def start(self, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
286
- """Start a session and return its ID and token.
286
+ """Start a sticky session and return its ID and token.
287
287
 
288
- Requests to the server URL that carry the returned token are routed to the same container until the session
289
- has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down for as long
290
- as it holds a live session.
288
+ Requests to the server URL that carry the returned token are routed to the same container until the
289
+ session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
290
+ for as long as it holds a live session.
291
291
 
292
292
  Args:
293
293
  idle_timeout: Seconds without an in-flight request before the session ends.
@@ -308,7 +308,9 @@ class _ServerSessionsManager:
308
308
  ...
309
309
 
310
310
  async def terminate(self, token: str) -> None:
311
- """Terminate a session. New requests to it will be rejected. Container will continue serving other sessions.
311
+ """Terminate a sticky session.
312
+
313
+ New requests to it will be rejected. The container continues serving other sessions.
312
314
 
313
315
  Args:
314
316
  token: The `token` of the `ServerSessionCredentials` to terminate.
@@ -334,11 +336,11 @@ class ServerSessionsManager:
334
336
 
335
337
  class __start_spec(typing_extensions.Protocol):
336
338
  def __call__(self, /, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
337
- """Start a session and return its ID and token.
339
+ """Start a sticky session and return its ID and token.
338
340
 
339
- Requests to the server URL that carry the returned token are routed to the same container until the session
340
- has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down for as long
341
- as it holds a live session.
341
+ Requests to the server URL that carry the returned token are routed to the same container until the
342
+ session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
343
+ for as long as it holds a live session.
342
344
 
343
345
  Args:
344
346
  idle_timeout: Seconds without an in-flight request before the session ends.
@@ -359,11 +361,11 @@ class ServerSessionsManager:
359
361
  ...
360
362
 
361
363
  async def aio(self, /, idle_timeout: int = 600) -> modal.types.ServerSessionCredentials:
362
- """Start a session and return its ID and token.
364
+ """Start a sticky session and return its ID and token.
363
365
 
364
- Requests to the server URL that carry the returned token are routed to the same container until the session
365
- has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down for as long
366
- as it holds a live session.
366
+ Requests to the server URL that carry the returned token are routed to the same container until the
367
+ session has had no connections for `idle_timeout` seconds or is terminated. A container won't be scaled down
368
+ for as long as it holds a live session.
367
369
 
368
370
  Args:
369
371
  idle_timeout: Seconds without an in-flight request before the session ends.
@@ -387,7 +389,9 @@ class ServerSessionsManager:
387
389
 
388
390
  class __terminate_spec(typing_extensions.Protocol):
389
391
  def __call__(self, /, token: str) -> None:
390
- """Terminate a session. New requests to it will be rejected. Container will continue serving other sessions.
392
+ """Terminate a sticky session.
393
+
394
+ New requests to it will be rejected. The container continues serving other sessions.
391
395
 
392
396
  Args:
393
397
  token: The `token` of the `ServerSessionCredentials` to terminate.
@@ -404,7 +408,9 @@ class ServerSessionsManager:
404
408
  ...
405
409
 
406
410
  async def aio(self, /, token: str) -> None:
407
- """Terminate a session. New requests to it will be rejected. Container will continue serving other sessions.
411
+ """Terminate a sticky session.
412
+
413
+ New requests to it will be rejected. The container continues serving other sessions.
408
414
 
409
415
  Args:
410
416
  token: The `token` of the `ServerSessionCredentials` to terminate.
@@ -26,7 +26,12 @@ from grpclib.exceptions import StreamTerminatedError
26
26
  from grpclib.protocol import H2Protocol
27
27
  from h2.exceptions import ProtocolError as H2ProtocolError
28
28
 
29
- from modal.exception import ClientClosed, ConnectionError, NotFoundError
29
+ from modal.exception import (
30
+ ClientClosed,
31
+ ConnectionError,
32
+ NotFoundError,
33
+ TimeoutError as ModalTimeoutError,
34
+ )
30
35
  from modal_proto import api_pb2
31
36
  from modal_version import __version__
32
37
 
@@ -486,6 +491,26 @@ class Retry:
486
491
  warning_message: RetryWarningMessage | None = None
487
492
 
488
493
 
494
+ class RetryTimeoutError(ModalTimeoutError):
495
+ """Raised by `_retry_transient_errors` when `Retry.total_timeout` is spent before an attempt succeeds.
496
+
497
+ Internal: callers with a user-supplied timeout should convert this into a plain `modal.exception.TimeoutError`
498
+ with a message referring to that timeout. `final_exception` is the failure of the last attempt (server errors
499
+ already converted to their `modal.exception` type), which the caller can inspect to decide how to report the
500
+ timeout.
501
+ """
502
+
503
+ final_exception: Exception
504
+
505
+ def __init__(self, final_exception: Exception):
506
+ super().__init__(
507
+ "The operation timed out after repeated failures "
508
+ f"(last error: {type(final_exception).__name__}: {final_exception}). "
509
+ "Please try again, and contact Modal support if the problem persists."
510
+ )
511
+ self.final_exception = final_exception
512
+
513
+
489
514
  async def retry_transient_errors(
490
515
  fn: "grpclib.client.UnaryUnaryMethod[RequestType, ResponseType]",
491
516
  req: RequestType,
@@ -527,14 +552,22 @@ def process_exception_before_retry(
527
552
  delay: float,
528
553
  idempotency_key: str,
529
554
  rpc_elapsed: float,
555
+ total_deadline_reached: bool = False,
530
556
  ):
531
557
  """Process exception before retry, used by `_retry_transient_errors`."""
532
558
  with suppress_tb_frame():
533
559
  if final_attempt:
560
+ reason = "total deadline consumed" if total_deadline_reached else "max retries reached"
534
561
  logger.debug(
535
- f"Final attempt failed with {repr(exc)} {n_retries=} {delay=} {rpc_elapsed=:0.2f}s "
562
+ f"Final attempt failed ({reason}) with {repr(exc)} {n_retries=} {delay=} {rpc_elapsed=:0.2f}s "
536
563
  f"for {fn_name} ({idempotency_key[:8]})"
537
564
  )
565
+ if total_deadline_reached:
566
+ import modal._grpc_client
567
+
568
+ if isinstance(exc, GRPCError):
569
+ exc = modal._grpc_client.grpc_error_to_modal_exception(exc)
570
+ raise RetryTimeoutError(exc) from exc
538
571
  if isinstance(exc, OSError):
539
572
  raise ConnectionError(str(exc))
540
573
  elif isinstance(exc, asyncio.TimeoutError):
@@ -664,6 +697,7 @@ async def _retry_transient_errors(
664
697
  server_delay,
665
698
  idempotency_key,
666
699
  elapsed_time,
700
+ total_deadline_reached=total_timeout_will_be_reached,
667
701
  )
668
702
 
669
703
  if last_server_retry_warning_time is None or (
@@ -687,12 +721,12 @@ async def _retry_transient_errors(
687
721
  # Client handles retry
688
722
  if isinstance(exc, GRPCError) and exc.status not in status_codes:
689
723
  raise exc
690
- if retry.max_retries is not None and n_retries >= retry.max_retries:
691
- final_attempt = True
692
- elif total_deadline is not None and time.time() + delay + retry.attempt_timeout_floor >= total_deadline:
693
- final_attempt = True
694
- else:
695
- final_attempt = False
724
+ max_retries_reached = retry.max_retries is not None and n_retries >= retry.max_retries
725
+ total_deadline_reached = total_deadline is not None and (
726
+ time.time() >= total_deadline
727
+ or (not max_retries_reached and time.time() + delay + retry.attempt_timeout_floor >= total_deadline)
728
+ )
729
+ final_attempt = max_retries_reached or total_deadline_reached
696
730
 
697
731
  with suppress_tb_frame():
698
732
  process_exception_before_retry(
@@ -703,6 +737,7 @@ async def _retry_transient_errors(
703
737
  delay,
704
738
  idempotency_key,
705
739
  time.monotonic() - attempt_started_at,
740
+ total_deadline_reached=total_deadline_reached,
706
741
  )
707
742
 
708
743
  n_retries += 1