modal 1.2.5.dev4__tar.gz → 1.2.5.dev6__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 (202) hide show
  1. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/PKG-INFO +1 -1
  2. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/grpc_utils.py +3 -1
  3. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/task_command_router_client.py +8 -6
  4. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/client.pyi +2 -2
  5. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/functions.pyi +6 -6
  6. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/io_streams.py +71 -93
  7. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/io_streams.pyi +28 -26
  8. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/PKG-INFO +1 -1
  9. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_version/__init__.py +1 -1
  10. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/LICENSE +0 -0
  11. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/README.md +0 -0
  12. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/__init__.py +0 -0
  13. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/__main__.py +0 -0
  14. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_billing.py +0 -0
  15. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_clustered_functions.py +0 -0
  16. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_clustered_functions.pyi +0 -0
  17. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_container_entrypoint.py +0 -0
  18. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_functions.py +0 -0
  19. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_grpc_client.py +0 -0
  20. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_ipython.py +0 -0
  21. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_load_context.py +0 -0
  22. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_location.py +0 -0
  23. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_object.py +0 -0
  24. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_output.py +0 -0
  25. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_partial_function.py +0 -0
  26. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_pty.py +0 -0
  27. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_resolver.py +0 -0
  28. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_resources.py +0 -0
  29. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/__init__.py +0 -0
  30. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/asgi.py +0 -0
  31. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/container_io_manager.py +0 -0
  32. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/container_io_manager.pyi +0 -0
  33. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/execution_context.py +0 -0
  34. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/execution_context.pyi +0 -0
  35. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  36. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/telemetry.py +0 -0
  37. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/user_code_event_loop.py +0 -0
  38. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_runtime/user_code_imports.py +0 -0
  39. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_serialization.py +0 -0
  40. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_traceback.py +0 -0
  41. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_tunnel.py +0 -0
  42. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_tunnel.pyi +0 -0
  43. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_type_manager.py +0 -0
  44. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/__init__.py +0 -0
  45. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/app_utils.py +0 -0
  46. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/async_utils.py +0 -0
  47. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/auth_token_manager.py +0 -0
  48. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/blob_utils.py +0 -0
  49. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/bytes_io_segment_payload.py +0 -0
  50. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/deprecation.py +0 -0
  51. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/docker_utils.py +0 -0
  52. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/function_utils.py +0 -0
  53. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/git_utils.py +0 -0
  54. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/grpc_testing.py +0 -0
  55. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/hash_utils.py +0 -0
  56. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/http_utils.py +0 -0
  57. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/jwt_utils.py +0 -0
  58. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/logger.py +0 -0
  59. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/mount_utils.py +0 -0
  60. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/name_utils.py +0 -0
  61. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/package_utils.py +0 -0
  62. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/pattern_utils.py +0 -0
  63. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/rand_pb_testing.py +0 -0
  64. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/shell_utils.py +0 -0
  65. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_utils/time_utils.py +0 -0
  66. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_vendor/__init__.py +0 -0
  67. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  68. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_vendor/cloudpickle.py +0 -0
  69. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_vendor/tblib.py +0 -0
  70. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/_watcher.py +0 -0
  71. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/app.py +0 -0
  72. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/app.pyi +0 -0
  73. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/billing.py +0 -0
  74. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/2023.12.312.txt +0 -0
  75. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/2023.12.txt +0 -0
  76. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/2024.04.txt +0 -0
  77. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/2024.10.txt +0 -0
  78. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/2025.06.txt +0 -0
  79. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/PREVIEW.txt +0 -0
  80. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/README.md +0 -0
  81. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/builder/base-images.json +0 -0
  82. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/call_graph.py +0 -0
  83. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/__init__.py +0 -0
  84. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/_download.py +0 -0
  85. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/_traceback.py +0 -0
  86. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/app.py +0 -0
  87. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/cluster.py +0 -0
  88. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/config.py +0 -0
  89. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/container.py +0 -0
  90. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/dict.py +0 -0
  91. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/entry_point.py +0 -0
  92. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/environment.py +0 -0
  93. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/import_refs.py +0 -0
  94. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/launch.py +0 -0
  95. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/network_file_system.py +0 -0
  96. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/profile.py +0 -0
  97. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/programs/__init__.py +0 -0
  98. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/programs/launch_instance_ssh.py +0 -0
  99. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/programs/run_jupyter.py +0 -0
  100. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/programs/run_marimo.py +0 -0
  101. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/programs/vscode.py +0 -0
  102. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/queues.py +0 -0
  103. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/run.py +0 -0
  104. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/secret.py +0 -0
  105. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/shell.py +0 -0
  106. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/token.py +0 -0
  107. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/utils.py +0 -0
  108. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cli/volume.py +0 -0
  109. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/client.py +0 -0
  110. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cloud_bucket_mount.py +0 -0
  111. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cloud_bucket_mount.pyi +0 -0
  112. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cls.py +0 -0
  113. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/cls.pyi +0 -0
  114. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/config.py +0 -0
  115. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/container_process.py +0 -0
  116. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/container_process.pyi +0 -0
  117. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/dict.py +0 -0
  118. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/dict.pyi +0 -0
  119. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/environments.py +0 -0
  120. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/environments.pyi +0 -0
  121. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/exception.py +0 -0
  122. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/experimental/__init__.py +0 -0
  123. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/experimental/flash.py +0 -0
  124. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/experimental/flash.pyi +0 -0
  125. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/experimental/ipython.py +0 -0
  126. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/file_io.py +0 -0
  127. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/file_io.pyi +0 -0
  128. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/file_pattern_matcher.py +0 -0
  129. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/functions.py +0 -0
  130. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/gpu.py +0 -0
  131. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/image.py +0 -0
  132. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/image.pyi +0 -0
  133. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/mount.py +0 -0
  134. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/mount.pyi +0 -0
  135. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/network_file_system.py +0 -0
  136. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/network_file_system.pyi +0 -0
  137. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/object.py +0 -0
  138. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/object.pyi +0 -0
  139. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/output.py +0 -0
  140. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/parallel_map.py +0 -0
  141. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/parallel_map.pyi +0 -0
  142. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/partial_function.py +0 -0
  143. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/partial_function.pyi +0 -0
  144. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/proxy.py +0 -0
  145. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/proxy.pyi +0 -0
  146. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/py.typed +0 -0
  147. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/queue.py +0 -0
  148. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/queue.pyi +0 -0
  149. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/retries.py +0 -0
  150. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/runner.py +0 -0
  151. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/runner.pyi +0 -0
  152. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/running_app.py +0 -0
  153. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/sandbox.py +0 -0
  154. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/sandbox.pyi +0 -0
  155. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/schedule.py +0 -0
  156. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/scheduler_placement.py +0 -0
  157. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/secret.py +0 -0
  158. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/secret.pyi +0 -0
  159. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/serving.py +0 -0
  160. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/serving.pyi +0 -0
  161. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/snapshot.py +0 -0
  162. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/snapshot.pyi +0 -0
  163. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/stream_type.py +0 -0
  164. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/token_flow.py +0 -0
  165. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/token_flow.pyi +0 -0
  166. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/volume.py +0 -0
  167. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal/volume.pyi +0 -0
  168. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/SOURCES.txt +0 -0
  169. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/dependency_links.txt +0 -0
  170. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/entry_points.txt +0 -0
  171. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/requires.txt +0 -0
  172. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal.egg-info/top_level.txt +0 -0
  173. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/__init__.py +0 -0
  174. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/gen_cli_docs.py +0 -0
  175. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/gen_reference_docs.py +0 -0
  176. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/mdmd/__init__.py +0 -0
  177. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/mdmd/mdmd.py +0 -0
  178. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_docs/mdmd/signatures.py +0 -0
  179. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/__init__.py +0 -0
  180. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api.proto +0 -0
  181. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api_grpc.py +0 -0
  182. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api_pb2.py +0 -0
  183. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api_pb2.pyi +0 -0
  184. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api_pb2_grpc.py +0 -0
  185. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/api_pb2_grpc.pyi +0 -0
  186. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/modal_api_grpc.py +0 -0
  187. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/py.typed +0 -0
  188. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router.proto +0 -0
  189. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router_grpc.py +0 -0
  190. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router_pb2.py +0 -0
  191. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router_pb2.pyi +0 -0
  192. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
  193. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
  194. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router.proto +0 -0
  195. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router_grpc.py +0 -0
  196. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router_pb2.py +0 -0
  197. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router_pb2.pyi +0 -0
  198. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  199. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  200. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/modal_version/__main__.py +0 -0
  201. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/pyproject.toml +0 -0
  202. {modal-1.2.5.dev4 → modal-1.2.5.dev6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev4
3
+ Version: 1.2.5.dev6
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -1,6 +1,7 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  import contextlib
4
+ import os
4
5
  import platform
5
6
  import socket
6
7
  import time
@@ -396,7 +397,8 @@ async def _retry_transient_errors(
396
397
  ):
397
398
  last_server_retry_warning_time = now
398
399
  logger.warning(
399
- f"Warning: Received {exc.status} status: {exc.message}. "
400
+ f"Warning: Received {exc.status}{os.linesep}"
401
+ f"{exc.message}{os.linesep}"
400
402
  f"Will retry in {server_delay:0.2f} seconds."
401
403
  )
402
404
 
@@ -5,7 +5,7 @@ import json
5
5
  import ssl
6
6
  import time
7
7
  import urllib.parse
8
- from typing import AsyncIterator, Optional
8
+ from typing import AsyncGenerator, Optional
9
9
 
10
10
  import grpclib.client
11
11
  import grpclib.config
@@ -18,6 +18,7 @@ from modal.exception import ExecTimeoutError
18
18
  from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
19
19
  from modal_proto.task_command_router_grpc import TaskCommandRouterStub
20
20
 
21
+ from .async_utils import aclosing
21
22
  from .grpc_utils import RETRYABLE_GRPC_STATUS_CODES, connect_channel
22
23
 
23
24
 
@@ -242,7 +243,7 @@ class TaskCommandRouterClient:
242
243
  # Quotes around the type required for protobuf 3.19.
243
244
  file_descriptor: "api_pb2.FileDescriptor.ValueType",
244
245
  deadline: Optional[float] = None,
245
- ) -> AsyncIterator[sr_pb2.TaskExecStdioReadResponse]:
246
+ ) -> AsyncGenerator[sr_pb2.TaskExecStdioReadResponse, None]:
246
247
  """Stream stdout/stderr batches from the task, properly retrying on transient errors.
247
248
 
248
249
  Args:
@@ -253,7 +254,7 @@ class TaskCommandRouterClient:
253
254
  None, wait forever. If the deadline is exceeded, raises an
254
255
  ExecTimeoutError.
255
256
  Returns:
256
- AsyncIterator[sr_pb2.TaskExecStdioReadResponse]: A stream of stdout/stderr batches.
257
+ AsyncGenerator[sr_pb2.TaskExecStdioReadResponse, None]: A stream of stdout/stderr batches.
257
258
  Raises:
258
259
  ExecTimeoutError: If the deadline is exceeded.
259
260
  Other errors: If retries are exhausted on transient errors or if there's an error
@@ -268,8 +269,9 @@ class TaskCommandRouterClient:
268
269
  else:
269
270
  raise ValueError(f"Invalid file descriptor: {file_descriptor}")
270
271
 
271
- async for item in self._stream_stdio(task_id, exec_id, sr_fd, deadline):
272
- yield item
272
+ async with aclosing(self._stream_stdio(task_id, exec_id, sr_fd, deadline)) as stream:
273
+ async for item in stream:
274
+ yield item
273
275
 
274
276
  async def exec_stdin_write(
275
277
  self, task_id: str, exec_id: str, offset: int, data: bytes, eof: bool
@@ -453,7 +455,7 @@ class TaskCommandRouterClient:
453
455
  # Quotes around the type required for protobuf 3.19.
454
456
  file_descriptor: "sr_pb2.TaskExecStdioFileDescriptor.ValueType",
455
457
  deadline: Optional[float] = None,
456
- ) -> AsyncIterator[sr_pb2.TaskExecStdioReadResponse]:
458
+ ) -> AsyncGenerator[sr_pb2.TaskExecStdioReadResponse, None]:
457
459
  """Stream stdio from the task, properly updating the offset and retrying on transient errors.
