modal 1.4.3.dev17__tar.gz → 1.4.3.dev19__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 (214) hide show
  1. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/PKG-INFO +1 -1
  2. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/blob_utils.py +10 -3
  3. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/task_command_router_client.py +5 -14
  4. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/app.py +0 -2
  5. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/cluster.py +1 -4
  6. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/container.py +1 -3
  7. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/client.pyi +2 -2
  8. modal-1.4.3.dev19/modal/container_process.py +224 -0
  9. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/container_process.pyi +11 -120
  10. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/file_io.py +0 -7
  11. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/io_streams.py +69 -199
  12. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/io_streams.pyi +125 -64
  13. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/sandbox.py +41 -91
  14. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/sandbox.pyi +9 -76
  15. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/PKG-INFO +1 -1
  16. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/api_pb2.pyi +1 -1
  17. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_version/__init__.py +1 -1
  18. modal-1.4.3.dev17/modal/container_process.py +0 -470
  19. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/LICENSE +0 -0
  20. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/README.md +0 -0
  21. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/__init__.py +0 -0
  22. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/__main__.py +0 -0
  23. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_billing.py +0 -0
  24. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_clustered_functions.py +0 -0
  25. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_clustered_functions.pyi +0 -0
  26. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_container_entrypoint.py +0 -0
  27. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_environments.py +0 -0
  28. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_functions.py +0 -0
  29. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_grpc_client.py +0 -0
  30. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_ipython.py +0 -0
  31. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_load_context.py +0 -0
  32. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_location.py +0 -0
  33. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_logs.py +0 -0
  34. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_object.py +0 -0
  35. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_output/__init__.py +0 -0
  36. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_output/manager.py +0 -0
  37. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_output/pty.py +0 -0
  38. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_output/rich.py +0 -0
  39. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_output/status.py +0 -0
  40. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_partial_function.py +0 -0
  41. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_resolver.py +0 -0
  42. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_resources.py +0 -0
  43. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/__init__.py +0 -0
  44. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/asgi.py +0 -0
  45. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/container_io_manager.py +0 -0
  46. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/container_io_manager.pyi +0 -0
  47. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/execution_context.py +0 -0
  48. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/execution_context.pyi +0 -0
  49. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  50. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/telemetry.py +0 -0
  51. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/user_code_event_loop.py +0 -0
  52. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_runtime/user_code_imports.py +0 -0
  53. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_serialization.py +0 -0
  54. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_server.py +0 -0
  55. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_traceback.py +0 -0
  56. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_tunnel.py +0 -0
  57. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_tunnel.pyi +0 -0
  58. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_type_manager.py +0 -0
  59. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/__init__.py +0 -0
  60. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/app_utils.py +0 -0
  61. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/async_utils.py +0 -0
  62. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/auth_token_manager.py +0 -0
  63. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/browser_utils.py +0 -0
  64. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/bytes_io_segment_payload.py +0 -0
  65. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/deprecation.py +0 -0
  66. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/docker_utils.py +0 -0
  67. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/function_utils.py +0 -0
  68. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/git_utils.py +0 -0
  69. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/grpc_testing.py +0 -0
  70. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/grpc_utils.py +0 -0
  71. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/hash_utils.py +0 -0
  72. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/http_utils.py +0 -0
  73. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/jwt_utils.py +0 -0
  74. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/logger.py +0 -0
  75. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/mount_utils.py +0 -0
  76. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/name_utils.py +0 -0
  77. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/package_utils.py +0 -0
  78. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/pattern_utils.py +0 -0
  79. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/rand_pb_testing.py +0 -0
  80. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/sandbox_fs_utils.py +0 -0
  81. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/shell_utils.py +0 -0
  82. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_utils/time_utils.py +0 -0
  83. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_vendor/__init__.py +0 -0
  84. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  85. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_vendor/cloudpickle.py +0 -0
  86. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_vendor/tblib.py +0 -0
  87. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_vendor/version.py +0 -0
  88. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/_watcher.py +0 -0
  89. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/app.pyi +0 -0
  90. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/billing.py +0 -0
  91. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/2023.12.312.txt +0 -0
  92. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/2023.12.txt +0 -0
  93. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/2024.04.txt +0 -0
  94. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/2024.10.txt +0 -0
  95. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/2025.06.txt +0 -0
  96. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/PREVIEW.txt +0 -0
  97. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/README.md +0 -0
  98. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/builder/base-images.json +0 -0
  99. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/call_graph.py +0 -0
  100. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/__init__.py +0 -0
  101. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/_download.py +0 -0
  102. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/_help.py +0 -0
  103. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/_traceback.py +0 -0
  104. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/app.py +0 -0
  105. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/billing.py +0 -0
  106. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/bootstrap.py +0 -0
  107. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/changelog.py +0 -0
  108. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/config.py +0 -0
  109. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/dashboard.py +0 -0
  110. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/dict.py +0 -0
  111. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/entry_point.py +0 -0
  112. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/environment.py +0 -0
  113. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/import_refs.py +0 -0
  114. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/launch.py +0 -0
  115. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/logo.py +0 -0
  116. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/network_file_system.py +0 -0
  117. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/profile.py +0 -0
  118. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/programs/__init__.py +0 -0
  119. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/programs/run_jupyter.py +0 -0
  120. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/programs/vscode.py +0 -0
  121. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/queues.py +0 -0
  122. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/run.py +0 -0
  123. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/secret.py +0 -0
  124. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/selector.py +0 -0
  125. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/shell.py +0 -0
  126. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/token.py +0 -0
  127. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/utils.py +0 -0
  128. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cli/volume.py +0 -0
  129. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/client.py +0 -0
  130. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cloud_bucket_mount.py +0 -0
  131. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cloud_bucket_mount.pyi +0 -0
  132. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cls.py +0 -0
  133. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/cls.pyi +0 -0
  134. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/config.py +0 -0
  135. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/dict.py +0 -0
  136. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/dict.pyi +0 -0
  137. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/environments.py +0 -0
  138. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/environments.pyi +0 -0
  139. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/exception.py +0 -0
  140. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/experimental/__init__.py +0 -0
  141. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/experimental/flash.py +0 -0
  142. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/experimental/flash.pyi +0 -0
  143. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/experimental/ipython.py +0 -0
  144. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/file_io.pyi +0 -0
  145. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/file_pattern_matcher.py +0 -0
  146. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/functions.py +0 -0
  147. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/functions.pyi +0 -0
  148. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/image.py +0 -0
  149. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/image.pyi +0 -0
  150. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/mount.py +0 -0
  151. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/mount.pyi +0 -0
  152. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/network_file_system.py +0 -0
  153. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/network_file_system.pyi +0 -0
  154. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/object.py +0 -0
  155. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/object.pyi +0 -0
  156. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/output.py +0 -0
  157. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/parallel_map.py +0 -0
  158. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/parallel_map.pyi +0 -0
  159. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/partial_function.py +0 -0
  160. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/partial_function.pyi +0 -0
  161. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/proxy.py +0 -0
  162. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/proxy.pyi +0 -0
  163. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/py.typed +0 -0
  164. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/queue.py +0 -0
  165. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/queue.pyi +0 -0
  166. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/retries.py +0 -0
  167. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/runner.py +0 -0
  168. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/runner.pyi +0 -0
  169. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/running_app.py +0 -0
  170. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/sandbox_fs.py +0 -0
  171. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/sandbox_fs.pyi +0 -0
  172. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/schedule.py +0 -0
  173. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/scheduler_placement.py +0 -0
  174. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/secret.py +0 -0
  175. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/secret.pyi +0 -0
  176. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/server.py +0 -0
  177. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/server.pyi +0 -0
  178. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/serving.py +0 -0
  179. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/serving.pyi +0 -0
  180. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/snapshot.py +0 -0
  181. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/snapshot.pyi +0 -0
  182. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/stream_type.py +0 -0
  183. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/token_flow.py +0 -0
  184. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/token_flow.pyi +0 -0
  185. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/volume.py +0 -0
  186. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal/volume.pyi +0 -0
  187. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/SOURCES.txt +0 -0
  188. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/dependency_links.txt +0 -0
  189. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/entry_points.txt +0 -0
  190. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/requires.txt +0 -0
  191. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal.egg-info/top_level.txt +0 -0
  192. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/__init__.py +0 -0
  193. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/gen_cli_docs.py +0 -0
  194. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/gen_cli_docs_main.py +0 -0
  195. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/gen_reference_docs.py +0 -0
  196. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/gen_reference_docs_main.py +0 -0
  197. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/mdmd/__init__.py +0 -0
  198. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/mdmd/mdmd.py +0 -0
  199. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_docs/mdmd/signatures.py +0 -0
  200. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/__init__.py +0 -0
  201. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/api_grpc.py +0 -0
  202. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/api_pb2.py +0 -0
  203. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/api_pb2_grpc.py +0 -0
  204. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/api_pb2_grpc.pyi +0 -0
  205. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/modal_api_grpc.py +0 -0
  206. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/py.typed +0 -0
  207. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/task_command_router_grpc.py +0 -0
  208. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/task_command_router_pb2.py +0 -0
  209. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/task_command_router_pb2.pyi +0 -0
  210. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  211. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  212. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/modal_version/__main__.py +0 -0
  213. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/pyproject.toml +0 -0
  214. {modal-1.4.3.dev17 → modal-1.4.3.dev19}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.3.dev17
