modal 1.6.2.dev11__tar.gz → 1.6.2.dev12__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 (245) hide show
  1. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/PKG-INFO +1 -1
  2. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/app.py +1 -1
  3. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/app.pyi +2 -2
  4. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/function.py +38 -13
  5. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/import_refs.py +57 -6
  6. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/run.py +4 -1
  7. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/server.py +29 -11
  8. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/shell.py +4 -1
  9. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/client.pyi +2 -2
  10. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/config.py +4 -0
  11. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/functions.pyi +6 -6
  12. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/PKG-INFO +1 -1
  13. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_version/__init__.py +1 -1
  14. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/LICENSE +0 -0
  15. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/README.md +0 -0
  16. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/__init__.py +0 -0
  17. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/__main__.py +0 -0
  18. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_billing.py +0 -0
  19. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_cluster.py +0 -0
  20. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_cluster.pyi +0 -0
  21. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_container_entrypoint.py +0 -0
  22. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_environments.py +0 -0
  23. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_function_variants.py +0 -0
  24. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_functions.py +0 -0
  25. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_grpc_client.py +0 -0
  26. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_image.py +0 -0
  27. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_ipython.py +0 -0
  28. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_load_context.py +0 -0
  29. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_location.py +0 -0
  30. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_logs.py +0 -0
  31. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_logs_manager.py +0 -0
  32. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_logs_manager.pyi +0 -0
  33. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_metrics.py +0 -0
  34. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_metrics.pyi +0 -0
  35. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_object.py +0 -0
  36. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_outbound_policy.py +0 -0
  37. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_outbound_policy.pyi +0 -0
  38. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_output/__init__.py +0 -0
  39. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_output/manager.py +0 -0
  40. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_output/pty.py +0 -0
  41. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_output/rich.py +0 -0
  42. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_output/status.py +0 -0
  43. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_partial_function.py +0 -0
  44. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_resolver.py +0 -0
  45. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_resources.py +0 -0
  46. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/__init__.py +0 -0
  47. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/asgi.py +0 -0
  48. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/container_io_manager.py +0 -0
  49. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/container_io_manager.pyi +0 -0
  50. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/execution_context.py +0 -0
  51. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/execution_context.pyi +0 -0
  52. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  53. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/task_lifecycle_manager.py +0 -0
  54. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  55. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/telemetry.py +0 -0
  56. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/user_code_event_loop.py +0 -0
  57. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_runtime/user_code_imports.py +0 -0
  58. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_serialization.py +0 -0
  59. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_server.py +0 -0
  60. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_server.pyi +0 -0
  61. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_supports_logs.py +0 -0
  62. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_traceback.py +0 -0
  63. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_tunnel.py +0 -0
  64. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_tunnel.pyi +0 -0
  65. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_type_manager.py +0 -0
  66. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/__init__.py +0 -0
  67. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/app_utils.py +0 -0
  68. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/async_utils.py +0 -0
  69. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/auth_token_manager.py +0 -0
  70. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/blob_utils.py +0 -0
  71. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/browser_utils.py +0 -0
  72. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/bytes_io_segment_payload.py +0 -0
  73. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/curl_utils.py +0 -0
  74. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/deprecation.py +0 -0
  75. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/display_utils.py +0 -0
  76. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/docker_utils.py +0 -0
  77. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/function_utils.py +0 -0
  78. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/git_utils.py +0 -0
  79. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/grpc_testing.py +0 -0
  80. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/grpc_utils.py +0 -0
  81. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/hash_utils.py +0 -0
  82. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/http_utils.py +0 -0
  83. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/idle_countdown.py +0 -0
  84. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/jwt_utils.py +0 -0
  85. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/logger.py +0 -0
  86. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/mount_utils.py +0 -0
  87. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/name_utils.py +0 -0
  88. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/package_utils.py +0 -0
  89. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/pattern_utils.py +0 -0
  90. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/proxy_support.py +0 -0
  91. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/rand_pb_testing.py +0 -0
  92. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/sandbox_fs_utils.py +0 -0
  93. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/shell_utils.py +0 -0
  94. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_utils/time_utils.py +0 -0
  95. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_vendor/__init__.py +0 -0
  96. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  97. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_vendor/cloudpickle.py +0 -0
  98. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_vendor/tblib.py +0 -0
  99. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_vendor/version.py +0 -0
  100. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_watcher.py +0 -0
  101. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/_workspace.py +0 -0
  102. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/billing.py +0 -0
  103. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/billing.pyi +0 -0
  104. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/2023.12.312.txt +0 -0
  105. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/2023.12.txt +0 -0
  106. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/2024.04.txt +0 -0
  107. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/2024.10.txt +0 -0
  108. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/2025.06.txt +0 -0
  109. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/PREVIEW.txt +0 -0
  110. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/README.md +0 -0
  111. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/builder/base-images.json +0 -0
  112. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/call_graph.py +0 -0
  113. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/__init__.py +0 -0
  114. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/_download.py +0 -0
  115. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/_help.py +0 -0
  116. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/_logs.py +0 -0
  117. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/_stats.py +0 -0
  118. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/_traceback.py +0 -0
  119. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/app.py +0 -0
  120. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/billing.py +0 -0
  121. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/changelog.py +0 -0
  122. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/config.py +0 -0
  123. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/container.py +0 -0
  124. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/curl.py +0 -0
  125. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/dashboard.py +0 -0
  126. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/dict.py +0 -0
  127. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/endpoint.py +0 -0
  128. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/entry_point.py +0 -0
  129. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/environment.py +0 -0
  130. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/image.py +0 -0
  131. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/logo.py +0 -0
  132. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/network_file_system.py +0 -0
  133. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/profile.py +0 -0
  134. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/queues.py +0 -0
  135. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/secret.py +0 -0
  136. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/selector.py +0 -0
  137. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/skills.py +0 -0
  138. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/token.py +0 -0
  139. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/utils.py +0 -0
  140. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/volume.py +0 -0
  141. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cli/workspace.py +0 -0
  142. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/client.py +0 -0
  143. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cloud_bucket_mount.py +0 -0
  144. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cloud_bucket_mount.pyi +0 -0
  145. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cls.py +0 -0
  146. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cls.pyi +0 -0
  147. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cluster.py +0 -0
  148. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/cluster.pyi +0 -0
  149. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/container_process.py +0 -0
  150. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/container_process.pyi +0 -0
  151. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/dict.py +0 -0
  152. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/dict.pyi +0 -0
  153. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/environments.py +0 -0
  154. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/environments.pyi +0 -0
  155. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/exception.py +0 -0
  156. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/__init__.py +0 -0
  157. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/_flash_container_entry.py +0 -0
  158. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/flash.py +0 -0
  159. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/flash.pyi +0 -0
  160. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/flash_server.py +0 -0
  161. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/flash_server.pyi +0 -0
  162. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/experimental/ipython.py +0 -0
  163. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/file_pattern_matcher.py +0 -0
  164. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/functions.py +0 -0
  165. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/image.py +0 -0
  166. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/image.pyi +0 -0
  167. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/io_streams.py +0 -0
  168. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/io_streams.pyi +0 -0
  169. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/mount.py +0 -0
  170. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/mount.pyi +0 -0
  171. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/network_file_system.py +0 -0
  172. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/network_file_system.pyi +0 -0
  173. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/object.py +0 -0
  174. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/object.pyi +0 -0
  175. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/output.py +0 -0
  176. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/parallel_map.py +0 -0
  177. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/parallel_map.pyi +0 -0
  178. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/partial_function.py +0 -0
  179. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/partial_function.pyi +0 -0
  180. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/proxy.py +0 -0
  181. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/proxy.pyi +0 -0
  182. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/py.typed +0 -0
  183. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/queue.py +0 -0
  184. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/queue.pyi +0 -0
  185. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/retries.py +0 -0
  186. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/runner.py +0 -0
  187. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/runner.pyi +0 -0
  188. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/running_app.py +0 -0
  189. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/__init__.py +0 -0
  190. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_common.py +0 -0
  191. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_filesystem.py +0 -0
  192. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_sandbox.py +0 -0
  193. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_sidecar.py +0 -0
  194. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_snapshot.py +0 -0
  195. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/_task_command_router_client.py +0 -0
  196. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/sandbox.py +0 -0
  197. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/sandbox/sandbox.pyi +0 -0
  198. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/schedule.py +0 -0
  199. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/scheduler_placement.py +0 -0
  200. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/secret.py +0 -0
  201. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/secret.pyi +0 -0
  202. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/server.py +0 -0
  203. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/server.pyi +0 -0
  204. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/serving.py +0 -0
  205. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/serving.pyi +0 -0
  206. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/skills/modal/SKILL.md +0 -0
  207. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/stream_type.py +0 -0
  208. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/token_flow.py +0 -0
  209. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/token_flow.pyi +0 -0
  210. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/types.py +0 -0
  211. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/volume.py +0 -0
  212. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/volume.pyi +0 -0
  213. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/workspace.py +0 -0
  214. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal/workspace.pyi +0 -0
  215. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/SOURCES.txt +0 -0
  216. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/dependency_links.txt +0 -0
  217. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/entry_points.txt +0 -0
  218. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/requires.txt +0 -0
  219. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal.egg-info/top_level.txt +0 -0
  220. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/__init__.py +0 -0
  221. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/gen_cli_docs.py +0 -0
  222. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/gen_cli_docs_main.py +0 -0
  223. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/gen_reference_docs.py +0 -0
  224. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/gen_reference_docs_main.py +0 -0
  225. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/mdmd/__init__.py +0 -0
  226. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/mdmd/mdmd.py +0 -0
  227. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/mdmd/signatures.py +0 -0
  228. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/mdmd/types.py +0 -0
  229. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_docs/reference_intro.md +0 -0
  230. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/__init__.py +0 -0
  231. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/api_grpc.py +0 -0
  232. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/api_pb2.py +0 -0
  233. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/api_pb2.pyi +0 -0
  234. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/api_pb2_grpc.py +0 -0
  235. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/api_pb2_grpc.pyi +0 -0
  236. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/modal_api_grpc.py +0 -0
  237. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/py.typed +0 -0
  238. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/task_command_router_grpc.py +0 -0
  239. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/task_command_router_pb2.py +0 -0
  240. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/task_command_router_pb2.pyi +0 -0
  241. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  242. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  243. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/modal_version/__main__.py +0 -0
  244. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/pyproject.toml +0 -0
  245. {modal-1.6.2.dev11 → modal-1.6.2.dev12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.6.2.dev11
3
+ Version: 1.6.2.dev12
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -1358,7 +1358,7 @@ class _App:
1358
1358
  fractional, e.g. 1.5 to target three concurrent requests per two containers.
1359
1359
  max_concurrency:
1360
1360
  Maximum number of concurrent requests per container. Requests above this limit
1361
- receive a 503 response. If set to 0 or unset, request concurrency is unlimited.
1361
+ receive a 429 response. If set to 0 or unset, request concurrency is unlimited.
1362
1362
  min_containers: Minimum number of containers to keep running regardless of demand.
1363
1363
  max_containers: Limit on the number of containers that can be concurrently running.
1364
1364
  buffer_containers: Extra containers to scale up beyond current demand.
@@ -799,7 +799,7 @@ class _App:
799
799
  fractional, e.g. 1.5 to target three concurrent requests per two containers.
800
800
  max_concurrency:
801
801
  Maximum number of concurrent requests per container. Requests above this limit
802
- receive a 503 response. If set to 0 or unset, request concurrency is unlimited.
802
+ receive a 429 response. If set to 0 or unset, request concurrency is unlimited.
803
803
  min_containers: Minimum number of containers to keep running regardless of demand.
804
804
  max_containers: Limit on the number of containers that can be concurrently running.
805
805
  buffer_containers: Extra containers to scale up beyond current demand.
@@ -1837,7 +1837,7 @@ class App:
1837
1837
  fractional, e.g. 1.5 to target three concurrent requests per two containers.
1838
1838
  max_concurrency:
1839
1839
  Maximum number of concurrent requests per container. Requests above this limit
1840
- receive a 503 response. If set to 0 or unset, request concurrency is unlimited.
1840
+ receive a 429 response. If set to 0 or unset, request concurrency is unlimited.
1841
1841
  min_containers: Minimum number of containers to keep running regardless of demand.
1842
1842
  max_containers: Limit on the number of containers that can be concurrently running.
1843
1843
  buffer_containers: Extra containers to scale up beyond current demand.
@@ -7,7 +7,8 @@ from typing import Any, AsyncGenerator, Sequence, cast
7
7
 
8
8
  import click
9
9
  from click import UsageError
10
- from rich.box import SIMPLE
10
+ from rich.box import SIMPLE, SIMPLE_HEAD
11
+ from rich.style import Style
11
12
  from rich.table import Column, Table
12
13
  from rich.text import Text
13
14
 
@@ -212,18 +213,20 @@ async def info(
212
213
  enabled = "Enabled"
213
214
  disabled = "Disabled"
214
215
 
215
- rows: list[str | Text | tuple[str | Text, str | Text]] = []
216
+ rows: list[str | Text | tuple[str | Text, str | Text] | None] = []
216
217
 
217
218
  if function_proto.is_class:
218
- rows.append(("Cls Name:", function_proto.function_name))
219
+ rows.append((Text("Cls Name:", style="bold"), function_proto.function_name))
219
220
  else:
220
- rows.append(("Function Name:", function_proto.function_name))
221
+ rows.append((Text("Function Name:", style="bold"), function_proto.function_name))
221
222
 
222
- rows.append(("Object ID:", f.object_id))
223
- rows.append(("App ID:", handle_metadata.app_id))
224
- rows.append(("Image ID:", str(info.image_info.image_id)))
223
+ rows.append(None)
224
+ rows.append((Text("Object ID:", style="bold"), f.object_id))
225
+ rows.append((Text("App ID:", style="bold"), handle_metadata.app_id))
226
+ rows.append((Text("Image ID:", style="bold"), str(info.image_info.image_id)))
225
227
 
226
228
  # --- Resources ---
229
+ rows.append(None)
227
230
  rows.append("Resources:")
228
231
  rows.append(
229
232
  (
@@ -261,6 +264,7 @@ async def info(
261
264
  )
262
265
 
263
266
  # --- Autoscaling ---
267
+ rows.append(None)
264
268
  rows.append("Autoscaling:")
265
269
  rows.append(
266
270
  (
@@ -289,6 +293,7 @@ async def info(
289
293
  )
290
294
  )
291
295
 
296
+ rows.append(None)
292
297
  rows.append("Execution:")
293
298
  if info.startup_timeout:
294
299
  rows.append((" Startup Timeout:", f"{info.startup_timeout} seconds"))
@@ -297,6 +302,7 @@ async def info(
297
302
  rows.append((" Max Retries:", not_configured if info.max_retries is None else str(info.max_retries)))
298
303
 
299
304
  if info.concurrency_info:
305
+ rows.append(None)
300
306
  rows.append("Concurrency:")
301
307
  rows.append((" Max Inputs:", str(info.concurrency_info.max_inputs)))
302
308
  rows.append(
@@ -309,11 +315,13 @@ async def info(
309
315
  )
310
316
 
311
317
  if info.batching_info:
318
+ rows.append(None)
312
319
  rows.append("Batching:")
313
320
  rows.append((" Max Batch Size:", str(info.batching_info.max_batch_size)))
314
321
  rows.append((" Wait Time:", f"{info.batching_info.wait_ms} milliseconds"))
315
322
 
316
323
  # --- Scheduling ---
324
+ rows.append(None)
317
325
  rows.append("Scheduling:")
318
326
  rows.append((" Compute Region(s):", not_configured if not info.regions else " | ".join(info.regions)))
319
327
  rows.append((" Nonpreemptible Capacity:", not_configured if not info.nonpreemptible else enabled))
@@ -322,6 +330,7 @@ async def info(
322
330
  rows.append((" Routing Region:", not_configured if not info.routing_region else info.routing_region))
323
331
 
324
332
  if info.cluster_info:
333
+ rows.append(None)
325
334
  rows.append("Clustering:")
326
335
  rows.append((" Cluster Size:", str(info.cluster_info.size)))
327
336
  rows.append((" RDMA:", enabled if info.cluster_info.rdma else disabled))
@@ -329,8 +338,10 @@ async def info(
329
338
  rows.append((" Fabric Size:", str(info.cluster_info.fabric_size)))
330
339
 
331
340
  if info.schedule is not None:
341
+ rows.append(None)
332
342
  rows.append(("Schedule:", info.schedule))
333
343
 
344
+ rows.append(None)
334
345
  rows.append("Security:")
335
346
  rows.append((" Outbound Networking:", disabled if info.block_network else enabled))
336
347
  rows.append((" Modal API Access:", disabled if info.restrict_modal_access else enabled))
@@ -338,6 +349,7 @@ async def info(
338
349
 
339
350
  # --- Web Functions ---
340
351
  if info.web_info is not None:
352
+ rows.append(None)
341
353
  rows.append("Web Info:")
342
354
 
343
355
  if info.web_info.web_url:
@@ -345,7 +357,9 @@ async def info(
345
357
  if info.web_info.method:
346
358
  url_str = f" URL ({info.web_info.method}):"
347
359
 
348
- rows.append((url_str, info.web_info.web_url))
360
+ url_text = Text(info.web_info.web_url, overflow="fold")
361
+ url_text.stylize(style=Style(link=info.web_info.web_url), end=len(info.web_info.web_url))
362
+ rows.append((url_str, url_text))
349
363
 
350
364
  rows.append((" Authentication:", disabled if info.web_info.unauthenticated else enabled))
351
365
 
@@ -353,6 +367,7 @@ async def info(
353
367
  if info.method_names is not None:
354
368
  assert info.method_details is not None
355
369
 
370
+ rows.append(None)
356
371
  rows.append("Methods:")
357
372
 
358
373
  for method_name in info.method_names:
@@ -368,10 +383,14 @@ async def info(
368
383
  if web_info.method:
369
384
  url_str = f" URL ({web_info.method}):"
370
385
 
371
- rows.append((url_str, web_info.web_url))
386
+ url_text = Text(web_info.web_url, overflow="fold")
387
+ url_text.stylize(style=Style(link=web_info.web_url), end=len(web_info.web_url))
388
+
389
+ rows.append((url_str, url_text))
372
390
 
373
391
  # --- Mounts ---
374
392
  if info.volumes:
393
+ rows.append(None)
375
394
  rows.append("Volume Mounts:")
376
395
  for mount_path, volume in info.volumes.items():
377
396
  assert volume.volume_id is not None
@@ -390,6 +409,7 @@ async def info(
390
409
  rows.append((f" {mount_path}", volume_text))
391
410
 
392
411
  if info.cloud_bucket_mounts:
412
+ rows.append(None)
393
413
  rows.append("Cloud Bucket Mounts:")
394
414
  for mount_path, cbm in info.cloud_bucket_mounts.items():
395
415
  extra = []
@@ -407,6 +427,7 @@ async def info(
407
427
 
408
428
  # --- Secrets ---
409
429
  if info.secrets:
430
+ rows.append(None)
410
431
  rows.append("Secrets:")
411
432
 
412
433
  async def _hydrate(secret_id: str) -> tuple[str, _Secret | None]:
@@ -446,18 +467,20 @@ async def info(
446
467
 
447
468
  rows.append((f" {secret_identifier}", ", ".join(display_keys)))
448
469
 
470
+ t = Table(box=SIMPLE_HEAD, show_header=False)
449
471
  for row in rows:
450
- t = Table().grid(padding=(0, 0, 3, 3), expand=True)
472
+ if row is None:
473
+ t.add_row()
474
+ continue
451
475
 
452
476
  if not isinstance(row, tuple):
453
477
  if isinstance(row, str):
454
- row = Text(row)
478
+ row = Text(row, style="bold")
455
479
 
456
480
  if not tty:
457
481
  output_manager.print(row)
458
482
  else:
459
483
  t.add_row(row)
460
- output_manager.print(t)
461
484
 
462
485
  continue
463
486
 
@@ -470,9 +493,11 @@ async def info(
470
493
  if isinstance(left, str):
471
494
  left = Text(left, overflow="fold")
472
495
  if isinstance(right, str):
473
- right = Text(right, overflow="fold", justify="right")
496
+ right = Text(right, overflow="fold")
474
497
 
475
498
  t.add_row(left, right)
499
+
500
+ if tty:
476
501
  output_manager.print(t)
477
502
 
478
503
 
@@ -15,6 +15,7 @@ import inspect
15
15
  import shutil
16
16
  import sys
17
17
  import tempfile
18
+ import types
18
19
  import typing
19
20
  from collections import defaultdict
20
21
  from dataclasses import dataclass
@@ -177,6 +178,41 @@ class AutoRunPriority:
177
178
  APP_FUNCTION = 3
178
179
 
179
180
 
181
+ _LazyImportType = getattr(types, "LazyImportType", None) # Python 3.15+ (PEP 810)
182
+
183
+ _MAX_LISTED_LAZY_IMPORTS = 10
184
+
185
+
186
+ def _is_lazy_import(module: types.ModuleType, name: str) -> bool:
187
+ return _LazyImportType is not None and isinstance(vars(module).get(name), _LazyImportType)
188
+
189
+
190
+ def _check_not_lazy_import(module: types.ModuleType, name: str, import_ref: ImportRef) -> None:
191
+ if _is_lazy_import(module, name):
192
+ raise InvalidError(
193
+ f"'{name}' is imported lazily in '{import_ref.file_or_module}'. Modal Apps, Functions, and Classes "
194
+ "can't be imported lazily; change it to a regular import."
195
+ )
196
+
197
+
198
+ def _get_module_members(module: types.ModuleType) -> tuple[dict[str, typing.Any], list[str]]:
199
+ """Like `dict(inspect.getmembers(module))`, but skips lazy imports instead of resolving them.
200
+
201
+ Returns the members and the names of the skipped lazy imports.
202
+ """
203
+ members = {}
204
+ lazy_imports = []
205
+ for name in dir(module):
206
+ if _is_lazy_import(module, name):
207
+ lazy_imports.append(name)
208
+ continue
209
+ try:
210
+ members[name] = getattr(module, name)
211
+ except AttributeError:
212
+ continue # matches inspect.getmembers
213
+ return members, lazy_imports
214
+
215
+
180
216
  def list_cli_commands(
181
217
  module_members: dict[str, typing.Any],
182
218
  include_service_functions: bool = False,
@@ -308,6 +344,7 @@ def import_app_from_ref(import_ref: ImportRef, base_cmd: str = "") -> App:
308
344
  if "." in object_path:
309
345
  raise click.UsageError(f"{object_path} is not a Modal App")
310
346
 
347
+ _check_not_lazy_import(module, object_path, import_ref)
311
348
  app = getattr(module, object_path)
312
349
 
313
350
  if app is None:
@@ -385,9 +422,9 @@ def import_and_filter(
385
422
  accept_webhook=False,
386
423
  accept_service_functions=False,
387
424
  allow_stdin=False,
388
- ) -> tuple[Runnable | None, list[CLICommand]]:
389
- """Takes a function ref string and returns a single determined "runnable" to use, and a list of all available
390
- runnables.
425
+ ) -> tuple[Runnable | None, list[CLICommand], list[str]]:
426
+ """Takes a function ref string and returns a single determined "runnable" to use, a list of all available
427
+ runnables, and the names of the lazy imports that were not searched for runnables.
391
428
 
392
429
  The function ref can leave out partial information (apart from the file name/module)
393
430
  as long as the runnable is uniquely identifiable by the provided information.
@@ -401,8 +438,10 @@ def import_and_filter(
401
438
  """
402
439
  # all commands:
403
440
  module = import_file_or_module(import_ref, base_cmd, allow_stdin=allow_stdin)
441
+ _check_not_lazy_import(module, import_ref.object_path.split(".", 1)[0], import_ref)
442
+ module_members, lazy_imports = _get_module_members(module)
404
443
  cli_commands = list_cli_commands(
405
- dict(inspect.getmembers(module)),
444
+ module_members,
406
445
  include_service_functions=accept_service_functions,
407
446
  )
408
447
 
@@ -413,10 +452,22 @@ def import_and_filter(
413
452
 
414
453
  if inferred_runnable:
415
454
  # if there is a single command with "highest run prio" - use that
416
- return inferred_runnable, all_usable_commands
455
+ return inferred_runnable, all_usable_commands, lazy_imports
417
456
 
418
457
  # otherwise, just return the list of all commands
419
- return None, all_usable_commands
458
+ return None, all_usable_commands, lazy_imports
459
+
460
+
461
+ def format_lazy_imports_note(lazy_imports: list[str], found_commands: bool) -> str:
462
+ if found_commands or not lazy_imports:
463
+ return ""
464
+ details = "\n".join(f" {name}" for name in lazy_imports[:_MAX_LISTED_LAZY_IMPORTS])
465
+ if len(lazy_imports) > _MAX_LISTED_LAZY_IMPORTS:
466
+ details += f"\n ... and {len(lazy_imports) - _MAX_LISTED_LAZY_IMPORTS} more"
467
+ return (
468
+ "\n\nNote: Modal does not look inside lazy imports, and Modal Apps, Functions, and Classes can't be "
469
+ f"imported lazily. If one of these is a Modal object, change it to a regular import:\n{details}"
470
+ )
420
471
 
421
472
 
422
473
  def infer_runnable(
@@ -31,6 +31,7 @@ from .import_refs import (
31
31
  CLICommand,
32
32
  MethodReference,
33
33
  _get_runnable_app,
34
+ format_lazy_imports_note,
34
35
  import_and_filter,
35
36
  import_app_from_ref,
36
37
  parse_import_ref,
@@ -472,7 +473,7 @@ class RunGroup(ModalGroup):
472
473
  from_stdin = import_ref.file_or_module == STDIN_REF
473
474
  if from_stdin and ctx.params["interactive"]:
474
475
  raise ClickException("Interactive mode can't be used when reading a script from stdin.")
475
- runnable, all_usable_commands = import_and_filter(
476
+ runnable, all_usable_commands, lazy_imports = import_and_filter(
476
477
  import_ref, base_cmd="modal run", accept_local_entrypoint=True, accept_webhook=False, allow_stdin=True
477
478
  )
478
479
  if not runnable and from_stdin:
@@ -498,6 +499,8 @@ class RunGroup(ModalGroup):
498
499
  else:
499
500
  help_footer = f"'{import_ref.file_or_module}' has no functions or local entrypoints."
500
501
 
502
+ help_footer += format_lazy_imports_note(lazy_imports, found_commands=bool(all_usable_commands))
503
+
501
504
  raise ClickException(f"{help_header}\n\n{help_footer}")
502
505
 
503
506
  app = _get_runnable_app(runnable)
@@ -9,7 +9,8 @@ from typing import AsyncGenerator, Sequence, cast
9
9
 
10
10
  import click
11
11
  from click import UsageError
12
- from rich.box import SIMPLE
12
+ from rich.box import SIMPLE, SIMPLE_HEAD
13
+ from rich.style import Style
13
14
  from rich.table import Column, Table
14
15
  from rich.text import Text
15
16
 
@@ -174,16 +175,22 @@ async def info(
174
175
  enabled = "Enabled"
175
176
  disabled = "Disabled"
176
177
 
177
- rows: list[str | Text | tuple[str | Text, str | Text]] = []
178
+ rows: list[str | Text | tuple[str | Text, str | Text] | None] = []
178
179
 
179
- rows.append(("Server Name:", handle_metadata.function_name))
180
- rows.append(("Object ID:", server_id))
181
- rows.append(("App ID:", handle_metadata.app_id))
182
- rows.append(("Image ID:", str(info.image_info.image_id)))
180
+ rows.append((Text("Server Name:", style="bold"), handle_metadata.function_name))
181
+ rows.append(None)
182
+ rows.append((Text("Object ID:", style="bold"), server_id))
183
+ rows.append((Text("App ID:", style="bold"), handle_metadata.app_id))
184
+ rows.append((Text("Image ID:", style="bold"), str(info.image_info.image_id)))
183
185
 
184
186
  # --- HTTP ---
187
+ rows.append(None)
185
188
  rows.append("HTTP Info:")
186
- rows.append((" URL:", info.server_url))
189
+
190
+ url = Text(info.server_url, overflow="fold")
191
+ url.stylize(style=Style(link=info.server_url), end=len(info.server_url))
192
+
193
+ rows.append((" URL:", url))
187
194
  rows.append((" Port:", not_configured if not info.port else str(info.port)))
188
195
  rows.append((" Authentication:", disabled if info.unauthenticated else enabled))
189
196
  rows.append((" HTTP/2:", disabled if not info.h2_enabled else enabled))
@@ -193,6 +200,7 @@ async def info(
193
200
  rows.append((" Exit Grace Period:", f"{info.exit_grace_period} seconds"))
194
201
 
195
202
  # --- Resources ---
203
+ rows.append(None)
196
204
  rows.append("Resources:")
197
205
  rows.append(
198
206
  (
@@ -230,6 +238,7 @@ async def info(
230
238
  )
231
239
 
232
240
  # --- Autoscaling ---
241
+ rows.append(None)
233
242
  rows.append("Autoscaling:")
234
243
  rows.append(
235
244
  (
@@ -276,6 +285,7 @@ async def info(
276
285
  )
277
286
 
278
287
  # --- Scheduling ---
288
+ rows.append(None)
279
289
  rows.append("Scheduling:")
280
290
  rows.append(
281
291
  (" Compute Region(s):", not_configured if not info.compute_regions else " | ".join(info.compute_regions))
@@ -285,6 +295,7 @@ async def info(
285
295
  rows.append((" Cloud Provider:", info.cloud))
286
296
 
287
297
  if info.cluster_info:
298
+ rows.append(None)
288
299
  rows.append("Clustering:")
289
300
  rows.append((" Cluster Size:", str(info.cluster_info.size)))
290
301
  rows.append((" RDMA:", enabled if info.cluster_info.rdma else disabled))
@@ -293,6 +304,7 @@ async def info(
293
304
 
294
305
  # --- Mounts ---
295
306
  if info.volumes:
307
+ rows.append(None)
296
308
  rows.append("Volume Mounts:")
297
309
  for mount_path, volume in info.volumes.items():
298
310
  assert volume.volume_id is not None
@@ -311,6 +323,7 @@ async def info(
311
323
  rows.append((f" {mount_path}", volume_text))
312
324
 
313
325
  if info.cloud_bucket_mounts:
326
+ rows.append(None)
314
327
  rows.append("Cloud Bucket Mounts:")
315
328
  for mount_path, cbm in info.cloud_bucket_mounts.items():
316
329
  extra = []
@@ -328,6 +341,7 @@ async def info(
328
341
 
329
342
  # --- Secrets ---
330
343
  if info.secrets:
344
+ rows.append(None)
331
345
  rows.append("Secrets:")
332
346
 
333
347
  async def _hydrate(secret_id: str) -> tuple[str, _Secret | None]:
@@ -367,18 +381,20 @@ async def info(
367
381
 
368
382
  rows.append((f" {secret_identifier}", ", ".join(display_keys)))
369
383
 
384
+ t = Table(box=SIMPLE_HEAD, show_header=False)
370
385
  for row in rows:
371
- t = Table().grid(padding=(0, 0, 3, 3), expand=True)
386
+ if row is None:
387
+ t.add_row()
388
+ continue
372
389
 
373
390
  if not isinstance(row, tuple):
374
391
  if isinstance(row, str):
375
- row = Text(row)
392
+ row = Text(row, style="bold")
376
393
 
377
394
  if not tty:
378
395
  output_manager.print(row)
379
396
  else:
380
397
  t.add_row(row)
381
- output_manager.print(t)
382
398
 
383
399
  continue
384
400
 
@@ -391,9 +407,11 @@ async def info(
391
407
  if isinstance(left, str):
392
408
  left = Text(left, overflow="fold")
393
409
  if isinstance(right, str):
394
- right = Text(right, overflow="fold", justify="right")
410
+ right = Text(right, overflow="fold")
395
411
 
396
412
  t.add_row(left, right)
413
+
414
+ if tty:
397
415
  output_manager.print(t)
398
416
 
399
417
 
@@ -28,6 +28,7 @@ from ..volume import Volume
28
28
  from ._help import ModalCommand
29
29
  from .import_refs import (
30
30
  MethodReference,
31
+ format_lazy_imports_note,
31
32
  import_and_filter,
32
33
  parse_import_ref,
33
34
  )
@@ -162,7 +163,7 @@ def _start_shell_in_running_container(ref: str, cmd: str, pty: bool) -> None:
162
163
 
163
164
  def _function_spec_from_ref(ref: str, use_module_mode: bool) -> _FunctionSpec:
164
165
  import_ref = parse_import_ref(ref, use_module_mode=use_module_mode, command="modal shell")
165
- runnable, all_usable_commands = import_and_filter(
166
+ runnable, all_usable_commands, lazy_imports = import_and_filter(
166
167
  import_ref,
167
168
  base_cmd="modal shell",
168
169
  accept_local_entrypoint=False,
@@ -181,6 +182,8 @@ def _function_spec_from_ref(ref: str, use_module_mode: bool) -> _FunctionSpec:
181
182
  else:
182
183
  help_footer = f"The selected module '{import_ref.file_or_module}' has no Modal functions or classes."
183
184
 
185
+ help_footer += format_lazy_imports_note(lazy_imports, found_commands=bool(all_usable_commands))
186
+
184
187
  raise ClickException(f"{help_header}\n\n{help_footer}")
185
188
 
186
189
  if isinstance(runnable, MethodReference):
@@ -80,7 +80,7 @@ class _Client:
80
80
  server_url: str,
81
81
  client_type: int,
82
82
  credentials: typing.Optional[tuple[str, str]],
83
- version: str = "1.6.2.dev11",
83
+ version: str = "1.6.2.dev12",
84
84
  *,
85
85
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
86
86
  ):
@@ -290,7 +290,7 @@ class Client:
290
290
  server_url: str,
291
291
  client_type: int,
292
292
  credentials: typing.Optional[tuple[str, str]],
293
- version: str = "1.6.2.dev11",
293
+ version: str = "1.6.2.dev12",
294
294
  *,
295
295
  oauth_credentials: typing.Optional[_OAuthCredentials] = None,
296
296
  ):
@@ -141,6 +141,8 @@ def _agent_environment() -> str | None:
141
141
  _TOOL_SPECIFIC_ENV_VARS: list[tuple[str, str]] = [
142
142
  ("CLAUDECODE", "claude-code"),
143
143
  ("CODEX_THREAD_ID", "codex"),
144
+ ("CODEX_SESSION_ID", "codex"), # unified exec, always injected
145
+ ("CODEX_SANDBOX", "codex"),
144
146
  ("GEMINI_CLI", "gemini-cli"),
145
147
  ("CURSOR_AGENT", "cursor"),
146
148
  ("CLINE_ACTIVE", "cline"),
@@ -152,6 +154,8 @@ def _agent_environment() -> str | None:
152
154
  ("COPILOT_AGENT_SESSION_ID", "copilot"),
153
155
  ("ANTIGRAVITY_AGENT", "antigravity"),
154
156
  ("JUNIE_SHIM_PATH", "junie"),
157
+ ("QWEN_CODE", "qwen-code"),
158
+ ("MUSE_SESSION_ID", "muse-code"),
155
159
  ]
156
160
  for env_var, agent_name in _TOOL_SPECIFIC_ENV_VARS:
157
161
  if os.environ.get(env_var):
@@ -619,7 +619,7 @@ class Function(
619
619
 
620
620
  _call_generator: ___call_generator_spec
621
621
 
622
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
622
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
623
623
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
624
624
  """Calls the function remotely, executing it with the given arguments and returning the execution's result.
625
625
 
@@ -644,7 +644,7 @@ class Function(
644
644
  """
645
645
  ...
646
646
 
647
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
647
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
648
648
 
649
649
  class __remote_gen_spec(typing_extensions.Protocol):
650
650
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -694,7 +694,7 @@ class Function(
694
694
  """
695
695
  ...
696
696
 
697
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
697
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
698
698
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
699
699
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
700
700
 
@@ -727,7 +727,7 @@ class Function(
727
727
  """
728
728
  ...
729
729
 
730
- _experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
730
+ _experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
731
731
 
732
732
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
733
733
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
@@ -735,7 +735,7 @@ class Function(
735
735
 
736
736
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
737
737
 
738
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
738
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
739
739
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
740
740
  """Calls the function with the given arguments, without waiting for the results.
741
741
 
@@ -768,7 +768,7 @@ class Function(
768
768
  """
769
769
  ...
770
770
 
771
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
771
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
772
772
 
773
773
  @property
774
774
  def _raw_f_(self) -> collections.abc.Callable[..., typing.Any]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.6.2.dev11
3
+ Version: 1.6.2.dev12
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2026
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.6.2.dev11"
4
+ __version__ = "1.6.2.dev12"
File without changes
File without changes
File without changes
File without changes