modal 1.5.3.dev13__tar.gz → 1.5.4.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/PKG-INFO +1 -1
  2. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_clustered_functions.py +11 -0
  3. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_clustered_functions.pyi +6 -2
  4. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_environments.py +66 -36
  5. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/environment.py +103 -41
  6. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/client.pyi +2 -2
  7. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/environments.py +3 -0
  8. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/environments.pyi +68 -53
  9. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/__init__.py +9 -1
  10. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/PKG-INFO +1 -1
  11. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2.py +274 -274
  12. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2.pyi +6 -1
  13. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_version/__init__.py +1 -1
  14. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/LICENSE +0 -0
  15. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/README.md +0 -0
  16. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/__init__.py +0 -0
  17. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/__main__.py +0 -0
  18. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_billing.py +0 -0
  19. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_container_entrypoint.py +0 -0
  20. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_function_variants.py +0 -0
  21. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_functions.py +0 -0
  22. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_grpc_client.py +0 -0
  23. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_image.py +0 -0
  24. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_ipython.py +0 -0
  25. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_load_context.py +0 -0
  26. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_location.py +0 -0
  27. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs.py +0 -0
  28. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs_manager.py +0 -0
  29. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_logs_manager.pyi +0 -0
  30. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_object.py +0 -0
  31. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/__init__.py +0 -0
  32. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/manager.py +0 -0
  33. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/pty.py +0 -0
  34. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/rich.py +0 -0
  35. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_output/status.py +0 -0
  36. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_partial_function.py +0 -0
  37. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_resolver.py +0 -0
  38. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_resources.py +0 -0
  39. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/__init__.py +0 -0
  40. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/asgi.py +0 -0
  41. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.py +0 -0
  42. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
  43. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/execution_context.py +0 -0
  44. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/execution_context.pyi +0 -0
  45. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  46. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.py +0 -0
  47. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  48. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/telemetry.py +0 -0
  49. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/user_code_event_loop.py +0 -0
  50. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_runtime/user_code_imports.py +0 -0
  51. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_serialization.py +0 -0
  52. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_server.py +0 -0
  53. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_supports_logs.py +0 -0
  54. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_traceback.py +0 -0
  55. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_tunnel.py +0 -0
  56. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_tunnel.pyi +0 -0
  57. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_type_manager.py +0 -0
  58. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/__init__.py +0 -0
  59. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/app_utils.py +0 -0
  60. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/async_utils.py +0 -0
  61. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/auth_token_manager.py +0 -0
  62. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/blob_utils.py +0 -0
  63. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/browser_utils.py +0 -0
  64. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  65. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/curl_utils.py +0 -0
  66. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/deprecation.py +0 -0
  67. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/display_utils.py +0 -0
  68. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/docker_utils.py +0 -0
  69. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/function_utils.py +0 -0
  70. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/git_utils.py +0 -0
  71. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/grpc_testing.py +0 -0
  72. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/grpc_utils.py +0 -0
  73. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/hash_utils.py +0 -0
  74. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/http_utils.py +0 -0
  75. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/jwt_utils.py +0 -0
  76. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/logger.py +0 -0
  77. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/mount_utils.py +0 -0
  78. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/name_utils.py +0 -0
  79. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/package_utils.py +0 -0
  80. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/pattern_utils.py +0 -0
  81. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/proxy_support.py +0 -0
  82. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/rand_pb_testing.py +0 -0
  83. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/sandbox_fs_utils.py +0 -0
  84. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/shell_utils.py +0 -0
  85. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/task_command_router_client.py +0 -0
  86. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_utils/time_utils.py +0 -0
  87. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/__init__.py +0 -0
  88. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  89. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/cloudpickle.py +0 -0
  90. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/tblib.py +0 -0
  91. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_vendor/version.py +0 -0
  92. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_watcher.py +0 -0
  93. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/_workspace.py +0 -0
  94. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/app.py +0 -0
  95. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/app.pyi +0 -0
  96. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/billing.py +0 -0
  97. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/billing.pyi +0 -0
  98. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2023.12.312.txt +0 -0
  99. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2023.12.txt +0 -0
  100. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2024.04.txt +0 -0
  101. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2024.10.txt +0 -0
  102. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/2025.06.txt +0 -0
  103. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/PREVIEW.txt +0 -0
  104. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/README.md +0 -0
  105. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/builder/base-images.json +0 -0
  106. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/call_graph.py +0 -0
  107. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/__init__.py +0 -0
  108. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_download.py +0 -0
  109. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_help.py +0 -0
  110. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/_traceback.py +0 -0
  111. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/app.py +0 -0
  112. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/billing.py +0 -0
  113. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/bootstrap.py +0 -0
  114. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/changelog.py +0 -0
  115. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/cluster.py +0 -0
  116. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/config.py +0 -0
  117. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/container.py +0 -0
  118. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/curl.py +0 -0
  119. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/dashboard.py +0 -0
  120. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/dict.py +0 -0
  121. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/endpoint.py +0 -0
  122. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/entry_point.py +0 -0
  123. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/image.py +0 -0
  124. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/import_refs.py +0 -0
  125. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/launch.py +0 -0
  126. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/logo.py +0 -0
  127. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/network_file_system.py +0 -0
  128. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/profile.py +0 -0
  129. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/__init__.py +0 -0
  130. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/run_jupyter.py +0 -0
  131. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/programs/vscode.py +0 -0
  132. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/queues.py +0 -0
  133. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/run.py +0 -0
  134. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/secret.py +0 -0
  135. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/selector.py +0 -0
  136. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/shell.py +0 -0
  137. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/skills.py +0 -0
  138. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/token.py +0 -0
  139. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/utils.py +0 -0
  140. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/volume.py +0 -0
  141. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cli/workspace.py +0 -0
  142. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/client.py +0 -0
  143. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.py +0 -0
  144. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cloud_bucket_mount.pyi +0 -0
  145. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cls.py +0 -0
  146. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/cls.pyi +0 -0
  147. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/config.py +0 -0
  148. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/container_process.py +0 -0
  149. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/container_process.pyi +0 -0
  150. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/dict.py +0 -0
  151. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/dict.pyi +0 -0
  152. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/exception.py +0 -0
  153. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/flash.py +0 -0
  154. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/flash.pyi +0 -0
  155. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/experimental/ipython.py +0 -0
  156. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_io.py +0 -0
  157. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_io.pyi +0 -0
  158. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/file_pattern_matcher.py +0 -0
  159. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/functions.py +0 -0
  160. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/functions.pyi +0 -0
  161. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/image.py +0 -0
  162. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/image.pyi +0 -0
  163. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/io_streams.py +0 -0
  164. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/io_streams.pyi +0 -0
  165. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/mount.py +0 -0
  166. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/mount.pyi +0 -0
  167. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/network_file_system.py +0 -0
  168. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/network_file_system.pyi +0 -0
  169. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/object.py +0 -0
  170. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/object.pyi +0 -0
  171. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/output.py +0 -0
  172. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/parallel_map.py +0 -0
  173. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/parallel_map.pyi +0 -0
  174. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/partial_function.py +0 -0
  175. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/partial_function.pyi +0 -0
  176. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/proxy.py +0 -0
  177. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/proxy.pyi +0 -0
  178. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/py.typed +0 -0
  179. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/queue.py +0 -0
  180. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/queue.pyi +0 -0
  181. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/retries.py +0 -0
  182. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/runner.py +0 -0
  183. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/runner.pyi +0 -0
  184. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/running_app.py +0 -0
  185. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox.py +0 -0
  186. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox.pyi +0 -0
  187. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox_fs.py +0 -0
  188. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/sandbox_fs.pyi +0 -0
  189. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/schedule.py +0 -0
  190. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/scheduler_placement.py +0 -0
  191. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/secret.py +0 -0
  192. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/secret.pyi +0 -0
  193. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/server.py +0 -0
  194. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/server.pyi +0 -0
  195. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/serving.py +0 -0
  196. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/serving.pyi +0 -0
  197. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/skills/modal/SKILL.md +0 -0
  198. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/snapshot.py +0 -0
  199. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/snapshot.pyi +0 -0
  200. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/stream_type.py +0 -0
  201. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/token_flow.py +0 -0
  202. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/token_flow.pyi +0 -0
  203. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/types.py +0 -0
  204. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/volume.py +0 -0
  205. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/volume.pyi +0 -0
  206. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/workspace.py +0 -0
  207. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal/workspace.pyi +0 -0
  208. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/SOURCES.txt +0 -0
  209. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/dependency_links.txt +0 -0
  210. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/entry_points.txt +0 -0
  211. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/requires.txt +0 -0
  212. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal.egg-info/top_level.txt +0 -0
  213. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/__init__.py +0 -0
  214. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs.py +0 -0
  215. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_cli_docs_main.py +0 -0
  216. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs.py +0 -0
  217. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/gen_reference_docs_main.py +0 -0
  218. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/__init__.py +0 -0
  219. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/mdmd.py +0 -0
  220. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/signatures.py +0 -0
  221. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/mdmd/types.py +0 -0
  222. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_docs/reference_intro.md +0 -0
  223. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/__init__.py +0 -0
  224. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_grpc.py +0 -0
  225. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.py +0 -0
  226. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/api_pb2_grpc.pyi +0 -0
  227. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/modal_api_grpc.py +0 -0
  228. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/py.typed +0 -0
  229. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_grpc.py +0 -0
  230. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.py +0 -0
  231. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2.pyi +0 -0
  232. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  233. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  234. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/modal_version/__main__.py +0 -0
  235. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/pyproject.toml +0 -0
  236. {modal-1.5.3.dev13 → modal-1.5.4.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.3.dev13
3
+ Version: 1.5.4.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -15,6 +15,7 @@ class ClusterInfo:
15
15
  cluster_id: str
16
16
  container_ips: list[str]
17
17
  container_ipv4_ips: list[str]
18
+ fabric_ids: list[str]
18
19
 
19
20
 
20
21
  cluster_info: ClusterInfo | None = None
@@ -29,6 +30,14 @@ def get_cluster_info() -> ClusterInfo:
29
30
  return cluster_info
30
31
 
31
32
 
33
+ def get_fabric_peers() -> list[int]:
34
+ info = get_cluster_info()
35
+ if info.rank >= len(info.fabric_ids) or not info.fabric_ids[info.rank]:
36
+ return [info.rank]
37
+ own_fabric_id = info.fabric_ids[info.rank]
38
+ return [rank for rank, fabric_id in enumerate(info.fabric_ids) if fabric_id == own_fabric_id]
39
+
40
+
32
41
  async def _initialize_clustered_function(client: _Client, task_id: str, world_size: int):
33
42
  global cluster_info
34
43
 
@@ -70,6 +79,7 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
70
79
  cluster_id=resp.cluster_id,
71
80
  container_ips=resp.container_ips,
72
81
  container_ipv4_ips=resp.container_ipv4_ips,
82
+ fabric_ids=resp.fabric_ids,
73
83
  )