3
+ Version: 1.4.3.dev19
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -717,7 +717,14 @@ def use_md5(url: str) -> bool:
717
717
  host = urlparse(url).netloc.split(":")[0]
718
718
  if host.endswith(".amazonaws.com") or host.endswith(".r2.cloudflarestorage.com"):
719
719
  return True
720
- elif host in ["127.0.0.1", "localhost", "172.20.0.1", "172.21.0.1"]:
720
+ if host == "localhost":
721
721
  return False
722
- else:
723
- raise Exception(f"Unknown S3 host: {host}")
722
+ try:
723
+ import ipaddress
724
+
725
+ addr = ipaddress.ip_address(host)
726
+ if addr.is_private or addr.is_loopback:
727
+ return False
728
+ except ValueError:
729
+ pass
730
+ raise Exception(f"Unknown S3 host: {host}")
@@ -16,7 +16,7 @@ from grpclib import GRPCError, Status
16
16
  from grpclib.exceptions import StreamTerminatedError
17
17
 
18
18
  from modal.config import logger
19
- from modal.exception import ConflictError, ExecTimeoutError
19
+ from modal.exception import ExecTimeoutError
20
20
  from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
21
21
  from modal_proto.task_command_router_grpc import TaskCommandRouterStub
22
22
 
@@ -196,21 +196,12 @@ class TaskCommandRouterClient:
196
196
  return cls(server_client, task_id, url, jwt, channel, loop, jwt_refresh_lock, sandbox_id=sandbox_id)
