modal 1.4.1.dev5__tar.gz → 1.4.2.dev0__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 (209) hide show
  1. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/PKG-INFO +2 -2
  2. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/sandbox_fs_utils.py +35 -0
  3. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/time_utils.py +55 -0
  4. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/client.pyi +2 -2
  5. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/exception.py +6 -0
  6. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/sandbox.py +19 -18
  7. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/sandbox.pyi +19 -18
  8. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/sandbox_fs.py +48 -0
  9. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/sandbox_fs.pyi +120 -0
  10. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/PKG-INFO +2 -2
  11. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/requires.txt +1 -1
  12. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_version/__init__.py +1 -1
  13. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/pyproject.toml +2 -2
  14. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/LICENSE +0 -0
  15. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/README.md +0 -0
  16. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/__init__.py +0 -0
  17. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/__main__.py +0 -0
  18. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_billing.py +0 -0
  19. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_clustered_functions.py +0 -0
  20. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_clustered_functions.pyi +0 -0
  21. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_container_entrypoint.py +0 -0
  22. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_functions.py +0 -0
  23. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_grpc_client.py +0 -0
  24. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_ipython.py +0 -0
  25. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_load_context.py +0 -0
  26. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_location.py +0 -0
  27. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_logs.py +0 -0
  28. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_object.py +0 -0
  29. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_output/__init__.py +0 -0
  30. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_output/manager.py +0 -0
  31. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_output/pty.py +0 -0
  32. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_output/rich.py +0 -0
  33. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_output/status.py +0 -0
  34. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_partial_function.py +0 -0
  35. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_resolver.py +0 -0
  36. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_resources.py +0 -0
  37. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/__init__.py +0 -0
  38. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/asgi.py +0 -0
  39. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/container_io_manager.py +0 -0
  40. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
  41. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/execution_context.py +0 -0
  42. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/execution_context.pyi +0 -0
  43. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  44. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/telemetry.py +0 -0
  45. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/user_code_event_loop.py +0 -0
  46. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_runtime/user_code_imports.py +0 -0
  47. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_serialization.py +0 -0
  48. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_server.py +0 -0
  49. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_traceback.py +0 -0
  50. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_tunnel.py +0 -0
  51. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_tunnel.pyi +0 -0
  52. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_type_manager.py +0 -0
  53. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/__init__.py +0 -0
  54. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/app_utils.py +0 -0
  55. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/async_utils.py +0 -0
  56. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/auth_token_manager.py +0 -0
  57. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/blob_utils.py +0 -0
  58. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/browser_utils.py +0 -0
  59. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  60. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/deprecation.py +0 -0
  61. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/docker_utils.py +0 -0
  62. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/function_utils.py +0 -0
  63. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/git_utils.py +0 -0
  64. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/grpc_testing.py +0 -0
  65. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/grpc_utils.py +0 -0
  66. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/hash_utils.py +0 -0
  67. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/http_utils.py +0 -0
  68. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/jwt_utils.py +0 -0
  69. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/logger.py +0 -0
  70. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/mount_utils.py +0 -0
  71. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/name_utils.py +0 -0
  72. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/package_utils.py +0 -0
  73. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/pattern_utils.py +0 -0
  74. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/rand_pb_testing.py +0 -0
  75. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/shell_utils.py +0 -0
  76. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_utils/task_command_router_client.py +0 -0
  77. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_vendor/__init__.py +0 -0
  78. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  79. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_vendor/cloudpickle.py +0 -0
  80. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_vendor/tblib.py +0 -0
  81. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_vendor/version.py +0 -0
  82. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/_watcher.py +0 -0
  83. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/app.py +0 -0
  84. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/app.pyi +0 -0
  85. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/billing.py +0 -0
  86. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/2023.12.312.txt +0 -0
  87. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/2023.12.txt +0 -0
  88. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/2024.04.txt +0 -0
  89. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/2024.10.txt +0 -0
  90. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/2025.06.txt +0 -0
  91. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/PREVIEW.txt +0 -0
  92. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/README.md +0 -0
  93. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/builder/base-images.json +0 -0
  94. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/call_graph.py +0 -0
  95. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/__init__.py +0 -0
  96. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/_download.py +0 -0
  97. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/_traceback.py +0 -0
  98. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/app.py +0 -0
  99. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/billing.py +0 -0
  100. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/changelog.py +0 -0
  101. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/cluster.py +0 -0
  102. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/config.py +0 -0
  103. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/container.py +0 -0
  104. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/dashboard.py +0 -0
  105. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/dict.py +0 -0
  106. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/entry_point.py +0 -0
  107. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/environment.py +0 -0
  108. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/import_refs.py +0 -0
  109. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/launch.py +0 -0
  110. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/network_file_system.py +0 -0
  111. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/profile.py +0 -0
  112. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/programs/__init__.py +0 -0
  113. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/programs/run_jupyter.py +0 -0
  114. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/programs/vscode.py +0 -0
  115. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/queues.py +0 -0
  116. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/run.py +0 -0
  117. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/secret.py +0 -0
  118. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/selector.py +0 -0
  119. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/shell.py +0 -0
  120. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/token.py +0 -0
  121. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/utils.py +0 -0
  122. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cli/volume.py +0 -0
  123. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/client.py +0 -0
  124. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cloud_bucket_mount.py +0 -0
  125. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cloud_bucket_mount.pyi +0 -0
  126. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cls.py +0 -0
  127. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/cls.pyi +0 -0
  128. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/config.py +0 -0
  129. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/container_process.py +0 -0
  130. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/container_process.pyi +0 -0
  131. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/dict.py +0 -0
  132. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/dict.pyi +0 -0
  133. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/environments.py +0 -0
  134. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/environments.pyi +0 -0
  135. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/experimental/__init__.py +0 -0
  136. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/experimental/flash.py +0 -0
  137. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/experimental/flash.pyi +0 -0
  138. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/experimental/ipython.py +0 -0
  139. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/file_io.py +0 -0
  140. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/file_io.pyi +0 -0
  141. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/file_pattern_matcher.py +0 -0
  142. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/functions.py +0 -0
  143. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/functions.pyi +0 -0
  144. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/image.py +0 -0
  145. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/image.pyi +0 -0
  146. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/io_streams.py +0 -0
  147. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/io_streams.pyi +0 -0
  148. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/mount.py +0 -0
  149. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/mount.pyi +0 -0
  150. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/network_file_system.py +0 -0
  151. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/network_file_system.pyi +0 -0
  152. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/object.py +0 -0
  153. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/object.pyi +0 -0
  154. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/output.py +0 -0
  155. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/parallel_map.py +0 -0
  156. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/parallel_map.pyi +0 -0
  157. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/partial_function.py +0 -0
  158. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/partial_function.pyi +0 -0
  159. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/proxy.py +0 -0
  160. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/proxy.pyi +0 -0
  161. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/py.typed +0 -0
  162. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/queue.py +0 -0
  163. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/queue.pyi +0 -0
  164. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/retries.py +0 -0
  165. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/runner.py +0 -0
  166. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/runner.pyi +0 -0
  167. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/running_app.py +0 -0
  168. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/schedule.py +0 -0
  169. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/scheduler_placement.py +0 -0
  170. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/secret.py +0 -0
  171. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/secret.pyi +0 -0
  172. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/server.py +0 -0
  173. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/server.pyi +0 -0
  174. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/serving.py +0 -0
  175. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/serving.pyi +0 -0
  176. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/snapshot.py +0 -0
  177. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/snapshot.pyi +0 -0
  178. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/stream_type.py +0 -0
  179. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/token_flow.py +0 -0
  180. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/token_flow.pyi +0 -0
  181. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/volume.py +0 -0
  182. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal/volume.pyi +0 -0
  183. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/SOURCES.txt +0 -0
  184. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/dependency_links.txt +0 -0
  185. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/entry_points.txt +0 -0
  186. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal.egg-info/top_level.txt +0 -0
  187. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/__init__.py +0 -0
  188. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/gen_cli_docs.py +0 -0
  189. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/gen_cli_docs_main.py +0 -0
  190. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/gen_reference_docs.py +0 -0
  191. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/gen_reference_docs_main.py +0 -0
  192. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/mdmd/__init__.py +0 -0
  193. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/mdmd/mdmd.py +0 -0
  194. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_docs/mdmd/signatures.py +0 -0
  195. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/__init__.py +0 -0
  196. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/api_grpc.py +0 -0
  197. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/api_pb2.py +0 -0
  198. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/api_pb2.pyi +0 -0
  199. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/api_pb2_grpc.py +0 -0
  200. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/api_pb2_grpc.pyi +0 -0
  201. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/modal_api_grpc.py +0 -0
  202. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/py.typed +0 -0
  203. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/task_command_router_grpc.py +0 -0
  204. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/task_command_router_pb2.py +0 -0
  205. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/task_command_router_pb2.pyi +0 -0
  206. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  207. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  208. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/modal_version/__main__.py +0 -0
  209. {modal-1.4.1.dev5 → modal-1.4.2.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.1.dev5
3
+ Version: 1.4.2.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -23,7 +23,7 @@ Requires-Dist: grpclib<0.4.10,>=0.4.7; python_version < "3.14"
23
23
  Requires-Dist: grpclib<0.4.10,>=0.4.9; python_version >= "3.14"
24
24
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
25
25
  Requires-Dist: rich>=12.0.0
26
- Requires-Dist: synchronicity~=0.12.0
26
+ Requires-Dist: synchronicity~=0.12.1
27
27
  Requires-Dist: toml
28
28
  Requires-Dist: typer>=0.9
29
29
  Requires-Dist: types-certifi
@@ -12,6 +12,7 @@ from ..exception import (
12
12
  Error as ModalError,
13
13
  InvalidError,
14
14
  NotFoundError,
15
+ SandboxFilesystemDirectoryNotEmptyError,
15
16
  SandboxFilesystemError,
16
17
  SandboxFilesystemFileTooLargeError,
17
18
  SandboxFilesystemIsADirectoryError,
@@ -153,6 +154,40 @@ def make_read_file_command(remote_path: str) -> str:
153
154
  return json.dumps({"ReadFile": {"path": remote_path}})
154
155
 
155
156
 
157
+ def make_remove_command(remote_path: str, recursive: bool) -> str:
158
+ """Build the JSON command string for a Remove operation.
159
+
160
+ The returned JSON must match the `Command` enum in the modal-sandbox-fs-tools
161
+ Rust crate (crates/modal-sandbox-fs-tools/src/lib.rs). Treat changes to
162
+ this schema like protobuf changes: fields must not be removed or renamed,
163
+ only added with backwards-compatible defaults.
164
+ """
165
+ return json.dumps({"Remove": {"path": remote_path, "recursive": recursive}})
166
+
167
+
168
+ def raise_remove_error(returncode: int, stderr: Union[str, bytes], remote_path: str) -> NoReturn:
169
+ if payload := try_parse_error_payload(stderr):
170
+ logger.debug(
171
+ f"sandbox-fs-tools remove error: path={remote_path}, "
172
+ f"error_kind={payload.error_kind}, message={payload.message}, detail={payload.detail}"
173
+ )
174
+ if payload.error_kind == "NotFound":
175
+ raise SandboxFilesystemNotFoundError(f"{payload.message}: {remote_path}")
176
+ if payload.error_kind == "DirectoryNotEmpty":
177
+ raise SandboxFilesystemDirectoryNotEmptyError(f"{payload.message}: {remote_path}")
178
+ if payload.error_kind == "NotSupported":
179
+ raise InvalidError(
180
+ f"{payload.message}: {remote_path} - this operation is not supported for CloudBucketMounts"
181
+ )
182
+ if payload.error_kind == "PermissionDenied":
183
+ raise SandboxFilesystemPermissionError(f"{payload.message}: {remote_path}")
184
+ raise SandboxFilesystemError(payload.message)
185
+
186
+ if stderr_text := _stderr_to_text(stderr):
187
+ logger.debug(f"Unstructured modal-sandbox-fs-tools stderr: {stderr_text}")
188
+ raise SandboxFilesystemError(f"Operation on '{remote_path}' failed with exit code {returncode}")
189
+
190
+
156
191
  def validate_absolute_remote_path(remote_path: str, operation: str) -> None:
157
192
  if not PurePosixPath(remote_path).is_absolute():
158
193
  raise InvalidError(f"Sandbox.filesystem.{operation}() currently only supports absolute remote_path values")
@@ -164,6 +164,61 @@ def parse_date_range(s: str, tz: Optional[tzinfo] = None) -> tuple[datetime, dat
164
164
  raise ValueError(f"Unrecognized range: '{s}'. Accepted values: {accepted}")
165
165
 
166
166
 
167
+ def relative_timestamp(dt: datetime) -> str:
168
+ """Convert a tz-aware datetime to a human-readable relative time string.
169
+
170
+ Examples: "just now", "30 seconds ago", "5 minutes ago", "2 hours ago",
171
+ "yesterday", "3 days ago", "2 weeks ago", "3 months ago", "1 year ago".
172
+
173
+ Raises ValueError if the datetime is naive (no tzinfo).
174
+ """
175
+ if dt.tzinfo is None:
176
+ raise ValueError("datetime must be timezone-aware")
177
+
178
+ now = datetime.now(timezone.utc)
179
+ delta = now - dt
180
+ total_seconds = int(delta.total_seconds())
181
+
182
+ if total_seconds < 0:
183
+ return "just now"
184
+
185
+ if total_seconds < 10:
186
+ return "just now"
187
+ if total_seconds < 60:
188
+ return f"{total_seconds} seconds ago"
189
+ if total_seconds < 120:
190
+ return "1 minute ago"
191
+
192
+ minutes = total_seconds // 60
193
+ if minutes < 60:
194
+ return f"{minutes} minutes ago"
195
+ if minutes < 120:
196
+ return "1 hour ago"
197
+
198
+ hours = minutes // 60
199
+ if hours < 24:
200
+ return f"{hours} hours ago"
201
+ if hours < 48:
202
+ return "yesterday"
203
+
204
+ days = hours // 24
205
+ if days < 14:
206
+ return f"{days} days ago"
207
+
208
+ weeks = days // 7
209
+ if days < 60:
210
+ return f"{weeks} weeks ago"
211
+
212
+ months = days // 30
213
+ if days < 365:
214
+ return f"{months} months ago"
215
+
216
+ years = days // 365
217
+ if years == 1:
218
+ return "1 year ago"
219
+ return f"{years} years ago"
220
+
221
+
167
222
  def locale_tz() -> tzinfo:
168
223
  return datetime.now().astimezone().tzinfo
169
224
 
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.4.1.dev5",
38
+ version: str = "1.4.2.dev0",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -171,7 +171,7 @@ class Client:
171
171
  server_url: str,
172
172
  client_type: int,
173
173
  credentials: typing.Optional[tuple[str, str]],
174
- version: str = "1.4.1.dev5",
174
+ version: str = "1.4.2.dev0",
175
175
  ):
176
176
  """mdmd:hidden
177
177
  The Modal client object is not intended to be instantiated directly by users.
@@ -351,6 +351,12 @@ class SandboxFilesystemNotFoundError(SandboxFilesystemError):
351
351
  pass
352
352
 
353
353
 
354
+ class SandboxFilesystemDirectoryNotEmptyError(SandboxFilesystemError):
355
+ """Raised when a directory is not empty."""
356
+
357
+ pass
358
+
359
+
354
360
  class SandboxFilesystemIsADirectoryError(SandboxFilesystemError):
355
361
  """Raised when a file operation in the sandbox targets a directory when it should target a non-directory file."""
356
362
 
@@ -123,24 +123,25 @@ class SandboxConnectCredentials:
123
123
  class Probe:
124
124
  """Probe configuration for the Sandbox Readiness Probe.
125
125
 
126
- Usage:
127
- ```py notest
128
- # Wait until a file exists.
129
- readiness_probe = modal.Probe.with_exec(
130
- "sh", "-c", "test -f /tmp/ready",
131
- )
132
-
133
- # Wait until a TCP port is accepting connections.
134
- readiness_probe = modal.Probe.with_tcp(8080)
135
-
136
- app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
137
- sandbox = modal.Sandbox.create(
138
- "python3", "-m", "http.server", "8080",
139
- readiness_probe=readiness_probe,
140
- app=app,
141
- )
142
- sandbox.wait_until_ready()
143
- ```
126
+ **Usage**
127
+
128
+ ```python notest
129
+ # Wait until a file exists.
130
+ readiness_probe = modal.Probe.with_exec(
131
+ "sh", "-c", "test -f /tmp/ready",
132
+ )
133
+
134
+ # Wait until a TCP port is accepting connections.
135
+ readiness_probe = modal.Probe.with_tcp(8080)
136
+
137
+ app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
138
+ sandbox = modal.Sandbox.create(
139
+ "python3", "-m", "http.server", "8080",
140
+ readiness_probe=readiness_probe,
141
+ app=app,
142
+ )
143
+ sandbox.wait_until_ready()
144
+ ```
144
145
  """
145
146
 
146
147
  tcp_port: Optional[int] = None
@@ -74,24 +74,25 @@ class SandboxConnectCredentials:
74
74
  class Probe:
75
75
  """Probe configuration for the Sandbox Readiness Probe.
76
76
 
77
- Usage:
78
- ```py notest
79
- # Wait until a file exists.
80
- readiness_probe = modal.Probe.with_exec(
81
- "sh", "-c", "test -f /tmp/ready",
82
- )
83
-
84
- # Wait until a TCP port is accepting connections.
85
- readiness_probe = modal.Probe.with_tcp(8080)
86
-
87
- app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
88
- sandbox = modal.Sandbox.create(
89
- "python3", "-m", "http.server", "8080",
90
- readiness_probe=readiness_probe,
91
- app=app,
92
- )
93
- sandbox.wait_until_ready()
94
- ```
77
+ **Usage**
78
+
79
+ ```python notest
80
+ # Wait until a file exists.
81
+ readiness_probe = modal.Probe.with_exec(
82
+ "sh", "-c", "test -f /tmp/ready",
83
+ )
84
+
85
+ # Wait until a TCP port is accepting connections.
86
+ readiness_probe = modal.Probe.with_tcp(8080)
87
+
88
+ app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
89
+ sandbox = modal.Sandbox.create(
90
+ "python3", "-m", "http.server", "8080",
91
+ readiness_probe=readiness_probe,
92
+ app=app,
93
+ )
94
+ sandbox.wait_until_ready()
95
+ ```
95
96
  """
96
97
 
97
98
  tcp_port: typing.Optional[int]
@@ -12,8 +12,10 @@ from ._utils.async_utils import synchronize_api
12
12
  from ._utils.logger import logger
13
13
  from ._utils.sandbox_fs_utils import (
14
14
  make_read_file_command,
15
+ make_remove_command,
15
16
  make_write_file_command,
16
17
  raise_read_file_error,
18
+ raise_remove_error,
17
19
  raise_write_file_error,
18
20
  translate_exec_errors,
19
21
  validate_absolute_remote_path,
@@ -303,5 +305,51 @@ class _SandboxFilesystem:
303
305
  dur_s = max(time.monotonic() - t0, 0.001)
304
306
  _log_throughput(f"copy_from_local {local_path} -> {remote_path}", total_bytes, dur_s)
305
307
 
308
+ async def remove(self, remote_path: str, *, recursive: bool = False) -> None:
309
+ """Remove a file or directory in the Sandbox.
310
+
311
+ `remote_path` must be an absolute path in the Sandbox.
312
+
313
+ When `remote_path` is a directory and `recursive` is `False` (the
314
+ default), removes it only if it is empty. When `recursive` is `True`,
315
+ removes the directory and all its contents.
316
+
317
+ Recursive directory removal is not supported on all mounts.
318
+ In particular, `CloudBucketMount` does not support it. An
319
+ `InvalidError` is raised in that case.
320
+
321
+ **Raises**
322
+
323
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
324
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
325
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
326
+ - `InvalidError`: the operation is not supported by the mount.
327
+ - `SandboxFilesystemError`: the command fails for any other reason.
328
+
329
+ **Usage**
330
+
331
+ To remove a file:
332
+
333
+ ```python fixture:sandbox
334
+ sandbox.filesystem.write_bytes(b"Hello, world!\\n", "/tmp/hello.bin")
335
+ sandbox.filesystem.remove("/tmp/hello.bin")
336
+ ```
337
+
338
+ To remove a directory and all its contents:
339
+
340
+ ```python fixture:sandbox
341
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
342
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
343
+ ```
344
+ """
345
+ validate_absolute_remote_path(remote_path, "remove")
346
+
347
+ with translate_exec_errors("remove", remote_path):
348
+ process = await self._sandbox.exec(_SANDBOX_FS_TOOLS_PATH, make_remove_command(remote_path, recursive))
349
+ stderr, returncode = await asyncio.gather(process.stderr.read(), process.wait())
350
+
351
+ if returncode != 0:
352
+ raise_remove_error(returncode, stderr, remote_path)
353
+
306
354
 
307
355
  SandboxFilesystem = synchronize_api(_SandboxFilesystem)
@@ -168,6 +168,45 @@ class _SandboxFilesystem:
168
168
  """
169
169
  ...
170
170
 
171
+ async def remove(self, remote_path: str, *, recursive: bool = False) -> None:
172
+ """Remove a file or directory in the Sandbox.
173
+
174
+ `remote_path` must be an absolute path in the Sandbox.
175
+
176
+ When `remote_path` is a directory and `recursive` is `False` (the
177
+ default), removes it only if it is empty. When `recursive` is `True`,
178
+ removes the directory and all its contents.
179
+
180
+ Recursive directory removal is not supported on all mounts.
181
+ In particular, `CloudBucketMount` does not support it. An
182
+ `InvalidError` is raised in that case.
183
+
184
+ **Raises**
185
+
186
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
187
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
188
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
189
+ - `InvalidError`: the operation is not supported by the mount.
190
+ - `SandboxFilesystemError`: the command fails for any other reason.
191
+
192
+ **Usage**
193
+
194
+ To remove a file:
195
+
196
+ ```python fixture:sandbox
197
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
198
+ sandbox.filesystem.remove("/tmp/hello.bin")
199
+ ```
200
+
201
+ To remove a directory and all its contents:
202
+
203
+ ```python fixture:sandbox
204
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
205
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
206
+ ```
207
+ """
208
+ ...
209
+
171
210
  class SandboxFilesystem:
172
211
  """mdmd:namespace
173
212
  Namespace for Sandbox filesystem APIs.
@@ -505,3 +544,84 @@ class SandboxFilesystem:
505
544
  ...
506
545
 
507
546
  copy_from_local: __copy_from_local_spec
547
+
548
+ class __remove_spec(typing_extensions.Protocol):
549
+ def __call__(self, /, remote_path: str, *, recursive: bool = False) -> None:
550
+ """Remove a file or directory in the Sandbox.
551
+
552
+ `remote_path` must be an absolute path in the Sandbox.
553
+
554
+ When `remote_path` is a directory and `recursive` is `False` (the
555
+ default), removes it only if it is empty. When `recursive` is `True`,
556
+ removes the directory and all its contents.
557
+
558
+ Recursive directory removal is not supported on all mounts.
559
+ In particular, `CloudBucketMount` does not support it. An
560
+ `InvalidError` is raised in that case.
561
+
562
+ **Raises**
563
+
564
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
565
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
566
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
567
+ - `InvalidError`: the operation is not supported by the mount.
568
+ - `SandboxFilesystemError`: the command fails for any other reason.
569
+
570
+ **Usage**
571
+
572
+ To remove a file:
573
+
574
+ ```python fixture:sandbox
575
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
576
+ sandbox.filesystem.remove("/tmp/hello.bin")
577
+ ```
578
+
579
+ To remove a directory and all its contents:
580
+
581
+ ```python fixture:sandbox
582
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
583
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
584
+ ```
585
+ """
586
+ ...
587
+
588
+ async def aio(self, /, remote_path: str, *, recursive: bool = False) -> None:
589
+ """Remove a file or directory in the Sandbox.
590
+
591
+ `remote_path` must be an absolute path in the Sandbox.
592
+
593
+ When `remote_path` is a directory and `recursive` is `False` (the
594
+ default), removes it only if it is empty. When `recursive` is `True`,
595
+ removes the directory and all its contents.
596
+
597
+ Recursive directory removal is not supported on all mounts.
598
+ In particular, `CloudBucketMount` does not support it. An
599
+ `InvalidError` is raised in that case.
600
+
601
+ **Raises**
602
+
603
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
604
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
605
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
606
+ - `InvalidError`: the operation is not supported by the mount.
607
+ - `SandboxFilesystemError`: the command fails for any other reason.
608
+
609
+ **Usage**
610
+
611
+ To remove a file:
612
+
613
+ ```python fixture:sandbox
614
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
615
+ sandbox.filesystem.remove("/tmp/hello.bin")
616
+ ```
617
+
618
+ To remove a directory and all its contents:
619
+
620
+ ```python fixture:sandbox
621
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
622
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
623
+ ```
624
+ """
625
+ ...
626
+
627
+ remove: __remove_spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.1.dev5
3
+ Version: 1.4.2.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -23,7 +23,7 @@ Requires-Dist: grpclib<0.4.10,>=0.4.7; python_version < "3.14"
23
23
  Requires-Dist: grpclib<0.4.10,>=0.4.9; python_version >= "3.14"
24
24
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
25
25
  Requires-Dist: rich>=12.0.0
26
- Requires-Dist: synchronicity~=0.12.0
26
+ Requires-Dist: synchronicity~=0.12.1
27
27
  Requires-Dist: toml
28
28
  Requires-Dist: typer>=0.9
29
29
  Requires-Dist: types-certifi
@@ -4,7 +4,7 @@ certifi
4
4
  click~=8.1
5
5
  protobuf!=4.24.0,<7.0,>=3.19
6
6
  rich>=12.0.0
7
- synchronicity~=0.12.0
7
+ synchronicity~=0.12.1
8
8
  toml
9
9
  typer>=0.9
10
10
  types-certifi
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2026
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.4.1.dev5"
4
+ __version__ = "1.4.2.dev0"
@@ -22,7 +22,7 @@ dependencies = [
22
22
  "grpclib>=0.4.9,<0.4.10 ; python_version >= '3.14'",
23
23
  "protobuf>=3.19,<7.0,!=4.24.0",
24
24
  "rich>=12.0.0",
25
- "synchronicity~=0.12.0",
25
+ "synchronicity~=0.12.1",
26
26
  "toml",
27
27
  "typer>=0.9",
28
28
  "types-certifi",
@@ -71,7 +71,7 @@ dev = [
71
71
  "pyright~=1.1.351",
72
72
  "console-ctrl~=0.1.0",
73
73
  "zstandard~=0.25.0",
74
- "synchronicity[compile]~=0.12.0",
74
+ "synchronicity[compile]~=0.12.1",
75
75
  "python-json-logger==2.0.7",
76
76
  "build~=1.3.0",
77
77
 
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