modal 1.5.4.dev8__tar.gz → 1.5.4.dev10__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 (238) hide show
  1. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/PKG-INFO +1 -1
  2. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_output/rich.py +12 -2
  3. modal-1.5.4.dev10/modal/_utils/auth_token_manager.py +181 -0
  4. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/task_command_router_client.py +14 -3
  5. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/client.pyi +2 -2
  6. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/sandbox.py +14 -1
  7. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/sandbox.pyi +2 -0
  8. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/PKG-INFO +1 -1
  9. modal-1.5.4.dev10/modal_proto/__init__.py +11 -0
  10. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/api_grpc.py +32 -0
  11. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/api_pb2.py +1193 -1153
  12. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/api_pb2.pyi +121 -9
  13. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/api_pb2_grpc.py +67 -0
  14. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/api_pb2_grpc.pyi +27 -0
  15. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/modal_api_grpc.py +2 -0
  16. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_version/__init__.py +1 -1
  17. modal-1.5.4.dev8/modal/_utils/auth_token_manager.py +0 -157
  18. modal-1.5.4.dev8/modal_proto/__init__.py +0 -4
  19. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/LICENSE +0 -0
  20. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/README.md +0 -0
  21. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/__init__.py +0 -0
  22. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/__main__.py +0 -0
  23. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_billing.py +0 -0
  24. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_clustered_functions.py +0 -0
  25. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_clustered_functions.pyi +0 -0
  26. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_container_entrypoint.py +0 -0
  27. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_environments.py +0 -0
  28. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_function_variants.py +0 -0
  29. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_functions.py +0 -0
  30. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_grpc_client.py +0 -0
  31. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_image.py +0 -0
  32. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_ipython.py +0 -0
  33. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_load_context.py +0 -0
  34. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_location.py +0 -0
  35. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_logs.py +0 -0
  36. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_logs_manager.py +0 -0
  37. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_logs_manager.pyi +0 -0
  38. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_object.py +0 -0
  39. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_output/__init__.py +0 -0
  40. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_output/manager.py +0 -0
  41. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_output/pty.py +0 -0
  42. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_output/status.py +0 -0
  43. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_partial_function.py +0 -0
  44. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_resolver.py +0 -0
  45. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_resources.py +0 -0
  46. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/__init__.py +0 -0
  47. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/asgi.py +0 -0
  48. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/container_io_manager.py +0 -0
  49. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/container_io_manager.pyi +0 -0
  50. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/execution_context.py +0 -0
  51. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/execution_context.pyi +0 -0
  52. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  53. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/task_lifecycle_manager.py +0 -0
  54. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  55. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/telemetry.py +0 -0
  56. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/user_code_event_loop.py +0 -0
  57. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_runtime/user_code_imports.py +0 -0
  58. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_serialization.py +0 -0
  59. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_server.py +0 -0
  60. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_supports_logs.py +0 -0
  61. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_traceback.py +0 -0
  62. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_tunnel.py +0 -0
  63. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_tunnel.pyi +0 -0
  64. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_type_manager.py +0 -0
  65. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/__init__.py +0 -0
  66. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/app_utils.py +0 -0
  67. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/async_utils.py +0 -0
  68. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/blob_utils.py +0 -0
  69. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/browser_utils.py +0 -0
  70. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/bytes_io_segment_payload.py +0 -0
  71. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/curl_utils.py +0 -0
  72. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/deprecation.py +0 -0
  73. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/display_utils.py +0 -0
  74. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/docker_utils.py +0 -0
  75. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/function_utils.py +0 -0
  76. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/git_utils.py +0 -0
  77. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/grpc_testing.py +0 -0
  78. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/grpc_utils.py +0 -0
  79. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/hash_utils.py +0 -0
  80. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/http_utils.py +0 -0
  81. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/jwt_utils.py +0 -0
  82. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/logger.py +0 -0
  83. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/mount_utils.py +0 -0
  84. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/name_utils.py +0 -0
  85. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/package_utils.py +0 -0
  86. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/pattern_utils.py +0 -0
  87. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/proxy_support.py +0 -0
  88. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/rand_pb_testing.py +0 -0
  89. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/sandbox_fs_utils.py +0 -0
  90. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/shell_utils.py +0 -0
  91. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_utils/time_utils.py +0 -0
  92. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_vendor/__init__.py +0 -0
  93. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  94. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_vendor/cloudpickle.py +0 -0
  95. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_vendor/tblib.py +0 -0
  96. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_vendor/version.py +0 -0
  97. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_watcher.py +0 -0
  98. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/_workspace.py +0 -0
  99. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/app.py +0 -0
  100. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/app.pyi +0 -0
  101. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/billing.py +0 -0
  102. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/billing.pyi +0 -0
  103. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/2023.12.312.txt +0 -0
  104. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/2023.12.txt +0 -0
  105. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/2024.04.txt +0 -0
  106. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/2024.10.txt +0 -0
  107. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/2025.06.txt +0 -0
  108. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/PREVIEW.txt +0 -0
  109. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/README.md +0 -0
  110. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/builder/base-images.json +0 -0
  111. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/call_graph.py +0 -0
  112. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/__init__.py +0 -0
  113. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/_download.py +0 -0
  114. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/_help.py +0 -0
  115. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/_traceback.py +0 -0
  116. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/app.py +0 -0
  117. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/billing.py +0 -0
  118. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/bootstrap.py +0 -0
  119. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/changelog.py +0 -0
  120. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/cluster.py +0 -0
  121. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/config.py +0 -0
  122. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/container.py +0 -0
  123. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/curl.py +0 -0
  124. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/dashboard.py +0 -0
  125. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/dict.py +0 -0
  126. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/endpoint.py +0 -0
  127. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/entry_point.py +0 -0
  128. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/environment.py +0 -0
  129. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/image.py +0 -0
  130. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/import_refs.py +0 -0
  131. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/launch.py +0 -0
  132. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/logo.py +0 -0
  133. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/network_file_system.py +0 -0
  134. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/profile.py +0 -0
  135. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/programs/__init__.py +0 -0
  136. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/programs/run_jupyter.py +0 -0
  137. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/programs/vscode.py +0 -0
  138. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/queues.py +0 -0
  139. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/run.py +0 -0
  140. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/secret.py +0 -0
  141. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/selector.py +0 -0
  142. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/shell.py +0 -0
  143. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/skills.py +0 -0
  144. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/token.py +0 -0
  145. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/utils.py +0 -0
  146. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/volume.py +0 -0
  147. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cli/workspace.py +0 -0
  148. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/client.py +0 -0
  149. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cloud_bucket_mount.py +0 -0
  150. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cloud_bucket_mount.pyi +0 -0
  151. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cls.py +0 -0
  152. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/cls.pyi +0 -0
  153. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/config.py +0 -0
  154. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/container_process.py +0 -0
  155. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/container_process.pyi +0 -0
  156. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/dict.py +0 -0
  157. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/dict.pyi +0 -0
  158. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/environments.py +0 -0
  159. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/environments.pyi +0 -0
  160. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/exception.py +0 -0
  161. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/experimental/__init__.py +0 -0
  162. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/experimental/flash.py +0 -0
  163. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/experimental/flash.pyi +0 -0
  164. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/experimental/ipython.py +0 -0
  165. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/file_io.py +0 -0
  166. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/file_io.pyi +0 -0
  167. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/file_pattern_matcher.py +0 -0
  168. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/functions.py +0 -0
  169. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/functions.pyi +0 -0
  170. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/image.py +0 -0
  171. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/image.pyi +0 -0
  172. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/io_streams.py +0 -0
  173. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/io_streams.pyi +0 -0
  174. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/mount.py +0 -0
  175. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/mount.pyi +0 -0
  176. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/network_file_system.py +0 -0
  177. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/network_file_system.pyi +0 -0
  178. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/object.py +0 -0
  179. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/object.pyi +0 -0
  180. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/output.py +0 -0
  181. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/parallel_map.py +0 -0
  182. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/parallel_map.pyi +0 -0
  183. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/partial_function.py +0 -0
  184. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/partial_function.pyi +0 -0
  185. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/proxy.py +0 -0
  186. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/proxy.pyi +0 -0
  187. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/py.typed +0 -0
  188. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/queue.py +0 -0
  189. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/queue.pyi +0 -0
  190. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/retries.py +0 -0
  191. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/runner.py +0 -0
  192. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/runner.pyi +0 -0
  193. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/running_app.py +0 -0
  194. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/sandbox_fs.py +0 -0
  195. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/sandbox_fs.pyi +0 -0
  196. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/schedule.py +0 -0
  197. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/scheduler_placement.py +0 -0
  198. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/secret.py +0 -0
  199. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/secret.pyi +0 -0
  200. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/server.py +0 -0
  201. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/server.pyi +0 -0
  202. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/serving.py +0 -0
  203. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/serving.pyi +0 -0
  204. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/skills/modal/SKILL.md +0 -0
  205. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/snapshot.py +0 -0
  206. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/snapshot.pyi +0 -0
  207. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/stream_type.py +0 -0
  208. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/token_flow.py +0 -0
  209. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/token_flow.pyi +0 -0
  210. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/types.py +0 -0
  211. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/volume.py +0 -0
  212. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/volume.pyi +0 -0
  213. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/workspace.py +0 -0
  214. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal/workspace.pyi +0 -0
  215. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/SOURCES.txt +0 -0
  216. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/dependency_links.txt +0 -0
  217. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/entry_points.txt +0 -0
  218. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/requires.txt +0 -0
  219. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal.egg-info/top_level.txt +0 -0
  220. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/__init__.py +0 -0
  221. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/gen_cli_docs.py +0 -0
  222. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/gen_cli_docs_main.py +0 -0
  223. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/gen_reference_docs.py +0 -0
  224. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/gen_reference_docs_main.py +0 -0
  225. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/mdmd/__init__.py +0 -0
  226. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/mdmd/mdmd.py +0 -0
  227. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/mdmd/signatures.py +0 -0
  228. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/mdmd/types.py +0 -0
  229. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_docs/reference_intro.md +0 -0
  230. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/py.typed +0 -0
  231. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/task_command_router_grpc.py +0 -0
  232. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/task_command_router_pb2.py +0 -0
  233. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/task_command_router_pb2.pyi +0 -0
  234. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  235. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  236. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/modal_version/__main__.py +0 -0
  237. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/pyproject.toml +0 -0
  238. {modal-1.5.4.dev8 → modal-1.5.4.dev10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.4.dev8
3
+ Version: 1.5.4.dev10
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -37,6 +37,7 @@ from rich.progress import (
37
37
  )
38
38
  from rich.spinner import Spinner
39
39
  from rich.status import Status
40
+ from rich.table import Column
40
41
  from rich.text import Text
41
42
  from rich.tree import Tree
42
43
 
@@ -476,7 +477,10 @@ class RichOutputManager(OutputManager):
476
477
  and adds it to the current render group."""
477
478
  if not self._function_queueing_progress:
478
479
  self._function_queueing_progress = Progress(
479
- TextColumn("[progress.description]{task.description}"),
480
+ TextColumn(
481
+ "[progress.description]{task.description}",
482
+ table_column=Column(no_wrap=False, overflow="fold"),
483
+ ),
480
484
  FunctionQueuingColumn(),
481
485
  console=self._console,
482
486
  transient=True,
@@ -581,11 +585,17 @@ class RichOutputManager(OutputManager):
581
585
  """Handle queueing updates, ignoring completion updates for functions that have no queue progress bar."""
582
586
  task_key = (function_id, api_pb2.FUNCTION_QUEUED)
583
587
  task_description = description or f"'{function_id}' function waiting on worker"
588
+ task_description = escape(task_description)
584
589
  task_desc = f"[yellow]{task_description}. Time in queue:"
585
590
  if task_key in self._task_progress_items:
586
591
  progress_task_id = self._task_progress_items[task_key]
587
592
  try:
588
- self._function_queueing_progress_bar.update(progress_task_id, completed=completed, total=total)
593
+ self._function_queueing_progress_bar.update(
594
+ progress_task_id,
595
+ description=task_desc if description else None,
596
+ completed=completed,
597
+ total=total,
598
+ )
589
599
  if completed == total:
590
600
  del self._task_progress_items[task_key]
591
601
  self._function_queueing_progress_bar.remove_task(progress_task_id)
@@ -0,0 +1,181 @@
1
+ # Copyright Modal Labs 2025
2
+ import asyncio
3
+ import base64
4
+ import json
5
+ import random
6
+ import time
7
+ from typing import Any
8
+
9
+ from modal._utils.grpc_utils import DEFAULT_MAX_RETRIES, Retry
10
+ from modal.exception import AuthError, ExecutionError, PermissionDeniedError
11
+ from modal_proto import api_pb2, modal_api_grpc
12
+
13
+ from .logger import logger
14
+
15
+ AUTH_DENIED_EXCEPTIONS = (AuthError, PermissionDeniedError)
16
+
17
+
18
+ class _AuthTokenManager:
19
+ """Handles fetching and refreshing of the input plane auth token."""
20
+
21
+ # Fraction of the token's lifetime to use before refreshing.
22
+ REFRESH_FRACTION = 0.5
23
+ # Width of the random band added on top of REFRESH_FRACTION, so that clients issued
24
+ # same-lifetime tokens at the same moment (a fan-out of containers starting together)
25
+ # spread their refreshes out instead of refreshing in lockstep every cycle. The band
26
+ # only ever delays a refresh, so the margin before expiry stays at no less than
27
+ # 1 - REFRESH_FRACTION - REFRESH_JITTER of the lifetime.
28
+ REFRESH_JITTER = 0.1
29
+ # Bound each AuthTokenGet attempt so a refresh can't hang.
30
+ AUTH_TOKEN_TIMEOUT = 5.0
31
+ AUTH_TOKEN_RETRY_TOTAL_TIMEOUT = 3 * AUTH_TOKEN_TIMEOUT
32
+ # After a failed refresh, wait before hitting the server again, growing exponentially with
33
+ # consecutive failures between these bounds (in seconds).
34
+ FAILURE_BACKOFF_BASE = 0.5
35
+ FAILURE_BACKOFF_MAX = 60.0
36
+ # If the token doesn't have an expiry field, default to current time plus this value (not expected).
37
+ DEFAULT_EXPIRY_OFFSET = 20 * 60
38
+
39
+ def __init__(self, stub: "modal_api_grpc.ModalClientModal"):
40
+ self._stub = stub
41
+ self._token = ""
42
+ self._expiry = 0.0
43
+ self._refresh_at = 0.0
44
+ self._retry_after = 0.0
45
+ self._backoff = self.FAILURE_BACKOFF_BASE
46
+ self._refresh_task: asyncio.Task | None = None
47
+
48
+ async def get_token(self) -> str:
49
+ """
50
+ When called, the AuthTokenManager can be in one of three states:
51
+ 1. Has a valid cached token. It is returned to the caller.
52
+ 2. Has no cached token, or the token is expired. We fetch a new one and cache it. If `get_token` is called
53
+ concurrently by multiple coroutines, all requests will block until the token has been fetched. But only one
54
+ coroutine will actually make a request to the control plane to fetch the new token. This ensures we do not hit
55
+ the control plane with more requests than needed.
56
+ 3. Has a valid cached token that has used up its refresh fraction of its lifetime. In this case we fetch a new
57
+ token and cache it. If `get_token` is called concurrently, only one request will fetch the new token, and the
58
+ others will be given the old (but still valid) token - i.e. they will not block.
59
+ """
60
+ if not self._token or self._is_expired():
61
+ # We either have no token or it is expired - block everyone until we get a new token
62
+ await self._try_refresh_token()
63
+ elif self._should_refresh():
64
+ # The token is still valid, but has used up its refresh fraction of its lifetime.
65
+ if self._refresh_task is not None:
66
+ # A refresh is in flight, so someone else is refreshing. Continue to use the old token.
67
+ return self._token
68
+ else:
69
+ # Nobody is refreshing, so we need to fetch a new token.
70
+ await self._try_refresh_token()
71
+
72
+ return self._token
73
+
74
+ async def _try_refresh_token(self):
75
+ try:
76
+ await self._refresh_token()
77
+ except AUTH_DENIED_EXCEPTIONS:
78
+ raise
79
+ except Exception as e:
80
+ if not self._token:
81
+ raise
82
+ logger.warning("Auth token refresh failed; falling back to cached token: %s", e)
83
+
84
+ async def _refresh_token(self):
85
+ """
86
+ Fetch a new token from the control plane. Concurrent callers share a single request: the first caller starts it
87
+ and the others await the same task, so a burst of callers results in one RPC whether it succeeds or fails. The
88
+ task is owned by the event loop rather than by the caller that started it, so it runs to completion even if that
89
+ caller goes away.
90
+ """
91
+ if self._refresh_task is None:
92
+ # Maybe another coroutine refreshed already, or we're cooling down after a failure.
93
+ if self._token and not self._should_refresh():
94
+ return
95
+ # Note: the task is created in async code so it binds to the synchronicity event loop.
96
+ self._refresh_task = asyncio.create_task(self._fetch_token())
97
+ self._refresh_task.add_done_callback(self._refresh_task_done)
98
+ # Shielded so a cancelled caller doesn't cancel the refresh for everyone else.
99
+ await asyncio.shield(self._refresh_task)
100
+
101
+ def _refresh_task_done(self, task: "asyncio.Task"):
102
+ if self._refresh_task is task:
103
+ self._refresh_task = None
104
+ if not task.cancelled():
105
+ # Retrieve the exception, if any, so a failure isn't logged as unhandled when no caller is waiting.
106
+ task.exception()
107
+
108
+ async def _fetch_token(self):
109
+ """Make the AuthTokenGet request and cache its token, or arm the failure backoff."""
110
+ try:
111
+ resp: api_pb2.AuthTokenGetResponse = await self._stub.AuthTokenGet(
112
+ api_pb2.AuthTokenGetRequest(),
113
+ # No cached token to fall back on, so a failure is user-visible: retry transient errors.
114
+ # Otherwise one attempt, and _retry_after handles the cooldown.
115
+ retry=Retry(
116
+ attempt_timeout=self.AUTH_TOKEN_TIMEOUT,
117
+ total_timeout=self.AUTH_TOKEN_RETRY_TOTAL_TIMEOUT,
118
+ max_retries=0 if self._token else DEFAULT_MAX_RETRIES,
119
+ ),
120
+ )
121
+ if not resp.token:
122
+ # Not expected
123
+ raise ExecutionError(
124
+ "Internal error: Did not receive auth token from server. Please contact Modal support."
125
+ )
126
+ if exp := self._decode_jwt(resp.token).get("exp"):
127
+ expiry = float(exp)
128
+ else:
129
+ # This should never happen.
130
+ logger.warning("x-modal-auth-token does not contain exp field")
131
+ expiry = time.time() + self.DEFAULT_EXPIRY_OFFSET
132
+ self._token = resp.token
133
+ self._set_expiry(expiry)
134
+ self._retry_after = 0.0
135
+ self._backoff = self.FAILURE_BACKOFF_BASE
136
+ except AUTH_DENIED_EXCEPTIONS:
137
+ raise
138
+ except Exception as e:
139
+ # Back off (exponentially on consecutive failures) so we don't hammer a struggling server.
140
+ self._retry_after = time.time() + self._backoff
141
+ self._backoff = min(self._backoff * 2, self.FAILURE_BACKOFF_MAX)
142
+ raise
143
+
144
+ @staticmethod
145
+ def _decode_jwt(token: str) -> dict[str, Any]:
146
+ """
147
+ Decodes a JWT into a dict without verifying signature. We do this manually instead of using a library to avoid
148
+ adding another dependency to the client.
149
+ """
150
+ try:
151
+ payload = token.split(".")[1]
152
+ padding = "=" * (-len(payload) % 4)
153
+ decoded_bytes = base64.urlsafe_b64decode(payload + padding)
154
+ return json.loads(decoded_bytes)
155
+ except Exception as e:
156
+ raise ValueError("Internal error: Cannot parse auth token. Please contact Modal support.") from e
157
+
158
+ def _in_backoff(self):
159
+ return time.time() < self._retry_after
160
+
161
+ def _should_refresh(self):
162
+ # Fetch only when the token is stale/expiring and we're not in a post-failure backoff.
163
+ return self._needs_refresh() and not self._in_backoff()
164
+
165
+ def _set_expiry(self, expiry: float) -> None:
166
+ """Record a new expiry and schedule the proactive refresh a jittered fraction of the way through.
167
+
168
+ The delay is measured from the remaining lifetime as this client sees it, so a client whose clock is
169
+ skewed relative to the issuer still keeps a margin proportional to the lifetime it observes.
170
+ """
171
+ now = time.time()
172
+ self._expiry = expiry
173
+ remaining = max(expiry - now, 0.0)
174
+ fraction = self.REFRESH_FRACTION + random.random() * self.REFRESH_JITTER
175
+ self._refresh_at = now + remaining * fraction
176
+
177
+ def _needs_refresh(self):
178
+ return time.time() >= self._refresh_at
179
+
180
+ def _is_expired(self):
181
+ return time.time() >= self._expiry
@@ -288,11 +288,22 @@ class TaskCommandRouterClient:
288
288
  server_client,
289
289
  sandbox_id: str,
290
290
  task_id: str,
291
+ access: api_pb2.CommandRouterAccess | None = None,
291
292
  ) -> "TaskCommandRouterClient":
292
- """Initialize a TaskCommandRouterClient for a V2 sandbox."""
293
- resp = await fetch_command_router_access_v2(server_client, sandbox_id=sandbox_id)
293
+ """Initialize a TaskCommandRouterClient for a V2 sandbox.
294
+
295
+ `access` is the router access returned by SandboxCreateV2/SandboxRestoreV2,
296
+ which lets a freshly scheduled sandbox connect without a round-trip. When it
297
+ is absent — a re-attached sandbox, or the server could not mint a token — we
298
+ ask for it.
299
+ """
300
+ if access is not None:
301
+ url, jwt = access.url, access.jwt
302
+ else:
303
+ resp = await fetch_command_router_access_v2(server_client, sandbox_id=sandbox_id)
304
+ url, jwt = resp.url, resp.jwt
294
305
  logger.debug(f"Using command router access for sandbox {sandbox_id}")
295
- return await cls._connect(server_client, task_id, resp.url, resp.jwt, sandbox_id=sandbox_id)
306
+ return await cls._connect(server_client, task_id, url, jwt, sandbox_id=sandbox_id)
296
307
 
297
308
  @classmethod
298
309
  async def init_v2_by_task_id(
@@ -37,7 +37,7 @@ class _Client:
37
37
  server_url: str,
38
38
  client_type: int,
39
39
  credentials: typing.Optional[tuple[str, str]],
40
- version: str = "1.5.4.dev8",
40
+ version: str = "1.5.4.dev10",
41
41
  ):
42
42
  """mdmd:hidden
43
43
  The Modal client object is not intended to be instantiated directly by users.
@@ -207,7 +207,7 @@ class Client:
207
207
  server_url: str,
208
208
  client_type: int,
209
209
  credentials: typing.Optional[tuple[str, str]],
210
- version: str = "1.5.4.dev8",
210
+ version: str = "1.5.4.dev10",
211
211
  ):
212
212
  """mdmd:hidden
213
213
  The Modal client object is not intended to be instantiated directly by users.
@@ -333,6 +333,7 @@ class _Sandbox(_Object, type_prefix="sb"):
333
333
  _tunnels: dict[int, Tunnel] | None
334
334
  _enable_snapshot: bool
335
335
  _command_router_client: TaskCommandRouterClient | None
336
+ _init_command_router_access: api_pb2.CommandRouterAccess | None
336
337
  _attached: bool
337
338
  _filesystem: _SandboxFilesystem | None
338
339
  _is_v2: bool = False
@@ -1034,6 +1035,10 @@ class _Sandbox(_Object, type_prefix="sb"):
1034
1035
  self._hydrate(sandbox_id, load_context.client, create_resp.metadata)
1035
1036
  self._is_v2 = True
1036
1037
  self._task_id = create_resp.task_id
1038
+ # An unset proto message reads as an empty one rather than None, so the
1039
+ # presence check is what distinguishes "no access" from "empty access".
1040
+ if create_resp.HasField("command_router_access"):
1041
+ self._init_command_router_access = create_resp.command_router_access
1037
1042
  self._hydrate_metadata_v2()
1038
1043
  # Unencrypted tunnel endpoints are not known at create time. Only
1039
1044
  # cache a complete set so tunnels() fetches the rest otherwise.
@@ -1121,6 +1126,7 @@ class _Sandbox(_Object, type_prefix="sb"):
1121
1126
  self._tunnels = None
1122
1127
  self._enable_snapshot = False
1123
1128
  self._command_router_client = None
1129
+ self._init_command_router_access = None
1124
1130
  self._filesystem = None
1125
1131
  self._is_v2 = _is_v2_sandbox_id(self.object_id)
1126
1132
  if self._is_v2:
@@ -1912,8 +1918,12 @@ class _Sandbox(_Object, type_prefix="sb"):
1912
1918
  if self._command_router_client is None:
1913
1919
  try:
1914
1920
  if self._is_v2:
1921
+ # Consume the seeded access, so if connecting with it fails the
1922
+ # retry falls back to the authoritative RPC instead of
1923
+ # re-trying the same credentials forever.
1924
+ access, self._init_command_router_access = self._init_command_router_access, None
1915
1925
  self._command_router_client = await TaskCommandRouterClient.init_v2_by_sandbox_id(
1916
- self._client, self.object_id, task_id
1926
+ self._client, self.object_id, task_id, access
1917
1927
  )
1918
1928
  else:
1919
1929
  self._command_router_client = await TaskCommandRouterClient.init(self._client, task_id)
@@ -2278,6 +2288,9 @@ class _Sandbox(_Object, type_prefix="sb"):
2278
2288
 
2279
2289
  sandbox = await _Sandbox.from_id(restore_resp.sandbox_id, client)
2280
2290
  sandbox._task_id = restore_resp.task_id
2291
+ # Seeded after from_id, whose hydration resets the field.
2292
+ if restore_resp.HasField("command_router_access"):
2293
+ sandbox._init_command_router_access = restore_resp.command_router_access
2281
2294
  return sandbox
2282
2295
 
2283
2296
  @property
@@ -147,6 +147,7 @@ class _Sandbox(modal._object._Object):
147
147
  _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
148
148
  _enable_snapshot: bool
149
149
  _command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient]
150
+ _init_command_router_access: typing.Optional[modal_proto.api_pb2.CommandRouterAccess]
150
151
  _attached: bool
151
152
  _filesystem: typing.Optional[modal.sandbox_fs._SandboxFilesystem]
152
153
  _is_v2: bool
@@ -1454,6 +1455,7 @@ class Sandbox(modal.object.Object):
1454
1455
  _tunnels: typing.Optional[dict[int, modal._tunnel.Tunnel]]
1455
1456
  _enable_snapshot: bool
1456
1457
  _command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient]
1458
+ _init_command_router_access: typing.Optional[modal_proto.api_pb2.CommandRouterAccess]
1457
1459
  _attached: bool
1458
1460
  _filesystem: typing.Optional[modal.sandbox_fs.SandboxFilesystem]
1459
1461
  _is_v2: bool
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.4.dev8
3
+ Version: 1.5.4.dev10
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -0,0 +1,11 @@
1
+ # Copyright Modal Labs 2024
2
+ """Modal gRPC types for Python.
3
+
4
+ Warning: direct usage of Modal's gRPC API is discouraged,
5
+ and no support or compatibility guarantees are provided.
6
+ Message fields or entire RPCs may be changed or removed
7
+ without notice. We recommend using official SDKs instead.
8
+ """
9
+ from pkgutil import extend_path
10
+
11
+ __path__ = extend_path(__path__, __name__) # type: ignore[has-type]
@@ -123,6 +123,10 @@ class ModalClientBase(abc.ABC):
123
123
  async def AuthTokenGet(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.AuthTokenGetRequest, modal_proto.api_pb2.AuthTokenGetResponse]') -> None:
124
124
  pass
125
125
 
126
+ @abc.abstractmethod
127
+ async def AutoscalerOverrideHistory(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.AutoscalerOverrideHistoryRequest, modal_proto.api_pb2.AutoscalerOverrideHistoryResponse]') -> None:
128
+ pass
129
+
126
130
  @abc.abstractmethod
127
131
  async def BlobCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.BlobCreateRequest, modal_proto.api_pb2.BlobCreateResponse]') -> None:
128
132
  pass
@@ -327,6 +331,10 @@ class ModalClientBase(abc.ABC):
327
331
  async def EnvironmentSetBudget(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EnvironmentSetBudgetRequest, google.protobuf.empty_pb2.Empty]') -> None:
328
332
  pass
329
333
 
334
+ @abc.abstractmethod
335
+ async def EnvironmentSetDefaultMemberRole(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EnvironmentSetDefaultMemberRoleRequest, google.protobuf.empty_pb2.Empty]') -> None:
336
+ pass
337
+
330
338
  @abc.abstractmethod
331
339
  async def EnvironmentSetManaged(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EnvironmentSetManagedRequest, google.protobuf.empty_pb2.Empty]') -> None:
332
340
  pass
@@ -1113,6 +1121,12 @@ class ModalClientBase(abc.ABC):
1113
1121
  modal_proto.api_pb2.AuthTokenGetRequest,
1114
1122
  modal_proto.api_pb2.AuthTokenGetResponse,
1115
1123
  ),
1124
+ '/modal.client.ModalClient/AutoscalerOverrideHistory': grpclib.const.Handler(
1125
+ self.AutoscalerOverrideHistory,
1126
+ grpclib.const.Cardinality.UNARY_UNARY,
1127
+ modal_proto.api_pb2.AutoscalerOverrideHistoryRequest,
1128
+ modal_proto.api_pb2.AutoscalerOverrideHistoryResponse,
1129
+ ),
1116
1130
  '/modal.client.ModalClient/BlobCreate': grpclib.const.Handler(
1117
1131
  self.BlobCreate,
1118
1132
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -1419,6 +1433,12 @@ class ModalClientBase(abc.ABC):
1419
1433
  modal_proto.api_pb2.EnvironmentSetBudgetRequest,
1420
1434
  google.protobuf.empty_pb2.Empty,
1421
1435
  ),
1436
+ '/modal.client.ModalClient/EnvironmentSetDefaultMemberRole': grpclib.const.Handler(
1437
+ self.EnvironmentSetDefaultMemberRole,
1438
+ grpclib.const.Cardinality.UNARY_UNARY,
1439
+ modal_proto.api_pb2.EnvironmentSetDefaultMemberRoleRequest,
1440
+ google.protobuf.empty_pb2.Empty,
1441
+ ),
1422
1442
  '/modal.client.ModalClient/EnvironmentSetManaged': grpclib.const.Handler(
1423
1443
  self.EnvironmentSetManaged,
1424
1444
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -2523,6 +2543,12 @@ class ModalClientStub:
2523
2543
  modal_proto.api_pb2.AuthTokenGetRequest,
2524
2544
  modal_proto.api_pb2.AuthTokenGetResponse,
2525
2545
  )
2546
+ self.AutoscalerOverrideHistory = grpclib.client.UnaryUnaryMethod(
2547
+ channel,
2548
+ '/modal.client.ModalClient/AutoscalerOverrideHistory',
2549
+ modal_proto.api_pb2.AutoscalerOverrideHistoryRequest,
2550
+ modal_proto.api_pb2.AutoscalerOverrideHistoryResponse,
2551
+ )
2526
2552
  self.BlobCreate = grpclib.client.UnaryUnaryMethod(
2527
2553
  channel,
2528
2554
  '/modal.client.ModalClient/BlobCreate',
@@ -2829,6 +2855,12 @@ class ModalClientStub:
2829
2855
  modal_proto.api_pb2.EnvironmentSetBudgetRequest,
2830
2856
  google.protobuf.empty_pb2.Empty,
2831
2857
  )
2858
+ self.EnvironmentSetDefaultMemberRole = grpclib.client.UnaryUnaryMethod(
2859
+ channel,
2860
+ '/modal.client.ModalClient/EnvironmentSetDefaultMemberRole',
2861
+ modal_proto.api_pb2.EnvironmentSetDefaultMemberRoleRequest,
2862
+ google.protobuf.empty_pb2.Empty,
2863
+ )
2832
2864
  self.EnvironmentSetManaged = grpclib.client.UnaryUnaryMethod(
2833
2865
  channel,
2834
2866
  '/modal.client.ModalClient/EnvironmentSetManaged',