197
197
 
198
198
  @classmethod
199
- async def try_init(
199
+ async def init(
200
200
  cls,
201
201
  server_client,
202
202
  task_id: str,
203
- ) -> Optional["TaskCommandRouterClient"]:
204
- """Attempt to initialize a TaskCommandRouterClient by fetching direct access.
205
-
206
- Returns None if command router access is not enabled (FAILED_PRECONDITION).
207
- """
208
- try:
209
- resp = await fetch_command_router_access(server_client, task_id)
210
- except ConflictError:
211
- logger.debug(f"Command router access is not enabled for task {task_id}")
212
- return None
213
-
203
+ ) -> "TaskCommandRouterClient":
204
+ resp = await fetch_command_router_access(server_client, task_id)
214
205
  logger.debug(f"Using command router access for task {task_id}")
215
206
  return await cls._connect(server_client, task_id, resp.url, resp.jwt)
216
207
 
@@ -241,7 +232,7 @@ class TaskCommandRouterClient:
241
232
  stream_stdio_retry_delay_factor: float = 2,
242
233
  stream_stdio_max_retries: int = 10,
243
234
  ) -> None:
244
- """Callers should not use this directly. Use TaskCommandRouterClient.try_init() instead."""
235
+ """Callers should not use this directly. Use TaskCommandRouterClient.init() instead."""
245
236
  # Record the loop this instance is bound to so __del__ can safely schedule cleanup
