modal 1.1.3.dev1__tar.gz → 1.1.3.dev3__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 (190) hide show
  1. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/PKG-INFO +1 -1
  2. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_functions.py +47 -25
  3. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/client.pyi +2 -2
  4. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/PKG-INFO +1 -1
  5. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api.proto +1 -1
  6. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api_pb2.pyi +1 -2
  7. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_version/__init__.py +1 -1
  8. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/LICENSE +0 -0
  9. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/README.md +0 -0
  10. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/__init__.py +0 -0
  11. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/__main__.py +0 -0
  12. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_clustered_functions.py +0 -0
  13. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_clustered_functions.pyi +0 -0
  14. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_container_entrypoint.py +0 -0
  15. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_ipython.py +0 -0
  16. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_location.py +0 -0
  17. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_object.py +0 -0
  18. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_output.py +0 -0
  19. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_partial_function.py +0 -0
  20. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_pty.py +0 -0
  21. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_resolver.py +0 -0
  22. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_resources.py +0 -0
  23. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/__init__.py +0 -0
  24. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/asgi.py +0 -0
  25. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/container_io_manager.py +0 -0
  26. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/container_io_manager.pyi +0 -0
  27. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/execution_context.py +0 -0
  28. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/execution_context.pyi +0 -0
  29. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  30. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/telemetry.py +0 -0
  31. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_runtime/user_code_imports.py +0 -0
  32. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_serialization.py +0 -0
  33. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_traceback.py +0 -0
  34. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_tunnel.py +0 -0
  35. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_tunnel.pyi +0 -0
  36. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_type_manager.py +0 -0
  37. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/__init__.py +0 -0
  38. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/app_utils.py +0 -0
  39. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/async_utils.py +0 -0
  40. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/auth_token_manager.py +0 -0
  41. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/blob_utils.py +0 -0
  42. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/bytes_io_segment_payload.py +0 -0
  43. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/deprecation.py +0 -0
  44. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/docker_utils.py +0 -0
  45. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/function_utils.py +0 -0
  46. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/git_utils.py +0 -0
  47. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/grpc_testing.py +0 -0
  48. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/grpc_utils.py +0 -0
  49. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/hash_utils.py +0 -0
  50. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/http_utils.py +0 -0
  51. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/jwt_utils.py +0 -0
  52. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/logger.py +0 -0
  53. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/mount_utils.py +0 -0
  54. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/name_utils.py +0 -0
  55. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/package_utils.py +0 -0
  56. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/pattern_utils.py +0 -0
  57. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/rand_pb_testing.py +0 -0
  58. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/shell_utils.py +0 -0
  59. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_utils/time_utils.py +0 -0
  60. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_vendor/__init__.py +0 -0
  61. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  62. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_vendor/cloudpickle.py +0 -0
  63. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_vendor/tblib.py +0 -0
  64. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/_watcher.py +0 -0
  65. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/app.py +0 -0
  66. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/app.pyi +0 -0
  67. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/2023.12.312.txt +0 -0
  68. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/2023.12.txt +0 -0
  69. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/2024.04.txt +0 -0
  70. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/2024.10.txt +0 -0
  71. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/2025.06.txt +0 -0
  72. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/PREVIEW.txt +0 -0
  73. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/README.md +0 -0
  74. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/builder/base-images.json +0 -0
  75. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/call_graph.py +0 -0
  76. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/__init__.py +0 -0
  77. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/_download.py +0 -0
  78. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/_traceback.py +0 -0
  79. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/app.py +0 -0
  80. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/cluster.py +0 -0
  81. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/config.py +0 -0
  82. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/container.py +0 -0
  83. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/dict.py +0 -0
  84. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/entry_point.py +0 -0
  85. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/environment.py +0 -0
  86. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/import_refs.py +0 -0
  87. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/launch.py +0 -0
  88. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/network_file_system.py +0 -0
  89. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/profile.py +0 -0
  90. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/programs/__init__.py +0 -0
  91. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/programs/launch_instance_ssh.py +0 -0
  92. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/programs/run_jupyter.py +0 -0
  93. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/programs/run_marimo.py +0 -0
  94. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/programs/vscode.py +0 -0
  95. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/queues.py +0 -0
  96. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/run.py +0 -0
  97. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/secret.py +0 -0
  98. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/token.py +0 -0
  99. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/utils.py +0 -0
  100. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cli/volume.py +0 -0
  101. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/client.py +0 -0
  102. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cloud_bucket_mount.py +0 -0
  103. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cloud_bucket_mount.pyi +0 -0
  104. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cls.py +0 -0
  105. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/cls.pyi +0 -0
  106. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/config.py +0 -0
  107. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/container_process.py +0 -0
  108. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/container_process.pyi +0 -0
  109. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/dict.py +0 -0
  110. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/dict.pyi +0 -0
  111. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/environments.py +0 -0
  112. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/environments.pyi +0 -0
  113. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/exception.py +0 -0
  114. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/experimental/__init__.py +0 -0
  115. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/experimental/flash.py +0 -0
  116. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/experimental/flash.pyi +0 -0
  117. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/experimental/ipython.py +0 -0
  118. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/file_io.py +0 -0
  119. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/file_io.pyi +0 -0
  120. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/file_pattern_matcher.py +0 -0
  121. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/functions.py +0 -0
  122. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/functions.pyi +0 -0
  123. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/gpu.py +0 -0
  124. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/image.py +0 -0
  125. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/image.pyi +0 -0
  126. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/io_streams.py +0 -0
  127. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/io_streams.pyi +0 -0
  128. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/mount.py +0 -0
  129. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/mount.pyi +0 -0
  130. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/network_file_system.py +0 -0
  131. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/network_file_system.pyi +0 -0
  132. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/object.py +0 -0
  133. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/object.pyi +0 -0
  134. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/output.py +0 -0
  135. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/parallel_map.py +0 -0
  136. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/parallel_map.pyi +0 -0
  137. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/partial_function.py +0 -0
  138. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/partial_function.pyi +0 -0
  139. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/proxy.py +0 -0
  140. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/proxy.pyi +0 -0
  141. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/py.typed +0 -0
  142. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/queue.py +0 -0
  143. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/queue.pyi +0 -0
  144. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/retries.py +0 -0
  145. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/runner.py +0 -0
  146. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/runner.pyi +0 -0
  147. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/running_app.py +0 -0
  148. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/sandbox.py +0 -0
  149. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/sandbox.pyi +0 -0
  150. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/schedule.py +0 -0
  151. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/scheduler_placement.py +0 -0
  152. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/secret.py +0 -0
  153. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/secret.pyi +0 -0
  154. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/serving.py +0 -0
  155. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/serving.pyi +0 -0
  156. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/snapshot.py +0 -0
  157. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/snapshot.pyi +0 -0
  158. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/stream_type.py +0 -0
  159. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/token_flow.py +0 -0
  160. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/token_flow.pyi +0 -0
  161. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/volume.py +0 -0
  162. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal/volume.pyi +0 -0
  163. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/SOURCES.txt +0 -0
  164. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/dependency_links.txt +0 -0
  165. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/entry_points.txt +0 -0
  166. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/requires.txt +0 -0
  167. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal.egg-info/top_level.txt +0 -0
  168. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/__init__.py +0 -0
  169. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/gen_cli_docs.py +0 -0
  170. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/gen_reference_docs.py +0 -0
  171. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/mdmd/__init__.py +0 -0
  172. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/mdmd/mdmd.py +0 -0
  173. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_docs/mdmd/signatures.py +0 -0
  174. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/__init__.py +0 -0
  175. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api_grpc.py +0 -0
  176. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api_pb2.py +0 -0
  177. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api_pb2_grpc.py +0 -0
  178. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/api_pb2_grpc.pyi +0 -0
  179. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/modal_api_grpc.py +0 -0
  180. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/modal_options_grpc.py +0 -0
  181. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options.proto +0 -0
  182. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options_grpc.py +0 -0
  183. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options_pb2.py +0 -0
  184. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options_pb2.pyi +0 -0
  185. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options_pb2_grpc.py +0 -0
  186. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/options_pb2_grpc.pyi +0 -0
  187. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_proto/py.typed +0 -0
  188. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/modal_version/__main__.py +0 -0
  189. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/pyproject.toml +0 -0
  190. {modal-1.1.3.dev1 → modal-1.1.3.dev3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.3.dev1
3
+ Version: 1.1.3.dev3
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -100,6 +100,10 @@ if TYPE_CHECKING:
100
100
  import modal.partial_function
101
101
 
102
102
  MAX_INTERNAL_FAILURE_COUNT = 8
103
+ TERMINAL_STATUSES = (
104
+ api_pb2.GenericResult.GENERIC_STATUS_SUCCESS,
105
+ api_pb2.GenericResult.GENERIC_STATUS_TERMINATED,
106
+ )
103
107
 
104
108
 
105
109
  @dataclasses.dataclass
@@ -300,11 +304,7 @@ class _Invocation:
300
304
 
301
305
  while True:
302
306
  item = await self._get_single_output(ctx.input_jwt)
303
- if item.result.status in (
304
- api_pb2.GenericResult.GENERIC_STATUS_SUCCESS,
305
- api_pb2.GenericResult.GENERIC_STATUS_TERMINATED,
306
- ):
307
- # success or cancellations are "final" results
307
+ if item.result.status in TERMINAL_STATUSES:
308
308
  return await _process_result(item.result, item.data_format, self.stub, self.client)
309
309
 
310
310
  if item.result.status != api_pb2.GenericResult.GENERIC_STATUS_INTERNAL_FAILURE:
@@ -411,6 +411,7 @@ class _InputPlaneInvocation:
411
411
  client: _Client,
412
412
  input_item: api_pb2.FunctionPutInputsItem,
413
413
  function_id: str,
414
+ retry_policy: api_pb2.FunctionRetryPolicy,
414
415
  input_plane_region: str,
415
416
  ):
