modal 1.3.1.dev0__tar.gz → 1.3.1.dev2__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 (194) hide show
  1. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/PKG-INFO +1 -1
  2. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/task_command_router_client.py +25 -38
  3. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/client.pyi +2 -2
  4. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/PKG-INFO +1 -1
  5. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api.proto +1 -1
  6. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api_pb2.py +306 -306
  7. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api_pb2.pyi +4 -1
  8. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_version/__init__.py +1 -1
  9. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/LICENSE +0 -0
  10. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/README.md +0 -0
  11. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/__init__.py +0 -0
  12. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/__main__.py +0 -0
  13. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_billing.py +0 -0
  14. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_clustered_functions.py +0 -0
  15. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_clustered_functions.pyi +0 -0
  16. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_container_entrypoint.py +0 -0
  17. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_functions.py +0 -0
  18. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_grpc_client.py +0 -0
  19. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_ipython.py +0 -0
  20. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_load_context.py +0 -0
  21. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_location.py +0 -0
  22. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_object.py +0 -0
  23. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_output.py +0 -0
  24. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_partial_function.py +0 -0
  25. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_pty.py +0 -0
  26. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_resolver.py +0 -0
  27. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_resources.py +0 -0
  28. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/__init__.py +0 -0
  29. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/asgi.py +0 -0
  30. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/container_io_manager.py +0 -0
  31. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/container_io_manager.pyi +0 -0
  32. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/execution_context.py +0 -0
  33. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/execution_context.pyi +0 -0
  34. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  35. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/telemetry.py +0 -0
  36. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/user_code_event_loop.py +0 -0
  37. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_runtime/user_code_imports.py +0 -0
  38. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_serialization.py +0 -0
  39. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_traceback.py +0 -0
  40. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_tunnel.py +0 -0
  41. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_tunnel.pyi +0 -0
  42. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_type_manager.py +0 -0
  43. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/__init__.py +0 -0
  44. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/app_utils.py +0 -0
  45. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/async_utils.py +0 -0
  46. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/auth_token_manager.py +0 -0
  47. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/blob_utils.py +0 -0
  48. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/bytes_io_segment_payload.py +0 -0
  49. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/deprecation.py +0 -0
  50. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/docker_utils.py +0 -0
  51. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/function_utils.py +0 -0
  52. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/git_utils.py +0 -0
  53. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/grpc_testing.py +0 -0
  54. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/grpc_utils.py +0 -0
  55. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/hash_utils.py +0 -0
  56. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/http_utils.py +0 -0
  57. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/jwt_utils.py +0 -0
  58. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/logger.py +0 -0
  59. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/mount_utils.py +0 -0
  60. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/name_utils.py +0 -0
  61. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/package_utils.py +0 -0
  62. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/pattern_utils.py +0 -0
  63. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/rand_pb_testing.py +0 -0
  64. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/shell_utils.py +0 -0
  65. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_utils/time_utils.py +0 -0
  66. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_vendor/__init__.py +0 -0
  67. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  68. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_vendor/cloudpickle.py +0 -0
  69. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_vendor/tblib.py +0 -0
  70. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/_watcher.py +0 -0
  71. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/app.py +0 -0
  72. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/app.pyi +0 -0
  73. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/billing.py +0 -0
  74. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/2023.12.312.txt +0 -0
  75. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/2023.12.txt +0 -0
  76. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/2024.04.txt +0 -0
  77. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/2024.10.txt +0 -0
  78. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/2025.06.txt +0 -0
  79. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/PREVIEW.txt +0 -0
  80. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/README.md +0 -0
  81. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/builder/base-images.json +0 -0
  82. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/call_graph.py +0 -0
  83. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/__init__.py +0 -0
  84. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/_download.py +0 -0
  85. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/_traceback.py +0 -0
  86. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/app.py +0 -0
  87. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/cluster.py +0 -0
  88. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/config.py +0 -0
  89. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/container.py +0 -0
  90. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/dict.py +0 -0
  91. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/entry_point.py +0 -0
  92. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/environment.py +0 -0
  93. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/import_refs.py +0 -0
  94. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/launch.py +0 -0
  95. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/network_file_system.py +0 -0
  96. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/profile.py +0 -0
  97. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/programs/__init__.py +0 -0
  98. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/programs/run_jupyter.py +0 -0
  99. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/programs/vscode.py +0 -0
  100. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/queues.py +0 -0
  101. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/run.py +0 -0
  102. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/secret.py +0 -0
  103. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/shell.py +0 -0
  104. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/token.py +0 -0
  105. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/utils.py +0 -0
  106. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cli/volume.py +0 -0
  107. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/client.py +0 -0
  108. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cloud_bucket_mount.py +0 -0
  109. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cloud_bucket_mount.pyi +0 -0
  110. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cls.py +0 -0
  111. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/cls.pyi +0 -0
  112. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/config.py +0 -0
  113. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/container_process.py +0 -0
  114. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/container_process.pyi +0 -0
  115. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/dict.py +0 -0
  116. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/dict.pyi +0 -0
  117. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/environments.py +0 -0
  118. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/environments.pyi +0 -0
  119. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/exception.py +0 -0
  120. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/experimental/__init__.py +0 -0
  121. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/experimental/flash.py +0 -0
  122. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/experimental/flash.pyi +0 -0
  123. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/experimental/ipython.py +0 -0
  124. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/file_io.py +0 -0
  125. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/file_io.pyi +0 -0
  126. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/file_pattern_matcher.py +0 -0
  127. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/functions.py +0 -0
  128. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/functions.pyi +0 -0
  129. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/gpu.py +0 -0
  130. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/image.py +0 -0
  131. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/image.pyi +0 -0
  132. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/io_streams.py +0 -0
  133. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/io_streams.pyi +0 -0
  134. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/mount.py +0 -0
  135. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/mount.pyi +0 -0
  136. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/network_file_system.py +0 -0
  137. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/network_file_system.pyi +0 -0
  138. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/object.py +0 -0
  139. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/object.pyi +0 -0
  140. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/output.py +0 -0
  141. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/parallel_map.py +0 -0
  142. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/parallel_map.pyi +0 -0
  143. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/partial_function.py +0 -0
  144. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/partial_function.pyi +0 -0
  145. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/proxy.py +0 -0
  146. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/proxy.pyi +0 -0
  147. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/py.typed +0 -0
  148. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/queue.py +0 -0
  149. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/queue.pyi +0 -0
  150. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/retries.py +0 -0
  151. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/runner.py +0 -0
  152. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/runner.pyi +0 -0
  153. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/running_app.py +0 -0
  154. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/sandbox.py +0 -0
  155. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/sandbox.pyi +0 -0
  156. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/schedule.py +0 -0
  157. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/scheduler_placement.py +0 -0
  158. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/secret.py +0 -0
  159. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/secret.pyi +0 -0
  160. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/serving.py +0 -0
  161. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/serving.pyi +0 -0
  162. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/snapshot.py +0 -0
  163. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/snapshot.pyi +0 -0
  164. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/stream_type.py +0 -0
  165. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/token_flow.py +0 -0
  166. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/token_flow.pyi +0 -0
  167. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/volume.py +0 -0
  168. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal/volume.pyi +0 -0
  169. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/SOURCES.txt +0 -0
  170. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/dependency_links.txt +0 -0
  171. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/entry_points.txt +0 -0
  172. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/requires.txt +0 -0
  173. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal.egg-info/top_level.txt +0 -0
  174. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/__init__.py +0 -0
  175. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/gen_cli_docs.py +0 -0
  176. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/gen_reference_docs.py +0 -0
  177. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/mdmd/__init__.py +0 -0
  178. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/mdmd/mdmd.py +0 -0
  179. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_docs/mdmd/signatures.py +0 -0
  180. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/__init__.py +0 -0
  181. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api_grpc.py +0 -0
  182. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api_pb2_grpc.py +0 -0
  183. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/api_pb2_grpc.pyi +0 -0
  184. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/modal_api_grpc.py +0 -0
  185. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/py.typed +0 -0
  186. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router.proto +0 -0
  187. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router_grpc.py +0 -0
  188. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router_pb2.py +0 -0
  189. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router_pb2.pyi +0 -0
  190. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  191. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  192. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/modal_version/__main__.py +0 -0
  193. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/pyproject.toml +0 -0
  194. {modal-1.3.1.dev0 → modal-1.3.1.dev2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev0
3
+ Version: 1.3.1.dev2
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -5,6 +5,8 @@ import json
5
5
  import ssl
6
6
  import time
7
7
  import urllib.parse
8
+ import weakref
9
+ from contextlib import suppress
8
10
  from typing import AsyncGenerator, Optional
9
11
 
10
12
  import grpclib.client
@@ -106,6 +108,15 @@ async def fetch_command_router_access(server_client, task_id: str) -> api_pb2.Ta
106
108
  )
107
109
 
108
110
 
111
+ def _finalize_channel(loop, channel):
112
+ if not loop.is_closed():
113
+ # only run if loop has not shut down
114
+ # call_soon_threadsafe could throw if the loop is torn down after calling
115
+ # is_closed. This is safe to ignore.
116
+ with suppress(Exception):
117
+ loop.call_soon_threadsafe(channel.close)
118
+
119
+
109
120
  class TaskCommandRouterClient:
110
121
  """
111
122
  Client used to talk directly to TaskCommandRouter service on worker hosts.
@@ -199,44 +210,17 @@ class TaskCommandRouterClient:
199
210
 
200
211
  self._closed = False
201
212
 
202
- async def send_request(event: grpclib.events.SendRequest) -> None:
203
- # This will get the most recent JWT for every request. No need to
204
- # lock _jwt_refresh_lock: reads and writes happen on the
205
- # single-threaded event loop and variable assignment is atomic.
206
- event.metadata["authorization"] = f"Bearer {self._jwt}"
207
-
208
- grpclib.events.listen(self._channel, grpclib.events.SendRequest, send_request)
213
+ self._channel_finalizer = weakref.finalize(
214
+ self,
215
+ _finalize_channel,
216
+ loop,
217
+ channel,
218
+ )
209
219
 
210
220
  self._stub = TaskCommandRouterStub(self._channel)
211
221
 
212
- def __del__(self) -> None:
213
- """Best-effort cleanup if the caller forgot to close().
214
-
215
- This object is typically used through synchronicity wrappers, which means this finalizer
216
- may run on a different thread than the event loop that owns the channel. Closing the
217
- channel is therefore scheduled onto the owning loop using call_soon_threadsafe.
218
-
219
- Use getattr in the event that attributes are not yet initialized or the
220
- object is in a half-torn-down state.
221
- """
222
- if getattr(self, "_closed", False):
223
- return
224
- self._closed = True
225
-
226
- channel = getattr(self, "_channel", None)
227
- if channel is None:
228
- return
229
-
230
- loop = getattr(self, "_loop", None)
231
-
232
- if loop is not None and not loop.is_closed():
233
- try:
234
- loop.call_soon_threadsafe(channel.close)
235
- except Exception:
236
- # call_soon_threadsafe could throw if the loop is torn down
237
- # after calling is_closed. This is safe to ignore, and we don't
238
- # want to raise an exception from a destructor.
239
- pass
222
+ def _get_metadata(self):
223
+ return {"authorization": f"Bearer {self._jwt}"}
240
224
 
241
225
  async def close(self) -> None:
242
226
  """Close the client."""
@@ -245,6 +229,9 @@ class TaskCommandRouterClient:
245
229
 
246
230
  self._closed = True
247
231
  self._channel.close()
232
+ if self._channel_finalizer.alive:
233
+ # skip the finalizer if we've closed the channel anyway
234
+ self._channel_finalizer.detach()
248
235
 
249
236
  async def exec_start(self, request: sr_pb2.TaskExecStartRequest) -> sr_pb2.TaskExecStartResponse:
250
237
  """Start an exec'd command, properly retrying on transient errors."""
@@ -420,12 +407,12 @@ class TaskCommandRouterClient:
420
407
 
421
408
  async def _call_with_auth_retry(self, func, *args, **kwargs):
422
409
  try:
423
- return await func(*args, **kwargs)
410
+ return await func(*args, **kwargs, metadata=self._get_metadata())
424
411
  except GRPCError as exc:
425
412
  if exc.status == Status.UNAUTHENTICATED:
426
413
  await self._refresh_jwt()
427
414
  # Retry with the original arguments preserved
428
- return await func(*args, **kwargs)
415
+ return await func(*args, **kwargs, metadata=self._get_metadata())
429
416
  raise
430
417
 
431
418
  async def _stream_stdio(
@@ -460,7 +447,7 @@ class TaskCommandRouterClient:
460
447
  while True:
461
448
  timeout = max(0, deadline - time.monotonic()) if deadline is not None else None
462
449
  try:
463
- stream = self._stub.TaskExecStdioRead.open(timeout=timeout)
450
+ stream = self._stub.TaskExecStdioRead.open(timeout=timeout, metadata=self._get_metadata())
464
451
  async with stream as s:
465
452
  req = sr_pb2.TaskExecStdioReadRequest(
466
453
  task_id=task_id,
@@ -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.3.1.dev0",
36
+ version: str = "1.3.1.dev2",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.3.1.dev0",
166
+ version: str = "1.3.1.dev2",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.3.1.dev0
3
+ Version: 1.3.1.dev2
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -2886,7 +2886,7 @@ message SandboxInfo {
2886
2886
  string app_id = 5;
2887
2887
  repeated SandboxTag tags = 6; // TODO: Not yet exposed in client library.
2888
2888
  string name = 7;
2889
-
2889
+ string image_id = 8;
2890
2890
  reserved 2; // modal.client.Sandbox definition
2891
2891
  }
2892
2892