modal 1.4.4.dev14__tar.gz → 1.4.4.dev16__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 (224) hide show
  1. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/PKG-INFO +1 -1
  2. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_container_entrypoint.py +158 -95
  3. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_functions.py +1 -1
  4. modal-1.4.4.dev14/modal/image.py → modal-1.4.4.dev16/modal/_image.py +17 -21
  5. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/execution_context.py +3 -2
  6. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/task_lifecycle_manager.py +24 -1
  7. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/task_lifecycle_manager.pyi +28 -4
  8. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/app.py +1 -1
  9. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/app.pyi +9 -8
  10. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/entry_point.py +2 -0
  11. modal-1.4.4.dev16/modal/cli/skills.py +310 -0
  12. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/client.pyi +2 -2
  13. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/__init__.py +6 -6
  14. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/functions.pyi +6 -6
  15. modal-1.4.4.dev16/modal/image.py +5 -0
  16. modal-1.4.4.dev16/modal/image.pyi +1368 -0
  17. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox.py +1 -1
  18. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox.pyi +11 -10
  19. modal-1.4.4.dev16/modal/skills/modal/SKILL.md +86 -0
  20. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/PKG-INFO +1 -1
  21. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/SOURCES.txt +3 -0
  22. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2.py +1010 -1010
  23. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2.pyi +39 -5
  24. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_version/__init__.py +1 -1
  25. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/pyproject.toml +1 -1
  26. modal-1.4.4.dev14/modal/image.pyi +0 -2748
  27. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/LICENSE +0 -0
  28. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/README.md +0 -0
  29. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/__init__.py +0 -0
  30. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/__main__.py +0 -0
  31. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_billing.py +0 -0
  32. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_clustered_functions.py +0 -0
  33. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_clustered_functions.pyi +0 -0
  34. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_environments.py +0 -0
  35. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_function_variants.py +0 -0
  36. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_grpc_client.py +0 -0
  37. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_ipython.py +0 -0
  38. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_load_context.py +0 -0
  39. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_location.py +0 -0
  40. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_logs.py +0 -0
  41. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_object.py +0 -0
  42. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/__init__.py +0 -0
  43. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/manager.py +0 -0
  44. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/pty.py +0 -0
  45. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/rich.py +0 -0
  46. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_output/status.py +0 -0
  47. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_partial_function.py +0 -0
  48. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_resolver.py +0 -0
  49. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_resources.py +0 -0
  50. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/__init__.py +0 -0
  51. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/asgi.py +0 -0
  52. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/container_io_manager.py +0 -0
  53. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/container_io_manager.pyi +0 -0
  54. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/execution_context.pyi +0 -0
  55. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  56. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/telemetry.py +0 -0
  57. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/user_code_event_loop.py +0 -0
  58. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_runtime/user_code_imports.py +0 -0
  59. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_serialization.py +0 -0
  60. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_server.py +0 -0
  61. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_traceback.py +0 -0
  62. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_tunnel.py +0 -0
  63. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_tunnel.pyi +0 -0
  64. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_type_manager.py +0 -0
  65. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/__init__.py +0 -0
  66. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/app_utils.py +0 -0
  67. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/async_utils.py +0 -0
  68. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/auth_token_manager.py +0 -0
  69. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/blob_utils.py +0 -0
  70. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/browser_utils.py +0 -0
  71. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/bytes_io_segment_payload.py +0 -0
  72. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/deprecation.py +0 -0
  73. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/docker_utils.py +0 -0
  74. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/function_utils.py +0 -0
  75. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/git_utils.py +0 -0
  76. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/grpc_testing.py +0 -0
  77. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/grpc_utils.py +0 -0
  78. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/hash_utils.py +0 -0
  79. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/http_utils.py +0 -0
  80. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/jwt_utils.py +0 -0
  81. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/logger.py +0 -0
  82. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/mount_utils.py +0 -0
  83. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/name_utils.py +0 -0
  84. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/package_utils.py +0 -0
  85. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/pattern_utils.py +0 -0
  86. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/rand_pb_testing.py +0 -0
  87. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/sandbox_fs_utils.py +0 -0
  88. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/shell_utils.py +0 -0
  89. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/task_command_router_client.py +0 -0
  90. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_utils/time_utils.py +0 -0
  91. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/__init__.py +0 -0
  92. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  93. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/cloudpickle.py +0 -0
  94. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/tblib.py +0 -0
  95. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_vendor/version.py +0 -0
  96. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_watcher.py +0 -0
  97. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/_workspace.py +0 -0
  98. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/billing.py +0 -0
  99. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2023.12.312.txt +0 -0
  100. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2023.12.txt +0 -0
  101. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2024.04.txt +0 -0
  102. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2024.10.txt +0 -0
  103. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/2025.06.txt +0 -0
  104. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/PREVIEW.txt +0 -0
  105. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/README.md +0 -0
  106. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/builder/base-images.json +0 -0
  107. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/call_graph.py +0 -0
  108. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/__init__.py +0 -0
  109. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_download.py +0 -0
  110. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_help.py +0 -0
  111. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/_traceback.py +0 -0
  112. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/app.py +0 -0
  113. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/billing.py +0 -0
  114. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/bootstrap.py +0 -0
  115. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/changelog.py +0 -0
  116. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/cluster.py +0 -0
  117. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/config.py +0 -0
  118. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/container.py +0 -0
  119. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/dashboard.py +0 -0
  120. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/dict.py +0 -0
  121. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/environment.py +0 -0
  122. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/import_refs.py +0 -0
  123. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/launch.py +0 -0
  124. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/logo.py +0 -0
  125. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/network_file_system.py +0 -0
  126. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/profile.py +0 -0
  127. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/__init__.py +0 -0
  128. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/run_jupyter.py +0 -0
  129. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/programs/vscode.py +0 -0
  130. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/queues.py +0 -0
  131. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/run.py +0 -0
  132. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/secret.py +0 -0
  133. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/selector.py +0 -0
  134. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/shell.py +0 -0
  135. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/token.py +0 -0
  136. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/utils.py +0 -0
  137. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cli/volume.py +0 -0
  138. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/client.py +0 -0
  139. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cloud_bucket_mount.py +0 -0
  140. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cloud_bucket_mount.pyi +0 -0
  141. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cls.py +0 -0
  142. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/cls.pyi +0 -0
  143. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/config.py +0 -0
  144. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/container_process.py +0 -0
  145. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/container_process.pyi +0 -0
  146. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/dict.py +0 -0
  147. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/dict.pyi +0 -0
  148. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/environments.py +0 -0
  149. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/environments.pyi +0 -0
  150. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/exception.py +0 -0
  151. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/flash.py +0 -0
  152. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/flash.pyi +0 -0
  153. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/experimental/ipython.py +0 -0
  154. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_io.py +0 -0
  155. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_io.pyi +0 -0
  156. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/file_pattern_matcher.py +0 -0
  157. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/functions.py +0 -0
  158. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/io_streams.py +0 -0
  159. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/io_streams.pyi +0 -0
  160. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/mount.py +0 -0
  161. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/mount.pyi +0 -0
  162. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/network_file_system.py +0 -0
  163. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/network_file_system.pyi +0 -0
  164. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/object.py +0 -0
  165. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/object.pyi +0 -0
  166. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/output.py +0 -0
  167. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/parallel_map.py +0 -0
  168. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/parallel_map.pyi +0 -0
  169. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/partial_function.py +0 -0
  170. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/partial_function.pyi +0 -0
  171. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/proxy.py +0 -0
  172. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/proxy.pyi +0 -0
  173. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/py.typed +0 -0
  174. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/queue.py +0 -0
  175. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/queue.pyi +0 -0
  176. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/retries.py +0 -0
  177. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/runner.py +0 -0
  178. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/runner.pyi +0 -0
  179. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/running_app.py +0 -0
  180. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox_fs.py +0 -0
  181. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/sandbox_fs.pyi +0 -0
  182. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/schedule.py +0 -0
  183. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/scheduler_placement.py +0 -0
  184. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/secret.py +0 -0
  185. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/secret.pyi +0 -0
  186. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/server.py +0 -0
  187. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/server.pyi +0 -0
  188. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/serving.py +0 -0
  189. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/serving.pyi +0 -0
  190. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/snapshot.py +0 -0
  191. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/snapshot.pyi +0 -0
  192. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/stream_type.py +0 -0
  193. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/token_flow.py +0 -0
  194. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/token_flow.pyi +0 -0
  195. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/volume.py +0 -0
  196. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/volume.pyi +0 -0
  197. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/workspace.py +0 -0
  198. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal/workspace.pyi +0 -0
  199. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/dependency_links.txt +0 -0
  200. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/entry_points.txt +0 -0
  201. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/requires.txt +0 -0
  202. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal.egg-info/top_level.txt +0 -0
  203. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/__init__.py +0 -0
  204. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_cli_docs.py +0 -0
  205. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_cli_docs_main.py +0 -0
  206. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_reference_docs.py +0 -0
  207. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/gen_reference_docs_main.py +0 -0
  208. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/__init__.py +0 -0
  209. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/mdmd.py +0 -0
  210. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/signatures.py +0 -0
  211. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_docs/mdmd/types.py +0 -0
  212. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/__init__.py +0 -0
  213. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_grpc.py +0 -0
  214. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2_grpc.py +0 -0
  215. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/api_pb2_grpc.pyi +0 -0
  216. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/modal_api_grpc.py +0 -0
  217. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/py.typed +0 -0
  218. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_grpc.py +0 -0
  219. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2.py +0 -0
  220. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2.pyi +0 -0
  221. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  222. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  223. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/modal_version/__main__.py +0 -0
  224. {modal-1.4.4.dev14 → modal-1.4.4.dev16}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev14
