modAL 1.2.3.dev10__tar.gz → 1.2.3.dev11__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 (200) hide show
  1. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/PKG-INFO +1 -1
  2. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/client.pyi +2 -2
  3. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/io_streams.py +101 -32
  4. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/io_streams.pyi +30 -5
  5. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/sandbox.py +2 -6
  6. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/PKG-INFO +1 -1
  7. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_version/__init__.py +1 -1
  8. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/LICENSE +0 -0
  9. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/README.md +0 -0
  10. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/__init__.py +0 -0
  11. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/__main__.py +0 -0
  12. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_billing.py +0 -0
  13. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_clustered_functions.py +0 -0
  14. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_clustered_functions.pyi +0 -0
  15. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_container_entrypoint.py +0 -0
  16. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_functions.py +0 -0
  17. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_grpc_client.py +0 -0
  18. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_ipython.py +0 -0
  19. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_load_context.py +0 -0
  20. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_location.py +0 -0
  21. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_object.py +0 -0
  22. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_output.py +0 -0
  23. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_partial_function.py +0 -0
  24. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_pty.py +0 -0
  25. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_resolver.py +0 -0
  26. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_resources.py +0 -0
  27. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/__init__.py +0 -0
  28. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/asgi.py +0 -0
  29. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/container_io_manager.py +0 -0
  30. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/container_io_manager.pyi +0 -0
  31. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/execution_context.py +0 -0
  32. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/execution_context.pyi +0 -0
  33. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  34. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/telemetry.py +0 -0
  35. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_runtime/user_code_imports.py +0 -0
  36. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_serialization.py +0 -0
  37. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_traceback.py +0 -0
  38. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_tunnel.py +0 -0
  39. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_tunnel.pyi +0 -0
  40. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_type_manager.py +0 -0
  41. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/__init__.py +0 -0
  42. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/app_utils.py +0 -0
  43. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/async_utils.py +0 -0
  44. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/auth_token_manager.py +0 -0
  45. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/blob_utils.py +0 -0
  46. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/bytes_io_segment_payload.py +0 -0
  47. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/deprecation.py +0 -0
  48. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/docker_utils.py +0 -0
  49. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/function_utils.py +0 -0
  50. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/git_utils.py +0 -0
  51. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/grpc_testing.py +0 -0
  52. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/grpc_utils.py +0 -0
  53. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/hash_utils.py +0 -0
  54. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/http_utils.py +0 -0
  55. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/jwt_utils.py +0 -0
  56. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/logger.py +0 -0
  57. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/mount_utils.py +0 -0
  58. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/name_utils.py +0 -0
  59. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/package_utils.py +0 -0
  60. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/pattern_utils.py +0 -0
  61. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/rand_pb_testing.py +0 -0
  62. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/shell_utils.py +0 -0
  63. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/task_command_router_client.py +0 -0
  64. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_utils/time_utils.py +0 -0
  65. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_vendor/__init__.py +0 -0
  66. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  67. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_vendor/cloudpickle.py +0 -0
  68. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_vendor/tblib.py +0 -0
  69. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/_watcher.py +0 -0
  70. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/app.py +0 -0
  71. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/app.pyi +0 -0
  72. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/billing.py +0 -0
  73. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/2023.12.312.txt +0 -0
  74. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/2023.12.txt +0 -0
  75. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/2024.04.txt +0 -0
  76. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/2024.10.txt +0 -0
  77. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/2025.06.txt +0 -0
  78. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/PREVIEW.txt +0 -0
  79. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/README.md +0 -0
  80. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/builder/base-images.json +0 -0
  81. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/call_graph.py +0 -0
  82. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/__init__.py +0 -0
  83. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/_download.py +0 -0
  84. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/_traceback.py +0 -0
  85. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/app.py +0 -0
  86. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/cluster.py +0 -0
  87. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/config.py +0 -0
  88. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/container.py +0 -0
  89. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/dict.py +0 -0
  90. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/entry_point.py +0 -0
  91. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/environment.py +0 -0
  92. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/import_refs.py +0 -0
  93. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/launch.py +0 -0
  94. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/network_file_system.py +0 -0
  95. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/profile.py +0 -0
  96. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/programs/__init__.py +0 -0
  97. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/programs/launch_instance_ssh.py +0 -0
  98. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/programs/run_jupyter.py +0 -0
  99. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/programs/run_marimo.py +0 -0
  100. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/programs/vscode.py +0 -0
  101. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/queues.py +0 -0
  102. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/run.py +0 -0
  103. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/secret.py +0 -0
  104. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/token.py +0 -0
  105. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/utils.py +0 -0
  106. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cli/volume.py +0 -0
  107. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/client.py +0 -0
  108. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cloud_bucket_mount.py +0 -0
  109. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cloud_bucket_mount.pyi +0 -0
  110. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cls.py +0 -0
  111. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/cls.pyi +0 -0
  112. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/config.py +0 -0
  113. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/container_process.py +0 -0
  114. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/container_process.pyi +0 -0
  115. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/dict.py +0 -0
  116. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/dict.pyi +0 -0
  117. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/environments.py +0 -0
  118. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/environments.pyi +0 -0
  119. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/exception.py +0 -0
  120. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/experimental/__init__.py +0 -0
  121. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/experimental/flash.py +0 -0
  122. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/experimental/flash.pyi +0 -0
  123. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/experimental/ipython.py +0 -0
  124. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/file_io.py +0 -0
  125. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/file_io.pyi +0 -0
  126. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/file_pattern_matcher.py +0 -0
  127. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/functions.py +0 -0
  128. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/functions.pyi +0 -0
  129. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/gpu.py +0 -0
  130. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/image.py +0 -0
  131. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/image.pyi +0 -0
  132. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/mount.py +0 -0
  133. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/mount.pyi +0 -0
  134. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/network_file_system.py +0 -0
  135. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/network_file_system.pyi +0 -0
  136. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/object.py +0 -0
  137. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/object.pyi +0 -0
  138. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/output.py +0 -0
  139. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/parallel_map.py +0 -0
  140. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/parallel_map.pyi +0 -0
  141. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/partial_function.py +0 -0
  142. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/partial_function.pyi +0 -0
  143. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/proxy.py +0 -0
  144. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/proxy.pyi +0 -0
  145. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/py.typed +0 -0
  146. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/queue.py +0 -0
  147. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/queue.pyi +0 -0
  148. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/retries.py +0 -0
  149. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/runner.py +0 -0
  150. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/runner.pyi +0 -0
  151. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/running_app.py +0 -0
  152. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/sandbox.pyi +0 -0
  153. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/schedule.py +0 -0
  154. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/scheduler_placement.py +0 -0
  155. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/secret.py +0 -0
  156. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/secret.pyi +0 -0
  157. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/serving.py +0 -0
  158. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/serving.pyi +0 -0
  159. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/snapshot.py +0 -0
  160. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/snapshot.pyi +0 -0
  161. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/stream_type.py +0 -0
  162. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/token_flow.py +0 -0
  163. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/token_flow.pyi +0 -0
  164. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/volume.py +0 -0
  165. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal/volume.pyi +0 -0
  166. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/SOURCES.txt +0 -0
  167. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/dependency_links.txt +0 -0
  168. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/entry_points.txt +0 -0
  169. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/requires.txt +0 -0
  170. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal.egg-info/top_level.txt +0 -0
  171. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/__init__.py +0 -0
  172. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/gen_cli_docs.py +0 -0
  173. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/gen_reference_docs.py +0 -0
  174. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/mdmd/__init__.py +0 -0
  175. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/mdmd/mdmd.py +0 -0
  176. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_docs/mdmd/signatures.py +0 -0
  177. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/__init__.py +0 -0
  178. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api.proto +0 -0
  179. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api_grpc.py +0 -0
  180. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api_pb2.py +0 -0
  181. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api_pb2.pyi +0 -0
  182. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api_pb2_grpc.py +0 -0
  183. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/api_pb2_grpc.pyi +0 -0
  184. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/modal_api_grpc.py +0 -0
  185. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/py.typed +0 -0
  186. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router.proto +0 -0
  187. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router_grpc.py +0 -0
  188. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2.py +0 -0
  189. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2.pyi +0 -0
  190. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
  191. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
  192. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router.proto +0 -0
  193. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router_grpc.py +0 -0
  194. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2.py +0 -0
  195. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2.pyi +0 -0
  196. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  197. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  198. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/modal_version/__main__.py +0 -0
  199. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/pyproject.toml +0 -0
  200. {modal-1.2.3.dev10 → modal-1.2.3.dev11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.3.dev10
3
+ Version: 1.2.3.dev11
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.2.3.dev10",
36
+ version: str = "1.2.3.dev11",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.2.3.dev10",
167
+ version: str = "1.2.3.dev11",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,6 +1,8 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  import codecs
4
+ import contextlib
5
+ import sys
4
6
  import time
5
7
  from collections.abc import AsyncGenerator, AsyncIterator
6
8
  from dataclasses import dataclass
@@ -377,7 +379,7 @@ async def _stdio_stream_from_command_router(
377
379
  return
378
380
 
379
381
 
380
- class _BytesStreamReaderThroughCommandRouter(Generic[T]):
382
+ class _BytesStreamReaderThroughCommandRouter:
381
383
  """
382
384
  StreamReader implementation that will read directly from the worker that
383
385
  hosts the sandbox.
@@ -396,27 +398,27 @@ class _BytesStreamReaderThroughCommandRouter(Generic[T]):
396
398
  def file_descriptor(self) -> int:
397
399
  return self._params.file_descriptor
398
400
 
399
- async def read(self) -> T:
401
+ async def read(self) -> bytes:
400
402
  data_bytes = b""
401
403
  async for part in self:
402
404
  data_bytes += cast(bytes, part)
403
- return cast(T, data_bytes)
405
+ return data_bytes
404
406
 
405
- def __aiter__(self) -> AsyncIterator[T]:
407
+ def __aiter__(self) -> AsyncIterator[bytes]:
406
408
  return self
407
409
 
408
- async def __anext__(self) -> T:
410
+ async def __anext__(self) -> bytes:
409
411
  if self._stream is None:
410
412
  self._stream = _stdio_stream_from_command_router(self._params)
411
413
  # This raises StopAsyncIteration if the stream is at EOF.
412
- return cast(T, await self._stream.__anext__())
414
+ return await self._stream.__anext__()
413
415
 
414
416
  async def aclose(self):
415
417
  if self._stream:
416
418
  await self._stream.aclose()
417
419
 
418
420
 
419
- class _TextStreamReaderThroughCommandRouter(Generic[T]):
421
+ class _TextStreamReaderThroughCommandRouter:
420
422
  """
421
423
  StreamReader implementation that will read directly from the worker
422
424
  that hosts the sandbox.
@@ -437,16 +439,16 @@ class _TextStreamReaderThroughCommandRouter(Generic[T]):
437
439
  def file_descriptor(self) -> int:
438
440
  return self._params.file_descriptor
439
441
 
440
- async def read(self) -> T:
442
+ async def read(self) -> str:
441
443
  data_str = ""
442
444
  async for part in self:
443
445
  data_str += cast(str, part)
444
- return cast(T, data_str)
446
+ return data_str
445
447
 
446
- def __aiter__(self) -> AsyncIterator[T]:
448
+ def __aiter__(self) -> AsyncIterator[str]:
447
449
  return self
448
450
 
449
- async def __anext__(self) -> T:
451
+ async def __anext__(self) -> str:
450
452
  if self._stream is None:
451
453
  bytes_stream = _stdio_stream_from_command_router(self._params)
452
454
  if self._by_line:
@@ -454,13 +456,88 @@ class _TextStreamReaderThroughCommandRouter(Generic[T]):
454
456
  else:
455
457
  self._stream = _decode_bytes_stream_to_str(bytes_stream)
456
458
  # This raises StopAsyncIteration if the stream is at EOF.
457
- return cast(T, await self._stream.__anext__())
459
+ return await self._stream.__anext__()
458
460
 
459
461
  async def aclose(self):
460
462
  if self._stream:
461
463
  await self._stream.aclose()
462
464
 
463
465
 
466
+ class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
467
+ """
468
+ StreamReader implementation for StreamType.STDOUT when using the task command router.
469
+
470
+ This mirrors the behavior from the server-backed implementation: the stream is printed to
471
+ the local stdout immediately and is not readable via StreamReader methods.
472
+ """
473
+
474
+ _reader: Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
475
+
476
+ def __init__(
477
+ self,
478
+ reader: Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter],
479
+ ) -> None:
480
+ self._reader = reader
481
+ self._task: Optional[asyncio.Task[None]] = None
482
+ self._closed = False
483
+ # Kick off a background task that reads from the underlying text stream and prints to stdout.
484
+ self._start_printing_task()
485
+
486
+ @property
487
+ def file_descriptor(self) -> int:
488
+ return self._reader.file_descriptor
489
+
490
+ def _start_printing_task(self) -> None:
491
+ async def _run():
492
+ try:
493
+
494
+ def print_text_part(part: Union[str, bytes]) -> None:
495
+ assert isinstance(part, str)
496
+ print(cast(str, part), end="")
497
+
498
+ def print_bytes_part(part: Union[str, bytes]) -> None:
499
+ assert isinstance(part, bytes)
500
+ sys.stdout.buffer.write(cast(bytes, part))
501
+ sys.stdout.buffer.flush()
502
+
503
+ if isinstance(self._reader, _BytesStreamReaderThroughCommandRouter):
504
+ print_part = print_bytes_part
505
+ elif isinstance(self._reader, _TextStreamReaderThroughCommandRouter):
506
+ print_part = print_text_part
507
+ else:
508
+ raise ValueError("Unsupported reader type")
509
+
510
+ async for part in self._reader:
511
+ print_part(part)
512
+ except Exception as e:
513
+ logger.exception(f"Error printing stream: {e}")
514
+ finally:
515
+ closed, self._closed = self._closed, True
516
+ if not closed:
517
+ await self._reader.aclose()
518
+
519
+ self._task = asyncio.create_task(_run())
520
+
521
+ async def read(self) -> T:
522
+ raise InvalidError("Output can only be retrieved using the PIPE stream type.")
523
+
524
+ def __aiter__(self) -> AsyncIterator[T]:
525
+ raise InvalidError("Output can only be retrieved using the PIPE stream type.")
526
+
527
+ async def __anext__(self) -> T:
528
+ raise InvalidError("Output can only be retrieved using the PIPE stream type.")
529
+
530
+ async def aclose(self):
531
+ if self._task is not None:
532
+ self._task.cancel()
533
+ with contextlib.suppress(asyncio.CancelledError):
534
+ await self._task
535
+ self._task = None
536
+ closed, self._closed = self._closed, True
537
+ if not closed:
538
+ await self._reader.aclose()
539
+
540
+
464
541
  class _DevnullStreamReader(Generic[T]):
465
542
  """StreamReader implementation for a stream configured with
466
543
  StreamType.DEVNULL. Throws an error if read or any other method is
@@ -499,6 +576,7 @@ class _StreamReader(Generic[T]):
499
576
  _DevnullStreamReader,
500
577
  _TextStreamReaderThroughCommandRouter,
501
578
  _BytesStreamReaderThroughCommandRouter,
579
+ _StdoutPrintingStreamReaderThroughCommandRouter,
502
580
  ]
503
581
 
504
582
  def __init__(
@@ -523,35 +601,26 @@ class _StreamReader(Generic[T]):
523
601
  file_descriptor, object_id, object_type, client, stream_type, text, by_line, deadline
524
602
  )
525
603
  else:
526
- # The only reason task_id is optional is because StreamReader is
527
- # also used for sandbox logs, which don't have a task ID available
528
- # when the StreamReader is created.
604
+ # The only reason task_id is optional is because StreamReader is also used for sandbox
605
+ # logs, which don't have a task ID available when the StreamReader is created.
529
606
  assert task_id is not None
530
607
  assert object_type == "container_process"
531
608
  if stream_type == StreamType.DEVNULL:
532
609
  self._impl = _DevnullStreamReader(file_descriptor)
533
610
  else:
534
611
  assert stream_type == StreamType.PIPE or stream_type == StreamType.STDOUT
535
- # TODO(saltzm): The original implementation of STDOUT StreamType in
536
- # _StreamReaderThroughServer prints to stdout immediately. This doesn't match
537
- # python subprocess.run, which uses None to print to stdout immediately, and uses
538
- # STDOUT as an argument to stderr to redirect stderr to the stdout stream. We should
539
- # implement the old behavior here before moving out of beta, but after that
540
- # we should consider changing the API to match python subprocess.run. I don't expect
541
- # many customers are using this in any case, so I think it's fine to leave this
542
- # unimplemented for now.
543
- if stream_type == StreamType.STDOUT:
544
- raise NotImplementedError(
545
- "Currently the STDOUT stream type is not supported when using exec "
546
- "through a task command router, which is currently in beta."
547
- )
548
612
  params = _StreamReaderThroughCommandRouterParams(
549
613
  file_descriptor, task_id, object_id, command_router_client, deadline
550
614
  )
551
615
  if text:
552
- self._impl = _TextStreamReaderThroughCommandRouter(params, by_line)
616
+ reader = _TextStreamReaderThroughCommandRouter(params, by_line)
617
+ else:
618
+ reader = _BytesStreamReaderThroughCommandRouter(params)
619
+
620
+ if stream_type == StreamType.STDOUT:
621
+ self._impl = _StdoutPrintingStreamReaderThroughCommandRouter(reader)
553
622
  else:
554
- self._impl = _BytesStreamReaderThroughCommandRouter(params)
623
+ self._impl = reader
555
624
 
556
625
  @property
557
626
  def file_descriptor(self) -> int:
@@ -560,7 +629,7 @@ class _StreamReader(Generic[T]):
560
629
 
561
630
  async def read(self) -> T:
562
631
  """Fetch the entire contents of the stream until EOF."""
563
- return await self._impl.read()
632
+ return cast(T, await self._impl.read())
564
633
 
565
634
  # TODO(saltzm): I'd prefer to have the implementation classes only implement __aiter__
566
635
  # and have them return generator functions directly, but synchronicity doesn't let us
@@ -572,7 +641,7 @@ class _StreamReader(Generic[T]):
572
641
 
573
642
  async def __anext__(self) -> T:
574
643
  """mdmd:hidden"""
575
- return await self._impl.__anext__()
644
+ return cast(T, await self._impl.__anext__())
576
645
 
577
646
  async def aclose(self):
578
647
  """mdmd:hidden"""
@@ -127,7 +127,7 @@ def _stdio_stream_from_command_router(
127
127
  """Stream raw bytes from the router client."""
128
128
  ...
129
129
 
130
- class _BytesStreamReaderThroughCommandRouter(typing.Generic[T]):
130
+ class _BytesStreamReaderThroughCommandRouter:
131
131
  """StreamReader implementation that will read directly from the worker that
132
132
  hosts the sandbox.
133
133
 
@@ -139,12 +139,12 @@ class _BytesStreamReaderThroughCommandRouter(typing.Generic[T]):
139
139
 
140
140
  @property
141
141
  def file_descriptor(self) -> int: ...
142
- async def read(self) -> T: ...
143
- def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
144
- async def __anext__(self) -> T: ...
142
+ async def read(self) -> bytes: ...
143
+ def __aiter__(self) -> collections.abc.AsyncIterator[bytes]: ...
144
+ async def __anext__(self) -> bytes: ...
145
145
  async def aclose(self): ...
146
146
 
147
- class _TextStreamReaderThroughCommandRouter(typing.Generic[T]):
147
+ class _TextStreamReaderThroughCommandRouter:
148
148
  """StreamReader implementation that will read directly from the worker
149
149
  that hosts the sandbox.
150
150
 
@@ -156,6 +156,29 @@ class _TextStreamReaderThroughCommandRouter(typing.Generic[T]):
156
156
 
157
157
  @property
158
158
  def file_descriptor(self) -> int: ...
159
+ async def read(self) -> str: ...
160
+ def __aiter__(self) -> collections.abc.AsyncIterator[str]: ...
161
+ async def __anext__(self) -> str: ...
162
+ async def aclose(self): ...
163
+
164
+ class _StdoutPrintingStreamReaderThroughCommandRouter(typing.Generic[T]):
165
+ """StreamReader implementation for StreamType.STDOUT when using the task command router.
166
+
167
+ This mirrors the behavior from the server-backed implementation: the stream is printed to
168
+ the local stdout immediately and is not readable via StreamReader methods.
169
+ """
170
+
171
+ _reader: typing.Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
172
+
173
+ def __init__(
174
+ self, reader: typing.Union[_TextStreamReaderThroughCommandRouter, _BytesStreamReaderThroughCommandRouter]
175
+ ) -> None:
176
+ """Initialize self. See help(type(self)) for accurate signature."""
177
+ ...
178
+
179
+ @property
180
+ def file_descriptor(self) -> int: ...
181
+ def _start_printing_task(self) -> None: ...
159
182
  async def read(self) -> T: ...
160
183
  def __aiter__(self) -> collections.abc.AsyncIterator[T]: ...
161
184
  async def __anext__(self) -> T: ...
@@ -189,6 +212,7 @@ class _StreamReader(typing.Generic[T]):
189
212
  _DevnullStreamReader,
190
213
  _TextStreamReaderThroughCommandRouter,
191
214
  _BytesStreamReaderThroughCommandRouter,
215
+ _StdoutPrintingStreamReaderThroughCommandRouter,
192
216
  ]
193
217
 
194
218
  def __init__(
@@ -357,6 +381,7 @@ class StreamReader(typing.Generic[T]):
357
381
  _DevnullStreamReader,
358
382
  _TextStreamReaderThroughCommandRouter,
359
383
  _BytesStreamReaderThroughCommandRouter,
384
+ _StdoutPrintingStreamReaderThroughCommandRouter,
360
385
  ]
361
386
 
362
387
  def __init__(
@@ -928,12 +928,8 @@ class _Sandbox(_Object, type_prefix="sb"):
928
928
  elif stdout == StreamType.DEVNULL:
929
929
  stdout_config = sr_pb2.TaskExecStdoutConfig.TASK_EXEC_STDOUT_CONFIG_DEVNULL
930
930
  elif stdout == StreamType.STDOUT:
931
- # TODO(saltzm): This is a behavior change from the old implementation. We should
932
- # probably implement the old behavior of printing to stdout before moving out of beta.
933
- raise NotImplementedError(
934
- "Currently the STDOUT stream type is not supported when using exec "
935
- "through a task command router, which is currently in beta."
936
- )
931
+ # Stream stdout to the client so that it can be printed locally in the reader.
932
+ stdout_config = sr_pb2.TaskExecStdoutConfig.TASK_EXEC_STDOUT_CONFIG_PIPE
937
933
  else:
938
934
  raise ValueError("Unsupported StreamType for stdout")
939
935
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.3.dev10
3
+ Version: 1.2.3.dev11
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.3.dev10"
4
+ __version__ = "1.2.3.dev11"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes