modal 1.5.1.dev7__tar.gz → 1.5.1.dev9__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 (226) hide show
  1. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/PKG-INFO +2 -2
  2. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/README.md +1 -1
  3. modal-1.5.1.dev9/modal/_billing.py +155 -0
  4. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_environments.py +77 -1
  5. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_functions.py +4 -4
  6. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_grpc_client.py +3 -3
  7. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_partial_function.py +1 -1
  8. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_server.py +1 -1
  9. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/task_command_router_client.py +27 -0
  10. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/time_utils.py +23 -0
  11. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_workspace.py +76 -1
  12. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/app.py +6 -3
  13. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/app.pyi +10 -6
  14. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/billing.py +3 -2
  15. modal-1.5.1.dev9/modal/billing.pyi +184 -0
  16. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/_help.py +3 -3
  17. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/billing.py +100 -69
  18. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/changelog.py +1 -1
  19. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/config.py +1 -1
  20. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/environment.py +141 -1
  21. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/shell.py +1 -1
  22. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/client.py +1 -1
  23. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/client.pyi +2 -2
  24. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cls.py +2 -2
  25. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cls.pyi +4 -4
  26. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/environments.py +2 -0
  27. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/environments.pyi +36 -1
  28. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/exception.py +1 -1
  29. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/functions.pyi +14 -14
  30. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/partial_function.pyi +1 -1
  31. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/runner.py +16 -12
  32. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/sandbox.py +93 -26
  33. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/sandbox.pyi +78 -15
  34. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/schedule.py +1 -1
  35. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/server.pyi +1 -1
  36. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/workspace.py +2 -1
  37. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/workspace.pyi +34 -0
  38. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/PKG-INFO +2 -2
  39. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/SOURCES.txt +1 -0
  40. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/gen_cli_docs.py +48 -2
  41. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/api_grpc.py +19 -3
  42. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/api_pb2.py +1146 -1103
  43. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/api_pb2.pyi +83 -5
  44. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/api_pb2_grpc.py +36 -3
  45. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/api_pb2_grpc.pyi +12 -2
  46. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/modal_api_grpc.py +1 -0
  47. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_version/__init__.py +1 -1
  48. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/pyproject.toml +1 -1
  49. modal-1.5.1.dev7/modal/_billing.py +0 -94
  50. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/LICENSE +0 -0
  51. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/__init__.py +0 -0
  52. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/__main__.py +0 -0
  53. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_clustered_functions.py +0 -0
  54. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_clustered_functions.pyi +0 -0
  55. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_container_entrypoint.py +0 -0
  56. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_function_variants.py +0 -0
  57. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_image.py +0 -0
  58. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_ipython.py +0 -0
  59. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_load_context.py +0 -0
  60. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_location.py +0 -0
  61. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_logs.py +0 -0
  62. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_object.py +0 -0
  63. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_output/__init__.py +0 -0
  64. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_output/manager.py +0 -0
  65. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_output/pty.py +0 -0
  66. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_output/rich.py +0 -0
  67. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_output/status.py +0 -0
  68. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_resolver.py +0 -0
  69. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_resources.py +0 -0
  70. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/__init__.py +0 -0
  71. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/asgi.py +0 -0
  72. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/container_io_manager.py +0 -0
  73. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/container_io_manager.pyi +0 -0
  74. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/execution_context.py +0 -0
  75. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/execution_context.pyi +0 -0
  76. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  77. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/task_lifecycle_manager.py +0 -0
  78. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  79. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/telemetry.py +0 -0
  80. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/user_code_event_loop.py +0 -0
  81. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_runtime/user_code_imports.py +0 -0
  82. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_serialization.py +0 -0
  83. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_traceback.py +0 -0
  84. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_tunnel.py +0 -0
  85. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_tunnel.pyi +0 -0
  86. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_type_manager.py +0 -0
  87. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/__init__.py +0 -0
  88. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/app_utils.py +0 -0
  89. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/async_utils.py +0 -0
  90. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/auth_token_manager.py +0 -0
  91. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/blob_utils.py +0 -0
  92. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/browser_utils.py +0 -0
  93. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/bytes_io_segment_payload.py +0 -0
  94. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/deprecation.py +0 -0
  95. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/docker_utils.py +0 -0
  96. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/function_utils.py +0 -0
  97. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/git_utils.py +0 -0
  98. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/grpc_testing.py +0 -0
  99. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/grpc_utils.py +0 -0
  100. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/hash_utils.py +0 -0
  101. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/http_utils.py +0 -0
  102. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/jwt_utils.py +0 -0
  103. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/logger.py +0 -0
  104. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/mount_utils.py +0 -0
  105. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/name_utils.py +0 -0
  106. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/package_utils.py +0 -0
  107. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/pattern_utils.py +0 -0
  108. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/rand_pb_testing.py +0 -0
  109. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/sandbox_fs_utils.py +0 -0
  110. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_utils/shell_utils.py +0 -0
  111. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_vendor/__init__.py +0 -0
  112. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  113. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_vendor/cloudpickle.py +0 -0
  114. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_vendor/tblib.py +0 -0
  115. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_vendor/version.py +0 -0
  116. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/_watcher.py +0 -0
  117. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/2023.12.312.txt +0 -0
  118. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/2023.12.txt +0 -0
  119. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/2024.04.txt +0 -0
  120. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/2024.10.txt +0 -0
  121. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/2025.06.txt +0 -0
  122. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/PREVIEW.txt +0 -0
  123. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/README.md +0 -0
  124. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/builder/base-images.json +0 -0
  125. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/call_graph.py +0 -0
  126. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/__init__.py +0 -0
  127. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/_download.py +0 -0
  128. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/_traceback.py +0 -0
  129. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/app.py +0 -0
  130. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/bootstrap.py +0 -0
  131. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/cluster.py +0 -0
  132. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/container.py +0 -0
  133. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/dashboard.py +0 -0
  134. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/dict.py +0 -0
  135. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/entry_point.py +0 -0
  136. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/image.py +0 -0
  137. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/import_refs.py +0 -0
  138. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/launch.py +0 -0
  139. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/logo.py +0 -0
  140. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/network_file_system.py +0 -0
  141. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/profile.py +0 -0
  142. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/programs/__init__.py +0 -0
  143. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/programs/run_jupyter.py +0 -0
  144. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/programs/vscode.py +0 -0
  145. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/queues.py +0 -0
  146. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/run.py +0 -0
  147. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/secret.py +0 -0
  148. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/selector.py +0 -0
  149. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/skills.py +0 -0
  150. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/token.py +0 -0
  151. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/utils.py +0 -0
  152. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cli/volume.py +0 -0
  153. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cloud_bucket_mount.py +0 -0
  154. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/cloud_bucket_mount.pyi +0 -0
  155. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/config.py +0 -0
  156. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/container_process.py +0 -0
  157. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/container_process.pyi +0 -0
  158. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/dict.py +0 -0
  159. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/dict.pyi +0 -0
  160. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/experimental/__init__.py +0 -0
  161. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/experimental/flash.py +0 -0
  162. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/experimental/flash.pyi +0 -0
  163. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/experimental/ipython.py +0 -0
  164. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/file_io.py +0 -0
  165. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/file_io.pyi +0 -0
  166. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/file_pattern_matcher.py +0 -0
  167. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/functions.py +0 -0
  168. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/image.py +0 -0
  169. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/image.pyi +0 -0
  170. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/io_streams.py +0 -0
  171. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/io_streams.pyi +0 -0
  172. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/mount.py +0 -0
  173. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/mount.pyi +0 -0
  174. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/network_file_system.py +0 -0
  175. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/network_file_system.pyi +0 -0
  176. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/object.py +0 -0
  177. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/object.pyi +0 -0
  178. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/output.py +0 -0
  179. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/parallel_map.py +0 -0
  180. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/parallel_map.pyi +0 -0
  181. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/partial_function.py +0 -0
  182. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/proxy.py +0 -0
  183. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/proxy.pyi +0 -0
  184. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/py.typed +0 -0
  185. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/queue.py +0 -0
  186. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/queue.pyi +0 -0
  187. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/retries.py +0 -0
  188. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/runner.pyi +0 -0
  189. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/running_app.py +0 -0
  190. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/sandbox_fs.py +0 -0
  191. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/sandbox_fs.pyi +0 -0
  192. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/scheduler_placement.py +0 -0
  193. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/secret.py +0 -0
  194. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/secret.pyi +0 -0
  195. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/server.py +0 -0
  196. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/serving.py +0 -0
  197. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/serving.pyi +0 -0
  198. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/skills/modal/SKILL.md +0 -0
  199. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/snapshot.py +0 -0
  200. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/snapshot.pyi +0 -0
  201. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/stream_type.py +0 -0
  202. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/token_flow.py +0 -0
  203. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/token_flow.pyi +0 -0
  204. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/volume.py +0 -0
  205. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal/volume.pyi +0 -0
  206. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/dependency_links.txt +0 -0
  207. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/entry_points.txt +0 -0
  208. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/requires.txt +0 -0
  209. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal.egg-info/top_level.txt +0 -0
  210. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/__init__.py +0 -0
  211. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/gen_cli_docs_main.py +0 -0
  212. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/gen_reference_docs.py +0 -0
  213. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/gen_reference_docs_main.py +0 -0
  214. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/mdmd/__init__.py +0 -0
  215. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/mdmd/mdmd.py +0 -0
  216. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/mdmd/signatures.py +0 -0
  217. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_docs/mdmd/types.py +0 -0
  218. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/__init__.py +0 -0
  219. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/py.typed +0 -0
  220. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/task_command_router_grpc.py +0 -0
  221. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/task_command_router_pb2.py +0 -0
  222. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/task_command_router_pb2.pyi +0 -0
  223. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  224. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  225. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/modal_version/__main__.py +0 -0
  226. {modal-1.5.1.dev7 → modal-1.5.1.dev9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.1.dev7
3
+ Version: 1.5.1.dev9
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -45,7 +45,7 @@ access to serverless cloud compute from Python scripts on your local computer.
45
45
  See the [online documentation](https://modal.com/docs/guide) for many
46
46
  [example applications](https://modal.com/docs/examples),
47
47
  a [user guide](https://modal.com/docs/guide), and the detailed
48
- [API reference](https://modal.com/docs/reference).
48
+ [API reference](https://modal.com/docs/sdk/py/latest).
49
49
 
50
50
  ## Installation
51
51
 
@@ -12,7 +12,7 @@ access to serverless cloud compute from Python scripts on your local computer.
12
12
  See the [online documentation](https://modal.com/docs/guide) for many
13
13
  [example applications](https://modal.com/docs/examples),
14
14
  a [user guide](https://modal.com/docs/guide), and the detailed
15
- [API reference](https://modal.com/docs/reference).
15
+ [API reference](https://modal.com/docs/sdk/py/latest).
16
16
 
17
17
  ## Installation
18
18
 
@@ -0,0 +1,155 @@
1
+ # Copyright Modal Labs 2025
2
+ from dataclasses import FrozenInstanceError, dataclass
3
+ from datetime import datetime, timezone
4
+ from decimal import Decimal
5
+ from typing import Any, Iterable, TypedDict
6
+
7
+ from modal_proto import api_pb2
8
+
9
+ from ._utils.deprecation import deprecation_warning
10
+ from .client import _Client
11
+
12
+ BILLING_DOCSTRING = """
13
+ The `start` and `end` parameters are required to either have a UTC timezone or to be
14
+ timezone-naive (which will be interpreted as UTC times). The timestamps in the result will
15
+ be in UTC. Cost will be reported for full intervals, even if the provided `start` or `end`
16
+ parameters are partial: `start` will be rounded to the beginning of its interval, while
17
+ partial `end` intervals will be excluded.
18
+
19
+ Additional user-provided metadata can be included in the report if the objects have tags
20
+ and `tag_names` (i.e., keys) are specified in the request. Alternatively, pass `tag_names=["*"]`
21
+ to include all tags in the report. Note that tags will be attributed to the entire interval even
22
+ if they were added or removed at some point within it. If the tag name was not in use during an
23
+ interval, it will be absent from the tags dictionary in that output row.
24
+
25
+ In most cases, billing data will be available in the database that this API queries within
26
+ minutes, although there may be collection delays. If completeness is important for your use
27
+ case, we recommend leaving a buffer after the end of the query interval.
28
+ """
29
+
30
+
31
+ class WorkspaceBillingReportItem(TypedDict):
32
+ object_id: str
33
+ description: str
34
+ environment_name: str
35
+ interval_start: datetime
36
+ cost: Decimal
37
+ tags: dict[str, str]
38
+
39
+
40
+ @dataclass(slots=True, frozen=True)
41
+ class BillingReportItem:
42
+ object_id: str
43
+ description: str
44
+ environment_name: str
45
+ interval_start: datetime
46
+ cost: Decimal
47
+ cost_by_resource: dict[str, Decimal]
48
+ tags: dict[str, str]
49
+
50
+ def __getitem__(self, key: str) -> Any:
51
+ """mdmd:ignore"""
52
+ if key not in self.__slots__:
53
+ raise KeyError(key)
54
+
55
+ return getattr(self, key)
56
+
57
+ def __setitem__(self, key: str, _: Any):
58
+ """mdmd:ignore"""
59
+ raise FrozenInstanceError(f"cannot assign to field {key!r}")
60
+
61
+ def keys(self) -> Iterable[str]:
62
+ """mdmd:ignore"""
63
+ yield from self.__slots__
64
+
65
+ def values(self) -> Iterable[Any]:
66
+ """mdmd:ignore"""
67
+ for k in self.__slots__:
68
+ yield getattr(self, k)
69
+
70
+ def items(self) -> Iterable[tuple[str, Any]]:
71
+ """mdmd:ignore"""
72
+ for k in self.__slots__:
73
+ yield k, getattr(self, k)
74
+
75
+ @classmethod
76
+ def _from_proto(cls, pb_item: api_pb2.WorkspaceBillingReportItem) -> "BillingReportItem":
77
+ return cls(
78
+ object_id=pb_item.object_id,
79
+ description=pb_item.description,
80
+ environment_name=pb_item.environment_name,
81
+ interval_start=pb_item.interval.ToDatetime().replace(tzinfo=timezone.utc),
82
+ cost=Decimal(pb_item.cost),
83
+ cost_by_resource={k: Decimal(v) for k, v in pb_item.cost_by_resource.items()},
84
+ tags=dict(pb_item.tags),
85
+ )
86
+
87
+
88
+ async def _workspace_billing_report(
89
+ *,
90
+ start: datetime, # Start of the report, inclusive
91
+ end: datetime | None = None, # End of the report, exclusive
92
+ resolution: str = "d", # Resolution, e.g. "d" for daily or "h" for hourly
93
+ tag_names: list[str] | None = None, # Optional additional metadata to include
94
+ client: _Client | None = None,
95
+ ) -> list[WorkspaceBillingReportItem]:
96
+ """Generate a tabular report of workspace usage by object and time.
97
+
98
+ The result will be a list of dictionaries for each interval (determined by `resolution`)
99
+ between the `start` and `end` limits. The dictionary represents a single Modal object
100
+ that billing can be attributed to (e.g., an App) along with metadata (including user-defined
101
+ tags) for identifying that object. The dictionary also contains a breakdown of the cost value
102
+ attributed to individual resources (for an App, this can be CPU, Memory, specific GPU types,
103
+ etc.). The specific resource types included in the breakdown are subject to change as
104
+ Modal's billing model evolves.
105
+
106
+ The `start` and `end` parameters are required to either have a UTC timezone or to be
107
+ timezone-naive (which will be interpreted as UTC times). The timestamps in the result will
108
+ be in UTC. Cost will be reported for full intervals, even if the provided `start` or `end`
109
+ parameters are partial: `start` will be rounded to the beginning of its interval, while
110
+ partial `end` intervals will be excluded.
111
+
112
+ Additional user-provided metadata can be included in the report if the objects have tags
113
+ and `tag_names` (i.e., keys) are specified in the request. Alternatively, pass `tag_names=["*"]`
114
+ to include all tags in the report. Note that tags will be attributed to the entire interval even
115
+ if they were added or removed at some point within it. If the tag name was not in use during an
116
+ interval, it will be absent from the tags dictionary in that output row.
117
+
118
+ In most cases, billing data will be available in the database that this API queries within
119
+ minutes, although there may be collection delays. If completeness is important for your use
120
+ case, we recommend leaving a buffer after the end of the query interval.
121
+
122
+ It's also possible to generate reports using the
123
+ [`modal billing report`](https://modal.com/docs/cli/latest/billing) CLI command. The CLI
124
+ has a few convenience features for generating reports across relative time ranges.
125
+
126
+ """
127
+
128
+ deprecation_warning(
129
+ (2026, 6, 18),
130
+ "`workspace_billing_report()` is deprecated. Use `Workspace.billing.report()` instead.",
131
+ )
132
+
133
+ from ._workspace import _Workspace
134
+
135
+ data = await _Workspace.from_context(client=client).billing.report(
136
+ start=start,
137
+ end=end,
138
+ resolution=resolution,
139
+ tag_names=tag_names,
140
+ )
141
+
142
+ res: list[WorkspaceBillingReportItem] = []
143
+ for datum in data:
144
+ item: WorkspaceBillingReportItem = {
145
+ "object_id": datum.object_id,
146
+ "description": datum.description,
147
+ "environment_name": datum.environment_name,
148
+ "interval_start": datum.interval_start,
149
+ "cost": Decimal(datum.cost),
150
+ "tags": dict(datum.tags),
151
+ }
152
+
153
+ res.append(item)
154
+
155
+ return res
@@ -3,6 +3,7 @@ import asyncio
3
3
  import builtins
4
4
  from collections.abc import Iterable, Mapping
5
5
  from dataclasses import dataclass
6
+ from datetime import datetime, timezone
6
7
  from typing import Literal
7
8
 
8
9
  from google.protobuf.empty_pb2 import Empty
@@ -12,6 +13,7 @@ from synchronicity import classproperty
12
13
 
13
14
  from modal_proto import api_pb2
14
15
 
16
+ from ._billing import BILLING_DOCSTRING, BillingReportItem
15
17
  from ._load_context import LoadContext
16
18
  from ._object import _Object
17
19
  from ._resolver import Resolver
@@ -318,7 +320,7 @@ class _Environment(_Object, type_prefix="en"):
318
320
  # def settings(self) -> EnvironmentSettings:
319
321
  # return self._settings
320
322
 
321
- def _hydrate_metadata(self, metadata: Message):
323
+ def _hydrate_metadata(self, metadata: Message | None):
322
324
  # Overridden concrete implementation of base class method
323
325
  assert metadata and isinstance(metadata, api_pb2.EnvironmentMetadata)
324
326
  self._name = metadata.name or None
@@ -392,6 +394,80 @@ class _Environment(_Object, type_prefix="en"):
392
394
  client=client,
393
395
  )
394
396
 
397
+ @property
398
+ def billing(self) -> "_EnvironmentBillingManager":
399
+ return _EnvironmentBillingManager(self)
400
+
401
+
402
+ class _EnvironmentBillingManager:
403
+ """mdmd:namespace
404
+ Namespace for Environment billing APIs
405
+ """
406
+
407
+ def __init__(self, environment: _Environment):
408
+ """mdmd:ignore"""
409
+ self._environment = environment
410
+
411
+ async def report(
412
+ self,
413
+ *,
414
+ start: datetime, # Start of the report, inclusive
415
+ end: datetime | None = None, # End of the report, exclusive
416
+ resolution: str = "d", # Resolution, e.g. "d" for daily or "h" for hourly
417
+ tag_names: list[str] | None = None, # Optional additional metadata to include
418
+ ) -> list[BillingReportItem]:
419
+ (
420
+ """Return a report of workspace usage by object and time, scoped to the calling Environment.
421
+
422
+ The result will be a list of dataclasses for each interval (determined by `resolution`)
423
+ between the `start` and `end` limits. Each item represents a single (Modal object, time interval)
424
+ pair that billing can be attributed to (e.g., an App) along with metadata (including user-defined
425
+ tags) to identify that object. The dataclass also contains a breakdown of the cost value
426
+ attributed to individual resources (for an App, this can be CPU, Memory, specific GPU types,
427
+ etc.). The specific resource types included in the breakdown are subject to change as
428
+ Modal's billing model evolves.
429
+
430
+ It's also possible to generate reports using the
431
+ [`modal environment billing report`](https://modal.com/docs/cli/latest/environment#modal-environment-billing-report)
432
+ CLI command. The CLI has a few convenience features for generating
433
+ reports across relative time ranges.
434
+
435
+ """
436
+ + BILLING_DOCSTRING
437
+ )
438
+
439
+ if tag_names is None:
440
+ tag_names = []
441
+
442
+ if end is None:
443
+ end = datetime.now(timezone.utc)
444
+
445
+ if start.tzinfo is None:
446
+ start = start.replace(tzinfo=timezone.utc)
447
+ elif start.tzinfo != timezone.utc:
448
+ raise InvalidError("Timezone-aware 'start' parameter must be in UTC.")
449
+
450
+ if end.tzinfo is None:
451
+ end = end.replace(tzinfo=timezone.utc)
452
+ elif end.tzinfo != timezone.utc:
453
+ raise InvalidError("Timezone-aware 'end' parameter must be in UTC.")
454
+
455
+ if not self._environment.is_hydrated:
456
+ await self._environment.hydrate()
457
+
458
+ request = api_pb2.WorkspaceBillingReportRequest(
459
+ resolution=resolution,
460
+ tag_names=tag_names,
461
+ environment_ids=[self._environment.object_id],
462
+ )
463
+ request.start_timestamp.FromDatetime(start)
464
+ request.end_timestamp.FromDatetime(end)
465
+
466
+ return [
467
+ BillingReportItem._from_proto(pb_item)
468
+ async for pb_item in self._environment.client.stub.WorkspaceBillingReport.unary_stream(request)
469
+ ]
470
+
395
471
 
396
472
  ENVIRONMENT_CACHE: dict[str, _Environment] = {}
397
473
 
@@ -1912,9 +1912,9 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1912
1912
  **kwargs: Keyword arguments forwarded to the remote function.
1913
1913
 
1914
1914
  Returns:
1915
- A [`modal.FunctionCall`](https://modal.com/docs/reference/modal.FunctionCall) object
1915
+ A [`modal.FunctionCall`](https://modal.com/docs/sdk/py/latest/modal.FunctionCall) object
1916
1916
  that can later be polled or waited for using
1917
- [`.get(timeout=...)`](https://modal.com/docs/reference/modal.FunctionCall#get).
1917
+ [`.get(timeout=...)`](https://modal.com/docs/sdk/py/latest/modal.FunctionCall#get).
1918
1918
  """
1919
1919
  self._check_no_web_url("spawn")
1920
1920
  if self._is_generator:
@@ -2065,7 +2065,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2065
2065
  """Returns a structure representing the call graph from a given root
2066
2066
  call ID, along with the status of execution for each node.
2067
2067
 
2068
- See [`modal.call_graph`](https://modal.com/docs/reference/modal.call_graph) reference page
2068
+ See [`modal.call_graph`](https://modal.com/docs/sdk/py/latest/modal.call_graph) reference page
2069
2069
  for documentation on the structure of the returned `InputInfo` items.
2070
2070
 
2071
2071
  Returns:
@@ -2083,7 +2083,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2083
2083
  terminate_containers: bool = False,
2084
2084
  ):
2085
2085
  """Cancels the function call, which will stop its execution and mark its inputs as
2086
- [`TERMINATED`](https://modal.com/docs/reference/modal.call_graph#modalcall_graphinputstatus).
2086
+ [`TERMINATED`](https://modal.com/docs/sdk/py/latest/modal.call_graph#modalcall_graphinputstatus).
2087
2087
 
2088
2088
  If `terminate_containers=True` - the containers running the cancelled inputs are all terminated
2089
2089
  causing any non-cancelled inputs on those containers to be rescheduled in new containers.
@@ -1,6 +1,6 @@
1
1
  # Copyright Modal Labs 2025
2
2
  from collections.abc import Collection, Mapping
3
- from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, overload
3
+ from typing import TYPE_CHECKING, Any, AsyncGenerator, Generic, Literal, TypeVar, overload
4
4
 
5
5
  import grpclib.client
6
6
  from google.protobuf.message import Message
@@ -178,9 +178,9 @@ class UnaryStreamWrapper(Generic[RequestType, ResponseType]):
178
178
 
179
179
  async def unary_stream(
180
180
  self,
181
- request,
181
+ request: RequestType,
182
182
  metadata: Any | None = None,
183
- ):
183
+ ) -> AsyncGenerator[ResponseType, None]:
184
184
  from .client import _Client
185
185
 
186
186
  if self.client._snapshotted:
@@ -477,7 +477,7 @@ def _wsgi_app(
477
477
  Web Server Gateway Interface (WSGI) is a standard for synchronous Python web apps.
478
478
  It has been [succeeded by the ASGI interface](https://asgi.readthedocs.io/en/latest/introduction.html#wsgi-compatibility)
479
479
  which is compatible with ASGI and supports additional functionality such as web sockets.
480
- Modal supports ASGI via [`asgi_app`](https://modal.com/docs/reference/modal.asgi_app).
480
+ Modal supports ASGI via [`asgi_app`](https://modal.com/docs/sdk/py/latest/modal.asgi_app).
481
481
 
482
482
  Examples:
483
483
  ```python
@@ -49,7 +49,7 @@ class _Server:
49
49
  See [lifecycle hooks](https://modal.com/docs/guide/lifecycle-functions) for more information.
50
50
 
51
51
  Generally, you will not construct a Server directly.
52
- Instead, use the [`@app.server()`](https://modal.com/docs/reference/modal.App#server) decorator.
52
+ Instead, use the [`@app.server()`](https://modal.com/docs/sdk/py/latest/modal.App#server) decorator.
53
53
 
54
54
  ```python notest
55
55
  @app.server(port=8000, routing_region="us-east")
@@ -491,6 +491,27 @@ class TaskCommandRouterClient:
491
491
  lambda: self._call_with_auth_retry(self._stub.SandboxStdinWriteV2, request)
492
492
  )
493
493
 
494
+ async def sandbox_wait_until_ready(self, task_id: str, timeout: float) -> sr_pb2.SandboxWaitUntilReadyTcrResponse:
495
+ """Wait until the sandbox's readiness probe reports ready.
496
+
497
+ Args:
498
+ task_id: The task ID hosting the sandbox.
499
+ timeout: Maximum time in seconds for the worker to wait.
500
+ Raises:
501
+ TimeoutError: If the sandbox does not become ready within `timeout`.
502
+ """
503
+ request = sr_pb2.SandboxWaitUntilReadyTcrRequest(task_id=task_id, timeout=timeout)
504
+ with grpc_error_converter():
505
+ try:
506
+ return await asyncio.wait_for(
507
+ call_with_retries_on_transient_errors(
508
+ lambda: self._call_with_auth_retry(self._stub.SandboxWaitUntilReady, request, timeout=timeout),
509
+ ),
510
+ timeout=timeout,
511
+ )
512
+ except asyncio.TimeoutError:
513
+ raise ModalTimeoutError("Timeout expired")
514
+
494
515
  async def exec_poll(self, task_id: str, exec_id: str, deadline: float | None = None) -> sr_pb2.TaskExecPollResponse:
495
516
  """Poll for the exit status of an exec'd command, properly retrying on transient errors.
496
517
 
@@ -784,6 +805,12 @@ class TaskCommandRouterClient:
784
805
  lambda: self._call_with_auth_retry(self._stub.TaskUnmountDirectory, request)
785
806
  )
786
807
 
808
+ async def set_network_access(self, request: sr_pb2.TaskSetNetworkAccessRequest):
809
+ with grpc_error_converter():
810
+ return await call_with_retries_on_transient_errors(
811
+ lambda: self._call_with_auth_retry(self._stub.TaskSetNetworkAccess, request)
812
+ )
813
+
787
814
  async def _snapshot_with_deadline(self, rpc, request, *, timeout: float, **kwargs):
788
815
  # helper method for snapshot_directory and snapshot_filesystem to handle grpc
789
816
  # deadlines in a consistent way, converting any error to TimeoutError after passing
@@ -256,3 +256,26 @@ def timestamp_to_localized_str(ts: float, isotz: bool = True) -> str | None:
256
256
  return f"{dt:%Y-%m-%d %H:%M %Z}"
257
257
  else:
258
258
  return None
259
+
260
+
261
+ def format_interval(
262
+ dt: datetime,
263
+ *,
264
+ tz: tzinfo | None = None,
265
+ isoformat: bool = False,
266
+ show_date_only: bool = False,
267
+ ) -> str:
268
+ if tz is not None:
269
+ dt = dt.astimezone(tz)
270
+ else:
271
+ # Strip UTC tzinfo so isoformat() doesn't append +00:00
272
+ dt = dt.replace(tzinfo=None)
273
+ if isoformat:
274
+ # Full ISO format for machine-readable output
275
+ return dt.isoformat()
276
+ elif show_date_only:
277
+ # Just the date for daily resolution
278
+ return dt.strftime("%Y-%m-%d")
279
+ else:
280
+ # Date and time without seconds for hourly resolution
281
+ return dt.strftime("%Y-%m-%dT%H:%M")
@@ -1,13 +1,15 @@
1
1
  # Copyright Modal Labs 2025
2
2
  import builtins
3
3
  from dataclasses import dataclass
4
- from datetime import datetime
4
+ from datetime import datetime, timezone
5
5
  from typing import Literal, Optional
6
6
 
7
7
  from google.protobuf.empty_pb2 import Empty
8
8
 
9
+ from modal.exception import InvalidError
9
10
  from modal_proto import api_pb2
10
11
 
12
+ from ._billing import BILLING_DOCSTRING, BillingReportItem
11
13
  from ._load_context import LoadContext
12
14
  from ._object import _Object
13
15
  from ._resolver import Resolver
@@ -114,3 +116,76 @@ class _Workspace(_Object, type_prefix="ac"):
114
116
  hydrate_lazily=True,
115
117
  load_context_overrides=LoadContext(client=client),
116
118
  )
119
+
120
+ @property
121
+ def billing(self) -> "_WorkspaceBillingManager":
122
+ return _WorkspaceBillingManager(self)
123
+
124
+
125
+ class _WorkspaceBillingManager:
126
+ """mdmd:namespace
127
+ Namespace for Workspace billing APIs
128
+ """
129
+
130
+ def __init__(self, workspace: _Workspace):
131
+ """mdmd:hidden"""
132
+ self._workspace = workspace
133
+
134
+ async def report(
135
+ self,
136
+ *,
137
+ start: datetime, # Start of the report, inclusive
138
+ end: datetime | None = None, # End of the report, exclusive
139
+ resolution: str = "d", # Resolution, e.g. "d" for daily or "h" for hourly
140
+ tag_names: list[str] | None = None, # Optional additional metadata to include
141
+ ) -> list[BillingReportItem]:
142
+ (
143
+ """Return a report of workspace usage by object and time.
144
+
145
+ The result will be a list of dataclasses for each interval (determined by `resolution`)
146
+ between the `start` and `end` limits. Each item represents a single (Modal object, time interval)
147
+ pair that billing can be attributed to (e.g., an App) along with metadata (including user-defined
148
+ tags) to identify that object. The dataclass also contains a breakdown of the cost value
149
+ attributed to individual resources (for an App, this can be CPU, Memory, specific GPU types,
150
+ etc.). The specific resource types included in the breakdown are subject to change as
151
+ Modal's billing model evolves.
152
+
153
+ It's also possible to generate reports using the
154
+ [`modal billing report`](https://modal.com/docs/cli/latest/billing#modal-billing-report)
155
+ CLI command. The CLI has a few convenience features for generating reports across relative
156
+ time ranges.
157
+
158
+ """
159
+ + BILLING_DOCSTRING
160
+ )
161
+
162
+ if tag_names is None:
163
+ tag_names = []
164
+
165
+ if end is None:
166
+ end = datetime.now(timezone.utc)
167
+
168
+ if start.tzinfo is None:
169
+ start = start.replace(tzinfo=timezone.utc)
170
+ elif start.tzinfo != timezone.utc:
171
+ raise InvalidError("Timezone-aware 'start' parameter must be in UTC.")
172
+
173
+ if end.tzinfo is None:
174
+ end = end.replace(tzinfo=timezone.utc)
175
+ elif end.tzinfo != timezone.utc:
176
+ raise InvalidError("Timezone-aware 'end' parameter must be in UTC.")
177
+
178
+ if not self._workspace.is_hydrated:
179
+ await self._workspace.hydrate()
180
+
181
+ request = api_pb2.WorkspaceBillingReportRequest(
182
+ resolution=resolution,
183
+ tag_names=tag_names,
184
+ )
185
+ request.start_timestamp.FromDatetime(start)
186
+ request.end_timestamp.FromDatetime(end)
187
+
188
+ return [
189
+ BillingReportItem._from_proto(pb_item)
190
+ async for pb_item in self._workspace.client.stub.WorkspaceBillingReport.unary_stream(request)
191
+ ]
@@ -710,7 +710,7 @@ class _App:
710
710
  Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
711
711
  those functions are executed remotely directly.
712
712
 
713
- Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
713
+ Note that an explicit [`app.run()`](https://modal.com/docs/sdk/py/latest/modal.App#run) is not needed, as an
714
714
  [app](https://modal.com/docs/guide/apps) is automatically created for you.
715
715
 
716
716
  Args:
@@ -1066,7 +1066,7 @@ class _App:
1066
1066
  max_inputs: int | None = None,
1067
1067
  ) -> Callable[[CLS_T | _PartialFunction], CLS_T]:
1068
1068
  """
1069
- Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App.
1069
+ Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/modal.Cls) with this App.
1070
1070
 
1071
1071
  Args:
1072
1072
  image: The image to run as the container for the class service.
@@ -1290,6 +1290,7 @@ class _App:
1290
1290
  scaleup_window: int | None = None, # Stabilization window (seconds) of sustained demand before scaling up
1291
1291
  scaledown_window: int | None = None, # Max idle time before scaling down (seconds)
1292
1292
  proxy: _Proxy | None = None, # Modal Proxy to use in front of this server
1293
+ unauthenticated: bool = False, # Whether the endpoint requires proxy authentication, required by default.
1293
1294
  port: int = 8000, # Port the HTTP server listens on
1294
1295
  startup_timeout: int = 30, # Maximum startup time in seconds
1295
1296
  exit_grace_period: int = 0, # Grace period for in-flight requests on shutdown
@@ -1334,10 +1335,11 @@ class _App:
1334
1335
  scaledown_window: Max idle time before scaling down (seconds).
1335
1336
  proxy: Modal Proxy to use in front of this server.
1336
1337
  port: Port the HTTP server listens on.
1338
+ unauthenticated: Whether the endpoint requires proxy authentication, required by default.
1337
1339
  startup_timeout: Maximum startup time in seconds.
1338
1340
  exit_grace_period: Grace period for in-flight requests on shutdown.
1339
1341
  routing_region: Region to route Server requests through.
1340
- h2_enabled: Enable HTTP/2 in the container.
1342
+ h2_enabled: Enable HTTP/2.
1341
1343
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1342
1344
  cloud: Cloud provider (aws, gcp, oci, auto).
1343
1345
  region: Region(s) to run on.
@@ -1385,6 +1387,7 @@ class _App:
1385
1387
  startup_timeout=startup_timeout,
1386
1388
  exit_grace_period=exit_grace_period,
1387
1389
  h2_enabled=h2_enabled,
1390
+ unauthenticated=unauthenticated,
1388
1391
  )
1389
1392
 
1390
1393
  # Build secrets list
@@ -494,7 +494,7 @@ class _App:
494
494
  Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
495
495
  those functions are executed remotely directly.
496
496
 
497
- Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
497
+ Note that an explicit [`app.run()`](https://modal.com/docs/sdk/py/latest/modal.App#run) is not needed, as an
498
498
  [app](https://modal.com/docs/guide/apps) is automatically created for you.
499
499
 
500
500
  Args:
@@ -683,7 +683,7 @@ class _App:
683
683
  _experimental_restrict_output: bool = False,
684
684
  max_inputs: typing.Optional[int] = None,
685
685
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
686
- """Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App.
686
+ """Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/modal.Cls) with this App.
687
687
 
688
688
  Args:
689
689
  image: The image to run as the container for the class service.
@@ -751,6 +751,7 @@ class _App:
751
751
  scaleup_window: typing.Optional[int] = None,
752
752
  scaledown_window: typing.Optional[int] = None,
753
753
  proxy: typing.Optional[modal.proxy._Proxy] = None,
754
+ unauthenticated: bool = False,
754
755
  port: int = 8000,
755
756
  startup_timeout: int = 30,
756
757
  exit_grace_period: int = 0,
@@ -795,10 +796,11 @@ class _App:
795
796
  scaledown_window: Max idle time before scaling down (seconds).
796
797
  proxy: Modal Proxy to use in front of this server.
797
798
  port: Port the HTTP server listens on.
799
+ unauthenticated: Whether the endpoint requires proxy authentication, required by default.
798
800
  startup_timeout: Maximum startup time in seconds.
799
801
  exit_grace_period: Grace period for in-flight requests on shutdown.
800
802
  routing_region: Region to route Server requests through.
801
- h2_enabled: Enable HTTP/2 in the container.
803
+ h2_enabled: Enable HTTP/2.
802
804
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
803
805
  cloud: Cloud provider (aws, gcp, oci, auto).
804
806
  region: Region(s) to run on.
@@ -1495,7 +1497,7 @@ class App:
1495
1497
  Modal functions can also be used as CLI entrypoints, but unlike `local_entrypoint`,
1496
1498
  those functions are executed remotely directly.
1497
1499
 
1498
- Note that an explicit [`app.run()`](https://modal.com/docs/reference/modal.App#run) is not needed, as an
1500
+ Note that an explicit [`app.run()`](https://modal.com/docs/sdk/py/latest/modal.App#run) is not needed, as an
1499
1501
  [app](https://modal.com/docs/guide/apps) is automatically created for you.
1500
1502
 
1501
1503
  Args:
@@ -1684,7 +1686,7 @@ class App:
1684
1686
  _experimental_restrict_output: bool = False,
1685
1687
  max_inputs: typing.Optional[int] = None,
1686
1688
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
1687
- """Decorator to register a new Modal [Cls](https://modal.com/docs/reference/modal.Cls) with this App.
1689
+ """Decorator to register a new Modal [Cls](https://modal.com/docs/sdk/py/latest/modal.Cls) with this App.
1688
1690
 
1689
1691
  Args:
1690
1692
  image: The image to run as the container for the class service.
@@ -1752,6 +1754,7 @@ class App:
1752
1754
  scaleup_window: typing.Optional[int] = None,
1753
1755
  scaledown_window: typing.Optional[int] = None,
1754
1756
  proxy: typing.Optional[modal.proxy.Proxy] = None,
1757
+ unauthenticated: bool = False,
1755
1758
  port: int = 8000,
1756
1759
  startup_timeout: int = 30,
1757
1760
  exit_grace_period: int = 0,
@@ -1794,10 +1797,11 @@ class App:
1794
1797
  scaledown_window: Max idle time before scaling down (seconds).
1795
1798
  proxy: Modal Proxy to use in front of this server.
1796
1799
  port: Port the HTTP server listens on.
1800
+ unauthenticated: Whether the endpoint requires proxy authentication, required by default.
1797
1801
  startup_timeout: Maximum startup time in seconds.
1798
1802
  exit_grace_period: Grace period for in-flight requests on shutdown.
1799
1803
  routing_region: Region to route Server requests through.
1800
- h2_enabled: Enable HTTP/2 in the container.
1804
+ h2_enabled: Enable HTTP/2.
1801
1805
  target_concurrency: Target concurrency for the server; 0 disables autoscaling.
1802
1806
  cloud: Cloud provider (aws, gcp, oci, auto).
1803
1807
  region: Region(s) to run on.
@@ -1,10 +1,11 @@
1
1
  # Copyright Modal Labs 2025
2
- from ._billing import WorkspaceBillingReportItem, _workspace_billing_report
2
+ from ._billing import BillingReportItem, WorkspaceBillingReportItem, _workspace_billing_report
3
3
  from ._utils.async_utils import synchronize_api
4
4
 
5
- workspace_billing_report = synchronize_api(_workspace_billing_report)
5
+ workspace_billing_report = synchronize_api(_workspace_billing_report, target_module=__name__)
6
6
 
7
7
  __all__ = [
8
8
  "workspace_billing_report",
9
9
  "WorkspaceBillingReportItem",
10
+ "BillingReportItem",
10
11
  ]