246
237
  # even if finalization happens from a different thread (e.g. via synchronicity).
247
238
  self._loop = loop
@@ -778,7 +778,6 @@ class _App:
778
778
  deprecation_warning(
779
779
  (2025, 12, 16),
780
780
  "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
781
- pending=True,
782
781
  )
783
782
  single_use_containers = max_inputs == 1
784
783
 
@@ -987,7 +986,6 @@ class _App:
987
986
  deprecation_warning(
988
987
  (2025, 12, 16),
989
988
  "The `max_inputs` parameter is deprecated. Please set `single_use_containers=True` instead.",
990
- pending=True,
991
989
  )
992
990
  single_use_containers = max_inputs == 1
993
991
 
@@ -16,7 +16,6 @@ from modal.cli.utils import display_table, env_option, is_tty
16
16
  from modal.client import _Client
17
17
  from modal.config import config
18
18
  from modal.container_process import _ContainerProcess
19
- from modal.exception import InvalidError
20
19
  from modal.output import OutputManager
21
20
  from modal.stream_type import StreamType
22
21
  from modal_proto import api_pb2, task_command_router_pb2 as sr_pb2
@@ -79,9 +78,7 @@ async def shell(cluster_id: str, rank: int = 0):
79
78
 
80
79
  pty = is_tty()
81
80
 
82
- command_router_client = await TaskCommandRouterClient.try_init(client, task_id)
83
- if command_router_client is None:
84
- raise InvalidError(f"Command router access is not available for container {task_id}")
81
+ command_router_client = await TaskCommandRouterClient.init(client, task_id)
85
82
 
86
83
  process_id = str(uuid.uuid4())
87
84
 
@@ -278,9 +278,7 @@ async def _exec_impl(
278
278
 
279
279
  client = await _Client.from_env()
280
280
 
281
- command_router_client = await TaskCommandRouterClient.try_init(client, container_id)
282
- if command_router_client is None:
283
- raise InvalidError(f"Command router access is not available for container {container_id}")
281
+ command_router_client = await TaskCommandRouterClient.init(client, container_id)
284
282
 
285
283
  process_id = str(uuid.uuid4())
286
284
 
@@ -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.3.dev17",
38
+ version: str = "1.4.3.dev19",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -175,7 +175,7 @@ class Client:
175
175
  server_url: str,
176
176
  client_type: int,
177
177
  credentials: typing.Optional[tuple[str, str]],
178
- version: str = "1.4.3.dev17",
178
+ version: str = "1.4.3.dev19",
179
179
  ):
180
180
  """mdmd:hidden
181
181
  The Modal client object is not intended to be instantiated directly by users.
@@ -0,0 +1,224 @@
1
+ # Copyright Modal Labs 2024
2
+ import asyncio
3
+ import platform
4
+ from typing import Generic, Optional, TypeVar
5
+
6
+ from modal_proto import api_pb2
7
+
8
+ from ._utils.async_utils import TaskContext, synchronize_api
9
+ from ._utils.shell_utils import stream_from_stdin, write_to_fd
10
+ from ._utils.task_command_router_client import TaskCommandRouterClient
11
+ from .client import _Client
12
+ from .config import logger
13
+ from .exception import ExecTimeoutError, InteractiveTimeoutError, InvalidError
14
+ from .io_streams import (
15
+ _StreamReader,
16
+ _StreamReaderThroughCommandRouterParams,
17
+ _StreamWriter,
18
+ _StreamWriterThroughCommandRouterParams,
19
+ )
20
+ from .stream_type import StreamType
21
+
22
+ T = TypeVar("T", str, bytes)
23
+
24
+
25
+ async def _iter_stream_as_bytes(stream: _StreamReader[T]):
26
+ """Yield raw bytes from a StreamReader regardless of text mode/backend."""
27
+ async for part in stream:
28
+ if isinstance(part, str):
29
+ yield part.encode("utf-8")
30
+ else:
31
+ yield part
32
+
33
+
34
+ class _ContainerProcess(Generic[T]):
35
+ """Represents a running process in a container.
36
+
37
+ Container processes communicate via direct communication with
38
+ the Modal worker where the container is running.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ process_id: str,
44
+ task_id: str,
45
+ client: _Client,
46
+ command_router_client: TaskCommandRouterClient,
47
+ stdout: StreamType = StreamType.PIPE,
48
+ stderr: StreamType = StreamType.PIPE,
49
+ exec_deadline: Optional[float] = None,
50
+ text: bool = True,
51
+ by_line: bool = False,
52
+ ) -> None:
53
+ self._client = client
54
+ self._command_router_client = command_router_client
55
+ self._process_id = process_id
56
+ self._exec_deadline = exec_deadline
57
+ self._text = text
58
+ self._by_line = by_line
59
+ self._task_id = task_id
60
+ self._stdout = _StreamReader[T](
61
+ _StreamReaderThroughCommandRouterParams(
62
+ file_descriptor=api_pb2.FILE_DESCRIPTOR_STDOUT,
63
+ task_id=self._task_id,
64
+ object_id=process_id,
65
+ command_router_client=self._command_router_client,
66
+ deadline=exec_deadline,
67
+ ),
68
+ stream_type=stdout,
69
+ text=text,
70
+ by_line=by_line,
71
+ )
72
+ self._stderr = _StreamReader[T](
73
+ _StreamReaderThroughCommandRouterParams(
74
+ file_descriptor=api_pb2.FILE_DESCRIPTOR_STDERR,
75
+ task_id=self._task_id,
76
+ object_id=process_id,
77
+ command_router_client=self._command_router_client,
78
+ deadline=exec_deadline,
79
+ ),
80
+ stream_type=stderr,
81
+ text=text,
82
+ by_line=by_line,
83
+ )
84
+ self._stdin = _StreamWriter(
85
+ _StreamWriterThroughCommandRouterParams(
86
+ task_id=self._task_id,
87
+ object_id=process_id,
88
+ command_router_client=self._command_router_client,
89
+ )
90
+ )
91
+ self._returncode = None
92
+
93
+ def __repr__(self) -> str:
94
+ return f"ContainerProcess(process_id={self._process_id!r})"
95
+
96
+ @property
97
+ def stdout(self) -> _StreamReader[T]:
98
+ """StreamReader for the container process's stdout stream."""
99
+ return self._stdout
100
+
101
+ @property
102
+ def stderr(self) -> _StreamReader[T]:
103
+ """StreamReader for the container process's stderr stream."""
104
+ return self._stderr
105
+
106
+ @property
107
+ def stdin(self) -> _StreamWriter:
108
+ """StreamWriter for the container process's stdin stream."""
109
+ return self._stdin
110
+
111
+ @property
112
+ def returncode(self) -> int:
113
+ if self._returncode is None:
114
+ raise InvalidError(
115
+ "You must call wait() before accessing the returncode. "
116
+ "To poll for the status of a running process, use poll() instead."
117
+ )
118
+ return self._returncode
119
+
120
+ async def poll(self) -> Optional[int]:
121
+ """Check if the container process has finished running.
122
+
123
+ Returns `None` if the process is still running, else returns the exit code.
124
+ """
125
+ if self._returncode is not None:
126
+ return self._returncode
127
+ try:
128
+ resp = await self._command_router_client.exec_poll(self._task_id, self._process_id, self._exec_deadline)
129
+ which = resp.WhichOneof("exit_status")
130
+ if which is None:
131
+ return None
132
+
133
+ if which == "code":
134
+ self._returncode = int(resp.code)
135
+ return self._returncode
136
+ elif which == "signal":
137
+ self._returncode = 128 + int(resp.signal)
138
+ return self._returncode
139
+ else:
140
+ logger.debug(f"ContainerProcess {self._process_id} exited with unexpected status: {which}")
141
+ raise InvalidError("Unexpected exit status")
142
+ except ExecTimeoutError:
143
+ logger.debug(f"ContainerProcess poll for {self._process_id} did not complete within deadline")
144
+ # TODO(saltzm): This is a weird API, but customers currently may rely on it. This
145
+ # should probably raise an ExecTimeoutError instead.
146
+ self._returncode = -1
147
+ return self._returncode
148
+ except Exception as e:
149
+ # Re-raise non-transient errors or errors resulting from exceeding retries on transient errors.
150
+ logger.warning(f"ContainerProcess poll for {self._process_id} failed: {e}")
151
+ raise
152
+
153
+ async def wait(self) -> int:
154
+ """Wait for the container process to finish running. Returns the exit code."""
155
+ if self._returncode is not None:
156
+ return self._returncode
157
+
158
+ try:
159
+ resp = await self._command_router_client.exec_wait(self._task_id, self._process_id, self._exec_deadline)
160
+ which = resp.WhichOneof("exit_status")
161
+ if which == "code":
162
+ self._returncode = int(resp.code)
163
+ elif which == "signal":
164
+ self._returncode = 128 + int(resp.signal)
165
+ else:
166
+ logger.debug(f"ContainerProcess {self._process_id} exited with unexpected status: {which}")
167
+ self._returncode = -1
168
+ raise InvalidError("Unexpected exit status")
169
+ except ExecTimeoutError:
170
+ logger.debug(f"ContainerProcess {self._process_id} did not complete within deadline")
171
+ # TODO(saltzm): This is a weird API, but customers currently may rely on it. This
172
+ # should be a ExecTimeoutError.
173
+ self._returncode = -1
174
+
175
+ return self._returncode
176
+
177
+ async def attach(self):
178
+ """mdmd:hidden"""
179
+ if platform.system() == "Windows":
180
+ print("interactive exec is not currently supported on Windows.") # noqa: T201
181
+ return
182
+
183
+ from .output import OutputManager
184
+
185
+ output = OutputManager.get()
186
+ connecting_status = output.status("Connecting...")
187
+ connecting_status.start()
188
+ on_connect = asyncio.Event()
189
+
190
+ async def _write_to_fd_loop(stream: _StreamReader[T]):
191
+ async for chunk in _iter_stream_as_bytes(stream):
192
+ if chunk is None:
193
+ break
194
+
195
+ if not on_connect.is_set():
196
+ connecting_status.stop()
197
+ on_connect.set()
198
+
199
+ await write_to_fd(stream.file_descriptor, chunk)
200
+
201
+ async def _handle_input(data: bytes, message_index: int):
202
+ self.stdin.write(data)
203
+ await self.stdin.drain()
204
+
205
+ async with TaskContext() as tc:
206
+ stdout_task = tc.create_task(_write_to_fd_loop(self.stdout))
207
+ stderr_task = tc.create_task(_write_to_fd_loop(self.stderr))
208
+
209
+ try:
210
+ # Time out if we can't connect fast enough.
211
+ await asyncio.wait_for(on_connect.wait(), timeout=60)
212
+
213
+ async with stream_from_stdin(_handle_input, use_raw_terminal=True):
214
+ await stdout_task
215
+ await stderr_task
216
+
217
+ except (asyncio.TimeoutError, TimeoutError):
218
+ connecting_status.stop()
219
+ stdout_task.cancel()
220
+ stderr_task.cancel()
221
+ raise InteractiveTimeoutError("Failed to establish connection to container. Please try again.")
222
+
223
+
224
+ ContainerProcess = synchronize_api(_ContainerProcess)
@@ -7,140 +7,27 @@ import typing_extensions
7
7
 