416
417
  self.stub = stub
@@ -418,6 +419,7 @@ class _InputPlaneInvocation:
418
419
  self.attempt_token = attempt_token
419
420
  self.input_item = input_item
420
421
  self.function_id = function_id
422
+ self.retry_policy = retry_policy
421
423
  self.input_plane_region = input_plane_region
422
424
 
423
425
  @staticmethod
@@ -453,11 +455,15 @@ class _InputPlaneInvocation:
453
455
  response = await retry_transient_errors(stub.AttemptStart, request, metadata=metadata)
454
456
  attempt_token = response.attempt_token
455
457
 
456
- return _InputPlaneInvocation(stub, attempt_token, client, input_item, function_id, input_plane_region)
458
+ return _InputPlaneInvocation(
459
+ stub, attempt_token, client, input_item, function_id, response.retry_policy, input_plane_region
460
+ )
457
461
 
458
462
  async def run_function(self) -> Any:
463
+ # User errors including timeouts are managed by the user-specified retry policy.
464
+ user_retry_manager = RetryManager(self.retry_policy)
465
+
459
466
  # This will retry when the server returns GENERIC_STATUS_INTERNAL_FAILURE, i.e. lost inputs or worker preemption
460
- # TODO(ryan): add logic to retry for user defined retry policy
461
467
  internal_failure_count = 0
