modAL 1.2.7.dev5__tar.gz → 1.2.7.dev7__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 (194) hide show
  1. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/PKG-INFO +1 -1
  2. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_functions.py +3 -10
  3. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/container_io_manager.py +3 -2
  4. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/app.py +34 -8
  5. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/app.pyi +8 -4
  6. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/client.pyi +2 -2
  7. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/file_io.py +8 -45
  8. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/file_io.pyi +12 -27
  9. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/functions.pyi +7 -7
  10. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/PKG-INFO +1 -1
  11. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api.proto +5 -0
  12. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api_pb2.py +656 -656
  13. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api_pb2.pyi +8 -1
  14. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_version/__init__.py +1 -1
  15. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/LICENSE +0 -0
  16. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/README.md +0 -0
  17. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/__init__.py +0 -0
  18. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/__main__.py +0 -0
  19. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_billing.py +0 -0
  20. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_clustered_functions.py +0 -0
  21. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_clustered_functions.pyi +0 -0
  22. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_container_entrypoint.py +0 -0
  23. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_grpc_client.py +0 -0
  24. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_ipython.py +0 -0
  25. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_load_context.py +0 -0
  26. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_location.py +0 -0
  27. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_object.py +0 -0
  28. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_output.py +0 -0
  29. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_partial_function.py +0 -0
  30. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_pty.py +0 -0
  31. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_resolver.py +0 -0
  32. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_resources.py +0 -0
  33. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/__init__.py +0 -0
  34. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/asgi.py +0 -0
  35. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/container_io_manager.pyi +0 -0
  36. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/execution_context.py +0 -0
  37. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/execution_context.pyi +0 -0
  38. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  39. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/telemetry.py +0 -0
  40. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/user_code_event_loop.py +0 -0
  41. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_runtime/user_code_imports.py +0 -0
  42. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_serialization.py +0 -0
  43. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_traceback.py +0 -0
  44. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_tunnel.py +0 -0
  45. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_tunnel.pyi +0 -0
  46. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_type_manager.py +0 -0
  47. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/__init__.py +0 -0
  48. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/app_utils.py +0 -0
  49. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/async_utils.py +0 -0
  50. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/auth_token_manager.py +0 -0
  51. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/blob_utils.py +0 -0
  52. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/bytes_io_segment_payload.py +0 -0
  53. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/deprecation.py +0 -0
  54. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/docker_utils.py +0 -0
  55. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/function_utils.py +0 -0
  56. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/git_utils.py +0 -0
  57. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/grpc_testing.py +0 -0
  58. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/grpc_utils.py +0 -0
  59. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/hash_utils.py +0 -0
  60. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/http_utils.py +0 -0
  61. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/jwt_utils.py +0 -0
  62. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/logger.py +0 -0
  63. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/mount_utils.py +0 -0
  64. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/name_utils.py +0 -0
  65. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/package_utils.py +0 -0
  66. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/pattern_utils.py +0 -0
  67. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/rand_pb_testing.py +0 -0
  68. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/shell_utils.py +0 -0
  69. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/task_command_router_client.py +0 -0
  70. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_utils/time_utils.py +0 -0
  71. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_vendor/__init__.py +0 -0
  72. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  73. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_vendor/cloudpickle.py +0 -0
  74. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_vendor/tblib.py +0 -0
  75. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/_watcher.py +0 -0
  76. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/billing.py +0 -0
  77. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/2023.12.312.txt +0 -0
  78. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/2023.12.txt +0 -0
  79. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/2024.04.txt +0 -0
  80. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/2024.10.txt +0 -0
  81. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/2025.06.txt +0 -0
  82. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/PREVIEW.txt +0 -0
  83. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/README.md +0 -0
  84. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/builder/base-images.json +0 -0
  85. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/call_graph.py +0 -0
  86. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/__init__.py +0 -0
  87. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/_download.py +0 -0
  88. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/_traceback.py +0 -0
  89. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/app.py +0 -0
  90. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/cluster.py +0 -0
  91. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/config.py +0 -0
  92. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/container.py +0 -0
  93. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/dict.py +0 -0
  94. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/entry_point.py +0 -0
  95. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/environment.py +0 -0
  96. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/import_refs.py +0 -0
  97. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/launch.py +0 -0
  98. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/network_file_system.py +0 -0
  99. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/profile.py +0 -0
  100. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/programs/__init__.py +0 -0
  101. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/programs/run_jupyter.py +0 -0
  102. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/programs/vscode.py +0 -0
  103. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/queues.py +0 -0
  104. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/run.py +0 -0
  105. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/secret.py +0 -0
  106. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/shell.py +0 -0
  107. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/token.py +0 -0
  108. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/utils.py +0 -0
  109. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cli/volume.py +0 -0
  110. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/client.py +0 -0
  111. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cloud_bucket_mount.py +0 -0
  112. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cloud_bucket_mount.pyi +0 -0
  113. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cls.py +0 -0
  114. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/cls.pyi +0 -0
  115. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/config.py +0 -0
  116. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/container_process.py +0 -0
  117. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/container_process.pyi +0 -0
  118. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/dict.py +0 -0
  119. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/dict.pyi +0 -0
  120. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/environments.py +0 -0
  121. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/environments.pyi +0 -0
  122. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/exception.py +0 -0
  123. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/experimental/__init__.py +0 -0
  124. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/experimental/flash.py +0 -0
  125. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/experimental/flash.pyi +0 -0
  126. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/experimental/ipython.py +0 -0
  127. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/file_pattern_matcher.py +0 -0
  128. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/functions.py +0 -0
  129. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/gpu.py +0 -0
  130. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/image.py +0 -0
  131. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/image.pyi +0 -0
  132. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/io_streams.py +0 -0
  133. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/io_streams.pyi +0 -0
  134. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/mount.py +0 -0
  135. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/mount.pyi +0 -0
  136. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/network_file_system.py +0 -0
  137. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/network_file_system.pyi +0 -0
  138. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/object.py +0 -0
  139. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/object.pyi +0 -0
  140. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/output.py +0 -0
  141. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/parallel_map.py +0 -0
  142. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/parallel_map.pyi +0 -0
  143. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/partial_function.py +0 -0
  144. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/partial_function.pyi +0 -0
  145. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/proxy.py +0 -0
  146. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/proxy.pyi +0 -0
  147. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/py.typed +0 -0
  148. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/queue.py +0 -0
  149. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/queue.pyi +0 -0
  150. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/retries.py +0 -0
  151. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/runner.py +0 -0
  152. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/runner.pyi +0 -0
  153. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/running_app.py +0 -0
  154. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/sandbox.py +0 -0
  155. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/sandbox.pyi +0 -0
  156. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/schedule.py +0 -0
  157. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/scheduler_placement.py +0 -0
  158. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/secret.py +0 -0
  159. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/secret.pyi +0 -0
  160. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/serving.py +0 -0
  161. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/serving.pyi +0 -0
  162. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/snapshot.py +0 -0
  163. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/snapshot.pyi +0 -0
  164. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/stream_type.py +0 -0
  165. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/token_flow.py +0 -0
  166. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/token_flow.pyi +0 -0
  167. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/volume.py +0 -0
  168. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal/volume.pyi +0 -0
  169. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/SOURCES.txt +0 -0
  170. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/dependency_links.txt +0 -0
  171. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/entry_points.txt +0 -0
  172. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/requires.txt +0 -0
  173. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal.egg-info/top_level.txt +0 -0
  174. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/__init__.py +0 -0
  175. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/gen_cli_docs.py +0 -0
  176. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/gen_reference_docs.py +0 -0
  177. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/mdmd/__init__.py +0 -0
  178. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/mdmd/mdmd.py +0 -0
  179. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_docs/mdmd/signatures.py +0 -0
  180. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/__init__.py +0 -0
  181. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api_grpc.py +0 -0
  182. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api_pb2_grpc.py +0 -0
  183. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/api_pb2_grpc.pyi +0 -0
  184. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/modal_api_grpc.py +0 -0
  185. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/py.typed +0 -0
  186. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router.proto +0 -0
  187. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router_grpc.py +0 -0
  188. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router_pb2.py +0 -0
  189. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router_pb2.pyi +0 -0
  190. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  191. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  192. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/modal_version/__main__.py +0 -0
  193. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/pyproject.toml +0 -0
  194. {modal-1.2.7.dev5 → modal-1.2.7.dev7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.7.dev5
3
+ Version: 1.2.7.dev7
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -694,7 +694,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
694
694
  # Experimental: Clustered functions
695
695
  cluster_size: Optional[int] = None,
696
696
  rdma: Optional[bool] = None,
697
- max_inputs: Optional[int] = None,
697
+ single_use_containers: bool = False,
698
698
  ephemeral_disk: Optional[int] = None,
699
699
  include_source: bool = True,
700
700
  experimental_options: Optional[dict[str, str]] = None,
@@ -810,14 +810,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
810
810
  if arg.default is not inspect.Parameter.empty:
811
811
  raise InvalidError(f"Modal batched function {func_name} does not accept default arguments.")
812
812
 
813
- if max_inputs is not None:
814
- if not isinstance(max_inputs, int):
815
- raise InvalidError(f"`max_inputs` must be an int, not {type(max_inputs).__name__}")
816
- if max_inputs <= 0:
817
- raise InvalidError("`max_inputs` must be positive")
818
- if max_inputs > 1:
819
- raise InvalidError("Only `max_inputs=1` is currently supported")
820
-
821
813
  # Validate volumes
822
814
  validated_volumes = validate_volumes(volumes)
823
815
  cloud_bucket_mounts = [(k, v) for k, v in validated_volumes if isinstance(v, _CloudBucketMount)]
@@ -1024,7 +1016,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1024
1016
  object_dependencies=object_dependencies,
1025
1017
  block_network=block_network,
1026
1018
  untrusted=restrict_modal_access,
1027
- max_inputs=max_inputs or 0,
1019
+ single_use_containers=single_use_containers,
1020
+ max_inputs=int(single_use_containers), # TODO(michael) remove after worker rollover
1028
1021
  cloud_bucket_mounts=cloud_bucket_mounts_to_proto(cloud_bucket_mounts),
1029
1022
  scheduler_placement=scheduler_placement,
1030
1023
  is_class=info.is_service_class(),
@@ -845,8 +845,9 @@ class _ContainerIOManager:
845
845
  yield inputs
846
846
  yielded = True
847
847
 
848
- # We only support max_inputs = 1 at the moment
849
- if final_input_received or self.function_def.max_inputs == 1:
848
+ # TODO(michael): Remove use of max_inputs after worker rollover
849
+ single_use_container = self.function_def.single_use_containers or self.function_def.max_inputs == 1
850
+ if final_input_received or single_use_container:
850
851
  return
851
852
  finally:
852
853
  if not yielded:
@@ -725,9 +725,7 @@ class _App:
725
725
  enable_memory_snapshot: bool = False, # Enable memory checkpointing for faster cold starts.
726
726
  block_network: bool = False, # Whether to block network access
727
727
  restrict_modal_access: bool = False, # Whether to allow this function access to other Modal resources
728
- # Maximum number of inputs a container should handle before shutting down.
729
- # With `max_inputs = 1`, containers will be single-use.
730
- max_inputs: Optional[int] = None,
728
+ single_use_containers: bool = False, # When True, containers will shut down after handling a single input
731
729
  i6pn: Optional[bool] = None, # Whether to enable IPv6 container networking within the region.
732
730
  # Whether the file or directory containing the Function's source should automatically be included
733
731
  # in the container. When unset, falls back to the App-level configuration, or is otherwise True by default.
@@ -742,6 +740,7 @@ class _App:
742
740
  concurrency_limit: Optional[int] = None, # Replaced with `max_containers`
743
741
  container_idle_timeout: Optional[int] = None, # Replaced with `scaledown_window`
744
742
  allow_concurrent_inputs: Optional[int] = None, # Replaced with the `@modal.concurrent` decorator
743
+ max_inputs: Optional[int] = None, # Replaced with `single_use_containers`
745
744
  _experimental_buffer_containers: Optional[int] = None, # Now stable API with `buffer_containers`
746
745
  _experimental_scheduler_placement: Optional[SchedulerPlacement] = None, # Replaced in favor of
747
746
  # using `region` and `nonpreemptible`
@@ -764,6 +763,20 @@ class _App:
764
763
  "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
765
764
  )