3
+ Version: 1.4.4.dev16
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -14,7 +14,6 @@ if telemetry_socket:
14
14
  from ._runtime.telemetry import instrument_imports
15
15
 
16
16
  instrument_imports(telemetry_socket)
17
-
18
17
  import asyncio
19
18
  import queue
20
19
  import signal
@@ -41,10 +40,11 @@ from ._runtime.container_io_manager import (
41
40
  ContainerIOManager,
42
41
  IOContext,
43
42
  )
44
- from ._runtime.task_lifecycle_manager import UserException
43
+ from ._runtime.task_lifecycle_manager import TaskLifecycleManager, UserException
45
44
 
46
45
  if TYPE_CHECKING:
47
46
  import modal._runtime.container_io_manager
47
+ import modal._runtime.task_lifecycle_manager
48
48
  import modal._runtime.user_code_imports
49
49
 
50
50
 
@@ -287,12 +287,146 @@ def get_serialized_user_class_and_function(
287
287
  return ser_usr_cls, ser_fun
288
288
 
289
289
 
290
- def main(container_args: api_pb2.ContainerArguments, client: Client):
291
- # This is a bit weird but we need both the blocking and async versions of ContainerIOManager.
292
- # At some point, we should fix that by having built-in support for running "user code"
293
- container_io_manager = ContainerIOManager(container_args, client)
290
+ def get_server_service(
291
+ container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
292
+ ) -> Service:
293
+ ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
294
+ service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
295
+ service_function_hydration_data = [
296
+ o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
297
+ ][0]
298
+ service = import_server_service(
299
+ function_def,
300
+ service_function_hydration_data,
301
+ client,
302
+ ser_usr_cls,
303
+ )
304
+ return service
305
+
306
+
307
+ def get_class_service(
308
+ container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
309
+ ) -> Service:
310
+ ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
311
+ if container_args.serialized_params:
312
+ param_args, param_kwargs = deserialize_params(container_args.serialized_params, function_def, _client)
313
+ else:
314
+ param_args = ()
315
+ param_kwargs = {}
316
+ # this is a bit ugly - match the function and class based on function name to get metadata
317
+ # This metadata is required in order to hydrate the class in case it's not globally
318
+ # decorated (or serialized)
319
+ service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
320
+ service_function_hydration_data = [
321
+ o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
322
+ ][0]
323
+ class_id = container_args.app_layout.class_ids[function_def.function_name.removesuffix(".*")]
324
+
325
+ service = import_class_service(
326
+ function_def,
327
+ service_function_hydration_data,
328
+ class_id,
329
+ client,
330
+ ser_usr_cls,
331
+ param_args,
332
+ param_kwargs,
333
+ )
334
+ return service
335
+
336
+
337
+ def get_function_service(function_def: api_pb2.Function, _client: _Client) -> Service:
338
+ ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
339
+ assert ser_usr_cls is None
340
+ service = import_single_function_service(
341
+ function_def,
342
+ ser_fun,
343
+ )
344
+ return service
345
+
346
+
347
+ def hydrate_function(
348
+ container_args: api_pb2.ContainerArguments,
349
+ task_lifecycle_manager: TaskLifecycleManager,
350
+ function_def: api_pb2.Function,
351
+ _client: _Client,
352
+ client: Client,
353
+ ) -> Service:
294
354
  active_app: _App
295
355
  service: Service
356
+ with task_lifecycle_manager.handle_task_lifecycle_exception():
357
+ with execution_context._import_context():
358
+ if function_def.is_server:
359
+ service = get_server_service(container_args, function_def, _client, client)
360
+ elif function_def.is_class:
361
+ service = get_class_service(container_args, function_def, _client, client)
362
+ else:
363
+ service = get_function_service(function_def, _client)
364
+
365
+ active_app = service.app
366
+
367
+ # Get ids and metadata for objects (primarily functions and classes) on the app
368
+ container_app: RunningApp = running_app_from_layout(container_args.app_id, container_args.app_layout)
369
+
370
+ # Initialize objects on the app.
371
+ # This is basically only functions and classes - anything else is deprecated and will be unsupported soon
372
+ app: App = cast(App, synchronizer._translate_out(active_app))
373
+ app._init_container(client, container_app)
374
+
375
+ # Hydrate all function dependencies.
376
+ # TODO(erikbern): we an remove this once we
377
+ # 1. Enable lazy hydration for all objects
378
+ # 2. Fully deprecate .new() objects
379
+ if service.service_deps is not None: # this is not set for serialized or non-global scope functions
380
+ dep_object_ids: list[str] = [dep.object_id for dep in function_def.object_dependencies]
381
+ if len(service.service_deps) != len(dep_object_ids):
382
+ raise ExecutionError(
383
+ f"Function has {len(service.service_deps)} dependencies"
384
+ f" but container got {len(dep_object_ids)} object ids.\n"
385
+ f"Code deps: {service.service_deps}\n"
386
+ f"Object ids: {dep_object_ids}\n"
387
+ "\n"
388
+ "This can happen if you are defining Modal objects under a conditional statement "
389
+ "that evaluates differently in the local and remote environments."
390
+ )
391
+ for object_id, obj in zip(dep_object_ids, service.service_deps):
392
+ metadata: Message | None = container_app.object_handle_metadata[object_id]
393
+ obj._hydrate(object_id, _client, metadata)
394
+
395
+ return service
396
+
397
+
398
+ def run_server(container_args: api_pb2.ContainerArguments, client: Client):
399
+ function_def = container_args.function_def
400
+ _client: _Client = cast(_Client, synchronizer._translate_in(client)) # TODO(erikbern): ugly
401
+ task_lifecycle_manager = TaskLifecycleManager(
402
+ container_args.task_id,
403
+ container_args.function_id,
404
+ container_args.function_def,
405
+ container_args.checkpoint_id or None,
406
+ client,
407
+ )
408
+
409
+ service: Service = hydrate_function(container_args, task_lifecycle_manager, function_def, _client, client)
410
+ # Initialize clustered functions.
411
+ if function_def._experimental_group_size > 0:
412
+ initialize_clustered_function(
413
+ client,
414
+ container_args.task_id,
415
+ function_def._experimental_group_size,
416
+ )
417
+
418
+ with UserCodeEventLoop() as event_loop:
419
+ with service.lifecycle_context(
420
+ event_loop,
421
+ task_lifecycle_manager=task_lifecycle_manager,
422
+ ):
423
+ call_server(event_loop)
424
+
425
+
426
+ def run_function(container_args: api_pb2.ContainerArguments, client: Client):
427
+ # This is a bit weird but we need both the blocking and async versions of ContainerIOManager.
428
+ # At some point, we should fix that by having built-in support for running "user code"
429
+ container_io_manager: Any = ContainerIOManager(container_args, client)
296
430
  function_def = container_args.function_def
297
431
  # The worker sets this flag to "1" for snapshot and restore tasks. Otherwise, this flag is unset,
298
432
  # in which case snapshots should be disabled.
@@ -306,92 +440,17 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
306
440
  container_io_manager.hello()
307
441
 
308
442
  with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
309
- # If this is a serialized function, fetch the definition from the server
310
- ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
311
-
312
443
  # Initialize the function, importing user code.
313
- with container_io_manager.get_task_lifecycle_manager().handle_task_lifecycle_exception():
314
- if container_args.serialized_params:
315
- param_args, param_kwargs = deserialize_params(container_args.serialized_params, function_def, _client)
316
- else:
317
- param_args = ()
318
- param_kwargs = {}
319
-
320
- with execution_context._import_context():
321
- if function_def.is_server:
322
- service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
323
- service_function_hydration_data = [
324
- o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
325
- ][0]
326
- service = import_server_service(
327
- function_def,
328
- service_function_hydration_data,
329
- client,
330
- ser_usr_cls,
331
- )
332
- elif function_def.is_class:
333
- # this is a bit ugly - match the function and class based on function name to get metadata
334
- # This metadata is required in order to hydrate the class in case it's not globally
335
- # decorated (or serialized)
336
- service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
337
- service_function_hydration_data = [
338
- o for o in container_args.app_layout.objects if o.object_id == service_base_function_id
339
- ][0]
340
- class_id = container_args.app_layout.class_ids[function_def.function_name.removesuffix(".*")]
341
-
342
- service = import_class_service(
343
- function_def,
344
- service_function_hydration_data,
345
- class_id,
346
- client,
347
- ser_usr_cls,
348
- param_args,
349
- param_kwargs,
350
- )
351
- else:
352
- assert ser_usr_cls is None
353
- service = import_single_function_service(
354
- function_def,
355
- ser_fun,
356
- )
357
-
358
- active_app = service.app
359
-
360
- if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
361
- # Concurrency and batching doesn't apply for `modal shell`.
362
- batch_max_size = 0
363
- batch_wait_ms = 0
364
- else:
365
- batch_max_size = function_def.batch_max_size or 0
366
- batch_wait_ms = function_def.batch_linger_ms or 0
367
-
368
- # Get ids and metadata for objects (primarily functions and classes) on the app
369
- container_app: RunningApp = running_app_from_layout(container_args.app_id, container_args.app_layout)
370
-
371
- # Initialize objects on the app.
372
- # This is basically only functions and classes - anything else is deprecated and will be unsupported soon
373
- app: App = cast(App, synchronizer._translate_out(active_app))
374
- app._init_container(client, container_app)
375
-
376
- # Hydrate all function dependencies.
377
- # TODO(erikbern): we an remove this once we
378
- # 1. Enable lazy hydration for all objects
379
- # 2. Fully deprecate .new() objects
380
- if service.service_deps is not None: # this is not set for serialized or non-global scope functions
381
- dep_object_ids: list[str] = [dep.object_id for dep in function_def.object_dependencies]
382
- if len(service.service_deps) != len(dep_object_ids):
383
- raise ExecutionError(
384
- f"Function has {len(service.service_deps)} dependencies"
385
- f" but container got {len(dep_object_ids)} object ids.\n"
386
- f"Code deps: {service.service_deps}\n"
387
- f"Object ids: {dep_object_ids}\n"
388
- "\n"
389
- "This can happen if you are defining Modal objects under a conditional statement "
390
- "that evaluates differently in the local and remote environments."
391
- )
392
- for object_id, obj in zip(dep_object_ids, service.service_deps):
393
- metadata: Message | None = container_app.object_handle_metadata[object_id]
394
- obj._hydrate(object_id, _client, metadata)
444
+ service: Service = hydrate_function(
445
+ container_args, container_io_manager.get_task_lifecycle_manager(), function_def, _client, client
446
+ )
447
+ if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
448
+ # Concurrency and batching doesn't apply for `modal shell`.
449
+ batch_max_size = 0
450
+ batch_wait_ms = 0
451
+ else:
452
+ batch_max_size = function_def.batch_max_size or 0
453
+ batch_wait_ms = function_def.batch_linger_ms or 0
395
454
 
396
455
  # Initialize clustered functions.
397
456
  if function_def._experimental_group_size > 0:
@@ -407,10 +466,14 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
407
466
  ) as finalized_functions:
408
467
  # This context managers handles pre/post snapshot lifecycle of the user code,
409
468
  # finalized functions, ASGI/WSGI lifespan, and volume commit.
410
- if function_def.is_server:
411
- call_server(event_loop)
412
- else:
413
- call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
469
+ call_function(event_loop, container_io_manager, finalized_functions, batch_max_size, batch_wait_ms)
470
+
471
+
472
+ def main(container_args: api_pb2.ContainerArguments, client: Client):
473
+ if container_args.function_def.is_server:
474
+ run_server(container_args, client)
475
+ else:
476
+ run_function(container_args, client)
414
477
 
415
478
 
416
479
  if __name__ == "__main__":
@@ -19,6 +19,7 @@ from modal_proto import api_pb2
19
19
  from modal_proto.modal_api_grpc import ModalClientModal
20
20
 
21
21
  from ._function_variants import _FunctionOptions, _make_function_variant
22
+ from ._image import _Image
22
23
  from ._load_context import LoadContext
23
24
  from ._object import _Object, live_method, live_method_gen
24
25
  from ._output.pty import get_pty_info
@@ -65,7 +66,6 @@ from .exception import (
65
66
  NotFoundError,
66
67
  OutputExpiredError,
67
68
  )
68
- from .image import _Image
69
69
  from .mount import _get_client_mount, _Mount
70
70
  from .network_file_system import _NetworkFileSystem, network_file_system_mount_protos
71
71
  from .output import OutputManager
@@ -8,7 +8,7 @@ import sys
8
8
  import typing
9
9
  import warnings
10
10
  from collections.abc import Callable, Collection, Sequence
11
- from dataclasses import dataclass
11
+ from dataclasses import dataclass, field
12
12
  from inspect import isfunction
13
13
  from pathlib import Path, PurePosixPath
14
14
  from typing import (
@@ -32,7 +32,7 @@ from ._load_context import LoadContext
32
32
  from ._object import _Object, live_method_gen
33
33
  from ._resolver import Resolver
34
34
  from ._serialization import get_preferred_payload_format, serialize
35
- from ._utils.async_utils import TaskContext, deprecate_aio_usage, synchronize_api, synchronizer
35
+ from ._utils.async_utils import TaskContext, deprecate_aio_usage, synchronizer
36
36
  from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
37
37
  from ._utils.docker_utils import (
38
38
  extract_copy_command_patterns,
@@ -362,9 +362,8 @@ class _ImageRegistryConfig:
362
362
 
363
363
  @dataclass
364
364
  class DockerfileSpec:
365
- # Ideally we would use field() with default_factory=, but doesn't work with synchronicity type-stub gen
366
- commands: list[str]
367
- context_files: dict[str, str]
365
+ commands: list[str] = field(default_factory=list)
366
+ context_files: dict[str, str] = field(default_factory=dict)
368
367
 
369
368
 
370
369
  async def _image_await_build_result(image_id: str, client: _Client) -> api_pb2.ImageJoinStreamingResponse:
@@ -538,7 +537,7 @@ class _Image(_Object, type_prefix="im"):
538
537
  raise InvalidError("Cannot run a build function with multiple base images!")
539
538
 
540
539
  def _deps() -> Sequence[_Object]:
541
- deps = tuple(base_images.values()) + tuple(secrets)
540
+ deps: tuple[_Object, ...] = tuple(base_images.values()) + tuple(secrets)
542
541
  if build_function:
543
542
  deps += (build_function,)
544
543
  if image_registry_config and image_registry_config.secret:
@@ -670,10 +669,10 @@ class _Image(_Object, type_prefix="im"):
670
669
  else:
671
670
  # not built or in the process of building - wait for build
672
671
  logger.debug("Waiting for image %s" % image_id)
673
- resp = await _image_await_build_result(image_id, load_context.client)
674
- result = resp.result
675
- if resp.HasField("metadata"):
676
- metadata = resp.metadata
672
+ build_resp = await _image_await_build_result(image_id, load_context.client)
673
+ result = build_resp.result
674
+ if build_resp.HasField("metadata"):
675
+ metadata = build_resp.metadata
677
676
 
678
677
  if result.status == api_pb2.GenericResult.GENERIC_STATUS_FAILURE:
679
678
  if result.exception:
@@ -701,7 +700,7 @@ class _Image(_Object, type_prefix="im"):
701
700
  raise RemoteError("Unknown status %s!" % result.status)
702
701
 
703
702
  self._hydrate(image_id, load_context.client, metadata)
704
- local_mounts = set()
703
+ local_mounts: set[_Mount] = set()
705
704
  for base in base_images.values():
706
705
  local_mounts |= base._serve_mounts
707
706
  if context_mount and context_mount.is_local():
@@ -1865,8 +1864,8 @@ class _Image(_Object, type_prefix="im"):
1865
1864
  """
1866
1865
  if not isinstance(entrypoint_commands, list) or not all(isinstance(x, str) for x in entrypoint_commands):
1867
1866
  raise InvalidError("entrypoint_commands must be a list of strings.")
1868
- args_str = _flatten_str_args("entrypoint", "entrypoint_commands", entrypoint_commands)
1869
- args_str = '"' + '", "'.join(args_str) + '"' if args_str else ""
1867
+ args = _flatten_str_args("entrypoint", "entrypoint_commands", entrypoint_commands)
1868
+ args_str = '"' + '", "'.join(args) + '"' if args else ""
1870
1869
  dockerfile_cmd = f"ENTRYPOINT [{args_str}]"
1871
1870
 
1872
1871
  return self.dockerfile_commands(dockerfile_cmd)
@@ -1885,8 +1884,8 @@ class _Image(_Object, type_prefix="im"):
1885
1884
  """
1886
1885
  if not isinstance(shell_commands, list) or not all(isinstance(x, str) for x in shell_commands):
1887
1886
  raise InvalidError("shell_commands must be a list of strings.")
1888
- args_str = _flatten_str_args("shell", "shell_commands", shell_commands)
1889
- args_str = '"' + '", "'.join(args_str) + '"' if args_str else ""
1887
+ args = _flatten_str_args("shell", "shell_commands", shell_commands)
1888
+ args_str = '"' + '", "'.join(args) + '"' if args else ""
1890
1889
  dockerfile_cmd = f"SHELL [{args_str}]"
1891
1890
 
1892
1891
  return self.dockerfile_commands(dockerfile_cmd)
@@ -2639,7 +2638,7 @@ class _Image(_Object, type_prefix="im"):
2639
2638
  function = _Function.from_local(
2640
2639
  info,
2641
2640
  app=None,
2642
- image=self, # type: ignore[reportArgumentType] # TODO: probably conflict with type stub?
2641
+ image=self,
2643
2642
  secrets=secrets,
2644
2643
  gpu=gpu,
2645
2644
  volumes=volumes,
@@ -2756,8 +2755,8 @@ class _Image(_Object, type_prefix="im"):
2756
2755
  if not isinstance(cmd, list) or not all(isinstance(x, str) for x in cmd):
2757
2756
  raise InvalidError("Image CMD must be a list of strings.")
2758
2757
 
2759
- cmd_str = _flatten_str_args("cmd", "cmd", cmd)
2760
- cmd_str = '"' + '", "'.join(cmd_str) + '"' if cmd_str else ""
2758
+ cmd_args = _flatten_str_args("cmd", "cmd", cmd)
2759
+ cmd_str = '"' + '", "'.join(cmd_args) + '"' if cmd_args else ""
2761
2760
  dockerfile_cmd = f"CMD [{cmd_str}]"
2762
2761
 
2763
2762
  return self.dockerfile_commands(dockerfile_cmd)
@@ -2849,6 +2848,3 @@ class _Image(_Object, type_prefix="im"):
2849
2848
  "Images cannot currently be hydrated on demand; you can build an Image by running an App that uses it."
2850
2849
  )
2851
2850
  return self
2852
-
2853
-
2854
- Image = synchronize_api(_Image)
@@ -7,6 +7,7 @@ from modal._utils.async_utils import synchronize_api
7
7
  from modal.exception import InvalidError
8
8
 
9
9
  from .container_io_manager import _ContainerIOManager
10
+ from .task_lifecycle_manager import _TaskLifecycleManager
10
11
 
11
12
 
12
13
  def is_local() -> bool:
@@ -18,7 +19,7 @@ def is_local() -> bool:
18
19
  even though those processes are running on Modal hardware.
19
20
 
20
21
  """
21
- return not _ContainerIOManager._singleton
22
+ return _TaskLifecycleManager._singleton is None
22
23
 
23
24
 
24
25
  async def _interact() -> None:
@@ -29,7 +30,7 @@ async def _interact() -> None:
29
30
  """
30
31
  container_io_manager = _ContainerIOManager._singleton
31
32
  if not container_io_manager:
32
- raise InvalidError("Interactivity only works inside a Modal container.")
33
+ raise InvalidError("Interactivity only works inside a Modal Function's container.")
33
34
  else:
34
35
  await container_io_manager.interact()
35
36
 
@@ -7,6 +7,10 @@ import sys
7
7
  import traceback
8
8
  from collections.abc import AsyncGenerator
9
9
  from pathlib import Path
10
+ from typing import (
11
+ ClassVar,
12
+ Optional,
13
+ )
10
14
 
11
15
  from synchronicity.async_wrap import asynccontextmanager
12
16
 
@@ -30,6 +34,8 @@ class UserException(Exception):
30
34
 
31
35
 
32
36
  class _TaskLifecycleManager:
37
+ _singleton: ClassVar[Optional["_TaskLifecycleManager"]] = None
38
+
33
39
  task_id: str
34
40
  function_id: str
35
41
  function_def: api_pb2.Function
@@ -37,7 +43,7 @@ class _TaskLifecycleManager:
37
43
  _client: _Client
38
44
  _cuda_checkpoint_session: gpu_memory_snapshot.CudaCheckpointSession | None
39
45
 
40
- def __init__(
46
+ def _init(
41
47
  self,
42
48
  task_id: str,
43
49
  function_id: str,
@@ -52,6 +58,23 @@ class _TaskLifecycleManager:
52
58
  self._client = client
53
59
  self._cuda_checkpoint_session = None
54
60
 
61
+ def __new__(
62
+ cls,
63
+ task_id: str,
64
+ function_id: str,
65
+ function_def: api_pb2.Function,
66
+ checkpoint_id: str | None,
67
+ client: _Client,
68
+ ) -> "_TaskLifecycleManager":
69
+ cls._singleton = super().__new__(cls)
70
+ cls._singleton._init(task_id, function_id, function_def, checkpoint_id, client)
71
+ return cls._singleton
72
+
73
+ @classmethod
74
+ def _reset_singleton(cls) -> None:
75
+ """Only used for tests."""
76
+ cls._singleton = None
77
+
55
78
  @asynccontextmanager
56
79
  async def handle_task_lifecycle_exception(
57
80
  self,
@@ -11,6 +11,7 @@ class UserException(Exception):
11
11
  ...
12
12
 
13
13
  class _TaskLifecycleManager:
14
+ _singleton: typing.ClassVar[typing.Optional[_TaskLifecycleManager]]
14
15
  task_id: str
15
16
  function_id: str
16
17
  function_def: modal_proto.api_pb2.Function
@@ -18,15 +19,29 @@ class _TaskLifecycleManager:
18
19
  _client: modal.client._Client
19
20
  _cuda_checkpoint_session: typing.Optional[modal._runtime.gpu_memory_snapshot.CudaCheckpointSession]
20
21
 
21
- def __init__(
22
+ def _init(
22
23
  self,
23
24
  task_id: str,
24
25
  function_id: str,
25
26
  function_def: modal_proto.api_pb2.Function,
26
27
  checkpoint_id: typing.Optional[str],
27
28
  client: modal.client._Client,
28
- ) -> None:
29
- """Initialize self. See help(type(self)) for accurate signature."""
29
+ ) -> None: ...
30
+ @staticmethod
31
+ def __new__(
32
+ cls,
33
+ task_id: str,
34
+ function_id: str,
35
+ function_def: modal_proto.api_pb2.Function,
36
+ checkpoint_id: typing.Optional[str],
37
+ client: modal.client._Client,
38
+ ) -> _TaskLifecycleManager:
39
+ """Create and return a new object. See help(type) for accurate signature."""
40
+ ...
41
+
42
+ @classmethod
43
+ def _reset_singleton(cls) -> None:
44
+ """Only used for tests."""
30
45
  ...
31
46
 
32
47
  def handle_task_lifecycle_exception(self) -> typing.AsyncContextManager[None]:
@@ -45,6 +60,7 @@ class _TaskLifecycleManager:
45
60
  ...
46
61
 
47
62
  class TaskLifecycleManager:
63
+ _singleton: typing.ClassVar[typing.Optional[TaskLifecycleManager]]
48
64
  task_id: str
49
65
  function_id: str
50
66
  function_def: modal_proto.api_pb2.Function
@@ -52,7 +68,11 @@ class TaskLifecycleManager:
52
68
  _client: modal.client.Client
53
69
  _cuda_checkpoint_session: typing.Optional[modal._runtime.gpu_memory_snapshot.CudaCheckpointSession]
54
70
 
55
- def __init__(
71
+ def __init__(self, /, *args, **kwargs):
72
+ """Initialize self. See help(type(self)) for accurate signature."""
73
+ ...
74
+
75
+ def _init(
56
76
  self,
57
77
  task_id: str,
58
78
  function_id: str,
@@ -60,6 +80,10 @@ class TaskLifecycleManager:
60
80
  checkpoint_id: typing.Optional[str],
61
81
  client: modal.client.Client,
62
82
  ) -> None: ...
83
+ @classmethod
84
+ def _reset_singleton(cls) -> None:
85
+ """Only used for tests."""
86
+ ...
63
87
 
64
88
  class __handle_task_lifecycle_exception_spec(typing_extensions.Protocol):
65
89
  def __call__(self, /) -> synchronicity.combined_types.AsyncAndBlockingContextManager[None]:
@@ -18,6 +18,7 @@ from synchronicity.async_wrap import asynccontextmanager
18
18
  from modal_proto import api_pb2
19
19
 
20
20
  from ._functions import _Function
21
+ from ._image import _Image
21
22
  from ._ipython import is_interactive_ipython
22
23
  from ._load_context import LoadContext
23
24
  from ._object import _get_environment_name, _Object
@@ -47,7 +48,6 @@ from .cls import _Cls, parameter
47
48
  from .config import logger
48
49
  from .exception import ExecutionError, InvalidError
49
50
  from .functions import Function
50
- from .image import _Image
51
51
  from .network_file_system import _NetworkFileSystem
52
52
  from .partial_function import PartialFunction
53
53
  from .proxy import _Proxy
@@ -1,5 +1,6 @@
1
1
  import collections.abc
2
2
  import modal._functions
3
+ import modal._image
3
4
  import modal._load_context
4
5
  import modal._partial_function
5
6
  import modal._server
@@ -80,7 +81,7 @@ class _LocalAppState:
80
81
 
81
82
  functions: dict[str, modal._functions._Function]
82
83
  classes: dict[str, modal.cls._Cls]
83
- image_default: typing.Optional[modal.image._Image]
84
+ image_default: typing.Optional[modal._image._Image]
84
85
  web_endpoints: list[str]
85
86
  local_entrypoints: dict[str, _LocalEntrypoint]
86
87
  tags: dict[str, str]
@@ -92,7 +93,7 @@ class _LocalAppState:
92
93
  self,
93
94
  functions: dict[str, modal._functions._Function],
94
95
  classes: dict[str, modal.cls._Cls],
95
- image_default: typing.Optional[modal.image._Image],
96
+ image_default: typing.Optional[modal._image._Image],
96
97
  web_endpoints: list[str],
97
98
  local_entrypoints: dict[str, _LocalEntrypoint],
98
99
  tags: dict[str, str],
@@ -163,7 +164,7 @@ class _App:
163
164
  name: typing.Optional[str] = None,
164
165
  *,
165
166
  tags: typing.Optional[dict[str, str]] = None,
166
- image: typing.Optional[modal.image._Image] = None,
167
+ image: typing.Optional[modal._image._Image] = None,
167
168
  secrets: collections.abc.Sequence[modal.secret._Secret] = [],
168
169
  volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume] = {},
169
170
  include_source: bool = True,
@@ -282,7 +283,7 @@ class _App:
282
283
 
283
284
  def _validate_blueprint_value(self, key: str, value: typing.Any): ...
284
285
  @property
285
- def image(self) -> modal.image._Image:
286
+ def image(self) -> modal._image._Image:
286
287
  """mdmd:hidden
287
288
  Retrieve the Image that will be used as the default for any Functions registered to the App.
288
289
 
@@ -546,7 +547,7 @@ class _App:
546
547
  self,
547
548
  _warn_parentheses_missing=None,
548
549
  *,
549
- image: typing.Optional[modal.image._Image] = None,
550
+ image: typing.Optional[modal._image._Image] = None,
550
551
  schedule: typing.Optional[modal.schedule.Schedule] = None,
551
552
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
552
553
  secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
@@ -645,7 +646,7 @@ class _App:
645
646
  self,
646
647
  _warn_parentheses_missing=None,
647
648
  *,
648
- image: typing.Optional[modal.image._Image] = None,
649
+ image: typing.Optional[modal._image._Image] = None,
649
650
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
650
651
  secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
651
652
  gpu: typing.Union[str, list[str], None] = None,
@@ -731,7 +732,7 @@ class _App:
731
732
  self,
732
733
  _warn_parentheses_missing=None,
733
734
  *,
734
- image: typing.Optional[modal.image._Image] = None,
735
+ image: typing.Optional[modal._image._Image] = None,
735
736
  env: typing.Optional[dict[str, typing.Optional[str]]] = None,
736
737
  secrets: typing.Optional[collections.abc.Collection[modal.secret._Secret]] = None,
737
738
  gpu: typing.Union[str, list[str], None] = None,
@@ -1948,4 +1949,4 @@ class App:
1948
1949
  """Only used for tests."""
1949
1950
  ...
1950
1951
 
1951
- _default_image: modal.image._Image
1952
+ _default_image: modal._image._Image