modal 1.5.6.dev22__tar.gz → 1.5.6.dev24__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 (241) hide show
  1. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/PKG-INFO +1 -1
  2. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_function_variants.py +157 -0
  3. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_functions.py +68 -2
  4. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/manager.py +8 -0
  5. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/rich.py +5 -1
  6. modal-1.5.6.dev24/modal/_output/status.py +92 -0
  7. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_server.py +32 -0
  8. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/config.py +8 -6
  9. modal-1.5.6.dev24/modal/cli/function.py +865 -0
  10. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/server.py +127 -1
  11. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/utils.py +40 -3
  12. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/volume.py +1 -13
  13. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/client.pyi +2 -2
  14. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/config.py +57 -46
  15. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/functions.pyi +30 -0
  16. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/mount.py +1 -1
  17. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/server.pyi +19 -0
  18. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/PKG-INFO +1 -1
  19. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_version/__init__.py +1 -1
  20. modal-1.5.6.dev22/modal/_output/status.py +0 -98
  21. modal-1.5.6.dev22/modal/cli/function.py +0 -432
  22. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/LICENSE +0 -0
  23. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/README.md +0 -0
  24. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/__init__.py +0 -0
  25. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/__main__.py +0 -0
  26. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_billing.py +0 -0
  27. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_clustered_functions.py +0 -0
  28. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_clustered_functions.pyi +0 -0
  29. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_container_entrypoint.py +0 -0
  30. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_environments.py +0 -0
  31. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_grpc_client.py +0 -0
  32. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_image.py +0 -0
  33. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_ipython.py +0 -0
  34. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_load_context.py +0 -0
  35. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_location.py +0 -0
  36. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs.py +0 -0
  37. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs_manager.py +0 -0
  38. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_logs_manager.pyi +0 -0
  39. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_object.py +0 -0
  40. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/__init__.py +0 -0
  41. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_output/pty.py +0 -0
  42. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_partial_function.py +0 -0
  43. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_resolver.py +0 -0
  44. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_resources.py +0 -0
  45. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/__init__.py +0 -0
  46. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/asgi.py +0 -0
  47. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/container_io_manager.py +0 -0
  48. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/container_io_manager.pyi +0 -0
  49. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/execution_context.py +0 -0
  50. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/execution_context.pyi +0 -0
  51. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  52. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/task_lifecycle_manager.py +0 -0
  53. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  54. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/telemetry.py +0 -0
  55. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/user_code_event_loop.py +0 -0
  56. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_runtime/user_code_imports.py +0 -0
  57. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_serialization.py +0 -0
  58. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_supports_logs.py +0 -0
  59. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_traceback.py +0 -0
  60. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_tunnel.py +0 -0
  61. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_tunnel.pyi +0 -0
  62. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_type_manager.py +0 -0
  63. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/__init__.py +0 -0
  64. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/app_utils.py +0 -0
  65. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/async_utils.py +0 -0
  66. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/auth_token_manager.py +0 -0
  67. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/blob_utils.py +0 -0
  68. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/browser_utils.py +0 -0
  69. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/bytes_io_segment_payload.py +0 -0
  70. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/curl_utils.py +0 -0
  71. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/deprecation.py +0 -0
  72. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/display_utils.py +0 -0
  73. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/docker_utils.py +0 -0
  74. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/function_utils.py +0 -0
  75. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/git_utils.py +0 -0
  76. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/grpc_testing.py +0 -0
  77. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/grpc_utils.py +0 -0
  78. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/hash_utils.py +0 -0
  79. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/http_utils.py +0 -0
  80. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/idle_countdown.py +0 -0
  81. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/jwt_utils.py +0 -0
  82. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/logger.py +0 -0
  83. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/mount_utils.py +0 -0
  84. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/name_utils.py +0 -0
  85. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/package_utils.py +0 -0
  86. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/pattern_utils.py +0 -0
  87. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/proxy_support.py +0 -0
  88. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/rand_pb_testing.py +0 -0
  89. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/sandbox_fs_utils.py +0 -0
  90. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/shell_utils.py +0 -0
  91. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/task_command_router_client.py +0 -0
  92. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_utils/time_utils.py +0 -0
  93. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/__init__.py +0 -0
  94. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  95. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/cloudpickle.py +0 -0
  96. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/tblib.py +0 -0
  97. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_vendor/version.py +0 -0
  98. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_watcher.py +0 -0
  99. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/_workspace.py +0 -0
  100. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/app.py +0 -0
  101. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/app.pyi +0 -0
  102. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/billing.py +0 -0
  103. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/billing.pyi +0 -0
  104. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2023.12.312.txt +0 -0
  105. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2023.12.txt +0 -0
  106. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2024.04.txt +0 -0
  107. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2024.10.txt +0 -0
  108. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/2025.06.txt +0 -0
  109. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/PREVIEW.txt +0 -0
  110. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/README.md +0 -0
  111. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/builder/base-images.json +0 -0
  112. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/call_graph.py +0 -0
  113. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/__init__.py +0 -0
  114. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_download.py +0 -0
  115. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_help.py +0 -0
  116. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_logs.py +0 -0
  117. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_stats.py +0 -0
  118. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/_traceback.py +0 -0
  119. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/app.py +0 -0
  120. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/billing.py +0 -0
  121. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/changelog.py +0 -0
  122. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/cluster.py +0 -0
  123. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/container.py +0 -0
  124. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/curl.py +0 -0
  125. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/dashboard.py +0 -0
  126. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/dict.py +0 -0
  127. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/endpoint.py +0 -0
  128. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/entry_point.py +0 -0
  129. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/environment.py +0 -0
  130. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/image.py +0 -0
  131. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/import_refs.py +0 -0
  132. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/logo.py +0 -0
  133. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/network_file_system.py +0 -0
  134. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/profile.py +0 -0
  135. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/queues.py +0 -0
  136. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/run.py +0 -0
  137. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/secret.py +0 -0
  138. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/selector.py +0 -0
  139. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/shell.py +0 -0
  140. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/skills.py +0 -0
  141. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/token.py +0 -0
  142. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cli/workspace.py +0 -0
  143. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/client.py +0 -0
  144. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cloud_bucket_mount.py +0 -0
  145. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cloud_bucket_mount.pyi +0 -0
  146. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cls.py +0 -0
  147. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/cls.pyi +0 -0
  148. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/container_process.py +0 -0
  149. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/container_process.pyi +0 -0
  150. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/dict.py +0 -0
  151. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/dict.pyi +0 -0
  152. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/environments.py +0 -0
  153. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/environments.pyi +0 -0
  154. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/exception.py +0 -0
  155. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/__init__.py +0 -0
  156. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/_flash_container_entry.py +0 -0
  157. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash.py +0 -0
  158. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash.pyi +0 -0
  159. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash_server.py +0 -0
  160. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/flash_server.pyi +0 -0
  161. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/experimental/ipython.py +0 -0
  162. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/file_pattern_matcher.py +0 -0
  163. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/functions.py +0 -0
  164. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/image.py +0 -0
  165. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/image.pyi +0 -0
  166. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/io_streams.py +0 -0
  167. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/io_streams.pyi +0 -0
  168. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/mount.pyi +0 -0
  169. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/network_file_system.py +0 -0
  170. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/network_file_system.pyi +0 -0
  171. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/object.py +0 -0
  172. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/object.pyi +0 -0
  173. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/outbound_policy.py +0 -0
  174. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/outbound_policy.pyi +0 -0
  175. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/output.py +0 -0
  176. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/parallel_map.py +0 -0
  177. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/parallel_map.pyi +0 -0
  178. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/partial_function.py +0 -0
  179. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/partial_function.pyi +0 -0
  180. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/proxy.py +0 -0
  181. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/proxy.pyi +0 -0
  182. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/py.typed +0 -0
  183. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/queue.py +0 -0
  184. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/queue.pyi +0 -0
  185. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/retries.py +0 -0
  186. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/runner.py +0 -0
  187. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/runner.pyi +0 -0
  188. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/running_app.py +0 -0
  189. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox.py +0 -0
  190. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox.pyi +0 -0
  191. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox_fs.py +0 -0
  192. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/sandbox_fs.pyi +0 -0
  193. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/schedule.py +0 -0
  194. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/scheduler_placement.py +0 -0
  195. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/secret.py +0 -0
  196. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/secret.pyi +0 -0
  197. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/server.py +0 -0
  198. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/serving.py +0 -0
  199. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/serving.pyi +0 -0
  200. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/skills/modal/SKILL.md +0 -0
  201. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/snapshot.py +0 -0
  202. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/snapshot.pyi +0 -0
  203. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/stream_type.py +0 -0
  204. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/token_flow.py +0 -0
  205. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/token_flow.pyi +0 -0
  206. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/types.py +0 -0
  207. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/volume.py +0 -0
  208. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/volume.pyi +0 -0
  209. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/workspace.py +0 -0
  210. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal/workspace.pyi +0 -0
  211. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/SOURCES.txt +0 -0
  212. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/dependency_links.txt +0 -0
  213. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/entry_points.txt +0 -0
  214. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/requires.txt +0 -0
  215. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal.egg-info/top_level.txt +0 -0
  216. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/__init__.py +0 -0
  217. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_cli_docs.py +0 -0
  218. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_cli_docs_main.py +0 -0
  219. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_reference_docs.py +0 -0
  220. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/gen_reference_docs_main.py +0 -0
  221. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/__init__.py +0 -0
  222. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/mdmd.py +0 -0
  223. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/signatures.py +0 -0
  224. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/mdmd/types.py +0 -0
  225. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_docs/reference_intro.md +0 -0
  226. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/__init__.py +0 -0
  227. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_grpc.py +0 -0
  228. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2.py +0 -0
  229. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2.pyi +0 -0
  230. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2_grpc.py +0 -0
  231. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/api_pb2_grpc.pyi +0 -0
  232. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/modal_api_grpc.py +0 -0
  233. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/py.typed +0 -0
  234. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_grpc.py +0 -0
  235. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2.py +0 -0
  236. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2.pyi +0 -0
  237. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  238. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  239. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/modal_version/__main__.py +0 -0
  240. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/pyproject.toml +0 -0
  241. {modal-1.5.6.dev22 → modal-1.5.6.dev24}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.6.dev22