458
460
  Raises ExecTimeoutError if the deadline is exceeded.
459
461
  """
@@ -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.5.dev4",
35
+ version: str = "1.2.5.dev6",
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.5.dev4",
166
+ version: str = "1.2.5.dev6",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -408,7 +408,7 @@ class Function(
408
408
 
409
409
  _call_generator: ___call_generator_spec[typing_extensions.Self]
410
410
 
411
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
411
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
412
412
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
413
413
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
414
414
  ...
@@ -417,7 +417,7 @@ class Function(
417
417
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
418
418
  ...
419
419
 
420
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
420
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
421
421
 
422
422
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
423
423
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -444,7 +444,7 @@ class Function(
444
444
  """
445
445
  ...
446
446
 
447
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
447
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
448
448
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
449
449
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
450
450
 
@@ -468,7 +468,7 @@ class Function(
468
468
  ...
469
469
 
470
470
  _experimental_spawn: ___experimental_spawn_spec[
471
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
471
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
472
472
  ]
473
473
 
474
474
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -477,7 +477,7 @@ class Function(
477
477
 
478
478
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
479
479
 
480
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
480
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
481
481
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
482
482
  """Calls the function with the given arguments, without waiting for the results.
483
483
 
@@ -498,7 +498,7 @@ class Function(
498
498
  """
499
499
  ...
500
500
 
501
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
501
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
502
502
 
503
503
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
504
504
  """Return the inner Python object wrapped by this Modal Function."""
@@ -11,6 +11,7 @@ from typing import (
11
11
  Generic,
12
12
  Literal,
13
13
  Optional,
14
+ TextIO,
14
15
  TypeVar,
15
16
  Union,
16
17
  cast,
@@ -22,7 +23,7 @@ from grpclib.exceptions import GRPCError, StreamTerminatedError
22
23
  from modal.exception import ClientClosed, ExecTimeoutError, InvalidError
23
24
  from modal_proto import api_pb2
24
25
 
25
- from ._utils.async_utils import synchronize_api
26
+ from ._utils.async_utils import aclosing, synchronize_api
26
27
  from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES
27
28
  from ._utils.task_command_router_client import TaskCommandRouterClient
28
29
  from .client import _Client
@@ -292,7 +293,7 @@ class _StreamReaderThroughServer(Generic[T]):
292
293
  yield self._line_buffer
293
294
  self._line_buffer = b""
294
295
 
295
- def _ensure_stream(self) -> AsyncGenerator[T, None]:
296
+ def __aiter__(self) -> AsyncGenerator[T, None]:
296
297
  if not self._stream:
297
298
  if self._by_line:
298
299
  # TODO: This is quite odd - it does line buffering in binary mode
@@ -306,11 +307,6 @@ class _StreamReaderThroughServer(Generic[T]):
306
307
  self._stream = cast(AsyncGenerator[T, None], stream)
307
308
  return self._stream
308
309
 
309
- async def __anext__(self) -> T:
310
- """mdmd:hidden"""
311
- stream = self._ensure_stream()
312
- return cast(T, await stream.__anext__())
313
-
314
310
  async def aclose(self):
315
311
  """mdmd:hidden"""
316
312
  if self._stream:
@@ -336,17 +332,23 @@ async def _decode_bytes_stream_to_str(stream: AsyncGenerator[bytes, None]) -> As
336
332
 
337
333
 
338
334
  async def _stream_by_line(stream: AsyncGenerator[bytes, None]) -> AsyncGenerator[bytes, None]:
339
- """Yield complete lines only (ending with \n), buffering partial lines until complete."""
335
+ """Yield complete lines only (ending with \n), buffering partial lines until complete.
336
+
337
+ When this generator returns, the underlying generator is closed.
338
+ """
340
339
  line_buffer = b""
341
- async for message in stream:
342
- assert isinstance(message, bytes)
343
- line_buffer += message
344
- while b"\n" in line_buffer:
345
- line, line_buffer = line_buffer.split(b"\n", 1)
346
- yield line + b"\n"
340
+ try:
341
+ async for message in stream:
342
+ assert isinstance(message, bytes)
343
+ line_buffer += message
344
+ while b"\n" in line_buffer:
345
+ line, line_buffer = line_buffer.split(b"\n", 1)
346
+ yield line + b"\n"
347
347
 
348
- if line_buffer:
349
- yield line_buffer
348
+ if line_buffer:
349
+ yield line_buffer
350
+ finally:
351
+ await stream.aclose()
350
352
 
351
353
 
352
354
  @dataclass
@@ -362,21 +364,23 @@ async def _stdio_stream_from_command_router(
362
364
  params: _StreamReaderThroughCommandRouterParams,
363
365
  ) -> AsyncGenerator[bytes, None]:
364
366
  """Stream raw bytes from the router client."""
365
- stream = params.command_router_client.exec_stdio_read(
366
- params.task_id, params.object_id, params.file_descriptor, params.deadline
367
- )
368
- try:
369
- async for item in stream:
370
- if len(item.data) == 0:
371
- # This is an error.
372
- raise ValueError("Received empty message streaming stdio from sandbox.")
373
-
374
- yield item.data
375
- except ExecTimeoutError:
376
- logger.debug(f"Deadline exceeded while streaming stdio for exec {params.object_id}")
377
- # TODO(saltzm): This is a weird API, but customers currently may rely on it. We
378
- # should probably raise this error rather than just ending the stream.
379
- return
367
+ async with aclosing(
368
+ params.command_router_client.exec_stdio_read(
369
+ params.task_id, params.object_id, params.file_descriptor, params.deadline
370
+ )
371
+ ) as stream:
372
+ try:
373
+ async for item in stream:
374
+ if len(item.data) == 0:
375
+ # This is an error.
376
+ raise ValueError("Received empty message streaming stdio from sandbox.")
377
+
378
+ yield item.data
379
+ except ExecTimeoutError:
380
+ logger.debug(f"Deadline exceeded while streaming stdio for exec {params.object_id}")
381
+ # TODO(saltzm): This is a weird API, but customers currently may rely on it. We
382
+ # should probably raise this error rather than just ending the stream.
383
+ return
380
384
 
381
385
 
382
386
  class _BytesStreamReaderThroughCommandRouter:
@@ -404,18 +408,13 @@ class _BytesStreamReaderThroughCommandRouter:
404
408
  data_bytes += cast(bytes, part)
405
409
  return data_bytes
406
410
 
407
- def __aiter__(self) -> AsyncIterator[bytes]:
408
- return self
411
+ def __aiter__(self) -> AsyncGenerator[bytes, None]:
412
+ return _stdio_stream_from_command_router(self._params)
409
413
 
410
- async def __anext__(self) -> bytes:
411
- if self._stream is None:
412
- self._stream = _stdio_stream_from_command_router(self._params)
413
- # This raises StopAsyncIteration if the stream is at EOF.
414
- return await self._stream.__anext__()
415
-
416
- async def aclose(self):
417
- if self._stream:
418
- await self._stream.aclose()
414
+ async def _print_all(self, output_stream: TextIO) -> None:
415
+ async for part in self:
416
+ output_stream.buffer.write(part)
417
+ output_stream.buffer.flush()
419
418
 
420
419
 
421
420
  class _TextStreamReaderThroughCommandRouter:
@@ -433,7 +432,6 @@ class _TextStreamReaderThroughCommandRouter:
433
432
  ) -> None:
434
433
  self._params = params
435
434
  self._by_line = by_line
436
- self._stream = None
437
435
 
438
436
  @property
439
437
  def file_descriptor(self) -> int:
@@ -445,22 +443,21 @@ class _TextStreamReaderThroughCommandRouter:
445
443
  data_str += cast(str, part)
446
444
  return data_str
447
445
 
448
- def __aiter__(self) -> AsyncIterator[str]:
449
- return self
450
-
451
- async def __anext__(self) -> str:
452
- if self._stream is None:
453
- bytes_stream = _stdio_stream_from_command_router(self._params)
446
+ async def __aiter__(self) -> AsyncGenerator[str, None]:
447
+ async with aclosing(_stdio_stream_from_command_router(self._params)) as bytes_stream:
454
448
  if self._by_line:
455
- self._stream = _decode_bytes_stream_to_str(_stream_by_line(bytes_stream))
449
+ stream = _decode_bytes_stream_to_str(_stream_by_line(bytes_stream))
456
450
  else:
457
- self._stream = _decode_bytes_stream_to_str(bytes_stream)
458
- # This raises StopAsyncIteration if the stream is at EOF.
459
- return await self._stream.__anext__()
451
+ stream = _decode_bytes_stream_to_str(bytes_stream)
460
452
 
461
- async def aclose(self):
462
- if self._stream:
463
- await self._stream.aclose()
453
+ async with aclosing(stream):
454
+ async for part in stream:
455
+ yield part
456
+
457
+ async def _print_all(self, output_stream: TextIO) -> None:
458
+ async with aclosing(self.__aiter__()) as stream:
459
+ async for part in stream:
460
+ output_stream.write(part)
464
461
 
465
462
 
466
463
  class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
@@ -479,7 +476,6 @@ class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
479
476
  ) -> None:
480
477
  self._reader = reader
481
478
  self._task: Optional[asyncio.Task[None]] = None
482
- self._closed = False
483
479
  # Kick off a background task that reads from the underlying text stream and prints to stdout.
484
480
  self._start_printing_task()
485
481
 
@@ -490,31 +486,9 @@ class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
490
486
  def _start_printing_task(self) -> None:
491
487
  async def _run():
492
488
  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)