8
8
  T = typing.TypeVar("T")
9
9
 
10
- class _ContainerProcessThroughServer(typing.Generic[T]):
11
- """Abstract base class for generic types.
12
-
13
- A generic type is typically declared by inheriting from
14
- this class parameterized with one or more type variables.
15
- For example, a generic mapping type might be defined as::
16
-
17
- class Mapping(Generic[KT, VT]):
18
- def __getitem__(self, key: KT) -> VT:
19
- ...
20
- # Etc.
21
-
22
- This class can then be used as follows::
23
-
24
- def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
25
- try:
26
- return mapping[key]
27
- except KeyError:
28
- return default
29
- """
30
-
31
- _process_id: typing.Optional[str]
32
- _stdout: modal.io_streams._StreamReader[T]
33
- _stderr: modal.io_streams._StreamReader[T]
34
- _stdin: modal.io_streams._StreamWriter
35
- _exec_deadline: typing.Optional[float]
36
- _text: bool
37
- _by_line: bool
38
- _returncode: typing.Optional[int]
39
-
40
- def __init__(
41
- self,
42
- process_id: str,
43
- task_id: str,
44
- client: modal.client._Client,
45
- stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
46
- stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
47
- exec_deadline: typing.Optional[float] = None,
48
- text: bool = True,
49
- by_line: bool = False,
50
- ) -> None:
51
- """Initialize self. See help(type(self)) for accurate signature."""
52
- ...
53
-
54
- def __repr__(self) -> str:
55
- """Return repr(self)."""
56
- ...
57
-
58
- @property
59
- def stdout(self) -> modal.io_streams._StreamReader[T]:
60
- """StreamReader for the container process's stdout stream."""
61
- ...
62
-
63
- @property
64
- def stderr(self) -> modal.io_streams._StreamReader[T]:
65
- """StreamReader for the container process's stderr stream."""
66
- ...
67
-
68
- @property
69
- def stdin(self) -> modal.io_streams._StreamWriter:
70
- """StreamWriter for the container process's stdin stream."""
71
- ...
72
-
73
- @property
74
- def returncode(self) -> int: ...
75
- async def poll(self) -> typing.Optional[int]:
76
- """Check if the container process has finished running.
77
-
78
- Returns `None` if the process is still running, else returns the exit code.
79
- """
80
- ...
81
-
82
- async def _wait_for_completion(self) -> int: ...
83
- async def wait(self) -> int:
84
- """Wait for the container process to finish running. Returns the exit code."""
85
- ...
86
-
87
- async def attach(self):
88
- """mdmd:hidden"""
89
- ...
90
-
91
10
  def _iter_stream_as_bytes(stream: modal.io_streams._StreamReader[T]):