74
84
  else:
75
85
  cluster_info = ClusterInfo(
@@ -77,6 +87,7 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
77
87
  cluster_id="", # No cluster ID for single-node # TODO(irfansharif): Is this right?
78
88
  container_ips=[container_ip],
79
89
  container_ipv4_ips=[], # No IPv4 IPs for single-node
90
+ fabric_ids=[],
80
91
  )
81
92
 
82
93
 
@@ -3,14 +3,17 @@ import types
3
3
  import typing_extensions
4
4
 
5
5
  class ClusterInfo:
6
- """ClusterInfo(rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str])"""
6
+ """ClusterInfo(rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str], fabric_ids: list[str])"""
7
7
 
8
8
  rank: int
9
9
  cluster_id: str
10
10
  container_ips: list[str]
11
11
  container_ipv4_ips: list[str]
12
+ fabric_ids: list[str]
12
13
 
13
- def __init__(self, rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str]) -> None:
14
+ def __init__(
15
+ self, rank: int, cluster_id: str, container_ips: list[str], container_ipv4_ips: list[str], fabric_ids: list[str]
16
+ ) -> None:
14
17
  """Initialize self. See help(type(self)) for accurate signature."""
15
18
  ...
16
19
 
@@ -23,6 +26,7 @@ class ClusterInfo:
23
26
  ...