3
+ Version: 1.5.6.dev24
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -4,12 +4,15 @@ from collections.abc import Collection, Sequence, Sized
4
4
  from pathlib import PurePosixPath
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
+ import google.protobuf.message
8
+
7
9
  from modal.types import CloudBucketMountInfo, FunctionInfo, VolumeMountInfo
8
10
  from modal_proto import api_pb2
9
11
 
10
12
  from ._resources import convert_fn_config_to_resources_config
11
13
  from ._serialization import (
12
14
  apply_defaults,
15
+ deserialize_proto_params,
13
16
  serialize,
14
17
  serialize_proto_params,
15
18
  validate_parameter_values,
@@ -17,11 +20,14 @@ from ._serialization import (
17
20
  from ._utils.function_utils import _parse_retries
18
21
  from ._utils.mount_utils import validate_volumes, validate_volumes_by_object_id
19
22
  from .cloud_bucket_mount import _CloudBucketMount, cloud_bucket_mounts_to_proto
23
+ from .exception import InvalidError
20
24
  from .retries import Retries
21
25
  from .secret import _Secret
22
26
  from .volume import _Volume, _volume_to_mount_proto
23
27
 
24
28
  if TYPE_CHECKING:
29
+ from modal.client import _Client
30
+
25
31
  from ._functions import _Function
26
32
  from ._load_context import LoadContext
27
33
  from ._object import _Object
@@ -190,6 +196,157 @@ class _FunctionOptions:
190
196
  )
191
197
 
192
198
 
199
+ @dataclasses.dataclass(frozen=True)
200
+ class _FunctionOptionsInfo:
201
+ """Configuration overrides applied to a Function variant.
202
+
203
+ Fields are named after the parameters of the builder methods that set them: `.with_options()`,
204
+ `.with_concurrency()` (`max_inputs`, `target_inputs`), and `.with_batching()` (`max_batch_size`,
205
+ `wait_ms`). Fields are None when the variant does not override that setting.
206
+ """
207
+
208
+ cpu: float | tuple[float, float] | None = None
209
+ memory: int | tuple[int, int] | None = None
210
+ gpu: str | None = None
211
+ secrets: list[str] | None = None
212
+ volumes: dict[str, VolumeMountInfo] | None = None # mount path -> volume and its mount options
213
+ cloud_bucket_mounts: dict[str, CloudBucketMountInfo] | None = None # mount path -> bucket mount
214
+ retries: Retries | None = None
215
+ max_containers: int | None = None
216
+ buffer_containers: int | None = None
217
+ scaledown_window: int | None = None
218
+ timeout: int | None = None
219
+ region: str | list[str] | None = None
220
+ cloud: str | None = None
221
+ routing_region: str | None = None
222
+ max_inputs: int | None = None
223
+ target_inputs: int | None = None
224
+ max_batch_size: int | None = None
225
+ wait_ms: int | None = None
226
+
227
+ @classmethod
228
+ def _from_proto(cls, proto: api_pb2.FunctionOptions) -> "_FunctionOptionsInfo":
229
+ cpu: float | tuple[float, float] | None = None
230
+ memory: int | tuple[int, int] | None = None
231
+ gpu: str | None = None
232
+ if proto.HasField("resources"):
233
+ resources = proto.resources
234
+ if resources.milli_cpu_max > 0:
235
+ cpu = (resources.milli_cpu / 1000, resources.milli_cpu_max / 1000)
236
+ elif resources.milli_cpu > 0:
237
+ cpu = resources.milli_cpu / 1000
238
+
239
+ if resources.memory_mb_max > 0:
240
+ memory = (resources.memory_mb, resources.memory_mb_max)
241
+ elif resources.memory_mb > 0:
242
+ memory = resources.memory_mb
243
+
244
+ if resources.gpu_config.count > 0:
245
+ gpu_config = resources.gpu_config
246
+ gpu = gpu_config.gpu_type if gpu_config.count == 1 else f"{gpu_config.gpu_type}:{gpu_config.count}"
247
+
248
+ retries: Retries | None = None
249
+ if proto.HasField("retry_policy"):
250
+ retry_policy = proto.retry_policy
251
+ retries = Retries(
252
+ max_retries=retry_policy.retries,
253
+ backoff_coefficient=retry_policy.backoff_coefficient or 2.0,
254
+ initial_delay=retry_policy.initial_delay_ms / 1000,
255
+ max_delay=retry_policy.max_delay_ms / 1000 or 60.0,
256
+ )
257
+
258
+ region: str | list[str] | None = None
259
+ if proto.HasField("scheduler_placement") and proto.scheduler_placement.regions:
260
+ region = list(proto.scheduler_placement.regions)
261
+
262
+ return cls(
263
+ cpu=cpu,
264
+ memory=memory,
265
+ gpu=gpu,
266
+ secrets=list(proto.secret_ids) or None,
267
+ volumes={
268
+ vm.mount_path: VolumeMountInfo(
269
+ name=None, # The mount records which Volume, not what it was called.
270
+ volume_id=vm.volume_id,
271
+ read_only=vm.read_only,
272
+ sub_path=vm.sub_path if vm.HasField("sub_path") else None,
273
+ )
274
+ for vm in proto.volume_mounts
275
+ }
276
+ or None,
277
+ cloud_bucket_mounts={
278
+ cbm.mount_path: CloudBucketMountInfo._from_proto(cbm) for cbm in proto.cloud_bucket_mounts
279
+ }
280
+ or None,
281
+ retries=retries,
282
+ max_containers=proto.concurrency_limit if proto.HasField("concurrency_limit") else None,
283
+ buffer_containers=proto.buffer_containers if proto.HasField("buffer_containers") else None,
284
+ scaledown_window=proto.task_idle_timeout_secs if proto.HasField("task_idle_timeout_secs") else None,
285
+ timeout=proto.timeout_secs if proto.HasField("timeout_secs") else None,
286
+ region=region,
287
+ cloud=proto.cloud_provider_str if proto.HasField("cloud_provider_str") else None,
288
+ routing_region=proto.routing_region if proto.HasField("routing_region") else None,
289
+ max_inputs=proto.max_concurrent_inputs if proto.HasField("max_concurrent_inputs") else None,
290
+ target_inputs=proto.target_concurrent_inputs if proto.HasField("target_concurrent_inputs") else None,
291
+ max_batch_size=proto.batch_max_size if proto.HasField("batch_max_size") else None,
292
+ wait_ms=proto.batch_linger_ms if proto.HasField("batch_linger_ms") else None,
293
+ )
294
+
295
+
296
+ @dataclasses.dataclass(frozen=True)
297
+ class _FunctionVariantInfo:
298
+ """Information about a variant of a Function, created by parametrization or `.with_options()`."""
299
+
300
+ function_id: str
301
+ parameters: dict[str, str | int | bytes | bool] | None # None when the parameters can't be decoded
302
+ options: _FunctionOptionsInfo | None
303
+
304
+ @classmethod
305
+ def _from_proto(cls, proto: api_pb2.FunctionVariantInfo) -> "_FunctionVariantInfo":
306
+ parameters: dict[str, str | int | bytes | bool] | None
307
+ try:
308
+ parameters = deserialize_proto_params(proto.serialized_params)
309
+ except (google.protobuf.message.DecodeError, InvalidError):
310
+ parameters = None
311
+
312
+ return cls(
313
+ function_id=proto.function_id,
314
+ parameters=parameters,
315
+ options=_FunctionOptionsInfo._from_proto(proto.function_options)
316
+ if proto.HasField("function_options")
317
+ else None,
318
+ )
319
+
320
+
321
+ @dataclasses.dataclass(frozen=True)
322
+ class _FunctionVariantListing:
323
+ """The variants of a Function, and how the server chose to order them."""
324
+
325
+ variants: list[_FunctionVariantInfo]
326
+ # True when the variants are ordered by how many tasks each is running, busiest first.
327
+ ordered_by_task_count: bool
328
+
329
+
330
+ async def _list_function_variants(
331
+ client: "_Client", function_id: str, *, limit: int | None = None
332
+ ) -> _FunctionVariantListing:
333
+ variants: list[_FunctionVariantInfo] = []
334
+ ordered_by_task_count = False
335
+ cursor = None
336
+ while True:
337
+ request = api_pb2.FunctionListVariantsRequest(function_id=function_id, cursor=cursor, limit=limit or 0)
338
+ response = await client.stub.FunctionListVariants(request)
339
+ # An ordered listing arrives as a single response, so there is nothing to reconcile across
340
+ # pages: every response of a multi-page listing reports itself unordered.
341
+ ordered_by_task_count = response.ordered_by_task_count
342
+ variants.extend(_FunctionVariantInfo._from_proto(info) for info in response.infos)
343
+ if limit is not None and len(variants) >= limit:
344
+ return _FunctionVariantListing(variants[:limit], ordered_by_task_count)
345
+ if not response.HasField("next_cursor"):
346
+ return _FunctionVariantListing(variants, ordered_by_task_count)
347
+ cursor = response.next_cursor
348
+
349
+
193
350
  async def _function_bind_params_cached(
194
351
  base_function: "_Function",
195
352
  req: api_pb2.FunctionBindParamsRequest,
@@ -10,7 +10,7 @@ from collections.abc import AsyncGenerator, AsyncIterator, Callable, Collection,
10
10
  from dataclasses import dataclass
11
11
  from datetime import datetime, timedelta, timezone
12
12
  from pathlib import PurePosixPath
13
- from typing import TYPE_CHECKING, Any
13
+ from typing import TYPE_CHECKING, Any, Literal
14
14
 
15
15
  import typing_extensions
16
16
  from google.protobuf.message import Message
@@ -945,7 +945,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
945
945
  self._hydrate(response.function_id, load_context.client, response.handle_metadata)
946
946
 
947
947
  async def _load(self: _Function, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None):
948
- with FunctionCreationStatus(tag) as function_creation_status:
948
+ with FunctionCreationStatus("Server" if is_server else "Function", tag) as function_creation_status:
949
949
  timeout_secs = timeout
950
950
 
951
951
  if app and app._is_interactive_ and not is_builder_function:
@@ -1388,6 +1388,72 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1388
1388
  _load_remote, rep, skip_reload=True, hydrate_lazily=True, load_context_overrides=load_context_overrides
1389
1389
  )
1390
1390
 
1391
+ @classmethod
1392
+ def _from_id(
1393
+ cls,
1394
+ function_id: str,
1395
+ *,
1396
+ load_context_overrides: LoadContext,
1397
+ called_from: Literal["Function", "Server"] = "Function",
1398
+ ):
1399
+ # internal function lookup implementation that allows lookup of class "service functions"
1400
+ # in addition to non-class functions
1401
+ async def _load_remote(
1402
+ self: _Function, resolver: Resolver, load_context: LoadContext, existing_object_id: str | None
1403
+ ):
1404
+ request = api_pb2.FunctionGetByIdRequest(function_id=function_id)
1405
+ try:
1406
+ response = await load_context.client.stub.FunctionGetById(request)
1407
+ except NotFoundError as exc:
1408
+ # refine the error message
1409
+ msg = f"Lookup failed for {called_from} '{function_id}': {exc}."
1410
+ raise NotFoundError(msg) from None
1411
+ if called_from == "Function" and (response.function.is_server or response.function.is_class):
1412
+ if response.function.is_server:
1413
+ raise InvalidError(f"{function_id} is a Server. Use \n ``modal.Server.from_id('{function_id}')``")
1414
+ raise InvalidError(f"{function_id} is a Cls and cannot be loaded with Function.from_id().")
1415
+ if called_from == "Server" and not response.function.is_server:
1416
+ if response.function.is_class:
1417
+ raise InvalidError(f"{function_id} is a Cls and cannot be loaded with Server.from_id().")
1418
+ raise InvalidError(f"{function_id} is a Function. Use \n ``modal.Function.from_id('{function_id}')``")
1419
+ self._hydrate(function_id, load_context.client, response.handle_metadata)
1420
+ self._function_info = FunctionInfo._from_function_proto(response.function)
1421
+
1422
+ rep = f"modal.Function.from_id('{function_id}')"
1423
+ return cls._from_loader(
1424
+ _load_remote, rep, skip_reload=True, hydrate_lazily=True, load_context_overrides=load_context_overrides
1425
+ )
1426
+
1427
+ @classmethod
1428
+ def from_id(
1429
+ cls: type["_Function"],
1430
+ function_id: str,
1431
+ *,
1432
+ client: _Client | None = None,
1433
+ ):
1434
+ """Reference a Function from a deployed or running App by its ID.
1435
+
1436
+ This is a lazy method that defers hydrating the local
1437
+ object with metadata from Modal servers until the first
1438
+ time it is actually used.
1439
+
1440
+ Args:
1441
+ function_id: ID of the function.
1442
+ client: Modal client to use; defaults to `Client.from_env()` when omitted.
1443
+
1444
+ Returns:
1445
+ A lazy `Function` handle.
1446
+
1447
+ Examples:
1448
+ ```python
1449
+ f = modal.Function.from_id("fu-123")
1450
+ ```
1451
+ """
1452
+ return cls._from_id(
1453
+ function_id,
1454
+ load_context_overrides=LoadContext(client=client),
1455
+ )
1456
+
1391
1457
  @classmethod
1392
1458
  def from_name(
1393
1459
  cls: type["_Function"],
@@ -58,6 +58,11 @@ class StatusRow(ABC):
58
58
  """Append a warning message associated with this row."""
59
59
  ...
60
60
 
61
+ @abstractmethod
62
+ def details(self, message: str) -> None:
63
+ """Append additional details to the row."""
64
+ ...
65
+
61
66
  @abstractmethod
62
67
  def finish(self, message: str) -> None:
63
68
  """Mark the row as finished with the given message."""
@@ -73,6 +78,9 @@ class DisabledStatusRow(StatusRow):
73
78
  def warn(self, warning: "api_pb2.Warning") -> None:
74
79
  pass
75
80
 
81
+ def details(self, message: str) -> None:
82
+ pass
83
+
76
84
  def finish(self, message: str) -> None:
77
85
  pass
78
86
 
@@ -194,6 +194,10 @@ class RichStatusRow(StatusRow):
194
194
  if self._spinner is not None:
195
195
  self._spinner.update(text=message)
196
196
 
197
+ def details(self, message: str) -> None:
198
+ if self._step_node is not None:
199
+ self._step_node.add(f"{message}")
200
+
197
201
  def warn(self, warning: api_pb2.Warning) -> None:
198
202
  if self._step_node is not None:
199
203
  self._step_node.add(f"[yellow]:warning:[/yellow] {warning.message}")
@@ -201,7 +205,7 @@ class RichStatusRow(StatusRow):
201
205
  def finish(self, message: str) -> None:
202
206
  if self._step_node is not None and self._spinner is not None:
203
207
  self._spinner.update(text=message)
204
- self._step_node.label = f"🔨 {message}"
208
+ self._step_node.label = f"[green]✓[/green] {message}"
205
209
 
206
210
 
207
211
  _LOG_FLUSH_THRESHOLD = 32768 # 32KB — flush batched log output roughly every ~400 lines
@@ -0,0 +1,92 @@
1
+ # Copyright Modal Labs 2022
2
+ """Function creation status tracking for Modal CLI.
3
+
4
+ This module contains the FunctionCreationStatus context manager used to
5
+ track and display function creation progress.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from modal_proto import api_pb2
11
+
12
+ from .manager import OutputManager
13
+
14
+
15
+ def _get_annotation_for_web_url(url_info: api_pb2.WebUrlInfo) -> str:
16
+ if url_info.truncated:
17
+ suffix = " [grey70](label truncated)[/grey70]"
18
+ elif url_info.label_stolen:
19
+ suffix = " [grey70](label stolen)[/grey70]"
20
+ else:
21
+ suffix = ""
22
+ return suffix
23
+
24
+
25
+ class FunctionCreationStatus:
26
+ """Context manager for tracking and displaying function creation progress."""
27
+
28
+ type: str
29
+ name: str
30
+ response: api_pb2.FunctionCreateResponse | None = None
31
+
32
+ def __init__(self, type: str, name: str):
33
+ self.type = type
34
+ self.name = name
35
+ self._output_mgr = OutputManager.get()
36
+
37
+ def __enter__(self):
38
+ self.status_row = self._output_mgr.add_status_row()
39
+ self.status_row.message(f"Creating {self.type} {self.name}...")
40
+ return self
41
+
42
+ def set_response(self, resp: api_pb2.FunctionCreateResponse):
43
+ self.response = resp
44
+
45
+ def __exit__(self, exc_type, exc_val, exc_tb):
46
+ if exc_type:
47
+ raise exc_val
48
+
49
+ if not self.response:
50
+ self.status_row.finish(f"Unknown error when creating {self.type} {self.name}")
51
+ return
52
+
53
+ for warning in self.response.server_warnings:
54
+ self.status_row.warn(warning)
55
+
56
+ if web_url := self.response.handle_metadata.web_url:
57
+ url_info = self.response.function.web_url_info
58
+ authenticated = self.response.function.webhook_config.requires_proxy_auth
59
+ auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
60
+ suffix = _get_annotation_for_web_url(url_info)
61
+
62
+ self.status_row.finish(f"Created {self.type} [green]{self.name}[/green]")
63
+ self.status_row.details(f"[dim underline]{web_url}[/dim underline]{auth_suffix}{suffix}")
64
+ for custom_domain in self.response.function.custom_domain_info:
65
+ self.status_row.details(f"Custom domain: [dim underline]{custom_domain.url}[/dim underline]")
66
+
67
+ elif self.response.function.flash_service_urls:
68
+ # Despite the gRPC API types, Servers only have one URL
69
+ authenticated = self.response.function.is_server and not self.response.function.http_config.unauthenticated
70
+ auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
71
+ self.status_row.finish(f"Created {self.type} [green]{self.name}[/green]")
72
+ url = self.response.function.flash_service_urls[0]
73
+ self.status_row.details(f"[dim underline]{url}[/dim underline]{auth_suffix}")
74
+
75
+ else:
76
+ self.status_row.finish(f"Created {self.type} [green]{self.name}[/green].")
77
+ if self.response.function.method_definitions_set:
78
+ for method_definition in self.response.function.method_definitions.values():
79
+ if method_definition.web_url:
80
+ url_info = method_definition.web_url_info
81
+ suffix = _get_annotation_for_web_url(url_info)
82
+ authenticated = method_definition.webhook_config.requires_proxy_auth
83
+ auth_suffix = " 🔑" if authenticated else " [yellow](unauthenticated)[/yellow]"
84
+ self.status_row.details(
85
+ f"{method_definition.function_name} -> "
86
+ f"[dim underline]{method_definition.web_url}[/dim underline]{auth_suffix}{suffix}"
87
+ )
88
+ for custom_domain in method_definition.custom_domain_info:
89
+ indent = len(method_definition.function_name) * " "
90
+ self.status_row.details(
91
+ f"{indent} -> [dim underline]{custom_domain.url}[/dim underline]{auth_suffix}"
92
+ )
@@ -273,6 +273,38 @@ class _Server:
273
273
  )
274
274
  return server
275
275
 
276
+ @classmethod
277
+ def from_id(
278
+ cls: type["_Server"],
279
+ server_id: str,
280
+ *,
281
+ client: _Client | None = None,
282
+ ):
283
+ """Reference a Server from a deployed or running App by its ID.
284
+
285
+ This is a lazy method that defers hydrating the local
286
+ object with metadata from Modal servers until the first
287
+ time it is actually used.
288
+
289
+ Args:
290
+ server_id: The ID of the server.
291
+ client: Modal client instance for this session.
292
+
293
+ Examples:
294
+ ```python notest
295
+ server = modal.Server.from_id("fu-456")
296
+ ```
297
+ """
298
+ load_context_overrides = LoadContext(client=client)
299
+
300
+ server = _Server()
301
+ server._service_function = _Function._from_id(
302
+ server_id,
303
+ load_context_overrides=load_context_overrides,
304
+ called_from="Server",
305
+ )
306
+ return server
307
+
276
308
  def _is_local(self) -> bool:
277
309
  """Returns True if this Server has local source code available."""
278
310
  return self._user_cls is not None
@@ -1,5 +1,5 @@
1
1
  # Copyright Modal Labs 2022
2
- import json
2
+ from json import dumps
3
3
 
4
4
  import click
5
5
 
@@ -21,11 +21,12 @@ config_cli = ModalGroup(
21
21
  )
22
22
 
23
23
 
24
- @config_cli.command("show", help="Show current configuration values (debugging command).")
24
+ @config_cli.command("show", help="Show current configuration values.")
25
25
  @click.option("--redact/--no-redact", default=True, help="Redact secret credential values.")
26
- def show(redact: bool):
27
- # This is just a test command
28
- config_dict = config.to_dict()
26
+ @click.option("--include-internal", is_flag=True, help="Include internal feature flags.", hidden=True)
27
+ @click.option("--json", is_flag=True, default=False)
28
+ def show(redact: bool, include_internal: bool, json: bool):
29
+ config_dict = config.to_dict(include_internal=include_internal)
29
30
  if redact and config_dict.get("token_secret"):
30
31
  config_dict["token_secret"] = "***"
31
32
  if redact and config_dict.get("oauth_refresh_token"):
@@ -33,7 +34,8 @@ def show(redact: bool):
33
34
  if redact and config_dict.get("oauth_client_secret"):
34
35
  config_dict["oauth_client_secret"] = "***"
35
36
 
36
- OutputManager.get().print_json(json.dumps(config_dict))
37
+ # Accept --json as a no-op for consistency; we may change the default formatting later
38
+ OutputManager.get().print_json(dumps(config_dict))
37
39
 
38
40
 
39
41
  SET_DEFAULT_ENV_HELP = """Set the default Modal environment for the active profile