92
11
  """Yield raw bytes from a StreamReader regardless of text mode/backend."""
93
12
  ...
94
13
 
95
- class _ContainerProcessThroughCommandRouter(typing.Generic[T]):
96
- """Container process implementation that works via direct communication with
14
+ class _ContainerProcess(typing.Generic[T]):
15
+ """Represents a running process in a container.
16
+
17
+ Container processes communicate via direct communication with
97
18
  the Modal worker where the container is running.
98
19
  """
99
- def __init__(
100
- self,
101
- process_id: str,
102
- client: modal.client._Client,
103
- command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
104
- task_id: str,
105
- *,
106
- stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
107
- stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
108
- exec_deadline: typing.Optional[float] = None,
109
- text: bool = True,
110
- by_line: bool = False,
111
- ) -> None:
112
- """Initialize self. See help(type(self)) for accurate signature."""
113
- ...
114
-
115
- def __repr__(self) -> str:
116
- """Return repr(self)."""
117
- ...
118
-
119
- @property
120
- def stdout(self) -> modal.io_streams._StreamReader[T]: ...
121
- @property
122
- def stderr(self) -> modal.io_streams._StreamReader[T]: ...
123
- @property
124
- def stdin(self) -> modal.io_streams._StreamWriter: ...
125
- @property
126
- def returncode(self) -> int: ...
127
- async def poll(self) -> typing.Optional[int]: ...
128
- async def wait(self) -> int: ...
129
- async def attach(self): ...
130
-
131
- class _ContainerProcess(typing.Generic[T]):
132
- """Represents a running process in a container."""
133
20
  def __init__(
134
21
  self,
135
22
  process_id: str,
136
23
  task_id: str,
137
24
  client: modal.client._Client,
25
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
138
26
  stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
139
27
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
140
28
  exec_deadline: typing.Optional[float] = None,
141
29
  text: bool = True,
142
30
  by_line: bool = False,
143
- command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
144
31
  ) -> None:
145
32
  """Initialize self. See help(type(self)) for accurate signature."""
146
33
  ...
@@ -182,18 +69,22 @@ class _ContainerProcess(typing.Generic[T]):
182
69
  ...
183
70
 
184
71
  class ContainerProcess(typing.Generic[T]):
185
- """Represents a running process in a container."""
72
+ """Represents a running process in a container.
73
+
74
+ Container processes communicate via direct communication with
75
+ the Modal worker where the container is running.
76
+ """
186
77
  def __init__(
187
78
  self,
188
79
  process_id: str,
189
80
  task_id: str,
190
81
  client: modal.client.Client,
82
+ command_router_client: modal._utils.task_command_router_client.TaskCommandRouterClient,
191
83
  stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
192
84
  stderr: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
193
85
  exec_deadline: typing.Optional[float] = None,
194
86
  text: bool = True,
195
87
  by_line: bool = False,
196
- command_router_client: typing.Optional[modal._utils.task_command_router_client.TaskCommandRouterClient] = None,
197
88
  ) -> None: ...
198
89
  def __repr__(self) -> str: ...
199
90
  @property
@@ -248,7 +248,6 @@ class _FileIO(Generic[T]):
248
248
  "`FileIO.read()` is deprecated."
249
249
  " Use `Sandbox.filesystem.read_text()`, `Sandbox.filesystem.read_bytes()`,"
250
250
  " or `Sandbox.filesystem.copy_to_local()` instead.",
251
- pending=True,
252
251
  )
253
252
  self._check_closed()
254
253
  self._check_readable()
@@ -266,7 +265,6 @@ class _FileIO(Generic[T]):
266
265
  "`FileIO.readline()` is deprecated."
267
266
  " Use `Sandbox.filesystem.read_text()`, `Sandbox.filesystem.read_bytes()`,"
268
267
  " or `Sandbox.filesystem.copy_to_local()` instead.",
269
- pending=True,
270
268
  )
271
269
  self._check_closed()
272
270
  self._check_readable()
@@ -288,7 +286,6 @@ class _FileIO(Generic[T]):
288
286
  "`FileIO.readlines()` is deprecated."
289
287
  " Use `Sandbox.filesystem.read_text()`, `Sandbox.filesystem.read_bytes()`,"
290
288
  " or `Sandbox.filesystem.copy_to_local()` instead.",
291
- pending=True,
292
289
  )
293
290
  self._check_closed()
294
291
  self._check_readable()
@@ -314,7 +311,6 @@ class _FileIO(Generic[T]):
314
311
  "`FileIO.write()` is deprecated."
315
312
  " Use `Sandbox.filesystem.write_text()`, `Sandbox.filesystem.write_bytes()`,"
316
313
  " or `Sandbox.filesystem.copy_from_local()` instead.",
317
- pending=True,
318
314
  )
319
315
  self._check_closed()
320
316
  self._check_writable()
@@ -343,7 +339,6 @@ class _FileIO(Generic[T]):
343
339
  "`FileIO.flush()` is deprecated."
344
340
  " Use `Sandbox.filesystem.write_text()`, `Sandbox.filesystem.write_bytes()`,"
345
341
  " or `Sandbox.filesystem.copy_from_local()` instead.",
346
- pending=True,
347
342
  )
348
343
  self._check_closed()
349
344
  self._check_writable()
@@ -407,7 +402,6 @@ class _FileIO(Generic[T]):
407
402
  deprecation_warning(
408
403
  (2026, 3, 9),
409
404
  "`FileIO.mkdir()` is deprecated. Use `Sandbox.filesystem.make_directory()` instead.",
410
- pending=True,
411
405
  )
412
406
  await mkdir(path, client, task_id, parents)
413
407
 
@@ -417,7 +411,6 @@ class _FileIO(Generic[T]):
417
411
  deprecation_warning(
418
412
  (2026, 3, 9),
419
413
  "`FileIO.rm()` is deprecated. Use `Sandbox.filesystem.remove()` instead.",
420
- pending=True,
421
414
  )
422
415
  await rm(path, client, task_id, recursive)
423
416