modal 1.2.1.dev5__tar.gz → 1.2.1.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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

Files changed (197) hide show
  1. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/PKG-INFO +1 -1
  2. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/_download.py +19 -3
  3. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/volume.py +7 -1
  4. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/client.pyi +2 -2
  5. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/functions.pyi +6 -6
  6. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/runner.py +11 -9
  7. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/volume.py +21 -3
  8. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/volume.pyi +30 -0
  9. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/PKG-INFO +1 -1
  10. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_version/__init__.py +1 -1
  11. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/LICENSE +0 -0
  12. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/README.md +0 -0
  13. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/__init__.py +0 -0
  14. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/__main__.py +0 -0
  15. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_billing.py +0 -0
  16. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_clustered_functions.py +0 -0
  17. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_clustered_functions.pyi +0 -0
  18. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_container_entrypoint.py +0 -0
  19. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_functions.py +0 -0
  20. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_ipython.py +0 -0
  21. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_location.py +0 -0
  22. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_object.py +0 -0
  23. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_output.py +0 -0
  24. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_partial_function.py +0 -0
  25. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_pty.py +0 -0
  26. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_resolver.py +0 -0
  27. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_resources.py +0 -0
  28. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/__init__.py +0 -0
  29. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/asgi.py +0 -0
  30. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/container_io_manager.py +0 -0
  31. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/container_io_manager.pyi +0 -0
  32. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/execution_context.py +0 -0
  33. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/execution_context.pyi +0 -0
  34. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  35. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/telemetry.py +0 -0
  36. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_runtime/user_code_imports.py +0 -0
  37. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_serialization.py +0 -0
  38. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_traceback.py +0 -0
  39. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_tunnel.py +0 -0
  40. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_tunnel.pyi +0 -0
  41. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_type_manager.py +0 -0
  42. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/__init__.py +0 -0
  43. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/app_utils.py +0 -0
  44. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/async_utils.py +0 -0
  45. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/auth_token_manager.py +0 -0
  46. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/blob_utils.py +0 -0
  47. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/bytes_io_segment_payload.py +0 -0
  48. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/deprecation.py +0 -0
  49. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/docker_utils.py +0 -0
  50. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/function_utils.py +0 -0
  51. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/git_utils.py +0 -0
  52. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/grpc_testing.py +0 -0
  53. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/grpc_utils.py +0 -0
  54. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/hash_utils.py +0 -0
  55. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/http_utils.py +0 -0
  56. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/jwt_utils.py +0 -0
  57. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/logger.py +0 -0
  58. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/mount_utils.py +0 -0
  59. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/name_utils.py +0 -0
  60. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/package_utils.py +0 -0
  61. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/pattern_utils.py +0 -0
  62. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/rand_pb_testing.py +0 -0
  63. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/shell_utils.py +0 -0
  64. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_utils/time_utils.py +0 -0
  65. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_vendor/__init__.py +0 -0
  66. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  67. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_vendor/cloudpickle.py +0 -0
  68. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_vendor/tblib.py +0 -0
  69. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/_watcher.py +0 -0
  70. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/app.py +0 -0
  71. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/app.pyi +0 -0
  72. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/billing.py +0 -0
  73. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/2023.12.312.txt +0 -0
  74. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/2023.12.txt +0 -0
  75. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/2024.04.txt +0 -0
  76. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/2024.10.txt +0 -0
  77. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/2025.06.txt +0 -0
  78. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/PREVIEW.txt +0 -0
  79. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/README.md +0 -0
  80. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/builder/base-images.json +0 -0
  81. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/call_graph.py +0 -0
  82. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/__init__.py +0 -0
  83. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/_traceback.py +0 -0
  84. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/app.py +0 -0
  85. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/cluster.py +0 -0
  86. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/config.py +0 -0
  87. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/container.py +0 -0
  88. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/dict.py +0 -0
  89. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/entry_point.py +0 -0
  90. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/environment.py +0 -0
  91. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/import_refs.py +0 -0
  92. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/launch.py +0 -0
  93. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/network_file_system.py +0 -0
  94. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/profile.py +0 -0
  95. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/programs/__init__.py +0 -0
  96. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/programs/launch_instance_ssh.py +0 -0
  97. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/programs/run_jupyter.py +0 -0
  98. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/programs/run_marimo.py +0 -0
  99. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/programs/vscode.py +0 -0
  100. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/queues.py +0 -0
  101. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/run.py +0 -0
  102. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/secret.py +0 -0
  103. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/token.py +0 -0
  104. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cli/utils.py +0 -0
  105. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/client.py +0 -0
  106. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cloud_bucket_mount.py +0 -0
  107. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cloud_bucket_mount.pyi +0 -0
  108. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cls.py +0 -0
  109. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/cls.pyi +0 -0
  110. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/config.py +0 -0
  111. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/container_process.py +0 -0
  112. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/container_process.pyi +0 -0
  113. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/dict.py +0 -0
  114. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/dict.pyi +0 -0
  115. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/environments.py +0 -0
  116. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/environments.pyi +0 -0
  117. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/exception.py +0 -0
  118. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/experimental/__init__.py +0 -0
  119. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/experimental/flash.py +0 -0
  120. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/experimental/flash.pyi +0 -0
  121. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/experimental/ipython.py +0 -0
  122. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/file_io.py +0 -0
  123. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/file_io.pyi +0 -0
  124. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/file_pattern_matcher.py +0 -0
  125. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/functions.py +0 -0
  126. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/gpu.py +0 -0
  127. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/image.py +0 -0
  128. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/image.pyi +0 -0
  129. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/io_streams.py +0 -0
  130. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/io_streams.pyi +0 -0
  131. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/mount.py +0 -0
  132. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/mount.pyi +0 -0
  133. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/network_file_system.py +0 -0
  134. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/network_file_system.pyi +0 -0
  135. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/object.py +0 -0
  136. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/object.pyi +0 -0
  137. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/output.py +0 -0
  138. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/parallel_map.py +0 -0
  139. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/parallel_map.pyi +0 -0
  140. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/partial_function.py +0 -0
  141. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/partial_function.pyi +0 -0
  142. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/proxy.py +0 -0
  143. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/proxy.pyi +0 -0
  144. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/py.typed +0 -0
  145. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/queue.py +0 -0
  146. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/queue.pyi +0 -0
  147. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/retries.py +0 -0
  148. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/runner.pyi +0 -0
  149. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/running_app.py +0 -0
  150. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/sandbox.py +0 -0
  151. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/sandbox.pyi +0 -0
  152. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/schedule.py +0 -0
  153. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/scheduler_placement.py +0 -0
  154. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/secret.py +0 -0
  155. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/secret.pyi +0 -0
  156. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/serving.py +0 -0
  157. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/serving.pyi +0 -0
  158. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/snapshot.py +0 -0
  159. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/snapshot.pyi +0 -0
  160. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/stream_type.py +0 -0
  161. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/token_flow.py +0 -0
  162. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal/token_flow.pyi +0 -0
  163. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/SOURCES.txt +0 -0
  164. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/dependency_links.txt +0 -0
  165. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/entry_points.txt +0 -0
  166. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/requires.txt +0 -0
  167. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal.egg-info/top_level.txt +0 -0
  168. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/__init__.py +0 -0
  169. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/gen_cli_docs.py +0 -0
  170. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/gen_reference_docs.py +0 -0
  171. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/mdmd/__init__.py +0 -0
  172. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/mdmd/mdmd.py +0 -0
  173. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_docs/mdmd/signatures.py +0 -0
  174. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/__init__.py +0 -0
  175. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api.proto +0 -0
  176. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api_grpc.py +0 -0
  177. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api_pb2.py +0 -0
  178. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api_pb2.pyi +0 -0
  179. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api_pb2_grpc.py +0 -0
  180. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/api_pb2_grpc.pyi +0 -0
  181. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/modal_api_grpc.py +0 -0
  182. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/py.typed +0 -0
  183. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router.proto +0 -0
  184. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router_grpc.py +0 -0
  185. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router_pb2.py +0 -0
  186. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router_pb2.pyi +0 -0
  187. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router_pb2_grpc.py +0 -0
  188. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/sandbox_router_pb2_grpc.pyi +0 -0
  189. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router.proto +0 -0
  190. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router_grpc.py +0 -0
  191. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router_pb2.py +0 -0
  192. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router_pb2.pyi +0 -0
  193. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  194. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  195. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/modal_version/__main__.py +0 -0
  196. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/pyproject.toml +0 -0
  197. {modal-1.2.1.dev5 → modal-1.2.1.dev7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.1.dev5
3
+ Version: 1.2.1.dev7
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 2023
2
2
  import asyncio
3
3
  import functools
4
+ import multiprocessing
4
5
  import os
5
6
  import shutil
6
7
  import sys
@@ -23,12 +24,22 @@ async def _volume_download(
23
24
  remote_path: str,
24
25
  local_destination: Path,
25
26
  overwrite: bool,
26
- progress_cb: Callable,
27
+ concurrency: Optional[int] = None,
28
+ progress_cb: Optional[Callable] = None,
27
29
  ):
30
+ if progress_cb is None:
31
+
32
+ def progress_cb(*_, **__):
33
+ pass
34
+
35
+ if concurrency is None:
36
+ concurrency = max(128, 2 * multiprocessing.cpu_count())
37
+
28
38
  is_pipe = local_destination == PIPE_PATH
29
39
 
30
40
  q: asyncio.Queue[tuple[Optional[Path], Optional[FileEntry]]] = asyncio.Queue()
31
- num_consumers = 1 if is_pipe else 10 # concurrency limit for downloading files
41
+ num_consumers = 1 if is_pipe else concurrency # concurrency limit for downloading files
42
+ download_semaphore = asyncio.Semaphore(concurrency)
32
43
 
33
44
  async def producer():
34
45
  iterator: AsyncIterator[FileEntry]
@@ -86,7 +97,12 @@ async def _volume_download(
86
97
 
87
98
  with output_path.open("wb") as fp:
88
99
  if isinstance(volume, _Volume):
89
- b = await volume.read_file_into_fileobj(entry.path, fp, file_progress_cb)
100
+ b = await volume._read_file_into_fileobj(
101
+ path=entry.path,
102
+ fileobj=fp,
103
+ download_semaphore=download_semaphore,
104
+ progress_cb=file_progress_cb,
105
+ )
90
106
  else:
91
107
  b = 0
92
108
  async for chunk in volume.read_file(entry.path):
@@ -96,7 +96,13 @@ async def get(
96
96
  console = make_console()
97
97
  progress_handler = ProgressHandler(type="download", console=console)
98
98
  with progress_handler.live:
99
- await _volume_download(volume, remote_path, destination, force, progress_cb=progress_handler.progress)
99
+ await _volume_download(
100
+ volume=volume,
101
+ remote_path=remote_path,
102
+ local_destination=destination,
103
+ overwrite=force,
104
+ progress_cb=progress_handler.progress,
105
+ )
100
106
  console.print(OutputManager.step_completed("Finished downloading files to local!"))
101
107
 
102
108
 
@@ -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.1.dev5",
36
+ version: str = "1.2.1.dev7",
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.1.dev5",
167
+ version: str = "1.2.1.dev7",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -401,7 +401,7 @@ class Function(
401
401
 
402
402
  _call_generator: ___call_generator_spec[typing_extensions.Self]
403
403
 
404
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
404
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
405
405
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
406
406
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
407
407
  ...
@@ -410,7 +410,7 @@ class Function(
410
410
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
411
411
  ...
412
412
 
413
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
413
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
414
414
 
415
415
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
416
416
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -437,7 +437,7 @@ class Function(
437
437
  """
438
438
  ...
439
439
 
440
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
440
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
441
441
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
442
442
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
443
443
 
@@ -461,7 +461,7 @@ class Function(
461
461
  ...
462
462
 
463
463
  _experimental_spawn: ___experimental_spawn_spec[
464
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
464
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
465
465
  ]
466
466
 
467
467
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -470,7 +470,7 @@ class Function(
470
470
 
471
471
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
472
472
 
473
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
473
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
474
474
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
475
475
  """Calls the function with the given arguments, without waiting for the results.
476
476
 
@@ -491,7 +491,7 @@ class Function(
491
491
  """
492
492
  ...
493
493
 
494
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
494
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
495
495
 
496
496
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
497
497
  """Return the inner Python object wrapped by this Modal Function."""
@@ -355,6 +355,14 @@ async def _run_app(
355
355
  await _status_based_disconnect(client, running_app.app_id, e)
356
356
  raise
357
357
 
358
+ detached_disconnect_msg = (
359
+ "The detached App will keep running. You can track its progress on the Dashboard: "
360
+ f"[magenta underline]{running_app.app_page_url}[/magenta underline]"
361
+ "\n"
362
+ f"\nStream logs: [green]modal app logs {running_app.app_id}[/green]"
363
+ f"\nStop the App: [green]modal app stop {running_app.app_id}[/green]"
364
+ )
365
+
358
366
  try:
359
367
  # Show logs from dynamically created images.
360
368
  # TODO: better way to do this
@@ -377,11 +385,7 @@ async def _run_app(
377
385
  if detach:
378
386
  if output_mgr := _get_output_manager():
379
387
  output_mgr.print(output_mgr.step_completed("Shutting down Modal client."))
380
- output_mgr.print(
381
- "The detached app keeps running. You can track its progress at: "
382
- f"[magenta]{running_app.app_page_url}[/magenta]"
383
- ""
384
- )
388
+ output_mgr.print(detached_disconnect_msg)
385
389
  if logs_loop:
386
390
  logs_loop.cancel()
387
391
  await _status_based_disconnect(client, running_app.app_id, e)
@@ -409,10 +413,8 @@ async def _run_app(
409
413
  # If we lose connection to the server after a detached App has started running, it will continue
410
414
  # I think we can only exit "nicely" if we are able to print output though, otherwise we should raise
411
415
  if detach and (output_mgr := _get_output_manager()):
412
- output_mgr.print(
413
- "Connection lost, but detached App will continue running: "
414
- f"[magenta]{running_app.app_page_url}[/magenta]"
415
- )
416
+ output_mgr.print(":white_exclamation_mark: Connection lost!")
417
+ output_mgr.print(detached_disconnect_msg)
416
418
  return
417
419
  raise
418
420
  except BaseException as e:
@@ -670,16 +670,33 @@ class _Volume(_Object, type_prefix="vo"):
670
670
 
671
671
  @live_method
672
672
  async def read_file_into_fileobj(
673
- self, path: str, fileobj: typing.IO[bytes], progress_cb: Optional[Callable[..., Any]] = None
673
+ self,
674
+ path: str,
675
+ fileobj: typing.IO[bytes],
676
+ progress_cb: Optional[Callable[..., Any]] = None,
674
677
  ) -> int:
675
678
  """mdmd:hidden
676
679
  Read volume file into file-like IO object.
677
680
  """
681
+ return await self._read_file_into_fileobj(path, fileobj, progress_cb=progress_cb)
682
+
683
+ @live_method
684
+ async def _read_file_into_fileobj(
685
+ self,
686
+ path: str,
687
+ fileobj: typing.IO[bytes],
688
+ concurrency: Optional[int] = None,
689
+ download_semaphore: Optional[asyncio.Semaphore] = None,
690
+ progress_cb: Optional[Callable[..., Any]] = None,
691
+ ) -> int:
678
692
  if progress_cb is None:
679
693
 
680
694
  def progress_cb(*_, **__):
681
695
  pass
682
696
 
697
+ if concurrency is None:
698
+ concurrency = multiprocessing.cpu_count()
699
+
683
700
  req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
684
701
 
685
702
  try:
@@ -687,8 +704,9 @@ class _Volume(_Object, type_prefix="vo"):
687
704
  except modal.exception.NotFoundError as exc:
688
705
  raise FileNotFoundError(exc.args[0])
689
706
 
690
- # TODO(dflemstr): Sane default limit? Make configurable?
691
- download_semaphore = asyncio.Semaphore(multiprocessing.cpu_count())
707
+ if download_semaphore is None:
708
+ download_semaphore = asyncio.Semaphore(concurrency)
709
+
692
710
  write_lock = asyncio.Lock()
693
711
  start_pos = fileobj.tell()
694
712
 
@@ -626,6 +626,14 @@ class _Volume(modal._object._Object):
626
626
  """
627
627
  ...
628
628
 
629
+ async def _read_file_into_fileobj(
630
+ self,
631
+ path: str,
632
+ fileobj: typing.IO[bytes],
633
+ concurrency: typing.Optional[int] = None,
634
+ download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
635
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
636
+ ) -> int: ...
629
637
  async def remove_file(self, path: str, recursive: bool = False) -> None:
630
638
  """Remove a file or directory from a volume."""
631
639
  ...
@@ -1065,6 +1073,28 @@ class Volume(modal.object.Object):
1065
1073
 
1066
1074
  read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
1067
1075
 
1076
+ class ___read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
1077
+ def __call__(
1078
+ self,
1079
+ /,
1080
+ path: str,
1081
+ fileobj: typing.IO[bytes],
1082
+ concurrency: typing.Optional[int] = None,
1083
+ download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
1084
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
1085
+ ) -> int: ...
1086
+ async def aio(
1087
+ self,
1088
+ /,
1089
+ path: str,
1090
+ fileobj: typing.IO[bytes],
1091
+ concurrency: typing.Optional[int] = None,
1092
+ download_semaphore: typing.Optional[asyncio.locks.Semaphore] = None,
1093
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
1094
+ ) -> int: ...
1095
+
1096
+ _read_file_into_fileobj: ___read_file_into_fileobj_spec[typing_extensions.Self]
1097
+
1068
1098
  class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
1069
1099
  def __call__(self, /, path: str, recursive: bool = False) -> None:
1070
1100
  """Remove a file or directory from a volume."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.1.dev5
3
+ Version: 1.2.1.dev7
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.1.dev5"
4
+ __version__ = "1.2.1.dev7"
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
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