489
+ await self._reader._print_all(sys.stdout)
512
490
  except Exception as e:
513
491
  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
492
 
519
493
  self._task = asyncio.create_task(_run())
520
494
 
@@ -533,9 +507,6 @@ class _StdoutPrintingStreamReaderThroughCommandRouter(Generic[T]):
533
507
  with contextlib.suppress(asyncio.CancelledError):
534
508
  await self._task
535
509
  self._task = None
536
- closed, self._closed = self._closed, True
537
- if not closed:
538
- await self._reader.aclose()
539
510
 
540
511
 
541
512
  class _DevnullStreamReader(Generic[T]):
@@ -578,6 +549,7 @@ class _StreamReader(Generic[T]):
578
549
  _BytesStreamReaderThroughCommandRouter,
579
550
  _StdoutPrintingStreamReaderThroughCommandRouter,
580
551
  ]
552
+ _read_gen: Optional[AsyncGenerator[T, None]] = None
581
553
 
582
554
  def __init__(
583
555
  self,
@@ -631,21 +603,27 @@ class _StreamReader(Generic[T]):
631
603
  """Fetch the entire contents of the stream until EOF."""
632
604
  return cast(T, await self._impl.read())
633
605
 
634
- # TODO(saltzm): I'd prefer to have the implementation classes only implement __aiter__
635
- # and have them return generator functions directly, but synchronicity doesn't let us
636
- # return self._impl.__aiter__() here because it won't properly wrap the implementation
637
- # classes.
638
- def __aiter__(self) -> AsyncIterator[T]:
639
- """mdmd:hidden"""
640
- return self
606
+ def __aiter__(self) -> AsyncGenerator[T, None]:
607
+ if not self._read_gen:
608
+ self._read_gen = cast(AsyncGenerator[T, None], self._impl.__aiter__())
609
+ return self._read_gen
641
610
 
642
611
  async def __anext__(self) -> T:
643
- """mdmd:hidden"""
644
- return cast(T, await self._impl.__anext__())
612
+ """Deprecated: This exists for backwards compatibility and will be removed in a future version of Modal
613
+
614
+ Only use next/anext on the return value of iter/aiter on the StreamReader object (treat streamreader as
615
+ an iterable, not an iterator).
616
+ """
617
+ if not self._read_gen:
618
+ self.__aiter__() # initialize the read generator
619
+ assert self._read_gen
620
+ return await self._read_gen.__anext__()
645
621
 
646
622
  async def aclose(self):
647
623
  """mdmd:hidden"""
648
- await self._impl.aclose()
624
+ if self._read_gen:
625
+ await self._read_gen.aclose()
626
+ self._read_gen = None
649
627
 
650
628
 
651
629
  MAX_BUFFER_SIZE = 2 * 1024 * 1024
@@ -68,11 +68,7 @@ class _StreamReaderThroughServer(typing.Generic[T]):
68
68
  """Process logs from the server and yield complete lines only."""
69
69
  ...
70
70
 
71
- def _ensure_stream(self) -> collections.abc.AsyncGenerator[T, None]: ...
72
- async def __anext__(self) -> T:
73
- """mdmd:hidden"""
74
- ...
75
-
71
+ def __aiter__(self) -> collections.abc.AsyncGenerator[T, None]: ...
76
72
  async def aclose(self):
77
73
  """mdmd:hidden"""
78
74
  ...
@@ -90,6 +86,9 @@ def _decode_bytes_stream_to_str(
90
86
  def _stream_by_line(stream: collections.abc.AsyncGenerator[bytes, None]) -> collections.abc.AsyncGenerator[bytes, None]:
91
87
  """Yield complete lines only (ending with
92
88
  ), buffering partial lines until complete.
89
+
90
+ When this generator returns, the underlying generator is closed.
91
+
93
92
  """
94
93
  ...
95
94
 
@@ -140,9 +139,8 @@ class _BytesStreamReaderThroughCommandRouter:
140
139
  @property
141
140
  def file_descriptor(self) -> int: ...
142
141
  async def read(self) -> bytes: ...
143
- def __aiter__(self) -> collections.abc.AsyncIterator[bytes]: ...
144
- async def __anext__(self) -> bytes: ...
145
- async def aclose(self): ...
142
+ def __aiter__(self) -> collections.abc.AsyncGenerator[bytes, None]: ...
143
+ async def _print_all(self, output_stream: typing.TextIO) -> None: ...
146
144
 
147
145
  class _TextStreamReaderThroughCommandRouter:
148
146
  """StreamReader implementation that will read directly from the worker
@@ -157,9 +155,8 @@ class _TextStreamReaderThroughCommandRouter:
157
155
  @property
158
156
  def file_descriptor(self) -> int: ...
159
157
  async def read(self) -> str: ...
160
- def __aiter__(self) -> collections.abc.AsyncIterator[str]: ...
161
- async def __anext__(self) -> str: ...
162
- async def aclose(self): ...
158
+ def __aiter__(self) -> collections.abc.AsyncGenerator[str, None]: ...
159
+ async def _print_all(self, output_stream: typing.TextIO) -> None: ...
163
160
 
164
161
  class _StdoutPrintingStreamReaderThroughCommandRouter(typing.Generic[T]):
165
162
  """StreamReader implementation for StreamType.STDOUT when using the task command router.
@@ -214,6 +211,7 @@ class _StreamReader(typing.Generic[T]):
214
211
  _BytesStreamReaderThroughCommandRouter,
215
212
  _StdoutPrintingStreamReaderThroughCommandRouter,
216
213
  ]
214
+ _read_gen: typing.Optional[collections.abc.AsyncGenerator[T, None]]
217
215
 
218
216
  def __init__(
219
217
  self,
@@ -240,12 +238,13 @@ class _StreamReader(typing.Generic[T]):
240
238
  """Fetch the entire contents of the stream until EOF."""
241
239
  ...
242
240
 
243
- def __aiter__(self) -> collections.abc.AsyncIterator[T]:
244
- """mdmd:hidden"""
245
- ...
246
-
241
+ def __aiter__(self) -> collections.abc.AsyncGenerator[T, None]: ...
247
242
  async def __anext__(self) -> T:
248
- """mdmd:hidden"""
243
+ """Deprecated: This exists for backwards compatibility and will be removed in a future version of Modal
244
+
245
+ Only use next/anext on the return value of iter/aiter on the StreamReader object (treat streamreader as
246
+ an iterable, not an iterator).
247
+ """
249
248
  ...
250
249
 
251
250
  async def aclose(self):
@@ -383,6 +382,7 @@ class StreamReader(typing.Generic[T]):
383
382
  _BytesStreamReaderThroughCommandRouter,
384
383
  _StdoutPrintingStreamReaderThroughCommandRouter,
385
384
  ]
385
+ _read_gen: typing.Optional[collections.abc.AsyncGenerator[T, None]]
386
386
 
387
387
  def __init__(
388
388
  self,
@@ -416,20 +416,22 @@ class StreamReader(typing.Generic[T]):
416
416
 
417
417
  read: __read_spec[T, typing_extensions.Self]
418
418
 
419
- def __iter__(self) -> typing.Iterator[T]:
420
- """mdmd:hidden"""
421
- ...
422
-
423
- def __aiter__(self) -> collections.abc.AsyncIterator[T]:
424
- """mdmd:hidden"""
425
- ...
426
-
419
+ def __iter__(self) -> typing.Generator[T, None, None]: ...
420
+ def __aiter__(self) -> collections.abc.AsyncGenerator[T, None]: ...
427
421
  def __next__(self) -> T:
428
- """mdmd:hidden"""
422
+ """Deprecated: This exists for backwards compatibility and will be removed in a future version of Modal
423
+
424
+ Only use next/anext on the return value of iter/aiter on the StreamReader object (treat streamreader as
425
+ an iterable, not an iterator).
426
+ """
429
427
  ...
430
428
 
431
429
  async def __anext__(self) -> T:
432
- """mdmd:hidden"""
430
+ """Deprecated: This exists for backwards compatibility and will be removed in a future version of Modal
431
+
432
+ Only use next/anext on the return value of iter/aiter on the StreamReader object (treat streamreader as
433
+ an iterable, not an iterator).
434
+ """
433
435
  ...
434
436
 
435
437
  def close(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.5.dev4
3
+ Version: 1.2.5.dev6
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.5.dev4"
4
+ __version__ = "1.2.5.dev6"
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