modal 1.4.4.dev16__tar.gz → 1.4.4.dev18__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 (223) hide show
  1. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/PKG-INFO +1 -1
  2. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_container_entrypoint.py +10 -12
  3. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_function_variants.py +43 -1
  4. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_functions.py +23 -1
  5. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_image.py +8 -3
  6. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_object.py +0 -18
  7. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/user_code_imports.py +2 -4
  8. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/grpc_utils.py +4 -1
  9. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/sandbox_fs_utils.py +47 -0
  10. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/billing.py +5 -1
  11. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/client.pyi +2 -2
  12. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cls.py +8 -0
  13. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cls.pyi +14 -0
  14. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/dict.py +0 -21
  15. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/dict.pyi +0 -56
  16. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/file_io.py +4 -19
  17. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/file_io.pyi +9 -34
  18. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/functions.pyi +21 -7
  19. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/object.pyi +0 -6
  20. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/queue.py +0 -18
  21. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/queue.pyi +0 -56
  22. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/sandbox.py +34 -17
  23. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/sandbox.pyi +35 -15
  24. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/sandbox_fs.py +141 -1
  25. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/sandbox_fs.pyi +196 -0
  26. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/secret.py +0 -18
  27. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/secret.pyi +0 -41
  28. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/volume.py +0 -54
  29. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/volume.pyi +0 -124
  30. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/PKG-INFO +1 -1
  31. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/api_grpc.py +48 -0
  32. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/api_pb2.py +621 -581
  33. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/api_pb2.pyi +83 -2
  34. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/api_pb2_grpc.py +99 -0
  35. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/api_pb2_grpc.pyi +30 -0
  36. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/modal_api_grpc.py +3 -0
  37. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_version/__init__.py +1 -1
  38. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/LICENSE +0 -0
  39. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/README.md +0 -0
  40. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/__init__.py +0 -0
  41. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/__main__.py +0 -0
  42. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_billing.py +0 -0
  43. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_clustered_functions.py +0 -0
  44. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_clustered_functions.pyi +0 -0
  45. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_environments.py +0 -0
  46. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_grpc_client.py +0 -0
  47. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_ipython.py +0 -0
  48. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_load_context.py +0 -0
  49. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_location.py +0 -0
  50. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_logs.py +0 -0
  51. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_output/__init__.py +0 -0
  52. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_output/manager.py +0 -0
  53. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_output/pty.py +0 -0
  54. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_output/rich.py +0 -0
  55. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_output/status.py +0 -0
  56. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_partial_function.py +0 -0
  57. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_resolver.py +0 -0
  58. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_resources.py +0 -0
  59. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/__init__.py +0 -0
  60. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/asgi.py +0 -0
  61. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/container_io_manager.py +0 -0
  62. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/container_io_manager.pyi +0 -0
  63. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/execution_context.py +0 -0
  64. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/execution_context.pyi +0 -0
  65. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  66. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/task_lifecycle_manager.py +0 -0
  67. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  68. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/telemetry.py +0 -0
  69. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_runtime/user_code_event_loop.py +0 -0
  70. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_serialization.py +0 -0
  71. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_server.py +0 -0
  72. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_traceback.py +0 -0
  73. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_tunnel.py +0 -0
  74. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_tunnel.pyi +0 -0
  75. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_type_manager.py +0 -0
  76. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/__init__.py +0 -0
  77. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/app_utils.py +0 -0
  78. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/async_utils.py +0 -0
  79. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/auth_token_manager.py +0 -0
  80. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/blob_utils.py +0 -0
  81. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/browser_utils.py +0 -0
  82. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/bytes_io_segment_payload.py +0 -0
  83. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/deprecation.py +0 -0
  84. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/docker_utils.py +0 -0
  85. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/function_utils.py +0 -0
  86. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/git_utils.py +0 -0
  87. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/grpc_testing.py +0 -0
  88. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/hash_utils.py +0 -0
  89. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/http_utils.py +0 -0
  90. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/jwt_utils.py +0 -0
  91. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/logger.py +0 -0
  92. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/mount_utils.py +0 -0
  93. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/name_utils.py +0 -0
  94. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/package_utils.py +0 -0
  95. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/pattern_utils.py +0 -0
  96. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/rand_pb_testing.py +0 -0
  97. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/shell_utils.py +0 -0
  98. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/task_command_router_client.py +0 -0
  99. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_utils/time_utils.py +0 -0
  100. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_vendor/__init__.py +0 -0
  101. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  102. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_vendor/cloudpickle.py +0 -0
  103. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_vendor/tblib.py +0 -0
  104. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_vendor/version.py +0 -0
  105. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_watcher.py +0 -0
  106. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/_workspace.py +0 -0
  107. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/app.py +0 -0
  108. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/app.pyi +0 -0
  109. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/2023.12.312.txt +0 -0
  110. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/2023.12.txt +0 -0
  111. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/2024.04.txt +0 -0
  112. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/2024.10.txt +0 -0
  113. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/2025.06.txt +0 -0
  114. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/PREVIEW.txt +0 -0
  115. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/README.md +0 -0
  116. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/builder/base-images.json +0 -0
  117. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/call_graph.py +0 -0
  118. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/__init__.py +0 -0
  119. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/_download.py +0 -0
  120. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/_help.py +0 -0
  121. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/_traceback.py +0 -0
  122. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/app.py +0 -0
  123. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/billing.py +0 -0
  124. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/bootstrap.py +0 -0
  125. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/changelog.py +0 -0
  126. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/cluster.py +0 -0
  127. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/config.py +0 -0
  128. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/container.py +0 -0
  129. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/dashboard.py +0 -0
  130. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/dict.py +0 -0
  131. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/entry_point.py +0 -0
  132. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/environment.py +0 -0
  133. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/import_refs.py +0 -0
  134. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/launch.py +0 -0
  135. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/logo.py +0 -0
  136. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/network_file_system.py +0 -0
  137. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/profile.py +0 -0
  138. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/programs/__init__.py +0 -0
  139. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/programs/run_jupyter.py +0 -0
  140. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/programs/vscode.py +0 -0
  141. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/queues.py +0 -0
  142. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/run.py +0 -0
  143. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/secret.py +0 -0
  144. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/selector.py +0 -0
  145. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/shell.py +0 -0
  146. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/skills.py +0 -0
  147. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/token.py +0 -0
  148. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/utils.py +0 -0
  149. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cli/volume.py +0 -0
  150. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/client.py +0 -0
  151. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cloud_bucket_mount.py +0 -0
  152. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/cloud_bucket_mount.pyi +0 -0
  153. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/config.py +0 -0
  154. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/container_process.py +0 -0
  155. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/container_process.pyi +0 -0
  156. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/environments.py +0 -0
  157. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/environments.pyi +0 -0
  158. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/exception.py +0 -0
  159. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/experimental/__init__.py +0 -0
  160. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/experimental/flash.py +0 -0
  161. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/experimental/flash.pyi +0 -0
  162. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/experimental/ipython.py +0 -0
  163. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/file_pattern_matcher.py +0 -0
  164. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/functions.py +0 -0
  165. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/image.py +0 -0
  166. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/image.pyi +0 -0
  167. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/io_streams.py +0 -0
  168. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/io_streams.pyi +0 -0
  169. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/mount.py +0 -0
  170. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/mount.pyi +0 -0
  171. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/network_file_system.py +0 -0
  172. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/network_file_system.pyi +0 -0
  173. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/object.py +0 -0
  174. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/output.py +0 -0
  175. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/parallel_map.py +0 -0
  176. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/parallel_map.pyi +0 -0
  177. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/partial_function.py +0 -0
  178. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/partial_function.pyi +0 -0
  179. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/proxy.py +0 -0
  180. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/proxy.pyi +0 -0
  181. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/py.typed +0 -0
  182. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/retries.py +0 -0
  183. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/runner.py +0 -0
  184. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/runner.pyi +0 -0
  185. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/running_app.py +0 -0
  186. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/schedule.py +0 -0
  187. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/scheduler_placement.py +0 -0
  188. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/server.py +0 -0
  189. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/server.pyi +0 -0
  190. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/serving.py +0 -0
  191. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/serving.pyi +0 -0
  192. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/skills/modal/SKILL.md +0 -0
  193. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/snapshot.py +0 -0
  194. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/snapshot.pyi +0 -0
  195. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/stream_type.py +0 -0
  196. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/token_flow.py +0 -0
  197. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/token_flow.pyi +0 -0
  198. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/workspace.py +0 -0
  199. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal/workspace.pyi +0 -0
  200. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/SOURCES.txt +0 -0
  201. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/dependency_links.txt +0 -0
  202. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/entry_points.txt +0 -0
  203. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/requires.txt +0 -0
  204. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal.egg-info/top_level.txt +0 -0
  205. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/__init__.py +0 -0
  206. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/gen_cli_docs.py +0 -0
  207. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/gen_cli_docs_main.py +0 -0
  208. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/gen_reference_docs.py +0 -0
  209. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/gen_reference_docs_main.py +0 -0
  210. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/mdmd/__init__.py +0 -0
  211. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/mdmd/mdmd.py +0 -0
  212. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/mdmd/signatures.py +0 -0
  213. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_docs/mdmd/types.py +0 -0
  214. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/__init__.py +0 -0
  215. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/py.typed +0 -0
  216. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/task_command_router_grpc.py +0 -0
  217. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/task_command_router_pb2.py +0 -0
  218. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/task_command_router_pb2.pyi +0 -0
  219. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  220. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  221. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/modal_version/__main__.py +0 -0
  222. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/pyproject.toml +0 -0
  223. {modal-1.4.4.dev16 → modal-1.4.4.dev18}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.4.dev16
3
+ Version: 1.4.4.dev18
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -28,7 +28,7 @@ from modal._clustered_functions import initialize_clustered_function
28
28
  from modal._runtime.user_code_event_loop import UserCodeEventLoop
29
29
  from modal._serialization import deserialize, deserialize_params
30
30
  from modal._utils.async_utils import TaskContext, aclosing, synchronizer
31
- from modal.app import App, _App
31
+ from modal.app import _App
32
32
  from modal.client import Client, _Client
33
33
  from modal.config import logger
34
34
  from modal.exception import ExecutionError, InputCancellation
@@ -288,7 +288,7 @@ def get_serialized_user_class_and_function(
288
288
 
289
289
 
290
290
  def get_server_service(
291
- container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
291
+ container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client
292
292
  ) -> Service:
293
293
  ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
294
294
  service_base_function_id = container_args.app_layout.function_ids[function_def.function_name]
@@ -298,14 +298,14 @@ def get_server_service(
298
298
  service = import_server_service(
299
299
  function_def,
300
300
  service_function_hydration_data,
301
- client,
301
+ _client,
302
302
  ser_usr_cls,
303
303
  )
304
304
  return service
305
305
 
306
306
 
307
307
  def get_class_service(
308
- container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client, client: Client
308
+ container_args: api_pb2.ContainerArguments, function_def: api_pb2.Function, _client: _Client
309
309
  ) -> Service:
310
310
  ser_usr_cls, ser_fun = get_serialized_user_class_and_function(function_def, _client)
311
311
  if container_args.serialized_params:
@@ -326,7 +326,7 @@ def get_class_service(
326
326
  function_def,
327
327
  service_function_hydration_data,
328
328
  class_id,
329
- client,
329
+ _client,
330
330
  ser_usr_cls,
331
331
  param_args,
332
332
  param_kwargs,
@@ -349,16 +349,15 @@ def hydrate_function(
349
349
  task_lifecycle_manager: TaskLifecycleManager,
350
350
  function_def: api_pb2.Function,
351
351
  _client: _Client,
352
- client: Client,
353
352
  ) -> Service:
354
353
  active_app: _App
355
354
  service: Service
356
355
  with task_lifecycle_manager.handle_task_lifecycle_exception():
357
356
  with execution_context._import_context():
358
357
  if function_def.is_server:
359
- service = get_server_service(container_args, function_def, _client, client)
358
+ service = get_server_service(container_args, function_def, _client)
360
359
  elif function_def.is_class:
361
- service = get_class_service(container_args, function_def, _client, client)
360
+ service = get_class_service(container_args, function_def, _client)
362
361
  else:
363
362
  service = get_function_service(function_def, _client)
364
363
 
@@ -369,8 +368,7 @@ def hydrate_function(
369
368
 
370
369
  # Initialize objects on the app.
371
370
  # 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)
371
+ active_app._init_container(_client, container_app)
374
372
 
375
373
  # Hydrate all function dependencies.
376
374
  # TODO(erikbern): we an remove this once we
@@ -406,7 +404,7 @@ def run_server(container_args: api_pb2.ContainerArguments, client: Client):
406
404
  client,
407
405
  )
408
406
 
409
- service: Service = hydrate_function(container_args, task_lifecycle_manager, function_def, _client, client)
407
+ service: Service = hydrate_function(container_args, task_lifecycle_manager, function_def, _client)
410
408
  # Initialize clustered functions.
411
409
  if function_def._experimental_group_size > 0:
412
410
  initialize_clustered_function(
@@ -442,7 +440,7 @@ def run_function(container_args: api_pb2.ContainerArguments, client: Client):
442
440
  with container_io_manager.heartbeats(is_snapshotting_function), UserCodeEventLoop() as event_loop:
443
441
  # Initialize the function, importing user code.
444
442
  service: Service = hydrate_function(
445
- container_args, container_io_manager.get_task_lifecycle_manager(), function_def, _client, client
443
+ container_args, container_io_manager.get_task_lifecycle_manager(), function_def, _client
446
444
  )
447
445
  if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
448
446
  # Concurrency and batching doesn't apply for `modal shell`.
@@ -185,6 +185,48 @@ class _FunctionOptions:
185
185
  )
186
186
 
187
187
 
188
+ async def _function_bind_params_cached(
189
+ base_function: "_Function",
190
+ req: api_pb2.FunctionBindParamsRequest,
191
+ ) -> api_pb2.FunctionBindParamsResponse:
192
+ """Cache layer for FunctionBindParams RPCs, scoped to a base Function handle.
193
+
194
+ We have this because users probably do not realize that Function invocations structured as
195
+
196
+ res = f.with_options(...).remote(...)
197
+
198
+ would always need to do two sequential RPCs (bind params / call function variant).
199
+
200
+ The bound Function ID from FunctionBindParams is deterministic with respect to the full request,
201
+ so we can avoid the unnecessary call and hydrate the new instance from a cached response.
202
+
203
+ The cache is stored on the base Function handle so that the variant cache behaves similarly to
204
+ the local reference to the base Function metadata.
205
+
206
+ """
207
+ cache = base_function._bind_params_cache
208
+ cache_key = req.SerializeToString(deterministic=True)
209
+
210
+ cached_response = cache.get(cache_key)
211
+ if cached_response is not None:
212
+ cache.move_to_end(cache_key)
213
+ response = api_pb2.FunctionBindParamsResponse()
214
+ response.ParseFromString(cached_response)
215
+ return response
216
+
217
+ assert base_function._client and base_function._client.stub
218
+ response = await base_function._client.stub.FunctionBindParams(req)
219
+
220
+ cache[cache_key] = response.SerializeToString(deterministic=True)
221
+ cache.move_to_end(cache_key)
222
+
223
+ max_cache_size = 32
224
+ while len(cache) > max_cache_size:
225
+ cache.popitem(last=False)
226
+
227
+ return response
228
+
229
+
188
230
  def _make_function_variant(
189
231
  base_function: "_Function",
190
232
  options: _FunctionOptions | None,
@@ -229,7 +271,7 @@ def _make_function_variant(
229
271
  or "", # TODO: investigate shouldn't environment name always be specified here?
230
272
  )
231
273
 
232
- response = await base_function._client.stub.FunctionBindParams(req)
274
+ response = await _function_bind_params_cached(base_function, req)
233
275
  function_variant._hydrate(response.bound_function_id, base_function._client, response.handle_metadata)
234
276
 
235
277
  def _deps():
@@ -5,6 +5,7 @@ import inspect
5
5
  import time
6
6
  import typing
7
7
  import warnings
8
+ from collections import OrderedDict
8
9
  from collections.abc import AsyncGenerator, AsyncIterator, Callable, Collection, Sequence
9
10
  from dataclasses import dataclass
10
11
  from pathlib import PurePosixPath
@@ -1208,6 +1209,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1208
1209
  app_name: str,
1209
1210
  name: str,
1210
1211
  *,
1212
+ version: int | None = None,
1211
1213
  load_context_overrides: LoadContext,
1212
1214
  ):
1213
1215
  # internal function lookup implementation that allows lookup of class "service functions"
@@ -1219,6 +1221,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1219
1221
  app_name=app_name,
1220
1222
  object_tag=name,
1221
1223
  environment_name=load_context.environment_name,
1224
+ app_version=version or 0,
1222
1225
  )
1223
1226
  try:
1224
1227
  response = await load_context.client.stub.FunctionGet(request)
@@ -1251,6 +1254,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1251
1254
  app_name: str,
1252
1255
  name: str,
1253
1256
  *,
1257
+ version: int | None = None,
1254
1258
  environment_name: str | None = None,
1255
1259
  client: _Client | None = None,
1256
1260
  ) -> "_Function":
@@ -1273,6 +1277,12 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1273
1277
  ```python
1274
1278
  f = modal.Function.from_name("other-app", "function")
1275
1279
  ```
1280
+
1281
+ The `version` parameter allows you to invoke a version-pinned function:
1282
+
1283
+ ```python
1284
+ f_v3 = modal.Function.from_name("other-app", "function", version=3)
1285
+ ```
1276
1286
  """
1277
1287
  if "." in name:
1278
1288
  raise InvalidError(
@@ -1281,7 +1291,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1281
1291
  )
1282
1292
 
1283
1293
  return cls._from_name(
1284
- app_name, name, load_context_overrides=LoadContext(environment_name=environment_name, client=client)
1294
+ app_name,
1295
+ name,
1296
+ load_context_overrides=LoadContext(environment_name=environment_name, client=client),
1297
+ version=version,
1285
1298
  )
1286
1299
 
1287
1300
  @property
@@ -1342,6 +1355,11 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1342
1355
  self._options = _FunctionOptions()
1343
1356
  self._base_function = None
1344
1357
 
1358
+ # An OrderedDict[bytes, bytes] LRU of FunctionBindParams responses for
1359
+ # variants derived from this base Function. Omitted from class-level annotations because
1360
+ # Synchronicity doesn't understand OrderedDict as a type hint.
1361
+ self._bind_params_cache = OrderedDict()
1362
+
1345
1363
  def _hydrate_metadata(self, metadata: Message | None):
1346
1364
  # Overridden concrete implementation of base class method
1347
1365
  assert metadata and isinstance(metadata, api_pb2.FunctionHandleMetadata), (
@@ -1370,6 +1388,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1370
1388
  )
1371
1389
  self._experimental_flash_urls = metadata._experimental_flash_urls
1372
1390
 
1391
+ # Invalidate the Function variant cache when we load new metadata, since the base Function handle
1392
+ # is now in sync with the server but any previously-cached variants may be stale.
1393
+ self._bind_params_cache.clear()
1394
+
1373
1395
  def _get_metadata(self):
1374
1396
  # Overridden concrete implementation of base class method
1375
1397
  assert self._function_name, f"Function name must be set before metadata can be retrieved for {self}"
@@ -60,6 +60,7 @@ from .secret import _Secret
60
60
  from .volume import _Volume, _volume_to_mount_proto
61
61
 
62
62
  if typing.TYPE_CHECKING:
63
+ import modal
63
64
  import modal._functions
64
65
  import modal.client
65
66
 
@@ -531,7 +532,7 @@ class _Image(_Object, type_prefix="im"):
531
532
 
532
533
  for secret in secrets:
533
534
  if not isinstance(secret, _Secret):
534
- raise InvalidError("All secrets of an image needs to be modal.Secret/AioSecret instances")
535
+ raise InvalidError("All secrets of an Image need to be modal.Secret instances.")
535
536
 
536
537
  if build_function and len(base_images) != 1:
537
538
  raise InvalidError("Cannot run a build function with multiple base images!")
@@ -2763,7 +2764,7 @@ class _Image(_Object, type_prefix="im"):
2763
2764
 
2764
2765
  def pipe(
2765
2766
  self,
2766
- func: Callable[Concatenate["_Image", P], "_Image"],
2767
+ func: Callable[Concatenate["modal.Image", P], "modal.Image"],
2767
2768
  *args: P.args,
2768
2769
  **kwargs: P.kwargs,
2769
2770
  ) -> "_Image":
@@ -2785,7 +2786,11 @@ class _Image(_Object, type_prefix="im"):
2785
2786
  )
2786
2787
  ```
2787
2788
  """
2788
- return func(self, *args, **kwargs)
2789
+ # Typing here is complicated because user callables accept the public
2790
+ # Image type, but `self` is an instance of the internal _Image type.
2791
+ self_ = synchronizer._translate_out(self)
2792
+ res = func(self_, *args, **kwargs) # type: ignore[reportUnknownReturnType]
2793
+ return typing.cast(_Image, synchronizer._translate_in(res))
2789
2794
 
2790
2795
  # Live handle methods
2791
2796
 
@@ -14,7 +14,6 @@ from modal._traceback import suppress_tb_frame
14
14
  from ._load_context import LoadContext
15
15
  from ._resolver import Resolver
16
16
  from ._utils.async_utils import TaskContext, aclosing
17
- from ._utils.deprecation import deprecation_warning
18
17
  from .client import _Client
19
18
  from .config import config, logger
20
19
  from .exception import ExecutionError, InvalidError
@@ -196,23 +195,6 @@ class _Object:
196
195
  f"{object_type} has not been hydrated with the metadata it needs to run on Modal{reason}."
197
196
  )
198
197
 
199
- def clone(self) -> Self:
200
- """mdmd:hidden Clone a given hydrated object.
201
-
202
- Note: This is not intended to be public API and has no public use. It will be removed in a future release.
203
- """
204
- object_class = self.__class__.__name__.strip("_")
205
- deprecation_warning(
206
- (2025, 6, 30),
207
- f"{object_class}.clone() is not intended to be public API and will be removed in a future release.",
208
- )
209
-
210
- # Object to clone must already be hydrated, otherwise from_loader is more suitable.
211
- self._validate_is_hydrated()
212
- obj = type(self).__new__(type(self))
213
- obj._initialize_from_other(self)
214
- return obj
215
-
216
198
  @classmethod
217
199
  def _from_loader(
218
200
  cls,
@@ -571,7 +571,7 @@ def import_class_service(
571
571
  function_def: api_pb2.Function,
572
572
  service_function_hydration_data: api_pb2.Object,
573
573
  class_id: str,
574
- client: "modal.client.Client",
574
+ _client: "modal.client._Client",
575
575
  ser_user_cls: type | None,
576
576
  cls_args,
577
577
  cls_kwargs,
@@ -599,7 +599,6 @@ def import_class_service(
599
599
  # Undecorated user class (serialized or local scope-decoration).
600
600
  service_deps = None # we can't infer service deps for now
601
601
  active_app = get_active_app_fallback(function_def)
602
- _client = typing.cast("modal.client._Client", synchronizer._translate_in(client))
603
602
  _service_function: modal._functions._Function[Any, Any, Any] = modal._functions._Function._new_hydrated(
604
603
  service_function_hydration_data.object_id,
605
604
  _client,
@@ -627,7 +626,7 @@ def import_class_service(
627
626
  def import_server_service(
628
627
  function_def: api_pb2.Function,
629
628
  service_function_hydration_data: api_pb2.Object,
630
- client: "modal.client.Client",
629
+ _client: "modal.client._Client",
631
630
  ser_user_cls: type | None,
632
631
  ) -> Service:
633
632
  """
@@ -654,7 +653,6 @@ def import_server_service(
654
653
  # Undecorated user class (serialized or local scope-decoration).
655
654
  service_deps = None # we can't infer service deps for now
656
655
  active_app = get_active_app_fallback(function_def)
657
- _client = typing.cast("modal.client._Client", synchronizer._translate_in(client))
658
656
  _service_function: modal._functions._Function[Any, Any, Any] = modal._functions._Function._new_hydrated(
659
657
  service_function_hydration_data.object_id,
660
658
  _client,
@@ -153,7 +153,10 @@ class CustomProtoStatusDetailsCodec(StatusDetailsCodecBase):
153
153
  if details is not None:
154
154
  for detail in details:
155
155
  detail_container = details_proto.details.add()
156
- detail_container.Pack(detail)
156
+ if detail.DESCRIPTOR.full_name.startswith("modal."):
157
+ detail_container.Pack(detail, type_url_prefix="type.modal.com/")
158
+ else:
159
+ detail_container.Pack(detail)
157
160
  return details_proto.SerializeToString()
158
161
 
159
162
  def decode(
@@ -285,6 +285,53 @@ def make_stat_command(remote_path: str) -> str:
285
285
  return json.dumps({"Stat": {"path": remote_path}})
286
286
 
287
287
 
288
+ def raise_watch_error(returncode: int, stderr: str | bytes, remote_path: str) -> NoReturn:
289
+ if payload := try_parse_error_payload(stderr):
290
+ logger.debug(
291
+ f"sandbox-fs-tools watch error: path={remote_path}, "
292
+ f"error_kind={payload.error_kind}, message={payload.message}, detail={payload.detail}"
293
+ )
294
+ if payload.error_kind == "NotFound":
295
+ raise SandboxFilesystemNotFoundError(f"{payload.message}: {remote_path}")
296
+ if payload.error_kind == "PermissionDenied":
297
+ raise SandboxFilesystemPermissionError(f"{payload.message}: {remote_path}")
298
+ if payload.error_kind == "NotSupported":
299
+ raise InvalidError(
300
+ f"{payload.message}: {remote_path} - this operation is not supported for CloudBucketMounts"
301
+ )
302
+ raise SandboxFilesystemError(payload.message)
303
+
304
+ if stderr_text := _stderr_to_text(stderr):
305
+ logger.debug(f"Unstructured modal-sandbox-fs-tools stderr: {stderr_text}")
306
+ raise SandboxFilesystemError(f"Operation on '{remote_path}' failed with exit code {returncode}")
307
+
308
+
309
+ def make_watch_command(
310
+ remote_path: str,
311
+ *,
312
+ recursive: bool,
313
+ filter: list[str] | None,
314
+ timeout: int | None,
315
+ ) -> str:
316
+ """Build the JSON command string for a Watch operation.
317
+
318
+ The returned JSON must match the `Command` enum in the modal-sandbox-fs-tools
319
+ Rust crate (crates/modal-sandbox-fs-tools/src/lib.rs). Treat changes to
320
+ this schema like protobuf changes: fields must not be removed or renamed,
321
+ only added with backwards-compatible defaults.
322
+ """
323
+ return json.dumps(
324
+ {
325
+ "Watch": {
326
+ "path": remote_path,
327
+ "recursive": recursive,
328
+ "filter": filter,
329
+ "timeout_secs": timeout,
330
+ }
331
+ }
332
+ )
333
+
334
+
288
335
  def validate_absolute_remote_path(remote_path: str, operation: str) -> None:
289
336
  if not PurePosixPath(remote_path).is_absolute():
290
337
  raise InvalidError(f"Sandbox.filesystem.{operation}() currently only supports absolute remote_path values")
@@ -3,4 +3,8 @@ from ._billing import WorkspaceBillingReportItem, _workspace_billing_report
3
3
  from ._utils.async_utils import synchronize_api
4
4
 
5
5
  workspace_billing_report = synchronize_api(_workspace_billing_report)
6
- assert WorkspaceBillingReportItem
6
+
7
+ __all__ = [
8
+ "workspace_billing_report",
9
+ "WorkspaceBillingReportItem",
10
+ ]
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.4.4.dev16",
38
+ version: str = "1.4.4.dev18",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -205,7 +205,7 @@ class Client:
205
205
  server_url: str,
206
206
  client_type: int,
207
207
  credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.4.4.dev16",
208
+ version: str = "1.4.4.dev18",
209
209
  ):
210
210
  """mdmd:hidden
211
211
  The Modal client object is not intended to be instantiated directly by users.
@@ -641,6 +641,7 @@ More information on class parameterization can be found here: https://modal.com/
641
641
  app_name: str,
642
642
  name: str,
643
643
  *,
644
+ version: int | None = None,
644
645
  environment_name: str | None = None,
645
646
  client: "_Client | None" = None,
646
647
  ) -> "_Cls":
@@ -663,6 +664,12 @@ More information on class parameterization can be found here: https://modal.com/
663
664
  ```python
664
665
  Model = modal.Cls.from_name("other-app", "Model")
665
666
  ```
667
+
668
+ The `version` parameter constructs a version-pinned Cls:
669
+
670
+ ```python
671
+ Modelv3 = modal.Cls.from_name("other-app", "Model", version=3)
672
+ ```
666
673
  """
667
674
 
668
675
  async def _load_remote(
@@ -705,6 +712,7 @@ More information on class parameterization can be found here: https://modal.com/
705
712
  app_name,
706
713
  class_service_name,
707
714
  load_context_overrides=load_context_overrides,
715
+ version=version,
708
716
  )
709
717
  cls._name = name
710
718
  return cls
@@ -291,6 +291,7 @@ class _Cls(modal._object._Object):
291
291
  app_name: str,
292
292
  name: str,
293
293
  *,
294
+ version: typing.Optional[int] = None,
294
295
  environment_name: typing.Optional[str] = None,
295
296
  client: typing.Optional[modal.client._Client] = None,
296
297
  ) -> _Cls:
@@ -313,6 +314,12 @@ class _Cls(modal._object._Object):
313
314
  ```python
314
315
  Model = modal.Cls.from_name("other-app", "Model")
315
316
  ```
317
+
318
+ The `version` parameter constructs a version-pinned Cls:
319
+
320
+ ```python
321
+ Modelv3 = modal.Cls.from_name("other-app", "Model", version=3)
322
+ ```
316
323
  """
317
324
  ...
318
325
 
@@ -498,6 +505,7 @@ class Cls(modal.object.Object):
498
505
  app_name: str,
499
506
  name: str,
500
507
  *,
508
+ version: typing.Optional[int] = None,
501
509
  environment_name: typing.Optional[str] = None,
502
510
  client: typing.Optional[modal.client.Client] = None,
503
511
  ) -> Cls:
@@ -520,6 +528,12 @@ class Cls(modal.object.Object):
520
528
  ```python
521
529
  Model = modal.Cls.from_name("other-app", "Model")
522
530
  ```
531
+
532
+ The `version` parameter constructs a version-pinned Cls:
533
+
534
+ ```python
535
+ Modelv3 = modal.Cls.from_name("other-app", "Model", version=3)
536
+ ```
523
537
  """
524
538
  ...
525
539
 
@@ -23,7 +23,6 @@ from ._object import (
23
23
  from ._resolver import Resolver
24
24
  from ._serialization import deserialize, serialize
25
25
  from ._utils.async_utils import TaskContext, synchronize_api
26
- from ._utils.deprecation import deprecation_warning
27
26
  from ._utils.name_utils import check_object_name
28
27
  from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
29
28
  from .client import _Client
@@ -482,26 +481,6 @@ class _Dict(_Object, type_prefix="di"):
482
481
  load_context_overrides=LoadContext(client=client),
483
482
  )
484
483
 
485
- @staticmethod
486
- async def delete(
487
- name: str,
488
- *,
489
- client: _Client | None = None,
490
- environment_name: str | None = None,
491
- ):
492
- """mdmd:hidden
493
- Delete a named Dict object.
494
-
495
- Warning: This deletes an *entire Dict*, not just a specific key.
496
- Deletion is irreversible and will affect any Apps currently using the Dict.
497
-
498
- DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
499
- """
500
- deprecation_warning(
501
- (2025, 8, 6), "`modal.Dict.delete` is deprecated; we recommend using `modal.Dict.objects.delete` instead."
502
- )
503
- await _Dict.objects.delete(name, environment_name=environment_name, client=client)
504
-
505
484
  @live_method
506
485
  async def info(self) -> DictInfo:
507
486
  """Return information about the Dict object."""
@@ -576,23 +576,6 @@ class _Dict(modal._object._Object):
576
576
  """
577
577
  ...
578
578
 
579
- @staticmethod
580
- async def delete(
581
- name: str,
582
- *,
583
- client: typing.Optional[modal.client._Client] = None,
584
- environment_name: typing.Optional[str] = None,
585
- ):
586
- """mdmd:hidden
587
- Delete a named Dict object.
588
-
589
- Warning: This deletes an *entire Dict*, not just a specific key.
590
- Deletion is irreversible and will affect any Apps currently using the Dict.
591
-
592
- DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
593
- """
594
- ...
595
-
596
579
  async def info(self) -> DictInfo:
597
580
  """Return information about the Dict object."""
598
581
  ...
@@ -877,45 +860,6 @@ class Dict(modal.object.Object):
877
860
  """
878
861
  ...
879
862
 
880
- class __delete_spec(typing_extensions.Protocol):
881
- def __call__(
882
- self,
883
- /,
884
- name: str,
885
- *,
886
- client: typing.Optional[modal.client.Client] = None,
887
- environment_name: typing.Optional[str] = None,
888
- ):
889
- """mdmd:hidden
890
- Delete a named Dict object.
891
-
892
- Warning: This deletes an *entire Dict*, not just a specific key.
893
- Deletion is irreversible and will affect any Apps currently using the Dict.
894
-
895
- DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
896
- """
897
- ...
898
-
899
- async def aio(
900
- self,
901
- /,
902
- name: str,
903
- *,
904
- client: typing.Optional[modal.client.Client] = None,
905
- environment_name: typing.Optional[str] = None,
906
- ):
907
- """mdmd:hidden
908
- Delete a named Dict object.
909
-
910
- Warning: This deletes an *entire Dict*, not just a specific key.
911
- Deletion is irreversible and will affect any Apps currently using the Dict.
912
-
913
- DEPRECATED: This method is deprecated; we recommend using `modal.Dict.objects.delete` instead.
914
- """
915
- ...
916
-
917
- delete: typing.ClassVar[__delete_spec]
918
-
919
863
  class __info_spec(typing_extensions.Protocol):
920
864
  def __call__(self, /) -> DictInfo:
921
865
  """Return information about the Dict object."""
@@ -1,10 +1,8 @@
1
1
  # Copyright Modal Labs 2024
2
2
  import asyncio
3
- import enum
4
3
  import io
5
4
  from collections.abc import AsyncIterator, Sequence
6
- from dataclasses import dataclass
7
- from typing import TYPE_CHECKING, Generic, TypeVar, Union, cast
5
+ from typing import TYPE_CHECKING, Generic, Optional, TypeVar, Union, cast
8
6
 
9
7
  if TYPE_CHECKING:
10
8
  import _typeshed
@@ -15,6 +13,7 @@ from grpclib.exceptions import StreamTerminatedError
15
13
 
16
14
  from modal._utils.async_utils import TaskContext
17
15
  from modal.exception import ClientClosed
16
+ from modal.sandbox_fs import FileWatchEvent, FileWatchEventType
18
17
  from modal_proto import api_pb2
19
18
 
20
19
  from ._utils.async_utils import synchronize_api
@@ -60,21 +59,7 @@ async def _replace_bytes(file: "_FileIO", data: bytes, start: int | None = None,
60
59
  deprecation_error((2025, 12, 3), "replace_bytes has been removed.")
61
60
 
62
61
 
63
- class FileWatchEventType(enum.Enum):
64
- Unknown = "Unknown"
65
- Access = "Access"
66
- Create = "Create"
67
- Modify = "Modify"
68
- Remove = "Remove"
69
-
70
-
71
- @dataclass
72
- class FileWatchEvent:
73
- paths: list[str]
74
- type: FileWatchEventType
75
-
76
-
77
- async def _consume_output(client: _Client, exec_id: str) -> AsyncIterator[bytes | None | Exception]:
62
+ async def _consume_output(client: _Client, exec_id: str) -> AsyncIterator[Optional[bytes] | Exception]:
78
63
  req = api_pb2.ContainerFilesystemExecGetOutputRequest(
79
64
  exec_id=exec_id,
80
65
  timeout=55,
@@ -424,7 +409,7 @@ class _FileIO(Generic[T]):
424
409
  ) -> AsyncIterator[FileWatchEvent]:
425
410
  deprecation_warning(
426
411
  (2026, 3, 9),
427
- "`FileIO.watch()` is deprecated. Use `Sandbox.watch()` instead.",
412
+ "`FileIO.watch()` is deprecated. Use `Sandbox.filesystem.watch()` instead.",
428
413
  )
429
414
  async for event in watch(path, client, task_id, filter, recursive, timeout):
430
415
  yield event