modal 1.5.3.dev4__tar.gz → 1.5.3.dev6__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.dev4 → modal-1.5.3.dev6}/PKG-INFO +1 -1
  2. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_environments.py +18 -39
  3. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_functions.py +11 -15
  4. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/task_command_router_client.py +127 -3
  5. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/client.pyi +2 -2
  6. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/container_process.py +13 -1
  7. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/container_process.pyi +27 -0
  8. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/environments.pyi +8 -8
  9. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/functions.pyi +20 -20
  10. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/sandbox.py +37 -14
  11. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/sandbox.pyi +6 -2
  12. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/sandbox_fs.py +32 -80
  13. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/sandbox_fs.pyi +24 -0
  14. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/PKG-INFO +1 -1
  15. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/gen_reference_docs.py +0 -1
  16. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/mdmd/mdmd.py +9 -2
  17. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/mdmd/types.py +1 -0
  18. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/api_grpc.py +16 -0
  19. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/api_pb2.py +764 -744
  20. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/api_pb2.pyi +63 -10
  21. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/api_pb2_grpc.py +33 -0
  22. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/api_pb2_grpc.pyi +10 -0
  23. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/modal_api_grpc.py +1 -0
  24. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_version/__init__.py +1 -1
  25. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/LICENSE +0 -0
  26. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/README.md +0 -0
  27. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/__init__.py +0 -0
  28. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/__main__.py +0 -0
  29. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_billing.py +0 -0
  30. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_clustered_functions.py +0 -0
  31. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_clustered_functions.pyi +0 -0
  32. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_container_entrypoint.py +0 -0
  33. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_function_variants.py +0 -0
  34. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_grpc_client.py +0 -0
  35. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_image.py +0 -0
  36. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_ipython.py +0 -0
  37. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_load_context.py +0 -0
  38. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_location.py +0 -0
  39. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_logs.py +0 -0
  40. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_object.py +0 -0
  41. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_output/__init__.py +0 -0
  42. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_output/manager.py +0 -0
  43. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_output/pty.py +0 -0
  44. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_output/rich.py +0 -0
  45. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_output/status.py +0 -0
  46. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_partial_function.py +0 -0
  47. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_resolver.py +0 -0
  48. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_resources.py +0 -0
  49. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/__init__.py +0 -0
  50. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/asgi.py +0 -0
  51. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/container_io_manager.py +0 -0
  52. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/container_io_manager.pyi +0 -0
  53. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/execution_context.py +0 -0
  54. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/execution_context.pyi +0 -0
  55. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  56. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/task_lifecycle_manager.py +0 -0
  57. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  58. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/telemetry.py +0 -0
  59. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/user_code_event_loop.py +0 -0
  60. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_runtime/user_code_imports.py +0 -0
  61. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_serialization.py +0 -0
  62. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_server.py +0 -0
  63. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_traceback.py +0 -0
  64. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_tunnel.py +0 -0
  65. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_tunnel.pyi +0 -0
  66. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_type_manager.py +0 -0
  67. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/__init__.py +0 -0
  68. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/app_utils.py +0 -0
  69. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/async_utils.py +0 -0
  70. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/auth_token_manager.py +0 -0
  71. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/blob_utils.py +0 -0
  72. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/browser_utils.py +0 -0
  73. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/bytes_io_segment_payload.py +0 -0
  74. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/curl_utils.py +0 -0
  75. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/deprecation.py +0 -0
  76. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/docker_utils.py +0 -0
  77. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/function_utils.py +0 -0
  78. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/git_utils.py +0 -0
  79. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/grpc_testing.py +0 -0
  80. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/grpc_utils.py +0 -0
  81. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/hash_utils.py +0 -0
  82. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/http_utils.py +0 -0
  83. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/jwt_utils.py +0 -0
  84. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/logger.py +0 -0
  85. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/mount_utils.py +0 -0
  86. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/name_utils.py +0 -0
  87. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/package_utils.py +0 -0
  88. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/pattern_utils.py +0 -0
  89. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/proxy_support.py +0 -0
  90. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/rand_pb_testing.py +0 -0
  91. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/sandbox_fs_utils.py +0 -0
  92. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/shell_utils.py +0 -0
  93. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_utils/time_utils.py +0 -0
  94. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_vendor/__init__.py +0 -0
  95. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  96. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_vendor/cloudpickle.py +0 -0
  97. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_vendor/tblib.py +0 -0
  98. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_vendor/version.py +0 -0
  99. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_watcher.py +0 -0
  100. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/_workspace.py +0 -0
  101. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/app.py +0 -0
  102. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/app.pyi +0 -0
  103. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/billing.py +0 -0
  104. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/billing.pyi +0 -0
  105. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/2023.12.312.txt +0 -0
  106. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/2023.12.txt +0 -0
  107. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/2024.04.txt +0 -0
  108. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/2024.10.txt +0 -0
  109. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/2025.06.txt +0 -0
  110. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/PREVIEW.txt +0 -0
  111. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/README.md +0 -0
  112. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/builder/base-images.json +0 -0
  113. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/call_graph.py +0 -0
  114. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/__init__.py +0 -0
  115. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/_download.py +0 -0
  116. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/_help.py +0 -0
  117. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/_traceback.py +0 -0
  118. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/app.py +0 -0
  119. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/billing.py +0 -0
  120. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/bootstrap.py +0 -0
  121. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/changelog.py +0 -0
  122. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/cluster.py +0 -0
  123. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/config.py +0 -0
  124. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/container.py +0 -0
  125. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/curl.py +0 -0
  126. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/dashboard.py +0 -0
  127. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/dict.py +0 -0
  128. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/endpoint.py +0 -0
  129. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/entry_point.py +0 -0
  130. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/environment.py +0 -0
  131. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/image.py +0 -0
  132. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/import_refs.py +0 -0
  133. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/launch.py +0 -0
  134. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/logo.py +0 -0
  135. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/network_file_system.py +0 -0
  136. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/profile.py +0 -0
  137. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/programs/__init__.py +0 -0
  138. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/programs/run_jupyter.py +0 -0
  139. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/programs/vscode.py +0 -0
  140. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/queues.py +0 -0
  141. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/run.py +0 -0
  142. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/secret.py +0 -0
  143. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/selector.py +0 -0
  144. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/shell.py +0 -0
  145. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/skills.py +0 -0
  146. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/token.py +0 -0
  147. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/utils.py +0 -0
  148. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/volume.py +0 -0
  149. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cli/workspace.py +0 -0
  150. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/client.py +0 -0
  151. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cloud_bucket_mount.py +0 -0
  152. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cloud_bucket_mount.pyi +0 -0
  153. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cls.py +0 -0
  154. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/cls.pyi +0 -0
  155. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/config.py +0 -0
  156. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/dict.py +0 -0
  157. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/dict.pyi +0 -0
  158. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/environments.py +0 -0
  159. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/exception.py +0 -0
  160. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/experimental/__init__.py +0 -0
  161. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/experimental/flash.py +0 -0
  162. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/experimental/flash.pyi +0 -0
  163. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/experimental/ipython.py +0 -0
  164. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/file_io.py +0 -0
  165. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/file_io.pyi +0 -0
  166. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/file_pattern_matcher.py +0 -0
  167. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/functions.py +0 -0
  168. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/image.py +0 -0
  169. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/image.pyi +0 -0
  170. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/io_streams.py +0 -0
  171. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/io_streams.pyi +0 -0
  172. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/mount.py +0 -0
  173. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/mount.pyi +0 -0
  174. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/network_file_system.py +0 -0
  175. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/network_file_system.pyi +0 -0
  176. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/object.py +0 -0
  177. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/object.pyi +0 -0
  178. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/output.py +0 -0
  179. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/parallel_map.py +0 -0
  180. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/parallel_map.pyi +0 -0
  181. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/partial_function.py +0 -0
  182. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/partial_function.pyi +0 -0
  183. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/proxy.py +0 -0
  184. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/proxy.pyi +0 -0
  185. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/py.typed +0 -0
  186. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/queue.py +0 -0
  187. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/queue.pyi +0 -0
  188. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/retries.py +0 -0
  189. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/runner.py +0 -0
  190. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/runner.pyi +0 -0
  191. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/running_app.py +0 -0
  192. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/schedule.py +0 -0
  193. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/scheduler_placement.py +0 -0
  194. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/secret.py +0 -0
  195. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/secret.pyi +0 -0
  196. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/server.py +0 -0
  197. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/server.pyi +0 -0
  198. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/serving.py +0 -0
  199. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/serving.pyi +0 -0
  200. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/skills/modal/SKILL.md +0 -0
  201. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/snapshot.py +0 -0
  202. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/snapshot.pyi +0 -0
  203. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/stream_type.py +0 -0
  204. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/token_flow.py +0 -0
  205. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/token_flow.pyi +0 -0
  206. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/types.py +0 -0
  207. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/volume.py +0 -0
  208. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/volume.pyi +0 -0
  209. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/workspace.py +0 -0
  210. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal/workspace.pyi +0 -0
  211. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/SOURCES.txt +0 -0
  212. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/dependency_links.txt +0 -0
  213. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/entry_points.txt +0 -0
  214. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/requires.txt +0 -0
  215. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal.egg-info/top_level.txt +0 -0
  216. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/__init__.py +0 -0
  217. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/gen_cli_docs.py +0 -0
  218. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/gen_cli_docs_main.py +0 -0
  219. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/gen_reference_docs_main.py +0 -0
  220. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/mdmd/__init__.py +0 -0
  221. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_docs/mdmd/signatures.py +0 -0
  222. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/__init__.py +0 -0
  223. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/py.typed +0 -0
  224. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/task_command_router_grpc.py +0 -0
  225. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/task_command_router_pb2.py +0 -0
  226. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/task_command_router_pb2.pyi +0 -0
  227. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  228. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  229. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/modal_version/__main__.py +0 -0
  230. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/pyproject.toml +0 -0
  231. {modal-1.5.3.dev4 → modal-1.5.3.dev6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.3.dev4
3
+ Version: 1.5.3.dev6
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -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],
@@ -2073,11 +2073,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2073
2073
 