24
27
 
25
28
  def get_cluster_info() -> ClusterInfo: ...
29
+ def get_fabric_peers() -> list[int]: ...
26
30
  async def _initialize_clustered_function(client: modal.client._Client, task_id: str, world_size: int): ...
27
31
 
28
32
  class __initialize_clustered_function_spec(typing_extensions.Protocol):
@@ -16,6 +16,7 @@ from modal_proto import api_pb2
16
16
  from ._load_context import LoadContext
17
17
  from ._object import _Object
18
18
  from ._resolver import Resolver
19
+ from ._utils.deprecation import deprecation_warning
19
20
  from ._utils.name_utils import check_environment_name
20
21
  from ._utils.time_utils import is_utc_month_aligned, parse_billing_cycle
21
22
  from .client import _Client
@@ -104,7 +105,7 @@ class _EnvironmentManager:
104
105
  await client.stub.EnvironmentDelete(api_pb2.EnvironmentDeleteRequest(name=name))
105
106
 
106
107
 
107
- class _EnvironmentMembersManager:
108
+ class _EnvironmentRolesManager:
108
109
  """mdmd:namespace"""
109
110
 
110
111
  def __init__(self, environment: "_Environment"):
@@ -112,13 +113,13 @@ class _EnvironmentMembersManager:
112
113
  self._environment = environment
