modal 1.5.3.dev3__tar.gz → 1.5.3.dev5__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 (231) hide show
  1. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/PKG-INFO +1 -1
  2. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_container_entrypoint.py +2 -4
  3. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_environments.py +18 -39
  4. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/container_io_manager.py +6 -30
  5. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/container_io_manager.pyi +5 -16
  6. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/client.py +13 -7
  7. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/client.pyi +15 -13
  8. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/config.py +28 -0
  9. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/environments.pyi +8 -8
  10. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/functions.pyi +6 -6
  11. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/sandbox.py +41 -8
  12. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/sandbox.pyi +75 -12
  13. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/PKG-INFO +1 -1
  14. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/api_grpc.py +16 -0
  15. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/api_pb2.py +1163 -1143
  16. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/api_pb2.pyi +90 -29
  17. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/api_pb2_grpc.py +33 -0
  18. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/api_pb2_grpc.pyi +10 -0
  19. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/modal_api_grpc.py +1 -0
  20. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/task_command_router_grpc.py +16 -0
  21. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/task_command_router_pb2.py +33 -13
  22. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/task_command_router_pb2.pyi +31 -0
  23. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/task_command_router_pb2_grpc.py +37 -0
  24. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/task_command_router_pb2_grpc.pyi +20 -0
  25. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_version/__init__.py +1 -1
  26. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/LICENSE +0 -0
  27. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/README.md +0 -0
  28. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/__init__.py +0 -0
  29. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/__main__.py +0 -0
  30. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_billing.py +0 -0
  31. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_clustered_functions.py +0 -0
  32. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_clustered_functions.pyi +0 -0
  33. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_function_variants.py +0 -0
  34. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_functions.py +0 -0
  35. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_grpc_client.py +0 -0
  36. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_image.py +0 -0
  37. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_ipython.py +0 -0
  38. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_load_context.py +0 -0
  39. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_location.py +0 -0
  40. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_logs.py +0 -0
  41. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_object.py +0 -0
  42. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_output/__init__.py +0 -0
  43. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_output/manager.py +0 -0
  44. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_output/pty.py +0 -0
  45. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_output/rich.py +0 -0
  46. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_output/status.py +0 -0
  47. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_partial_function.py +0 -0
  48. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_resolver.py +0 -0
  49. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_resources.py +0 -0
  50. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/__init__.py +0 -0
  51. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/asgi.py +0 -0
  52. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/execution_context.py +0 -0
  53. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/execution_context.pyi +0 -0
  54. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  55. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/task_lifecycle_manager.py +0 -0
  56. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  57. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/telemetry.py +0 -0
  58. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/user_code_event_loop.py +0 -0
  59. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_runtime/user_code_imports.py +0 -0
  60. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_serialization.py +0 -0
  61. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_server.py +0 -0
  62. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_traceback.py +0 -0
  63. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_tunnel.py +0 -0
  64. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_tunnel.pyi +0 -0
  65. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_type_manager.py +0 -0
  66. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/__init__.py +0 -0
  67. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/app_utils.py +0 -0
  68. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/async_utils.py +0 -0
  69. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/auth_token_manager.py +0 -0
  70. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/blob_utils.py +0 -0
  71. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/browser_utils.py +0 -0
  72. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/bytes_io_segment_payload.py +0 -0
  73. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/curl_utils.py +0 -0
  74. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/deprecation.py +0 -0
  75. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/docker_utils.py +0 -0
  76. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/function_utils.py +0 -0
  77. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/git_utils.py +0 -0
  78. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/grpc_testing.py +0 -0
  79. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/grpc_utils.py +0 -0
  80. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/hash_utils.py +0 -0
  81. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/http_utils.py +0 -0
  82. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/jwt_utils.py +0 -0
  83. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/logger.py +0 -0
  84. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/mount_utils.py +0 -0
  85. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/name_utils.py +0 -0
  86. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/package_utils.py +0 -0
  87. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/pattern_utils.py +0 -0
  88. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/proxy_support.py +0 -0
  89. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/rand_pb_testing.py +0 -0
  90. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/sandbox_fs_utils.py +0 -0
  91. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/shell_utils.py +0 -0
  92. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/task_command_router_client.py +0 -0
  93. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_utils/time_utils.py +0 -0
  94. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_vendor/__init__.py +0 -0
  95. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  96. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_vendor/cloudpickle.py +0 -0
  97. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_vendor/tblib.py +0 -0
  98. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_vendor/version.py +0 -0
  99. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_watcher.py +0 -0
  100. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/_workspace.py +0 -0
  101. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/app.py +0 -0
  102. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/app.pyi +0 -0
  103. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/billing.py +0 -0
  104. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/billing.pyi +0 -0
  105. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/2023.12.312.txt +0 -0
  106. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/2023.12.txt +0 -0
  107. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/2024.04.txt +0 -0
  108. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/2024.10.txt +0 -0
  109. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/2025.06.txt +0 -0
  110. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/PREVIEW.txt +0 -0
  111. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/README.md +0 -0
  112. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/builder/base-images.json +0 -0
  113. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/call_graph.py +0 -0
  114. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/__init__.py +0 -0
  115. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/_download.py +0 -0
  116. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/_help.py +0 -0
  117. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/_traceback.py +0 -0
  118. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/app.py +0 -0
  119. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/billing.py +0 -0
  120. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/bootstrap.py +0 -0
  121. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/changelog.py +0 -0
  122. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/cluster.py +0 -0
  123. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/config.py +0 -0
  124. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/container.py +0 -0
  125. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/curl.py +0 -0
  126. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/dashboard.py +0 -0
  127. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/dict.py +0 -0
  128. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/endpoint.py +0 -0
  129. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/entry_point.py +0 -0
  130. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/environment.py +0 -0
  131. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/image.py +0 -0
  132. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/import_refs.py +0 -0
  133. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/launch.py +0 -0
  134. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/logo.py +0 -0
  135. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/network_file_system.py +0 -0
  136. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/profile.py +0 -0
  137. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/programs/__init__.py +0 -0
  138. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/programs/run_jupyter.py +0 -0
  139. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/programs/vscode.py +0 -0
  140. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/queues.py +0 -0
  141. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/run.py +0 -0
  142. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/secret.py +0 -0
  143. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/selector.py +0 -0
  144. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/shell.py +0 -0
  145. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/skills.py +0 -0
  146. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/token.py +0 -0
  147. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/utils.py +0 -0
  148. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/volume.py +0 -0
  149. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cli/workspace.py +0 -0
  150. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cloud_bucket_mount.py +0 -0
  151. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cloud_bucket_mount.pyi +0 -0
  152. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cls.py +0 -0
  153. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/cls.pyi +0 -0
  154. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/container_process.py +0 -0
  155. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/container_process.pyi +0 -0
  156. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/dict.py +0 -0
  157. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/dict.pyi +0 -0
  158. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/environments.py +0 -0
  159. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/exception.py +0 -0
  160. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/experimental/__init__.py +0 -0
  161. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/experimental/flash.py +0 -0
  162. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/experimental/flash.pyi +0 -0
  163. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/experimental/ipython.py +0 -0
  164. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/file_io.py +0 -0
  165. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/file_io.pyi +0 -0
  166. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/file_pattern_matcher.py +0 -0
  167. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/functions.py +0 -0
  168. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/image.py +0 -0
  169. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/image.pyi +0 -0
  170. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/io_streams.py +0 -0
  171. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/io_streams.pyi +0 -0
  172. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/mount.py +0 -0
  173. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/mount.pyi +0 -0
  174. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/network_file_system.py +0 -0
  175. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/network_file_system.pyi +0 -0
  176. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/object.py +0 -0
  177. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/object.pyi +0 -0
  178. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/output.py +0 -0
  179. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/parallel_map.py +0 -0
  180. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/parallel_map.pyi +0 -0
  181. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/partial_function.py +0 -0
  182. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/partial_function.pyi +0 -0
  183. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/proxy.py +0 -0
  184. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/proxy.pyi +0 -0
  185. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/py.typed +0 -0
  186. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/queue.py +0 -0
  187. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/queue.pyi +0 -0
  188. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/retries.py +0 -0
  189. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/runner.py +0 -0
  190. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/runner.pyi +0 -0
  191. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/running_app.py +0 -0
  192. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/sandbox_fs.py +0 -0
  193. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/sandbox_fs.pyi +0 -0
  194. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/schedule.py +0 -0
  195. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/scheduler_placement.py +0 -0
  196. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/secret.py +0 -0
  197. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/secret.pyi +0 -0
  198. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/server.py +0 -0
  199. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/server.pyi +0 -0
  200. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/serving.py +0 -0
  201. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/serving.pyi +0 -0
  202. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/skills/modal/SKILL.md +0 -0
  203. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/snapshot.py +0 -0
  204. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/snapshot.pyi +0 -0
  205. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/stream_type.py +0 -0
  206. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/token_flow.py +0 -0
  207. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/token_flow.pyi +0 -0
  208. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/types.py +0 -0
  209. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/volume.py +0 -0
  210. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/volume.pyi +0 -0
  211. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/workspace.py +0 -0
  212. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal/workspace.pyi +0 -0
  213. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/SOURCES.txt +0 -0
  214. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/dependency_links.txt +0 -0
  215. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/entry_points.txt +0 -0
  216. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/requires.txt +0 -0
  217. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal.egg-info/top_level.txt +0 -0
  218. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/__init__.py +0 -0
  219. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/gen_cli_docs.py +0 -0
  220. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/gen_cli_docs_main.py +0 -0
  221. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/gen_reference_docs.py +0 -0
  222. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/gen_reference_docs_main.py +0 -0
  223. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/mdmd/__init__.py +0 -0
  224. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/mdmd/mdmd.py +0 -0
  225. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/mdmd/signatures.py +0 -0
  226. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_docs/mdmd/types.py +0 -0
  227. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/__init__.py +0 -0
  228. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_proto/py.typed +0 -0
  229. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/modal_version/__main__.py +0 -0
  230. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/pyproject.toml +0 -0
  231. {modal-1.5.3.dev3 → modal-1.5.3.dev5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.3.dev3
3
+ Version: 1.5.3.dev5
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -145,7 +145,7 @@ def call_function(
145
145
  item_count += 1
146
146
 
147
147
  await container_io_manager._send_outputs.aio(
148
- started_at, io_context.output_items_generator_done(started_at, item_count)
148
+ io_context.output_items_generator_done(started_at, item_count)
149
149
  )
150
150
  else:
151
151
  value = await io_context.call_function_async()
@@ -182,9 +182,7 @@ def call_function(
182
182
  container_io_manager._queue_put(generator_queue, value)
183
183
  item_count += 1
184
184
 
185
- container_io_manager._send_outputs(
186
- started_at, io_context.output_items_generator_done(started_at, item_count)
187
- )
185
+ container_io_manager._send_outputs(io_context.output_items_generator_done(started_at, item_count))
188
186
  else:
189
187
  values = io_context.call_function_sync()
190
188
  container_io_manager.push_outputs(io_context, started_at, values)
@@ -103,26 +103,6 @@ class _EnvironmentManager:
103
103
  await client.stub.EnvironmentDelete(api_pb2.EnvironmentDeleteRequest(name=name))
104
104
 
105
105
 
106
- def _role_to_proto(role: str) -> "api_pb2.EnvironmentRole.ValueType":
107
- match role:
108
- case "viewer":
109
- return api_pb2.ENVIRONMENT_ROLE_VIEWER
110
- case "contributor":
111
- return api_pb2.ENVIRONMENT_ROLE_CONTRIBUTOR
112
- case _:
113
- raise InvalidError(f"Invalid Environment role: {role!r} (expected 'viewer' or 'contributor')")
114
-
115
-
116
- def _role_from_proto(proto_value: int) -> str:
117
- match proto_value:
118
- case api_pb2.ENVIRONMENT_ROLE_VIEWER:
119
- return "viewer"
120
- case api_pb2.ENVIRONMENT_ROLE_CONTRIBUTOR:
121
- return "contributor"
122
- case _:
123
- raise ValueError(f"Unknown environment role: {proto_value}")
124
-
125
-
126
106
  class _EnvironmentMembersManager:
127
107
  """mdmd:namespace
128
108
  Namespace with methods for managing the membership of a restricted Environment.
@@ -135,31 +115,30 @@ class _EnvironmentMembersManager:
135
115
  self._environment = environment
136
116
 
137
117
  async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
138
- """Return the members of a restricted Environment with their roles.
118
+ """Enumerate the environment roles for each user and service user in the workspace.
139
119
 
140
120
  **Examples:**
141
121
 
142
122
  ```python notest
143
- members = modal.Environment.from_name("my-restricted-env").members.list()
123
+ members = modal.Environment.from_name("my-env").members.list()
144
124
  print(members)
145
125
  # {
146
- # "users": {"alice": "contributor", "bob": "viewer"},
147
- # "service_users": {"alice-bot": "contributor"},
126
+ # "users": {"alice": "contributor", "bob": "viewer", "carol": "contributor"},
127
+ # "service_users": {"alice-bot": "contributor", "ops-bot": "viewer", "ci-bot": "no-access"},
148
128
  # }
149
129
  ```
150
130
  """
151
131
  await self._environment.hydrate()
152
- req = api_pb2.EnvironmentGetManagedRequest(environment_id=self._environment.object_id)
153
- resp = await self._environment.client.stub.EnvironmentGetManaged(req)
132
+ req = api_pb2.EnvironmentGetRolesRequest(environment_id=self._environment.object_id)
133
+ resp = await self._environment.client.stub.EnvironmentGetRoles(req)
154
134
 
155
135
  users: dict[str, str] = {}
156
136
  service_users: dict[str, str] = {}
157
137
  for principal in resp.principal_roles:
158
- role = _role_from_proto(principal.role)
159
138
  if principal.user_id:
160
- users[principal.user_name] = role
139
+ users[principal.user_name] = principal.role_str
161
140
  elif principal.service_user_id:
162
- service_users[principal.service_user_name] = role
141
+ service_users[principal.service_user_name] = principal.role_str
163
142
 
164
143
  return {"users": users, "service_users": service_users}
165
144
 
@@ -188,13 +167,13 @@ class _EnvironmentMembersManager:
188
167
  users = users or {}
189
168
  service_users = service_users or {}
190
169
 
191
- req = api_pb2.EnvironmentGetManagedRequest(environment_id=self._environment.object_id)
192
- resp = await self._environment.client.stub.EnvironmentGetManaged(req)
170
+ req = api_pb2.EnvironmentGetRolesRequest(environment_id=self._environment.object_id)
171
+ resp = await self._environment.client.stub.EnvironmentGetRoles(req)
193
172
 
194
- # Both current members and additional eligible workspace principals can be assigned a role
173
+ # EnvironmentGetRoles returns every workspace principal
195
174
  user_name_to_id: dict[str, str] = {}
196
175
  service_user_name_to_id: dict[str, str] = {}
197
- for principal in [*resp.principal_roles, *resp.additional_roles]:
176
+ for principal in resp.principal_roles:
198
177
  if principal.user_id:
199
178
  user_name_to_id[principal.user_name] = principal.user_id
200
179
  elif principal.service_user_id:
@@ -207,7 +186,7 @@ class _EnvironmentMembersManager:
207
186
  requests[f"User {name!r}"] = api_pb2.EnvironmentRoleSetRequest(
208
187
  environment_id=self._environment.object_id,
209
188
  user_id=user_name_to_id[name],
210
- role=_role_to_proto(role),
189
+ role_str=role,
211
190
  )
212
191
  for name, role in service_users.items():
213
192
  if name not in service_user_name_to_id:
@@ -215,7 +194,7 @@ class _EnvironmentMembersManager:
215
194
  requests[f"Service user {name!r}"] = api_pb2.EnvironmentRoleSetRequest(
216
195
  environment_id=self._environment.object_id,
217
196
  service_user_id=service_user_name_to_id[name],
218
- role=_role_to_proto(role),
197
+ role_str=role,
219
198
  )
220
199
 
221
200
  await self._dispatch_role_updates(requests)
@@ -242,8 +221,8 @@ class _EnvironmentMembersManager:
242
221
  users = users or []
243
222
  service_users = service_users or []
244
223
 
245
- req = api_pb2.EnvironmentGetManagedRequest(environment_id=self._environment.object_id)
246
- resp = await self._environment.client.stub.EnvironmentGetManaged(req)
224
+ req = api_pb2.EnvironmentGetRolesRequest(environment_id=self._environment.object_id)
225
+ resp = await self._environment.client.stub.EnvironmentGetRoles(req)
247
226
 
248
227
  user_name_to_id: dict[str, str] = {}
249
228
  service_user_name_to_id: dict[str, str] = {}
@@ -256,7 +235,7 @@ class _EnvironmentMembersManager:
256
235
  requests: dict[str, api_pb2.EnvironmentRoleSetRequest] = {}
257
236
  for name in users:
258
237
  if name not in user_name_to_id:
259
- raise InvalidError(f"User {name!r} is not a member of this Environment")
238
+ raise InvalidError(f"User {name!r} not found in workspace")
260
239
  requests[f"User {name!r}"] = api_pb2.EnvironmentRoleSetRequest(
261
240
  environment_id=self._environment.object_id,
262
241
  user_id=user_name_to_id[name],
@@ -264,7 +243,7 @@ class _EnvironmentMembersManager:
264
243
  )
265
244
  for name in service_users:
266
245
  if name not in service_user_name_to_id:
267
- raise InvalidError(f"Service user {name!r} is not a member of this Environment")
246
+ raise InvalidError(f"Service user {name!r} not found in workspace")
268
247
  requests[f"Service user {name!r}"] = api_pb2.EnvironmentRoleSetRequest(
269
248
  environment_id=self._environment.object_id,
270
249
  service_user_id=service_user_name_to_id[name],
@@ -1,7 +1,6 @@
1
1
  # Copyright Modal Labs 2024
2
2
  import asyncio
3
3
  import inspect
4
- import math
5
4
  import sys
6
5
  import time
7
6
  import traceback
@@ -48,8 +47,6 @@ DYNAMIC_CONCURRENCY_INTERVAL_SECS = 3
48
47
  DYNAMIC_CONCURRENCY_TIMEOUT_SECS = 10
49
48
  MAX_OUTPUT_BATCH_SIZE: int = 49
50
49
 
51
- RTT_S: float = 0.5 # conservative estimate of RTT in seconds.
52
-
53
50
 
54
51
  class Sentinel:
55
52
  """Used to get type-stubs to work with this object."""
@@ -474,8 +471,6 @@ class _ContainerIOManager:
474
471
  _task_lifecycle_manager: _TaskLifecycleManager
475
472
  input_plane_server_url: str | None
476
473
 
477
- calls_completed: int
478
- total_user_time: float
479
474
  current_input_id: str | None
480
475
  current_inputs: dict[str, IOContext] # input_id -> IOContext
481
476
  current_input_started_at: float | None
@@ -510,8 +505,6 @@ class _ContainerIOManager:
510
505
 
511
506
  self.input_plane_server_url = container_args.input_plane_server_url
512
507
 
513
- self.calls_completed = 0
514
- self.total_user_time = 0.0
515
508
  self.current_input_id = None
516
509
  self.current_inputs = {}
517
510
  self.current_input_started_at = None
@@ -791,18 +784,6 @@ class _ContainerIOManager:
791
784
  """Put a value onto a queue, using the synchronicity event loop."""
792
785
  await queue.put(value)
793
786
 
794
- def get_average_call_time(self) -> float:
795
- if self.calls_completed == 0:
796
- return 0
797
-
798
- return self.total_user_time / self.calls_completed
799
-
800
- def get_max_inputs_to_fetch(self):
801
- if self.calls_completed == 0:
802
- return 1
803
-
804
- return math.ceil(RTT_S / max(self.get_average_call_time(), 1e-6))
805
-
806
787
  @synchronizer.no_io_translation
807
788
  async def _generate_inputs(
808
789
  self,
@@ -814,8 +795,6 @@ class _ContainerIOManager:
814
795
  while self._fetching_inputs:
815
796
  await self._input_slots.acquire()
816
797
 
817
- request.average_call_time = self.get_average_call_time()
818
- request.max_values = self.get_max_inputs_to_fetch() # Deprecated; remove.
819
798
  request.input_concurrency = self.get_input_concurrency()
820
799
  request.batch_max_size, request.batch_linger_ms = batch_max_size, batch_wait_ms
821
800
 
@@ -888,7 +867,7 @@ class _ContainerIOManager:
888
867
  # collect all active input slots, meaning all inputs have wrapped up.
889
868
  await self._input_slots.close()
890
869
 
891
- async def _send_outputs(self, started_at: float, outputs: list[api_pb2.FunctionPutOutputsItem]) -> None:
870
+ async def _send_outputs(self, outputs: list[api_pb2.FunctionPutOutputsItem]) -> None:
892
871
  """Send pre-built output items with retry and chunking."""
893
872
  # There are multiple outputs for a single IOContext in the case of @modal.batched.
894
873
  # Limit the batch size to 20 to stay within message size limits and buffer size limits.
@@ -902,7 +881,7 @@ class _ContainerIOManager:
902
881
  ),
903
882
  )
904
883
  input_ids = [output.input_id for output in outputs]
905
- self.exit_context(started_at, input_ids)
884
+ self.exit_context(input_ids)
906
885
 
907
886
  @asynccontextmanager
908
887
  async def handle_input_exception(
@@ -922,7 +901,7 @@ class _ContainerIOManager:
922
901
  raise
923
902
  except (InputCancellation, asyncio.CancelledError):
924
903
  outputs = await io_context.output_items_cancellation(started_at)
925
- await self._send_outputs(started_at, outputs)
904
+ await self._send_outputs(outputs)
926
905
  logger.warning(f"Successfully canceled input {io_context.input_ids}")
927
906
  return
928
907
  except BaseException as exc:
@@ -933,12 +912,9 @@ class _ContainerIOManager:
933
912
  # print exception so it's logged
934
913
  print_exception(*sys.exc_info())
935
914
  outputs = await io_context.output_items_exception(started_at, self.task_id, exc)
936
- await self._send_outputs(started_at, outputs)
937
-
938
- def exit_context(self, started_at, input_ids: list[str]):
939
- self.total_user_time += time.time() - started_at
940
- self.calls_completed += 1
915
+ await self._send_outputs(outputs)
941
916
 
917
+ def exit_context(self, input_ids: list[str]):
942
918
  for input_id in input_ids:
943
919
  self.current_inputs.pop(input_id)
944
920
 
@@ -954,7 +930,7 @@ class _ContainerIOManager:
954
930
  ) -> None:
955
931
  # The standard output encoding+sending method for successful function outputs
956
932
  outputs = await io_context.output_items(started_at, output_data)
957
- await self._send_outputs(started_at, outputs)
933
+ await self._send_outputs(outputs)
958
934
 
959
935
  @asynccontextmanager
960
936
  async def snapshot_context_manager(self) -> AsyncGenerator[None, None]:
@@ -100,8 +100,6 @@ class _ContainerIOManager:
100
100
  app_id: str
101
101
  _task_lifecycle_manager: modal._runtime.task_lifecycle_manager._TaskLifecycleManager
102
102
  input_plane_server_url: typing.Optional[str]
103
- calls_completed: int
104
- total_user_time: float
105
103
  current_input_id: typing.Optional[str]
106
104
  current_inputs: dict[str, IOContext]
107
105
  current_input_started_at: typing.Optional[float]
@@ -185,8 +183,6 @@ class _ContainerIOManager:
185
183
  """Put a value onto a queue, using the synchronicity event loop."""
186
184
  ...
187
185
 
188
- def get_average_call_time(self) -> float: ...
189
- def get_max_inputs_to_fetch(self): ...
190
186
  def _generate_inputs(
191
187
  self, batch_max_size: int, batch_wait_ms: int
192
188
  ) -> collections.abc.AsyncIterator[list[tuple[str, int, str, str, modal_proto.api_pb2.FunctionInput]]]: ...
@@ -196,7 +192,7 @@ class _ContainerIOManager:
196
192
  batch_max_size: int = 0,
197
193
  batch_wait_ms: int = 0,
198
194
  ) -> collections.abc.AsyncIterator[IOContext]: ...
199
- async def _send_outputs(self, started_at: float, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
195
+ async def _send_outputs(self, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
200
196
  """Send pre-built output items with retry and chunking."""
201
197
  ...
202
198
 
@@ -204,7 +200,7 @@ class _ContainerIOManager:
204
200
  """Handle an exception while processing a function input."""
205
201
  ...
206
202
 
207
- def exit_context(self, started_at, input_ids: list[str]): ...
203
+ def exit_context(self, input_ids: list[str]): ...
208
204
  async def push_outputs(self, io_context: IOContext, started_at: float, output_data: list[typing.Any]) -> None: ...
209
205
  def snapshot_context_manager(self) -> typing.AsyncContextManager[None]: ...
210
206
  async def interact(self, from_breakpoint: bool = False): ...
@@ -246,8 +242,6 @@ class ContainerIOManager:
246
242
  app_id: str
247
243
  _task_lifecycle_manager: modal._runtime.task_lifecycle_manager.TaskLifecycleManager
248
244
  input_plane_server_url: typing.Optional[str]
249
- calls_completed: int
250
- total_user_time: float
251
245
  current_input_id: typing.Optional[str]
252
246
  current_inputs: dict[str, IOContext]
253
247
  current_input_started_at: typing.Optional[float]
@@ -413,9 +407,6 @@ class ContainerIOManager:
413
407
 
414
408
  _queue_put: ___queue_put_spec
415
409
 
416
- def get_average_call_time(self) -> float: ...
417
- def get_max_inputs_to_fetch(self): ...
418
-
419
410
  class ___generate_inputs_spec(typing_extensions.Protocol):
420
411
  def __call__(
421
412
  self, /, batch_max_size: int, batch_wait_ms: int
@@ -445,11 +436,11 @@ class ContainerIOManager:
445
436
  run_inputs_outputs: __run_inputs_outputs_spec
446
437
 
447
438
  class ___send_outputs_spec(typing_extensions.Protocol):
448
- def __call__(self, /, started_at: float, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
439
+ def __call__(self, /, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
449
440
  """Send pre-built output items with retry and chunking."""
450
441
  ...
451
442
 
452
- async def aio(self, /, started_at: float, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
443
+ async def aio(self, /, outputs: list[modal_proto.api_pb2.FunctionPutOutputsItem]) -> None:
453
444
  """Send pre-built output items with retry and chunking."""
454
445
  ...
455
446
 
@@ -468,7 +459,7 @@ class ContainerIOManager:
468
459
 
469
460
  handle_input_exception: __handle_input_exception_spec
470
461
 
471
- def exit_context(self, started_at, input_ids: list[str]): ...
462
+ def exit_context(self, input_ids: list[str]): ...
472
463
 
473
464
  class __push_outputs_spec(typing_extensions.Protocol):
474
465
  def __call__(self, /, io_context: IOContext, started_at: float, output_data: list[typing.Any]) -> None: ...
@@ -517,5 +508,3 @@ class ContainerIOManager:
517
508
  def stop_fetching_inputs(cls): ...
518
509
 
519
510
  MAX_OUTPUT_BATCH_SIZE: int
520
-
521
- RTT_S: float
@@ -6,7 +6,7 @@ import sys
6
6
  import urllib.parse
7
7
  import warnings
8
8
  from collections.abc import AsyncGenerator, Collection, Mapping
9
- from typing import Any, ClassVar, TypeVar
9
+ from typing import Any, ClassVar, Coroutine, TypeVar
10
10
 
11
11
  import grpclib.client
12
12
  from google.protobuf import empty_pb2
@@ -41,7 +41,7 @@ def _get_metadata(client_type: int, credentials: tuple[str, str] | None, version
41
41
  # sys.version_info is structured unlike sys.version or platform.python_version()
42
42
  python_version = "%d.%d.%d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro)
43
43
 
44
- metadata = {
44
+ metadata: dict[str, str] = {
45
45
  "x-modal-client-version": version,
46
46
  "x-modal-client-type": str(client_type),
47
47
  "x-modal-python-version": python_version,
@@ -58,9 +58,15 @@ def _get_metadata(client_type: int, credentials: tuple[str, str] | None, version
58
58
  )
59
59
  if agent_env := _agent_environment():
60
60
  metadata["x-modal-agent-harness"] = urllib.parse.quote(agent_env)
61
+
62
+ override_headers = config.get("override_headers")
63
+ if override_headers:
64
+ metadata.update(override_headers)
65
+
61
66
  return metadata
62
67
 
63
68
 
69
+ T = TypeVar("T")
64
70
  ReturnType = TypeVar("ReturnType")
65
71
  _Value = str | bytes
66
72
  _MetadataLike = Mapping[str, _Value] | Collection[tuple[str, _Value]]
@@ -308,7 +314,7 @@ class _Client:
308
314
  ("x-modal-auth-token", token),
309
315
  ]
310
316
 
311
- async def _call_safely(self, coro, readable_method: str):
317
+ async def _call_safely(self, coro: Coroutine[Any, Any, T], readable_method: str) -> T:
312
318
  """Runs coroutine wrapped in a task that's part of the client's task context
313
319
 
314
320
  * Raises ClientClosed in case the client is closed while the coroutine is executed
@@ -364,11 +370,11 @@ class _Client:
364
370
  async def _call_unary(
365
371
  self,
366
372
  grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
367
- request: Any,
373
+ request: RequestType,
368
374
  *,
369
375
  timeout: float | None = None,
370
376
  metadata: _MetadataLike | None = None,
371
- ) -> Any:
377
+ ) -> ResponseType:
372
378
  coro = grpclib_method(request, timeout=timeout, metadata=metadata)
373
379
  return await self._call_safely(coro, grpclib_method.name)
374
380
 
@@ -376,10 +382,10 @@ class _Client:
376
382
  async def _call_stream(
377
383
  self,
378
384
  grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
379
- request: Any,
385
+ request: RequestType,
380
386
  *,
381
387
  metadata: _MetadataLike | None,
382
- ) -> AsyncGenerator[Any, None]:
388
+ ) -> AsyncGenerator[ResponseType, None]:
383
389
  stream_context = grpclib_method.open(metadata=metadata)
384
390
  stream = await self._call_safely(stream_context.__aenter__(), f"{grpclib_method.name}.open")
385
391
  try:
@@ -13,6 +13,8 @@ import typing_extensions
13
13
 
14
14
  def _get_metadata(client_type: int, credentials: typing.Optional[tuple[str, str]], version: str) -> dict[str, str]: ...
15
15
 
16
+ T = typing.TypeVar("T")
17
+
16
18
  ReturnType = typing.TypeVar("ReturnType")
17
19
 
18
20
  RequestType = typing.TypeVar("RequestType", bound="google.protobuf.message.Message")
@@ -35,7 +37,7 @@ class _Client:
35
37
  server_url: str,
36
38
  client_type: int,
37
39
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.5.3.dev3",
40
+ version: str = "1.5.3.dev5",
39
41
  ):
40
42
  """mdmd:hidden
41
43
  The Modal client object is not intended to be instantiated directly by users.
@@ -154,7 +156,7 @@ class _Client:
154
156
  """
155
157
  ...
156
158
 
157
- async def _call_safely(self, coro, readable_method: str):
159
+ async def _call_safely(self, coro: typing.Coroutine[typing.Any, typing.Any, T], readable_method: str) -> T:
158
160
  """Runs coroutine wrapped in a task that's part of the client's task context
159
161
 
160
162
  * Raises ClientClosed in case the client is closed while the coroutine is executed
@@ -168,7 +170,7 @@ class _Client:
168
170
  async def _call_unary(
169
171
  self,
170
172
  grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
171
- request: typing.Any,
173
+ request: RequestType,
172
174
  *,
173
175
  timeout: typing.Optional[float] = None,
174
176
  metadata: typing.Union[
@@ -176,18 +178,18 @@ class _Client:
176
178
  collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
177
179
  None,
178
180
  ] = None,
179
- ) -> typing.Any: ...
181
+ ) -> ResponseType: ...
180
182
  def _call_stream(
181
183
  self,
182
184
  grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
183
- request: typing.Any,
185
+ request: RequestType,
184
186
  *,
185
187
  metadata: typing.Union[
186
188
  collections.abc.Mapping[str, typing.Union[str, bytes]],
187
189
  collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
188
190
  None,
189
191
  ],
190
- ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
192
+ ) -> collections.abc.AsyncGenerator[ResponseType, None]: ...
191
193
 
192
194
  class Client:
193
195
  _client_from_env: typing.ClassVar[typing.Optional[Client]]
@@ -205,7 +207,7 @@ class Client:
205
207
  server_url: str,
206
208
  client_type: int,
207
209
  credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.5.3.dev3",
210
+ version: str = "1.5.3.dev5",
209
211
  ):
210
212
  """mdmd:hidden
211
213
  The Modal client object is not intended to be instantiated directly by users.
@@ -429,7 +431,7 @@ class Client:
429
431
  get_input_plane_metadata: __get_input_plane_metadata_spec
430
432
 
431
433
  class ___call_safely_spec(typing_extensions.Protocol):
432
- def __call__(self, /, coro, readable_method: str):
434
+ def __call__(self, /, coro: T, readable_method: str) -> T:
433
435
  """Runs coroutine wrapped in a task that's part of the client's task context
434
436
 
435
437
  * Raises ClientClosed in case the client is closed while the coroutine is executed
@@ -438,7 +440,7 @@ class Client:
438
440
  """
439
441
  ...
440
442
 
441
- async def aio(self, /, coro, readable_method: str):
443
+ async def aio(self, /, coro: typing.Coroutine[typing.Any, typing.Any, T], readable_method: str) -> T:
442
444
  """Runs coroutine wrapped in a task that's part of the client's task context
443
445
 
444
446
  * Raises ClientClosed in case the client is closed while the coroutine is executed
@@ -464,7 +466,7 @@ class Client:
464
466
  async def _call_unary(
465
467
  self,
466
468
  grpclib_method: grpclib.client.UnaryUnaryMethod[RequestType, ResponseType],
467
- request: typing.Any,
469
+ request: RequestType,
468
470
  *,
469
471
  timeout: typing.Optional[float] = None,
470
472
  metadata: typing.Union[
@@ -472,18 +474,18 @@ class Client:
472
474
  collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
473
475
  None,
474
476
  ] = None,
475
- ) -> typing.Any: ...
477
+ ) -> ResponseType: ...
476
478
  def _call_stream(
477
479
  self,
478
480
  grpclib_method: grpclib.client.UnaryStreamMethod[RequestType, ResponseType],
479
- request: typing.Any,
481
+ request: RequestType,
480
482
  *,
481
483
  metadata: typing.Union[
482
484
  collections.abc.Mapping[str, typing.Union[str, bytes]],
483
485
  collections.abc.Collection[tuple[str, typing.Union[str, bytes]]],
484
486
  None,
485
487
  ],
486
- ) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
488
+ ) -> collections.abc.AsyncGenerator[ResponseType, None]: ...
487
489
 
488
490
  HEARTBEAT_INTERVAL: float
489
491
 
@@ -264,6 +264,33 @@ def _enforce_suffix_rules(x: str) -> str:
264
264
  return x
265
265
 
266
266
 
267
+ def _transform_headers(s: str) -> dict[str, str]:
268
+ error_msg = (
269
+ "Must be a comma-separated list of `<header>:<value>` pairs (whitespace is ignored). "
270
+ "<header> and <value> must contain at least one non-whitespace character, and may "
271
+ "not contain commas."
272
+ )
273
+
274
+ if s == "":
275
+ return {}
276
+
277
+ pairs = s.split(",")
278
+
279
+ headers: dict[str, str] = {}
280
+ for pair in pairs:
281
+ res = pair.split(":", maxsplit=1)
282
+ if len(res) != 2:
283
+ raise ValueError(error_msg)
284
+
285
+ header, value = res[0].strip(), res[1].strip()
286
+ if len(header) == 0 or len(value) == 0:
287
+ raise ValueError(error_msg)
288
+
289
+ headers[header] = value
290
+
291
+ return headers
292
+
293
+
267
294
  class _Setting(typing.NamedTuple):
268
295
  default: typing.Any = None
269
296
  transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
@@ -306,6 +333,7 @@ _SETTINGS = {
306
333
  "max_throttle_wait": _Setting(None, transform=lambda x: int(x) if x else None),
307
334
  "async_warnings": _Setting(True, transform=_to_boolean), # Activate synchronicity usage warnings
308
335
  "disable_api_proxy": _Setting(False, transform=_to_boolean),
336
+ "override_headers": _Setting(None, transform=_transform_headers),
309
337
  }
310
338
 
311
339
 
@@ -113,32 +113,32 @@ class EnvironmentMembersManager:
113
113
 
114
114
  class __list_spec(typing_extensions.Protocol):
115
115
  def __call__(self, /) -> dict[typing.Literal["users", "service_users"], dict[str, str]]:
116
- """Return the members of a restricted Environment with their roles.
116
+ """Enumerate the environment roles for each user and service user in the workspace.
117
117
 
118
118
  **Examples:**
119
119
 
120
120
  ```python notest
121
- members = modal.Environment.from_name("my-restricted-env").members.list()
121
+ members = modal.Environment.from_name("my-env").members.list()
122
122
  print(members)
123
123
  # {
124
- # "users": {"alice": "contributor", "bob": "viewer"},
125
- # "service_users": {"alice-bot": "contributor"},
124
+ # "users": {"alice": "contributor", "bob": "viewer", "carol": "contributor"},
125
+ # "service_users": {"alice-bot": "contributor", "ops-bot": "viewer", "ci-bot": "no-access"},
126
126
  # }
127
127
  ```
128
128
  """
129
129
  ...
130
130
 
131
131
  async def aio(self, /) -> dict[typing.Literal["users", "service_users"], dict[str, str]]:
132
- """Return the members of a restricted Environment with their roles.
132
+ """Enumerate the environment roles for each user and service user in the workspace.
133
133
 
134
134
  **Examples:**
135
135
 
136
136
  ```python notest
137
- members = modal.Environment.from_name("my-restricted-env").members.list()
137
+ members = modal.Environment.from_name("my-env").members.list()
138
138
  print(members)
139
139
  # {
140
- # "users": {"alice": "contributor", "bob": "viewer"},
141
- # "service_users": {"alice-bot": "contributor"},
140
+ # "users": {"alice": "contributor", "bob": "viewer", "carol": "contributor"},
141
+ # "service_users": {"alice-bot": "contributor", "ops-bot": "viewer", "ci-bot": "no-access"},
142
142
  # }
143
143
  ```
144
144
  """