2074
2074
  @live_method
2075
2075
  async def get_call_graph(self) -> list[InputInfo]:
2076
- """Returns a structure representing the call graph from a given root
2077
- call ID, along with the status of execution for each node.
2076
+ """Fetch information about the graph of Inputs this FunctionCall is part of.
2078
2077
 
2079
- See [`modal.call_graph`](https://modal.com/docs/sdk/py/latest/modal.call_graph) reference page
2080
- for documentation on the structure of the returned `InputInfo` items.
2078
+ Note: the call graph data is not populated in real-time, and its capture is best-effort.
2079
+ We do not recommend relying on this method for critical use cases.
2080
+
2081
+ See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
2082
+ on the return values.
2081
2083
 
2082
2084
  Returns:
2083
2085
  A list of `InputInfo` nodes describing the call graph.
@@ -2088,19 +2090,13 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2088
2090
  return _reconstruct_call_graph(response)
2089
2091
 
2090
2092
  @live_method
2091
- async def cancel(
2092
- self,
2093
- # if true, containers running the inputs are forcibly terminated
2094
- terminate_containers: bool = False,
2095
- ):
2096
- """Cancels the function call, which will stop its execution and mark its inputs as
2097
- [`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
2098
-
2099
- If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
2100
- causing any non-cancelled inputs on those containers to be rescheduled in new containers.
2093
+ async def cancel(self, terminate_containers: bool = False):
2094
+ """Cancel the FunctionCall and terminate its inputs without retrying.
2101
2095
 
2102
2096
  Args:
2103
- terminate_containers: If True, forcibly terminate workers running cancelled inputs.
2097
+ terminate_containers: If True, terminate the containers running the cancelled
2098
+ inputs. Any other inputs running concurrently on those containers will be
2099
+ rescheduled.
2104
2100
  """
2105
2101
  request = api_pb2.FunctionCallCancelRequest(
2106
2102
  function_call_id=self.object_id, terminate_containers=terminate_containers
@@ -1,6 +1,7 @@
1
1
  # Copyright Modal Labs 2025
2
2
  import asyncio
3
3
  import base64
4
+ import io
4
5
  import json
5
6
  import socket
6
7
  import ssl
@@ -8,16 +9,16 @@ import time
8
9
  import typing
9
10
  import urllib.parse
10
11
  import weakref
11
- from collections.abc import AsyncGenerator, Callable
12
+ from collections.abc import AsyncGenerator, AsyncIterable, Callable
12
13
  from contextlib import suppress
13
- from typing import TypeVar
14
+ from typing import BinaryIO, TypeVar
14
15
 
15
16
  import grpclib.client
16
17
  from grpclib import GRPCError, Status
17
18
  from grpclib.exceptions import StreamTerminatedError
18
19
 
19
20
  from modal.config import logger
20
- from modal.exception import ExecTimeoutError, TimeoutError as ModalTimeoutError
21
+ from modal.exception import AuthError, ExecTimeoutError, InternalError, ServiceError, TimeoutError as ModalTimeoutError
21
22
  from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
22
23
  from modal_proto.task_command_router_grpc import TaskCommandRouterStub
23
24
 
@@ -26,6 +27,17 @@ from .._utils.grpc_utils import ModalChannel, create_channel_config
26
27
  from .async_utils import aclosing, retry
27
28
  from .grpc_utils import RETRYABLE_GRPC_STATUS_CODES
28
29
 
30
+ STREAMING_STDIN_CHUNK_SIZE = 256 * 1024
31
+
32
+ _STREAMING_STDIN_RESUMABLE_EXCEPTIONS = (
33
+ AuthError,
34
+ InternalError,
35
+ ServiceError,
36
+ StreamTerminatedError,
37
+ OSError,
38
+ asyncio.TimeoutError,
39
+ )
40
+
29
41
 
30
42
  @retry(n_attempts=34, base_delay=1, max_delay=10, attempt_timeout=10, total_timeout=310)
31
43
  async def _connect_channel(channel: grpclib.client.Channel):
@@ -468,6 +480,118 @@ class TaskCommandRouterClient:
468
480
  lambda: self._call_with_auth_retry(self._stub.TaskExecStdinWrite, request)
469
481
  )
470
482
 
483
+ async def exec_stdin_write_stream(
484
+ self,
485
+ task_id: str,
486
+ exec_id: str,
487
+ source: BinaryIO,
488
+ chunk_size: int = STREAMING_STDIN_CHUNK_SIZE,
489
+ max_resume_attempts: int = 9,
490
+ ) -> int:
491
+ """Stream `source` into the exec's stdin, with bounded resume on transient failures.
492
+
493
+ Streams the full contents of `source` in one client-streaming RPC and
494
+ closes stdin (EOF) on success. On a resumable error, queries
495
+ `exec_stdin_status` for the server's canonical offset, seeks `source`
496
+ to that point, and reopens the stream.
497
+
498
+ Args:
499
+ task_id: The task ID of the task running the exec'd command.
500
+ exec_id: The execution ID of the command to write to.
501
+ source: A seekable byte source (file, BytesIO, etc.).
502
+ chunk_size: Bytes per outbound message.
503
+ max_resume_attempts: Upper bound on resume retries before giving up.
504
+
505
+ Returns:
506
+ The total number of bytes streamed into the exec's stdin.
507
+ """
508
+ offset = 0
509
+ attempt = 0
510
+ while True:
511
+
512
+ async def _chunks():
513
+ while True:
514
+ chunk = source.read(chunk_size)
515
+ if not chunk:
516
+ return
517
+ yield chunk
518
+
519
+ try:
520
+ source.seek(offset)
521
+ await self._exec_stdin_write_stream(task_id, exec_id, offset, _chunks())
522
+ return source.tell()
523
+ except (*_STREAMING_STDIN_RESUMABLE_EXCEPTIONS, AttributeError) as e:
524
+ # StreamTerminatedError surfaces as AttributeError in grpclib<=0.4.7;
525
+ # see call_with_retries_on_transient_errors.
526
+ if isinstance(e, AttributeError) and "_write_appdata" not in str(e):
527
+ raise
528
+ attempt += 1
529
+ if attempt > max_resume_attempts:
530
+ raise
531
+ status = await self.exec_stdin_status(task_id=task_id, exec_id=exec_id)
532
+ if status.closed:
533
+ pos = source.tell()
534
+ if status.num_bytes_written == pos and source.seek(0, io.SEEK_END) == pos:
535
+ logger.debug(f"exec_stdin_write_stream completed but response was lost: {e}")
536
+ return pos
537
+ raise
538
+ offset = status.num_bytes_written
539
+ logger.debug(f"exec_stdin_write_stream resuming from offset {offset} after error: {e}")
540
+
541
+ async def _exec_stdin_write_stream(
542
+ self,
543
+ task_id: str,
544
+ exec_id: str,
545
+ start_offset: int,
546
+ chunks: AsyncIterable[bytes],
547
+ ) -> sr_pb2.TaskExecStdinWriteStreamResponse | None:
548
+ """Single client-streaming attempt: Start, Data chunks, then End (EOF).
549
+
550
+ Does not retry; `exec_stdin_write_stream` owns resume.
551
+ """
552
+ with grpc_error_converter():
553
+ stream = self._stub.TaskExecStdinWriteStream.open(metadata=self._get_metadata())
554
+ async with stream as s:
555
+ start = sr_pb2.TaskExecStdinWriteStreamRequest(
556
+ start=sr_pb2.TaskExecStdinWriteStreamStart(
557
+ task_id=task_id,
558
+ exec_id=exec_id,
559
+ offset=start_offset,
560
+ ),
561
+ )
562
+ await s.send_message(start)
563
+ async for data in chunks:
564
+ if not data:
565
+ continue
566
+ await s.send_message(sr_pb2.TaskExecStdinWriteStreamRequest(data=data))
567
+ # The server closes stdin only on this explicit End message. A
568
+ # stream that breaks before it leaves stdin open for resume.
569
+ await s.send_message(
570
+ sr_pb2.TaskExecStdinWriteStreamRequest(end=sr_pb2.TaskExecStdinWriteStreamEnd()),
571
+ end=True,
572
+ )
573
+ return await s.recv_message()
574
+
575
+ async def exec_stdin_status(self, task_id: str, exec_id: str) -> sr_pb2.TaskExecStdinStatusResponse:
576
+ """Read the current stdin write status for an exec'd command, to support retries from the right offset.
577
+
578
+ Used by streaming clients to find the resume offset after a stream
579
+ failure. Evicts any in-flight stdin stream for the exec, so it is not
580
+ safe to call for read-only observability.
581
+
582
+ Args:
583
+ task_id: The task ID of the task running the exec'd command.
584
+ exec_id: The execution ID of the command to query.
585
+ Raises:
586
+ Other errors: If retries are exhausted on transient errors or if there's an error
587
+ from the RPC itself.
588
+ """
589
+ request = sr_pb2.TaskExecStdinStatusRequest(task_id=task_id, exec_id=exec_id)
590
+ with grpc_error_converter():
591
+ return await call_with_retries_on_transient_errors(
592
+ lambda: self._call_with_auth_retry(self._stub.TaskExecStdinStatus, request)
593
+ )
594
+
471
595
  async def sandbox_stdin_write_v2(
472
596
  self, task_id: str, offset: int, data: bytes, eof: bool
473
597
  ) -> sr_pb2.SandboxStdinWriteV2Response:
@@ -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.3.dev4",
40
+ version: str = "1.5.3.dev6",
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.3.dev4",
210
+ version: str = "1.5.3.dev6",
211
211
  ):
212
212
  """mdmd:hidden
213
213
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,7 +1,7 @@
1
1
  # Copyright Modal Labs 2024
2
2
  import asyncio
3
3
  import platform
4
- from typing import Generic, TypeVar
4
+ from typing import BinaryIO, Generic, TypeVar
5
5
 
6
6
  from modal_proto import api_pb2
7
7
 
@@ -117,6 +117,18 @@ class _ContainerProcess(Generic[T]):
117
117
  )
118
118
  return self._returncode
119
119
 
120
+ async def _stdin_write_stream(self, source: BinaryIO) -> int:
121
+ """mdmd:hidden
122
+ Stream `source` into the process's stdin and close it (EOF) on success.
123
+
124
+ Returns the total bytes streamed.
125
+ """
126
+ return await self._command_router_client.exec_stdin_write_stream(
127
+ task_id=self._task_id,
128
+ exec_id=self._process_id,
129
+ source=source,
130
+ )
131
+
120
132
  async def poll(self) -> int | None:
121
133
  """Check if the container process has finished running.
122
134
 
@@ -53,6 +53,14 @@ class _ContainerProcess(typing.Generic[T]):
53
53
 
54
54
  @property
55
55
  def returncode(self) -> int: ...
56
+ async def _stdin_write_stream(self, source: typing.BinaryIO) -> int:
57
+ """mdmd:hidden
58
+ Stream `source` into the process's stdin and close it (EOF) on success.
59
+
60
+ Returns the total bytes streamed.
61
+ """
62
+ ...
63
+
56
64
  async def poll(self) -> typing.Optional[int]:
57
65
  """Check if the container process has finished running.
58
66
 
@@ -105,6 +113,25 @@ class ContainerProcess(typing.Generic[T]):
105
113
  @property
106
114
  def returncode(self) -> int: ...
107
115
 
116
+ class ___stdin_write_stream_spec(typing_extensions.Protocol):
117
+ def __call__(self, /, source: typing.BinaryIO) -> int:
118
+ """mdmd:hidden
119
+ Stream `source` into the process's stdin and close it (EOF) on success.
120
+
121
+ Returns the total bytes streamed.
122
+ """
123
+ ...
124
+
125
+ async def aio(self, /, source: typing.BinaryIO) -> int:
126
+ """mdmd:hidden
127
+ Stream `source` into the process's stdin and close it (EOF) on success.
128
+
129
+ Returns the total bytes streamed.
130
+ """
131
+ ...
132
+
133
+ _stdin_write_stream: ___stdin_write_stream_spec
134
+
108
135
  class __poll_spec(typing_extensions.Protocol):
109
136
  def __call__(self, /) -> typing.Optional[int]:
110
137
  """Check if the container process has finished running.
@@ -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
  """
@@ -1044,11 +1044,13 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
1044
1044
 
1045
1045
  class __get_call_graph_spec(typing_extensions.Protocol):
1046
1046
  def __call__(self, /) -> list[modal.types.InputInfo]:
1047
- """Returns a structure representing the call graph from a given root
1048
- call ID, along with the status of execution for each node.
1047
+ """Fetch information about the graph of Inputs this FunctionCall is part of.
1049
1048
 
1050
- See [`modal.call_graph`](https://modal.com/docs/sdk/py/latest/modal.call_graph) reference page
1051
- for documentation on the structure of the returned `InputInfo` items.
1049
+ Note: the call graph data is not populated in real-time, and its capture is best-effort.
1050
+ We do not recommend relying on this method for critical use cases.
1051
+
1052
+ See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
1053
+ on the return values.
1052
1054
 
1053
1055
  Returns:
1054
1056
  A list of `InputInfo` nodes describing the call graph.
@@ -1056,11 +1058,13 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
1056
1058
  ...
1057
1059
 
1058
1060
  async def aio(self, /) -> list[modal.types.InputInfo]:
1059
- """Returns a structure representing the call graph from a given root
1060
- call ID, along with the status of execution for each node.
1061
+ """Fetch information about the graph of Inputs this FunctionCall is part of.
1062
+
1063
+ Note: the call graph data is not populated in real-time, and its capture is best-effort.
1064
+ We do not recommend relying on this method for critical use cases.
1061
1065
 
1062
- See [`modal.call_graph`](https://modal.com/docs/sdk/py/latest/modal.call_graph) reference page
1063
- for documentation on the structure of the returned `InputInfo` items.
1066
+ See the [`modal.types`](/docs/sdk/py/latest/modal.types) reference for information
1067
+ on the return values.
1064
1068
 
1065
1069
  Returns:
1066
1070
  A list of `InputInfo` nodes describing the call graph.
@@ -1071,26 +1075,22 @@ class FunctionCall(typing.Generic[modal._functions.ReturnType], modal.object.Obj
1071
1075
 
1072
1076
  class __cancel_spec(typing_extensions.Protocol):
1073
1077
  def __call__(self, /, terminate_containers: bool = False):
1074
- """Cancels the function call, which will stop its execution and mark its inputs as
1075
- [`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
1076
-
1077
- If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
1078
- causing any non-cancelled inputs on those containers to be rescheduled in new containers.
1078
+ """Cancel the FunctionCall and terminate its inputs without retrying.
1079
1079
 
1080
1080
  Args:
1081
- terminate_containers: If True, forcibly terminate workers running cancelled inputs.
1081
+ terminate_containers: If True, terminate the containers running the cancelled
1082
+ inputs. Any other inputs running concurrently on those containers will be
1083
+ rescheduled.
1082
1084
  """
1083
1085
  ...
1084
1086
 
1085
1087
  async def aio(self, /, terminate_containers: bool = False):
1086
- """Cancels the function call, which will stop its execution and mark its inputs as
1087
- [`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
1088
-
1089
- If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
1090
- causing any non-cancelled inputs on those containers to be rescheduled in new containers.
1088
+ """Cancel the FunctionCall and terminate its inputs without retrying.
1091
1089
 
1092
1090
  Args:
1093
- terminate_containers: If True, forcibly terminate workers running cancelled inputs.
1091
+ terminate_containers: If True, terminate the containers running the cancelled
1092
+ inputs. Any other inputs running concurrently on those containers will be
1093
+ rescheduled.
1094
1094
  """
1095
1095
  ...
1096
1096