766
765
 
766
+ if max_inputs is not None:
767
+ if not isinstance(max_inputs, int):
768
+ raise InvalidError(f"`max_inputs` must be an int, not {type(max_inputs).__name__}")
769
+ if max_inputs <= 0:
770
+ raise InvalidError("`max_inputs` must be positive")
771
+ if max_inputs > 1:
772
+ raise InvalidError("Only `max_inputs=1` is currently supported")
773
+ deprecation_warning(
774
+ (2025, 12, 16),
775
+ "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
776
+ pending=True,
777
+ )
778
+ single_use_containers = max_inputs == 1
779
+
767
780
  if _experimental_scheduler_placement is not None:
768
781
  deprecation_warning(
769
782
  (2025, 11, 17),
@@ -912,7 +925,7 @@ class _App:
912
925
  enable_memory_snapshot=enable_memory_snapshot,
913
926
  block_network=block_network,
914
927
  restrict_modal_access=restrict_modal_access,
915
- max_inputs=max_inputs,
928
+ single_use_containers=single_use_containers,
916
929
  i6pn_enabled=i6pn_enabled,
917
930
  cluster_size=cluster_size, # Experimental: Clustered functions
918
931
  rdma=rdma,
@@ -969,9 +982,7 @@ class _App:
969
982
  enable_memory_snapshot: bool = False, # Enable memory checkpointing for faster cold starts.
970
983
  block_network: bool = False, # Whether to block network access
971
984
  restrict_modal_access: bool = False, # Whether to allow this class access to other Modal resources
972
- # Limits the number of inputs a container handles before shutting down.
973
- # Use `max_inputs = 1` for single-use containers.
974
- max_inputs: Optional[int] = None,
985
+ single_use_containers: bool = False, # When True, containers will shut down after handling a single input
975
986
  i6pn: Optional[bool] = None, # Whether to enable IPv6 container networking within the region.
976
987
  include_source: Optional[bool] = None, # When `False`, don't automatically add the App source to the container.
977
988
  experimental_options: Optional[dict[str, Any]] = None,
@@ -984,6 +995,7 @@ class _App:
984
995
  concurrency_limit: Optional[int] = None, # Replaced with `max_containers`
985
996
  container_idle_timeout: Optional[int] = None, # Replaced with `scaledown_window`
986
997
  allow_concurrent_inputs: Optional[int] = None, # Replaced with the `@modal.concurrent` decorator
998
+ max_inputs: Optional[int] = None, # Replaced with `single_use_containers`
987
999
  _experimental_buffer_containers: Optional[int] = None, # Now stable API with `buffer_containers`
988
1000
  _experimental_scheduler_placement: Optional[SchedulerPlacement] = None, # Replaced in favor of
989
1001
  # using `region` and `nonpreemptible`
@@ -1002,6 +1014,20 @@ class _App:
1002
1014
  "\n\nSee https://modal.com/docs/guide/modal-1-0-migration for more information.",
1003
1015
  )
1004
1016
 
1017
+ if max_inputs is not None:
1018
+ if not isinstance(max_inputs, int):
1019
+ raise InvalidError(f"`max_inputs` must be an int, not {type(max_inputs).__name__}")
1020
+ if max_inputs <= 0:
1021
+ raise InvalidError("`max_inputs` must be positive")
1022
+ if max_inputs > 1:
1023
+ raise InvalidError("Only `max_inputs=1` is currently supported")
1024
+ deprecation_warning(
1025
+ (2025, 12, 16),
1026
+ "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
1027
+ pending=True,
1028
+ )
1029
+ single_use_containers = max_inputs == 1
1030
+
1005
1031
  if _experimental_scheduler_placement is not None:
1006
1032
  deprecation_warning(
1007
1033
  (2025, 11, 17),
@@ -1139,7 +1165,7 @@ class _App:
1139
1165
  enable_memory_snapshot=enable_memory_snapshot,
1140
1166
  block_network=block_network,
1141
1167
  restrict_modal_access=restrict_modal_access,
1142
- max_inputs=max_inputs,
1168
+ single_use_containers=single_use_containers,
1143
1169
  http_config=http_config,
1144
1170
  i6pn_enabled=i6pn_enabled,
1145
1171
  cluster_size=cluster_size,
@@ -491,7 +491,7 @@ class _App:
491
491
  enable_memory_snapshot: bool = False,
492
492
  block_network: bool = False,
493
493
  restrict_modal_access: bool = False,
494
- max_inputs: typing.Optional[int] = None,
494
+ single_use_containers: bool = False,
495
495
  i6pn: typing.Optional[bool] = None,
496
496
  include_source: typing.Optional[bool] = None,
497
497
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
@@ -502,6 +502,7 @@ class _App:
502
502
  concurrency_limit: typing.Optional[int] = None,
503
503
  container_idle_timeout: typing.Optional[int] = None,
504
504
  allow_concurrent_inputs: typing.Optional[int] = None,
505
+ max_inputs: typing.Optional[int] = None,
505
506
  _experimental_buffer_containers: typing.Optional[int] = None,
506
507
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
507
508
  ) -> _FunctionDecoratorType:
@@ -545,7 +546,7 @@ class _App:
545
546
  enable_memory_snapshot: bool = False,
546
547
  block_network: bool = False,
547
548
  restrict_modal_access: bool = False,
548
- max_inputs: typing.Optional[int] = None,
549
+ single_use_containers: bool = False,
549
550
  i6pn: typing.Optional[bool] = None,
550
551
  include_source: typing.Optional[bool] = None,
551
552
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
@@ -556,6 +557,7 @@ class _App:
556
557
  concurrency_limit: typing.Optional[int] = None,
557
558
  container_idle_timeout: typing.Optional[int] = None,
558
559
  allow_concurrent_inputs: typing.Optional[int] = None,
560
+ max_inputs: typing.Optional[int] = None,
559
561
  _experimental_buffer_containers: typing.Optional[int] = None,
560
562
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
561
563
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal._partial_function._PartialFunction]], CLS_T]:
@@ -1154,7 +1156,7 @@ class App:
1154
1156
  enable_memory_snapshot: bool = False,
1155
1157
  block_network: bool = False,
1156
1158
  restrict_modal_access: bool = False,
1157
- max_inputs: typing.Optional[int] = None,
1159
+ single_use_containers: bool = False,
1158
1160
  i6pn: typing.Optional[bool] = None,