113
114
 
114
115
  async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
115
- """Enumerate the environment roles for each user and service user in the workspace.
116
+ """Enumerate the Environment Role for each user and service user in the workspace.
116
117
 
117
118
  **Examples:**
118
119
 
119
120
  ```python notest
120
- members = modal.Environment.from_name("my-env").members.list()
121
- print(members)
121
+ roles = modal.Environment.from_name("my-env").roles.list()
122
+ print(roles)
122
123
  # {
123
124
  # "users": {"alice": "contributor", "bob": "viewer", "carol": "contributor"},
124
125
  # "service_users": {"alice-bot": "contributor", "ops-bot": "viewer", "ci-bot": "no-access"},
@@ -145,16 +146,17 @@ class _EnvironmentMembersManager:
145
146
  users: Mapping[str, str] | None = None,
146
147
  service_users: Mapping[str, str] | None = None,
147
148
  ) -> None:
148
- """Add or modify roles for members of a restricted Environment.
149
+ """Update the Environment Role of users and service users.
149
150
 
150
- Each user or service user will be added to the Environment if not currently a member;
151
- if already a member, the user or service user's role will be updated.
151
+ Each role is one of 'contributor', 'viewer', or 'no-access'. Service users can be
152
+ assigned a role on any Environment, while workspace members can only be assigned a
153
+ role on restricted Environments.
152
154
 
153
155
  **Examples:**
154
156
 
155
157
  ```python notest
156
158
  env = modal.Environment.from_name("my-restricted-env")