462
468
  while True:
463
469
  await_request = api_pb2.AttemptAwaitRequest(
@@ -474,32 +480,48 @@ class _InputPlaneInvocation:
474
480
  )
475
481
 
476
482
  if await_response.HasField("output"):
483
+ if await_response.output.result.status in TERMINAL_STATUSES:
484
+ return await _process_result(
485
+ await_response.output.result, await_response.output.data_format, self.client.stub, self.client
486
+ )
487
+
477
488
  if await_response.output.result.status == api_pb2.GenericResult.GENERIC_STATUS_INTERNAL_FAILURE:
478
489
  internal_failure_count += 1
479
490
  # Limit the number of times we retry
480
491
  if internal_failure_count < MAX_INTERNAL_FAILURE_COUNT:
481
492
  # For system failures on the server, we retry immediately,
482
493
  # and the failure does not count towards the retry policy.
483
- retry_request = api_pb2.AttemptRetryRequest(
484
- function_id=self.function_id,
485
- parent_input_id=current_input_id() or "",
486
- input=self.input_item,
487
- attempt_token=self.attempt_token,
488
- )
489
- # TODO(ryan): Add exponential backoff?
490
- retry_response = await retry_transient_errors(
491
- self.stub.AttemptRetry,
492
- retry_request,
493
- metadata=metadata,
494
- )
495
- self.attempt_token = retry_response.attempt_token
494
+ self.attempt_token = await self._retry_input(metadata)
496
495
  continue