1159
1161
  include_source: typing.Optional[bool] = None,
1160
1162
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
@@ -1165,6 +1167,7 @@ class App:
1165
1167
  concurrency_limit: typing.Optional[int] = None,
1166
1168
  container_idle_timeout: typing.Optional[int] = None,
1167
1169
  allow_concurrent_inputs: typing.Optional[int] = None,
1170
+ max_inputs: typing.Optional[int] = None,
1168
1171
  _experimental_buffer_containers: typing.Optional[int] = None,
1169
1172
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
1170
1173
  ) -> _FunctionDecoratorType:
@@ -1208,7 +1211,7 @@ class App:
1208
1211
  enable_memory_snapshot: bool = False,
1209
1212
  block_network: bool = False,
1210
1213
  restrict_modal_access: bool = False,
1211
- max_inputs: typing.Optional[int] = None,
1214
+ single_use_containers: bool = False,
1212
1215
  i6pn: typing.Optional[bool] = None,
1213
1216
  include_source: typing.Optional[bool] = None,
1214
1217
  experimental_options: typing.Optional[dict[str, typing.Any]] = None,
@@ -1219,6 +1222,7 @@ class App:
1219
1222
  concurrency_limit: typing.Optional[int] = None,
1220
1223
  container_idle_timeout: typing.Optional[int] = None,
1221
1224
  allow_concurrent_inputs: typing.Optional[int] = None,
1225
+ max_inputs: typing.Optional[int] = None,
1222
1226
  _experimental_buffer_containers: typing.Optional[int] = None,
1223
1227
  _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
1224
1228
  ) -> collections.abc.Callable[[typing.Union[CLS_T, modal.partial_function.PartialFunction]], CLS_T]:
@@ -32,7 +32,7 @@ class _Client:
32
32
  server_url: str,
33
33
  client_type: int,
34
34
  credentials: typing.Optional[tuple[str, str]],
35
- version: str = "1.2.7.dev5",
35
+ version: str = "1.2.7.dev7",
36
36
  ):
37
37
  """mdmd:hidden
38
38
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.2.7.dev5",
166
+ version: str = "1.2.7.dev7",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -17,9 +17,10 @@ from modal.exception import ClientClosed
17
17
  from modal_proto import api_pb2
18
18
 
19
19
  from ._utils.async_utils import synchronize_api
20
+ from ._utils.deprecation import deprecation_error
20
21
  from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES
21
22
  from .client import _Client
22
- from .exception import FilesystemExecutionError, InvalidError
23
+ from .exception import FilesystemExecutionError
23
24
 
24
25
  WRITE_CHUNK_SIZE = 16 * 1024 * 1024 # 16 MiB
25
26
  WRITE_FILE_SIZE_LIMIT = 1024 * 1024 * 1024 # 1 GiB
@@ -46,55 +47,17 @@ T = TypeVar("T", str, bytes)
46
47
 
47
48
 
48
49
  async def _delete_bytes(file: "_FileIO", start: Optional[int] = None, end: Optional[int] = None) -> None:
49
- """Delete a range of bytes from the file.
50
-
51
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
52
- If either is None, the start or end of the file is used, respectively.
50
+ """mdmd:hidden
51
+ This method has been removed.
53
52
  """
54
- assert file._file_descriptor is not None
55
- file._check_closed()
56
- if start is not None and end is not None:
57
- if start >= end:
58
- raise ValueError("start must be less than end")
59
- resp = await file._client.stub.ContainerFilesystemExec(
60
- api_pb2.ContainerFilesystemExecRequest(
61
- file_delete_bytes_request=api_pb2.ContainerFileDeleteBytesRequest(
62
- file_descriptor=file._file_descriptor,
63
- start_inclusive=start,
64
- end_exclusive=end,
65
- ),
66
- task_id=file._task_id,
67
- ),
68
- )
69
- await file._wait(resp.exec_id)
53
+ deprecation_error((2025, 12, 3), "delete_bytes has been removed.")
70
54
 
71
55
 
72
56
  async def _replace_bytes(file: "_FileIO", data: bytes, start: Optional[int] = None, end: Optional[int] = None) -> None:
73
- """Replace a range of bytes in the file with new data. The length of the data does not
74
- have to be the same as the length of the range being replaced.
75
-
76
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
77
- If either is None, the start or end of the file is used, respectively.
57
+ """mdmd:hidden
58
+ This method has been removed.
78
59
  """
79
- assert file._file_descriptor is not None
80
- file._check_closed()
81
- if start is not None and end is not None:
82
- if start >= end:
83
- raise InvalidError("start must be less than end")
84
- if len(data) > WRITE_CHUNK_SIZE:
85
- raise InvalidError("Write request payload exceeds 16 MiB limit")
86
- resp = await file._client.stub.ContainerFilesystemExec(
87
- api_pb2.ContainerFilesystemExecRequest(
88
- file_write_replace_bytes_request=api_pb2.ContainerFileWriteReplaceBytesRequest(
89
- file_descriptor=file._file_descriptor,
90
- data=data,
91
- start_inclusive=start,
92
- end_exclusive=end,
93
- ),
94
- task_id=file._task_id,
95
- ),
96
- )
97
- await file._wait(resp.exec_id)
60
+ deprecation_error((2025, 12, 3), "replace_bytes has been removed.")
98
61
 
99
62
 
100
63
  class FileWatchEventType(enum.Enum):
@@ -7,21 +7,16 @@ import typing_extensions
7
7
  T = typing.TypeVar("T")
8
8
 
9
9
  async def _delete_bytes(file: _FileIO, start: typing.Optional[int] = None, end: typing.Optional[int] = None) -> None:
10
- """Delete a range of bytes from the file.
11
-
12
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
13
- If either is None, the start or end of the file is used, respectively.
10
+ """mdmd:hidden
11
+ This method has been removed.
14
12
  """
15
13
  ...
16
14
 
17
15
  async def _replace_bytes(
18
16
  file: _FileIO, data: bytes, start: typing.Optional[int] = None, end: typing.Optional[int] = None
19
17
  ) -> None:
20
- """Replace a range of bytes in the file with new data. The length of the data does not
21
- have to be the same as the length of the range being replaced.
22
-
23
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
24
- If either is None, the start or end of the file is used, respectively.
18
+ """mdmd:hidden
19
+ This method has been removed.
25
20
  """
26
21
  ...
27
22
 
@@ -174,18 +169,14 @@ class _FileIO(typing.Generic[T]):
174
169
 
175
170
  class __delete_bytes_spec(typing_extensions.Protocol):
176
171
  def __call__(self, /, file: FileIO, start: typing.Optional[int] = None, end: typing.Optional[int] = None) -> None:
177
- """Delete a range of bytes from the file.
178
-
179
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
180
- If either is None, the start or end of the file is used, respectively.
172
+ """mdmd:hidden
173
+ This method has been removed.
181
174
  """
182
175
  ...
183
176
 
184
177
  async def aio(self, /, file: FileIO, start: typing.Optional[int] = None, end: typing.Optional[int] = None) -> None:
185
- """Delete a range of bytes from the file.
186
-
187
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
188
- If either is None, the start or end of the file is used, respectively.
178
+ """mdmd:hidden
179
+ This method has been removed.
189
180
  """
190
181
  ...
191
182
 
@@ -195,22 +186,16 @@ class __replace_bytes_spec(typing_extensions.Protocol):
195
186
  def __call__(
196
187
  self, /, file: FileIO, data: bytes, start: typing.Optional[int] = None, end: typing.Optional[int] = None
197
188
  ) -> None:
198
- """Replace a range of bytes in the file with new data. The length of the data does not
199
- have to be the same as the length of the range being replaced.
200
-
201
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
202
- If either is None, the start or end of the file is used, respectively.
189
+ """mdmd:hidden
190
+ This method has been removed.
203
191
  """
204
192
  ...
205
193
 
206
194
  async def aio(
207
195
  self, /, file: FileIO, data: bytes, start: typing.Optional[int] = None, end: typing.Optional[int] = None
208
196
  ) -> None:
209
- """Replace a range of bytes in the file with new data. The length of the data does not
210
- have to be the same as the length of the range being replaced.
211
-
212
- `start` and `end` are byte offsets. `start` is inclusive, `end` is exclusive.
213
- If either is None, the start or end of the file is used, respectively.
197
+ """mdmd:hidden
198
+ This method has been removed.
214
199
  """
215
200
  ...
216
201
 
@@ -106,7 +106,7 @@ class Function(
106
106
  i6pn_enabled: bool = False,
107
107
  cluster_size: typing.Optional[int] = None,
108
108
  rdma: typing.Optional[bool] = None,
109
- max_inputs: typing.Optional[int] = None,
109
+ single_use_containers: bool = False,
110
110
  ephemeral_disk: typing.Optional[int] = None,
111
111
  include_source: bool = True,
112
112
  experimental_options: typing.Optional[dict[str, str]] = None,
@@ -409,7 +409,7 @@ class Function(
409
409
 
410
410
  _call_generator: ___call_generator_spec[typing_extensions.Self]
411
411
 
412
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
412
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
413
413
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
414
414
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
415
415
  ...
@@ -418,7 +418,7 @@ class Function(
418
418
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
419
419
  ...
420
420
 
421
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
421
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
422
422
 
423
423
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
424
424
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -445,7 +445,7 @@ class Function(
445
445
  """
446
446
  ...
447
447
 
448
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
448
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
450
450
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
451
451
 
@@ -469,7 +469,7 @@ class Function(
469
469
  ...
470
470
 
471
471
  _experimental_spawn: ___experimental_spawn_spec[
472
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
472
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
473
473
  ]
474
474
 
475
475
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -478,7 +478,7 @@ class Function(
478
478
 
479
479
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
480
480
 
481
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
481
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
482
482
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
483
483
  """Calls the function with the given arguments, without waiting for the results.
484
484
 
@@ -499,7 +499,7 @@ class Function(
499
499
  """
500
500
  ...
501
501
 
502
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
502
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
503
503
 
504
504
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
505
505
  """Return the inner Python object wrapped by this Modal Function."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.7.dev5
3
+ Version: 1.2.7.dev7
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1469,6 +1469,8 @@ message Function {
1469
1469
 
1470
1470
  bool block_network = 44;
1471
1471
 
1472
+ // In the SDK, we've deprecated `max_inputs` (which only every implemented `max_inputs=1`)
1473
+ // in favor of a boolean `single_use_containers` parameter.
1472
1474
  uint32 max_inputs = 46;
1473
1475
 
1474
1476
  repeated S3Mount s3_mounts = 47;
@@ -1556,6 +1558,9 @@ message Function {
1556
1558
  // Attribute on the module with the implementation, which may differ from function_name
1557
1559
  // when the user provided a custom name= for the Function inside the Application namespace
1558
1560
  string implementation_name = 90;
1561
+
1562
+ bool single_use_containers = 91; // When True, containers will shut down after handling a single input
1563
+
1559
1564
  }
1560
1565
 
1561
1566
  message FunctionAsyncInvokeRequest {