157
- env.members.update(
159
+ env.roles.update(
158
160
  users={"alice": "contributor", "bob": "viewer"},
159
161
  service_users={"alice-bot": "contributor"},
160
162
  )
@@ -196,24 +198,59 @@ class _EnvironmentMembersManager:
196
198
 
197
199
  await self._dispatch_role_updates(requests)
198
200
 
201
+ async def _dispatch_role_updates(self, requests: dict[str, api_pb2.EnvironmentRoleSetRequest]) -> None:
202
+ """Send batched EnvironmentRoleSet RPCs and report all errors encountered."""
203
+ results = await asyncio.gather(
204
+ *(self._environment.client.stub.EnvironmentRoleSet(req) for req in requests.values()),
205
+ return_exceptions=True,
206
+ )
207
+ errors = [(label, result) for label, result in zip(requests.keys(), results) if isinstance(result, Exception)]
208
+ if errors:
209
+ n = len(errors)
210
+ header = f"{n} error{'s' if n != 1 else ''} occurred while updating Environment roles:"
211
+ details = "\n".join(f" - {label}: {e}" for label, e in errors)
212
+ raise WorkspaceManagementError(f"{header}\n{details}")
213
+
214
+
215
+ # Deprecated alias for `_EnvironmentRolesManager`; each method warns and delegates.
216
+ class _EnvironmentMembersManager:
217
+ """mdmd:hidden"""
218
+
219
+ def __init__(self, environment: "_Environment"):
220
+ """mdmd:hidden"""
221
+ self._environment = environment
222
+
223
+ async def list(self) -> dict[Literal["users", "service_users"], dict[str, str]]:
224
+ deprecation_warning(
225
+ (2026, 7, 23),
226
+ "`Environment.members.list()` is deprecated; use `Environment.roles.list()` instead.",
227
+ )
228
+ return await _EnvironmentRolesManager(self._environment).list()
229
+
230
+ async def update(
231
+ self,
232
+ *,
233
+ users: Mapping[str, str] | None = None,
234
+ service_users: Mapping[str, str] | None = None,
235
+ ) -> None:
236
+ deprecation_warning(
237
+ (2026, 7, 23),
238
+ "`Environment.members.update()` is deprecated; use `Environment.roles.update()` instead.",
239
+ )
240
+ await _EnvironmentRolesManager(self._environment).update(users=users, service_users=service_users)
241
+
199
242
  async def remove(
200
243
  self,
201
244
  *,
202
245
  users: Iterable[str] | None = None,
203
246
  service_users: Iterable[str] | None = None,
204
247
  ) -> None:
205
- """Remove members from a restricted Environment.
206
-
207
- **Examples:**
208
-
209
- ```python notest
210
- env = modal.Environment.from_name("my-restricted-env")
211
- env.members.remove(
212
- users=["alice"],
213
- service_users=["alice-bot"],
248
+ """Remove the Environment Role of users and service users, reverting them to the default."""
249
+ deprecation_warning(
250
+ (2026, 7, 23),
251
+ "`Environment.members.remove()` is deprecated. Environment Roles are now explicit; "
252
+ "set a role (e.g. 'no-access') with `Environment.roles.update()` instead.",
214
253
  )
215
- ```
216
- """
217
254
  await self._environment.hydrate()
218
255
  users = users or []
219
256
  service_users = service_users or []
@@ -247,20 +284,7 @@ class _EnvironmentMembersManager:
247
284
  role=api_pb2.ENVIRONMENT_ROLE_UNSPECIFIED,
248
285
  )
249
286
 
250
- await self._dispatch_role_updates(requests)
251
-
252
- async def _dispatch_role_updates(self, requests: dict[str, api_pb2.EnvironmentRoleSetRequest]) -> None:
253
- """Send batched EnvironmentRoleSet RPCs and report all errors encountered."""
254
- results = await asyncio.gather(
255
- *(self._environment.client.stub.EnvironmentRoleSet(req) for req in requests.values()),
256
- return_exceptions=True,
257
- )
258
- errors = [(label, result) for label, result in zip(requests.keys(), results) if isinstance(result, Exception)]
259
- if errors:
260
- n = len(errors)
261
- header = f"{n} error{'s' if n != 1 else ''} occurred while updating Environment members:"
262
- details = "\n".join(f" - {label}: {e}" for label, e in errors)
263
- raise WorkspaceManagementError(f"{header}\n{details}")
287
+ await _EnvironmentRolesManager(self._environment)._dispatch_role_updates(requests)
264
288
 
265
289
 
266
290
  class _Environment(_Object, type_prefix="en"):
@@ -284,11 +308,17 @@ class _Environment(_Object, type_prefix="en"):
284
308
  return _EnvironmentManager()
285
309
 
286
310
  @property
287
- def members(self) -> "_EnvironmentMembersManager":
288
- """Namespace with methods for managing the membership of a restricted Environment.
311
+ def roles(self) -> "_EnvironmentRolesManager":
312
+ """Namespace with methods for managing the Environment Roles of users and service users.
289
313
 
290
- See https://modal.com/docs/guide/rbac for more information on restricted Environments.
314
+ See https://modal.com/docs/guide/rbac for more information on Environment Roles.
291
315
  """
316
+ return _EnvironmentRolesManager(self)
317
+
318
+ @property
319
+ def members(self) -> "_EnvironmentMembersManager":
320
+ """mdmd:hidden"""
321
+ # Deprecated alias for `Environment.roles`.
292
322
  return _EnvironmentMembersManager(self)
293
323
 
294
324
  # TODO(michael) Keeping this private for now until we decide what else should be in it
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2023
2
2
  import json as json_mod
3
+ import warnings
3
4
 
4
5
  import click
5
6
  import rich
@@ -11,6 +12,7 @@ from rich.text import Text
11
12
  from modal import environments
12
13
  from modal._environments import _Environment
13
14
  from modal._utils.async_utils import synchronizer
15
+ from modal._utils.deprecation import deprecation_warning
14
16
  from modal._utils.display_utils import pretty_decimal
15
17
  from modal._utils.name_utils import check_environment_name
16
18
  from modal._utils.time_utils import format_interval
@@ -18,7 +20,7 @@ from modal.cli.billing import DATE_HELP, _validate_and_parse_interval
18
20
  from modal.cli.utils import _col_name_to_json_key, confirm_or_suggest_yes, display_table, yes_option
19
21
  from modal.config import config
20
22
  from modal.environments import Environment
21
- from modal.exception import InvalidError
23
+ from modal.exception import DeprecationError, InvalidError
22
24
  from modal.output import OutputManager
23
25
 
24
26
  from ._help import ModalGroup
@@ -55,7 +57,7 @@ def list_(json: bool = False):
55
57
  # over the server default
56
58
  active_env = config.get("environment") or ""
57
59
  for env in envs:
58
- if env.default is True and active_env is None:
60
+ if env.default is True and not active_env:
59
61
  active_env = env.name
60
62
 
61
63
  table_data = []
@@ -123,67 +125,127 @@ def update(
123
125
  rich.print("[green]✓[/green] Environment updated")
124
126
 
125
127
 
126
- MEMBERS_HELP_TEXT = """Manage members and their roles in a restricted Environment.
127
-
128
- Restricted Environments use RBAC to limit the actions that can be performed by
129
- users (and service users) based on roles: https://modal.com/docs/guide/rbac.
130
- """
131
-
132
- members_cli = ModalGroup(name="members", help=MEMBERS_HELP_TEXT)
133
- environment_cli.add_command(members_cli)
134
-
135
128
  service_user_option = click.option(
136
- "--service-user", is_flag=True, default=False, help="Treat MEMBER as the name of a service user"
129
+ "--service-user", is_flag=True, default=False, help="Treat PRINCIPAL as the name of a service user"
137
130
  )
138
131
 
139
132
 
140
- @members_cli.command("list", help="List the members of a restricted Environment", no_args_is_help=True)
141
- @click.argument("environment")
142
- @click.option("--json", is_flag=True, default=False)
143
- def members_list(environment: str, json: bool = False):
144
- members = Environment.from_name(environment).members.list()
133
+ def _render_roles_list(environment: str, json: bool) -> None:
134
+ roles = Environment.from_name(environment).roles.list()
145
135
  if json:
146
136
  # Mirror the API output shape: a nested dict keyed by users / service_users.
147
- OutputManager.get().print_json(json_mod.dumps(members))
137
+ OutputManager.get().print_json(json_mod.dumps(roles))
148
138
  return
149
139
  rows = []
150
- for name, role in members.get("users", {}).items():
140
+ for name, role in roles.get("users", {}).items():
151
141
  rows.append([name, role])
152
- for name, role in members.get("service_users", {}).items():
142
+ for name, role in roles.get("service_users", {}).items():
153
143
  rows.append([f"{name} (service user)", role])
154
144
  rows = sorted(rows, key=lambda x: x[0])
155
145
  display_table(["name", "role"], rows)
156
146
 
157
147
 
158
- @members_cli.command("update", help="Add or update a member's role in a restricted Environment", no_args_is_help=True)
159
- @click.argument("environment")
160
- @click.argument("member")
161
- @click.option(
162
- "--role", type=click.Choice(["contributor", "viewer"]), required=True, help="Role to assign to the member"
163
- )
164
- @service_user_option
165
- def members_update(environment: str, member: str, role: str, service_user: bool = False):
148
+ def _apply_role_update(environment: str, principal: str, role: str, service_user: bool) -> None:
166
149
  env = Environment.from_name(environment)
167
150
  if service_user:
168
- env.members.update(service_users={member: role})
151
+ env.roles.update(service_users={principal: role})
169
152
  else:
170
- env.members.update(users={member: role})
153
+ env.roles.update(users={principal: role})
171
154
  kind = "service user" if service_user else "user"
172
- rich.print(f"[green]✓[/green] Set {kind} {member!r} to role {role!r} in environment {environment!r}")
155
+ rich.print(f"[green]✓[/green] Set {kind} {principal!r} to role {role!r} in environment {environment!r}")
173
156
 
174
157
 
175
- @members_cli.command("remove", help="Remove a member from a restricted Environment", no_args_is_help=True)
176
- @click.argument("environment")
177
- @click.argument("member")
178
- @service_user_option
179
- def members_remove(environment: str, member: str, service_user: bool = False):
158
+ def _apply_role_remove(environment: str, principal: str, service_user: bool) -> None:
180
159
  env = Environment.from_name(environment)
181
- if service_user:
182
- env.members.remove(service_users=[member])
183
- else:
184
- env.members.remove(users=[member])
160
+ # `remove` only lives on the deprecated `members` manager, which warns. The caller (a
161
+ # deprecated `members` command) has already warned, so suppress the redundant SDK warning.
162
+ with warnings.catch_warnings():
163
+ warnings.simplefilter("ignore", DeprecationError)
164
+ if service_user:
165
+ env.members.remove(service_users=[principal])
166
+ else:
167
+ env.members.remove(users=[principal])
185
168
  kind = "service user" if service_user else "user"
186
- rich.print(f"[green]✓[/green] Removed {kind} {member!r} from environment {environment!r}")
169
+ rich.print(f"[green]✓[/green] Removed {kind} {principal!r} from environment {environment!r}")
170
+
171
+
172
+ ROLES_HELP_TEXT = """Manage the Environment Roles of users and service users.
173
+
174
+ An Environment Role is one of 'contributor' (read-write), 'viewer' (read-only), or
175
+ 'no-access', and dictates access to Environments. See
176
+ https://modal.com/docs/guide/rbac for details on which principals can be assigned
177
+ which roles.
178
+ """
179
+
180
+ roles_cli = ModalGroup(name="roles", help=ROLES_HELP_TEXT)
181
+ environment_cli.add_command(roles_cli)
182
+
183
+
184
+ @roles_cli.command("list", help="List the roles of each user and service user in an Environment", no_args_is_help=True)
185
+ @click.argument("environment")
186
+ @click.option("--json", is_flag=True, default=False)
187
+ def roles_list(environment: str, json: bool = False):
188
+ _render_roles_list(environment, json)
189
+
190
+
191
+ @roles_cli.command("update", help="Update a user's or service user's role in an Environment", no_args_is_help=True)
192
+ @click.argument("environment")
193
+ @click.argument("principal")
194
+ @click.option("--role", type=click.Choice(["contributor", "viewer", "no-access"]), required=True, help="Role to assign")
195
+ @service_user_option
196
+ def roles_update(environment: str, principal: str, role: str, service_user: bool = False):
197
+ _apply_role_update(environment, principal, role, service_user)
198
+
199
+
200
+ # The deprecated `members` command group keeps working for existing automation, but is hidden
201
+ # from `--help` so it is not discoverable. Each subcommand warns with its own guidance.
202
+ members_cli = ModalGroup(name="members", help="[Deprecated] Use `modal environment roles`.", hidden=True)
203
+ environment_cli.add_command(members_cli)
204
+
205
+
206
+ @members_cli.command("list", help="[Deprecated] Use `modal environment roles list`", no_args_is_help=True)
207
+ @click.argument("environment")
208
+ @click.option("--json", is_flag=True, default=False)
209
+ def members_list(environment: str, json: bool = False):
210
+ deprecation_warning(
211
+ (2026, 7, 23),
212
+ "`modal environment members list` is deprecated; use `modal environment roles list`.",
213
+ show_source=False,
214
+ )
215
+ _render_roles_list(environment, json)
216
+
217
+
218
+ @members_cli.command("update", help="[Deprecated] Use `modal environment roles update`", no_args_is_help=True)
219
+ @click.argument("environment")
220
+ @click.argument("principal")
221
+ @click.option(
222
+ "--role",
223
+ type=click.Choice(["contributor", "viewer", "no-access"]),
224
+ required=True,
225
+ help="Role to assign to the principal",
226
+ )
227
+ @service_user_option
228
+ def members_update(environment: str, principal: str, role: str, service_user: bool = False):
229
+ deprecation_warning(
230
+ (2026, 7, 23),
231
+ "`modal environment members update` is deprecated; use `modal environment roles update`.",
232
+ show_source=False,
233
+ )
234
+ _apply_role_update(environment, principal, role, service_user)
235
+
236
+
237
+ @members_cli.command("remove", help="[Deprecated] Use `modal environment roles update`", no_args_is_help=True)
238
+ @click.argument("environment")
239
+ @click.argument("principal")
240
+ @service_user_option
241
+ def members_remove(environment: str, principal: str, service_user: bool = False):
242
+ deprecation_warning(
243
+ (2026, 7, 23),
244
+ "`modal environment members remove` is deprecated; roles are now explicit — set a role "
245
+ "(e.g. `--role no-access`) with `modal environment roles update` instead.",
246
+ show_source=False,
247
+ )
248
+ _apply_role_remove(environment, principal, service_user)
187
249
 
188
250
 
189
251
  billing_cli = ModalGroup(name="billing", help="View billing and usage info for the given Environment.")
@@ -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.dev13",
40
+ version: str = "1.5.4.dev0",
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.dev13",
210
+ version: str = "1.5.4.dev0",
211
211
  ):
212
212
  """mdmd:hidden
213
213
  The Modal client object is not intended to be instantiated directly by users.
@@ -6,12 +6,15 @@ from ._environments import (
6
6
  _EnvironmentBillingManager,
7
7
  _EnvironmentManager,
8
8
  _EnvironmentMembersManager,
9
+ _EnvironmentRolesManager,
9
10
  _list_environments,
10
11
  _update_environment,
11
12
  )
12
13
  from ._utils.async_utils import synchronize_api, synchronizer
13
14
 
14
15
  EnvironmentManager = synchronize_api(_EnvironmentManager, target_module=__name__)
16
+ EnvironmentRolesManager = synchronize_api(_EnvironmentRolesManager, target_module=__name__)
17
+ # Deprecated alias for `EnvironmentRolesManager`, retired with `Environment.members`.
15
18
  EnvironmentMembersManager = synchronize_api(_EnvironmentMembersManager, target_module=__name__)
16
19
  EnvironmentBillingManager = synchronize_api(_EnvironmentBillingManager, target_module=__name__)
17
20
  Environment = synchronize_api(_Environment, target_module=__name__)