497
496
 
498
- control_plane_stub = self.client.stub
499
- # Note: Blob download is done on the control plane stub, not the input plane stub!
500
- return await _process_result(
501
- await_response.output.result, await_response.output.data_format, control_plane_stub, self.client
502
- )
497
+ # We add delays between retries for non-internal failures.
498
+ delay_ms = user_retry_manager.get_delay_ms()
499
+ if delay_ms is None:
500
+ # No more retries either because we reached the retry limit or user didn't set a retry policy
501
+ # and the limit defaulted to 0.
502
+ # An unsuccessful status should raise an error when it's converted to an exception.
503
+ # Note: Blob download is done on the control plane stub not the input plane stub!
504
+ return await _process_result(
505
+ await_response.output.result, await_response.output.data_format, self.client.stub, self.client
506
+ )
507
+ await asyncio.sleep(delay_ms / 1000)
508
+
509
+ await self._retry_input(metadata)
510
+
511
+ async def _retry_input(self, metadata: list[tuple[str, str]]) -> str:
512
+ retry_request = api_pb2.AttemptRetryRequest(
513
+ function_id=self.function_id,
514
+ parent_input_id=current_input_id() or "",
515
+ input=self.input_item,
516
+ attempt_token=self.attempt_token,
517
+ )
518
+ # TODO(ryan): Add exponential backoff?
519
+ retry_response = await retry_transient_errors(
520
+ self.stub.AttemptRetry,
521
+ retry_request,
522
+ metadata=metadata,
523
+ )
524
+ return retry_response.attempt_token
503
525
 
504
526
  async def run_generator(self):
505
527
  items_received = 0
@@ -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.1.3.dev1",
36
+ version: str = "1.1.3.dev3",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.1.3.dev1",
167
+ version: str = "1.1.3.dev3",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  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.1.3.dev1
3
+ Version: 1.1.3.dev3
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -618,7 +618,7 @@ message AttemptStartRequest {
618
618
 
619
619
  message AttemptStartResponse {
620
620
  string attempt_token = 1;
621
- FunctionRetryPolicy retry_policy = 2; // TODO(ben-okeefe) TODO(nathan): Not currently used
621
+ FunctionRetryPolicy retry_policy = 2;
622
622
  }
623
623
 
624
624
  message AuthTokenGetRequest {
@@ -1959,8 +1959,7 @@ class AttemptStartResponse(google.protobuf.message.Message):
1959
1959
  RETRY_POLICY_FIELD_NUMBER: builtins.int
1960
1960
  attempt_token: builtins.str
1961
1961
  @property
1962
- def retry_policy(self) -> global___FunctionRetryPolicy:
1963
- """TODO(ben-okeefe) TODO(nathan): Not currently used"""
1962
+ def retry_policy(self) -> global___FunctionRetryPolicy: ...
1964
1963
  def __init__(
1965
1964
  self,
1966
1965
  *,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.1.3.dev1"
4
+ __version__ = "1.1.3.dev3"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes