modal 0.62.115__py3-none-any.whl → 0.72.13__py3-none-any.whl
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.
- modal/__init__.py +13 -9
- modal/__main__.py +41 -3
- modal/_clustered_functions.py +80 -0
- modal/_clustered_functions.pyi +22 -0
- modal/_container_entrypoint.py +402 -398
- modal/_ipython.py +3 -13
- modal/_location.py +17 -10
- modal/_output.py +243 -99
- modal/_pty.py +2 -2
- modal/_resolver.py +55 -60
- modal/_resources.py +26 -7
- modal/_runtime/__init__.py +1 -0
- modal/_runtime/asgi.py +519 -0
- modal/_runtime/container_io_manager.py +1025 -0
- modal/{execution_context.py → _runtime/execution_context.py} +11 -2
- modal/_runtime/telemetry.py +169 -0
- modal/_runtime/user_code_imports.py +356 -0
- modal/_serialization.py +123 -6
- modal/_traceback.py +47 -187
- modal/_tunnel.py +50 -14
- modal/_tunnel.pyi +19 -36
- modal/_utils/app_utils.py +3 -17
- modal/_utils/async_utils.py +386 -104
- modal/_utils/blob_utils.py +157 -186
- modal/_utils/bytes_io_segment_payload.py +97 -0
- modal/_utils/deprecation.py +89 -0
- modal/_utils/docker_utils.py +98 -0
- modal/_utils/function_utils.py +299 -98
- modal/_utils/grpc_testing.py +47 -34
- modal/_utils/grpc_utils.py +54 -21
- modal/_utils/hash_utils.py +51 -10
- modal/_utils/http_utils.py +39 -9
- modal/_utils/logger.py +2 -1
- modal/_utils/mount_utils.py +34 -16
- modal/_utils/name_utils.py +58 -0
- modal/_utils/package_utils.py +14 -1
- modal/_utils/pattern_utils.py +205 -0
- modal/_utils/rand_pb_testing.py +3 -3
- modal/_utils/shell_utils.py +15 -49
- modal/_vendor/a2wsgi_wsgi.py +62 -72
- modal/_vendor/cloudpickle.py +1 -1
- modal/_watcher.py +12 -10
- modal/app.py +561 -323
- modal/app.pyi +474 -262
- modal/call_graph.py +7 -6
- modal/cli/_download.py +22 -6
- modal/cli/_traceback.py +200 -0
- modal/cli/app.py +203 -42
- modal/cli/config.py +12 -5
- modal/cli/container.py +61 -13
- modal/cli/dict.py +128 -0
- modal/cli/entry_point.py +26 -13
- modal/cli/environment.py +40 -9
- modal/cli/import_refs.py +21 -48
- modal/cli/launch.py +28 -14
- modal/cli/network_file_system.py +57 -21
- modal/cli/profile.py +1 -1
- modal/cli/programs/run_jupyter.py +34 -9
- modal/cli/programs/vscode.py +58 -8
- modal/cli/queues.py +131 -0
- modal/cli/run.py +199 -96
- modal/cli/secret.py +5 -4
- modal/cli/token.py +7 -2
- modal/cli/utils.py +74 -8
- modal/cli/volume.py +97 -56
- modal/client.py +248 -144
- modal/client.pyi +156 -124
- modal/cloud_bucket_mount.py +43 -30
- modal/cloud_bucket_mount.pyi +32 -25
- modal/cls.py +528 -141
- modal/cls.pyi +189 -145
- modal/config.py +32 -15
- modal/container_process.py +177 -0
- modal/container_process.pyi +82 -0
- modal/dict.py +50 -54
- modal/dict.pyi +120 -164
- modal/environments.py +106 -5
- modal/environments.pyi +77 -25
- modal/exception.py +30 -43
- modal/experimental.py +62 -2
- modal/file_io.py +537 -0
- modal/file_io.pyi +235 -0
- modal/file_pattern_matcher.py +196 -0
- modal/functions.py +846 -428
- modal/functions.pyi +446 -387
- modal/gpu.py +57 -44
- modal/image.py +943 -417
- modal/image.pyi +584 -245
- modal/io_streams.py +434 -0
- modal/io_streams.pyi +122 -0
- modal/mount.py +223 -90
- modal/mount.pyi +241 -243
- modal/network_file_system.py +85 -86
- modal/network_file_system.pyi +151 -110
- modal/object.py +66 -36
- modal/object.pyi +166 -143
- modal/output.py +63 -0
- modal/parallel_map.py +73 -47
- modal/parallel_map.pyi +51 -63
- modal/partial_function.py +272 -107
- modal/partial_function.pyi +219 -120
- modal/proxy.py +15 -12
- modal/proxy.pyi +3 -8
- modal/queue.py +96 -72
- modal/queue.pyi +210 -135
- modal/requirements/2024.04.txt +2 -1
- modal/requirements/2024.10.txt +16 -0
- modal/requirements/README.md +21 -0
- modal/requirements/base-images.json +22 -0
- modal/retries.py +45 -4
- modal/runner.py +325 -203
- modal/runner.pyi +124 -110
- modal/running_app.py +27 -4
- modal/sandbox.py +509 -231
- modal/sandbox.pyi +396 -169
- modal/schedule.py +2 -2
- modal/scheduler_placement.py +20 -3
- modal/secret.py +41 -25
- modal/secret.pyi +62 -42
- modal/serving.py +39 -49
- modal/serving.pyi +37 -43
- modal/stream_type.py +15 -0
- modal/token_flow.py +5 -3
- modal/token_flow.pyi +37 -32
- modal/volume.py +123 -137
- modal/volume.pyi +228 -221
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/METADATA +5 -5
- modal-0.72.13.dist-info/RECORD +174 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/top_level.txt +0 -1
- modal_docs/gen_reference_docs.py +3 -1
- modal_docs/mdmd/mdmd.py +0 -1
- modal_docs/mdmd/signatures.py +1 -2
- modal_global_objects/images/base_images.py +28 -0
- modal_global_objects/mounts/python_standalone.py +2 -2
- modal_proto/__init__.py +1 -1
- modal_proto/api.proto +1231 -531
- modal_proto/api_grpc.py +750 -430
- modal_proto/api_pb2.py +2102 -1176
- modal_proto/api_pb2.pyi +8859 -0
- modal_proto/api_pb2_grpc.py +1329 -675
- modal_proto/api_pb2_grpc.pyi +1416 -0
- modal_proto/modal_api_grpc.py +149 -0
- modal_proto/modal_options_grpc.py +3 -0
- modal_proto/options_pb2.pyi +20 -0
- modal_proto/options_pb2_grpc.pyi +7 -0
- modal_proto/py.typed +0 -0
- modal_version/__init__.py +1 -1
- modal_version/_version_generated.py +2 -2
- modal/_asgi.py +0 -370
- modal/_container_exec.py +0 -128
- modal/_container_io_manager.py +0 -646
- modal/_container_io_manager.pyi +0 -412
- modal/_sandbox_shell.py +0 -49
- modal/app_utils.py +0 -20
- modal/app_utils.pyi +0 -17
- modal/execution_context.pyi +0 -37
- modal/shared_volume.py +0 -23
- modal/shared_volume.pyi +0 -24
- modal-0.62.115.dist-info/RECORD +0 -207
- modal_global_objects/images/conda.py +0 -15
- modal_global_objects/images/debian_slim.py +0 -15
- modal_global_objects/images/micromamba.py +0 -15
- test/__init__.py +0 -1
- test/aio_test.py +0 -12
- test/async_utils_test.py +0 -279
- test/blob_test.py +0 -67
- test/cli_imports_test.py +0 -149
- test/cli_test.py +0 -674
- test/client_test.py +0 -203
- test/cloud_bucket_mount_test.py +0 -22
- test/cls_test.py +0 -636
- test/config_test.py +0 -149
- test/conftest.py +0 -1485
- test/container_app_test.py +0 -50
- test/container_test.py +0 -1405
- test/cpu_test.py +0 -23
- test/decorator_test.py +0 -85
- test/deprecation_test.py +0 -34
- test/dict_test.py +0 -51
- test/e2e_test.py +0 -68
- test/error_test.py +0 -7
- test/function_serialization_test.py +0 -32
- test/function_test.py +0 -791
- test/function_utils_test.py +0 -101
- test/gpu_test.py +0 -159
- test/grpc_utils_test.py +0 -82
- test/helpers.py +0 -47
- test/image_test.py +0 -814
- test/live_reload_test.py +0 -80
- test/lookup_test.py +0 -70
- test/mdmd_test.py +0 -329
- test/mount_test.py +0 -162
- test/mounted_files_test.py +0 -327
- test/network_file_system_test.py +0 -188
- test/notebook_test.py +0 -66
- test/object_test.py +0 -41
- test/package_utils_test.py +0 -25
- test/queue_test.py +0 -115
- test/resolver_test.py +0 -59
- test/retries_test.py +0 -67
- test/runner_test.py +0 -85
- test/sandbox_test.py +0 -191
- test/schedule_test.py +0 -15
- test/scheduler_placement_test.py +0 -57
- test/secret_test.py +0 -89
- test/serialization_test.py +0 -50
- test/stub_composition_test.py +0 -10
- test/stub_test.py +0 -361
- test/test_asgi_wrapper.py +0 -234
- test/token_flow_test.py +0 -18
- test/traceback_test.py +0 -135
- test/tunnel_test.py +0 -29
- test/utils_test.py +0 -88
- test/version_test.py +0 -14
- test/volume_test.py +0 -397
- test/watcher_test.py +0 -58
- test/webhook_test.py +0 -145
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/LICENSE +0 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/WHEEL +0 -0
- {modal-0.62.115.dist-info → modal-0.72.13.dist-info}/entry_points.txt +0 -0
modal/queue.pyi
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import collections.abc
|
1
2
|
import modal.client
|
2
3
|
import modal.object
|
3
4
|
import synchronicity.combined_types
|
@@ -5,196 +6,270 @@ import typing
|
|
5
6
|
import typing_extensions
|
6
7
|
|
7
8
|
class _Queue(modal.object._Object):
|
9
|
+
def __init__(self): ...
|
8
10
|
@staticmethod
|
9
|
-
def
|
10
|
-
...
|
11
|
-
|
12
|
-
def __init__(self):
|
13
|
-
...
|
14
|
-
|
15
|
-
@staticmethod
|
16
|
-
def validate_partition_key(partition: typing.Union[str, None]) -> bytes:
|
17
|
-
...
|
18
|
-
|
11
|
+
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
19
12
|
@classmethod
|
20
|
-
def ephemeral(
|
21
|
-
|
22
|
-
|
13
|
+
def ephemeral(
|
14
|
+
cls: type[_Queue],
|
15
|
+
client: typing.Optional[modal.client._Client] = None,
|
16
|
+
environment_name: typing.Optional[str] = None,
|
17
|
+
_heartbeat_sleep: float = 300,
|
18
|
+
) -> typing.AsyncContextManager[_Queue]: ...
|
23
19
|
@staticmethod
|
24
|
-
def from_name(
|
25
|
-
|
26
|
-
|
20
|
+
def from_name(
|
21
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
22
|
+
) -> _Queue: ...
|
27
23
|
@staticmethod
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
async def lookup(
|
25
|
+
name: str,
|
26
|
+
namespace=1,
|
27
|
+
client: typing.Optional[modal.client._Client] = None,
|
28
|
+
environment_name: typing.Optional[str] = None,
|
29
|
+
create_if_missing: bool = False,
|
30
|
+
) -> _Queue: ...
|
35
31
|
@staticmethod
|
36
|
-
async def delete(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
async def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
32
|
+
async def delete(
|
33
|
+
name: str,
|
34
|
+
*,
|
35
|
+
client: typing.Optional[modal.client._Client] = None,
|
36
|
+
environment_name: typing.Optional[str] = None,
|
37
|
+
): ...
|
38
|
+
async def _get_nonblocking(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
|
39
|
+
async def _get_blocking(
|
40
|
+
self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
|
41
|
+
) -> list[typing.Any]: ...
|
42
|
+
async def clear(self, *, partition: typing.Optional[str] = None, all: bool = False) -> None: ...
|
43
|
+
async def get(
|
44
|
+
self, block: bool = True, timeout: typing.Optional[float] = None, *, partition: typing.Optional[str] = None
|
45
|
+
) -> typing.Optional[typing.Any]: ...
|
46
|
+
async def get_many(
|
47
|
+
self,
|
48
|
+
n_values: int,
|
49
|
+
block: bool = True,
|
50
|
+
timeout: typing.Optional[float] = None,
|
51
|
+
*,
|
52
|
+
partition: typing.Optional[str] = None,
|
53
|
+
) -> list[typing.Any]: ...
|
54
|
+
async def put(
|
55
|
+
self,
|
56
|
+
v: typing.Any,
|
57
|
+
block: bool = True,
|
58
|
+
timeout: typing.Optional[float] = None,
|
59
|
+
*,
|
60
|
+
partition: typing.Optional[str] = None,
|
61
|
+
partition_ttl: int = 86400,
|
62
|
+
) -> None: ...
|
63
|
+
async def put_many(
|
64
|
+
self,
|
65
|
+
vs: list[typing.Any],
|
66
|
+
block: bool = True,
|
67
|
+
timeout: typing.Optional[float] = None,
|
68
|
+
*,
|
69
|
+
partition: typing.Optional[str] = None,
|
70
|
+
partition_ttl: int = 86400,
|
71
|
+
) -> None: ...
|
72
|
+
async def _put_many_blocking(
|
73
|
+
self,
|
74
|
+
partition: typing.Optional[str],
|
75
|
+
partition_ttl: int,
|
76
|
+
vs: list[typing.Any],
|
77
|
+
timeout: typing.Optional[float] = None,
|
78
|
+
): ...
|
79
|
+
async def _put_many_nonblocking(
|
80
|
+
self, partition: typing.Optional[str], partition_ttl: int, vs: list[typing.Any]
|
81
|
+
): ...
|
82
|
+
async def len(self, *, partition: typing.Optional[str] = None, total: bool = False) -> int: ...
|
83
|
+
def iterate(
|
84
|
+
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
85
|
+
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
69
86
|
|
70
87
|
class Queue(modal.object.Object):
|
71
|
-
def __init__(self):
|
72
|
-
...
|
73
|
-
|
88
|
+
def __init__(self): ...
|
74
89
|
@staticmethod
|
75
|
-
def
|
76
|
-
...
|
77
|
-
|
78
|
-
@staticmethod
|
79
|
-
def validate_partition_key(partition: typing.Union[str, None]) -> bytes:
|
80
|
-
...
|
81
|
-
|
90
|
+
def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
|
82
91
|
@classmethod
|
83
|
-
def ephemeral(
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
def ephemeral(
|
93
|
+
cls: type[Queue],
|
94
|
+
client: typing.Optional[modal.client.Client] = None,
|
95
|
+
environment_name: typing.Optional[str] = None,
|
96
|
+
_heartbeat_sleep: float = 300,
|
97
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[Queue]: ...
|
90
98
|
@staticmethod
|
91
|
-
def
|
92
|
-
|
99
|
+
def from_name(
|
100
|
+
name: str, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
101
|
+
) -> Queue: ...
|
93
102
|
|
94
103
|
class __lookup_spec(typing_extensions.Protocol):
|
95
|
-
def __call__(
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
104
|
+
def __call__(
|
105
|
+
self,
|
106
|
+
name: str,
|
107
|
+
namespace=1,
|
108
|
+
client: typing.Optional[modal.client.Client] = None,
|
109
|
+
environment_name: typing.Optional[str] = None,
|
110
|
+
create_if_missing: bool = False,
|
111
|
+
) -> Queue: ...
|
112
|
+
async def aio(
|
113
|
+
self,
|
114
|
+
name: str,
|
115
|
+
namespace=1,
|
116
|
+
client: typing.Optional[modal.client.Client] = None,
|
117
|
+
environment_name: typing.Optional[str] = None,
|
118
|
+
create_if_missing: bool = False,
|
119
|
+
) -> Queue: ...
|
100
120
|
|
101
121
|
lookup: __lookup_spec
|
102
122
|
|
103
123
|
class __delete_spec(typing_extensions.Protocol):
|
104
|
-
def __call__(
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
124
|
+
def __call__(
|
125
|
+
self,
|
126
|
+
name: str,
|
127
|
+
*,
|
128
|
+
client: typing.Optional[modal.client.Client] = None,
|
129
|
+
environment_name: typing.Optional[str] = None,
|
130
|
+
): ...
|
131
|
+
async def aio(
|
132
|
+
self,
|
133
|
+
name: str,
|
134
|
+
*,
|
135
|
+
client: typing.Optional[modal.client.Client] = None,
|
136
|
+
environment_name: typing.Optional[str] = None,
|
137
|
+
): ...
|
109
138
|
|
110
139
|
delete: __delete_spec
|
111
140
|
|
112
141
|
class ___get_nonblocking_spec(typing_extensions.Protocol):
|
113
|
-
def __call__(self, partition: typing.
|
114
|
-
|
115
|
-
|
116
|
-
async def aio(self, *args, **kwargs) -> typing.List[typing.Any]:
|
117
|
-
...
|
142
|
+
def __call__(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
|
143
|
+
async def aio(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
|
118
144
|
|
119
145
|
_get_nonblocking: ___get_nonblocking_spec
|
120
146
|
|
121
147
|
class ___get_blocking_spec(typing_extensions.Protocol):
|
122
|
-
def __call__(
|
123
|
-
|
124
|
-
|
125
|
-
async def aio(
|
126
|
-
|
148
|
+
def __call__(
|
149
|
+
self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
|
150
|
+
) -> list[typing.Any]: ...
|
151
|
+
async def aio(
|
152
|
+
self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
|
153
|
+
) -> list[typing.Any]: ...
|
127
154
|
|
128
155
|
_get_blocking: ___get_blocking_spec
|
129
156
|
|
130
|
-
class
|
131
|
-
def __call__(self,
|
132
|
-
|
157
|
+
class __clear_spec(typing_extensions.Protocol):
|
158
|
+
def __call__(self, *, partition: typing.Optional[str] = None, all: bool = False) -> None: ...
|
159
|
+
async def aio(self, *, partition: typing.Optional[str] = None, all: bool = False) -> None: ...
|
160
|
+
|
161
|
+
clear: __clear_spec
|
133
162
|
|
134
|
-
|
135
|
-
|
163
|
+
class __get_spec(typing_extensions.Protocol):
|
164
|
+
def __call__(
|
165
|
+
self, block: bool = True, timeout: typing.Optional[float] = None, *, partition: typing.Optional[str] = None
|
166
|
+
) -> typing.Optional[typing.Any]: ...
|
167
|
+
async def aio(
|
168
|
+
self, block: bool = True, timeout: typing.Optional[float] = None, *, partition: typing.Optional[str] = None
|
169
|
+
) -> typing.Optional[typing.Any]: ...
|
136
170
|
|
137
171
|
get: __get_spec
|
138
172
|
|
139
173
|
class __get_many_spec(typing_extensions.Protocol):
|
140
|
-
def __call__(
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
174
|
+
def __call__(
|
175
|
+
self,
|
176
|
+
n_values: int,
|
177
|
+
block: bool = True,
|
178
|
+
timeout: typing.Optional[float] = None,
|
179
|
+
*,
|
180
|
+
partition: typing.Optional[str] = None,
|
181
|
+
) -> list[typing.Any]: ...
|
182
|
+
async def aio(
|
183
|
+
self,
|
184
|
+
n_values: int,
|
185
|
+
block: bool = True,
|
186
|
+
timeout: typing.Optional[float] = None,
|
187
|
+
*,
|
188
|
+
partition: typing.Optional[str] = None,
|
189
|
+
) -> list[typing.Any]: ...
|
145
190
|
|
146
191
|
get_many: __get_many_spec
|
147
192
|
|
148
193
|
class __put_spec(typing_extensions.Protocol):
|
149
|
-
def __call__(
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
194
|
+
def __call__(
|
195
|
+
self,
|
196
|
+
v: typing.Any,
|
197
|
+
block: bool = True,
|
198
|
+
timeout: typing.Optional[float] = None,
|
199
|
+
*,
|
200
|
+
partition: typing.Optional[str] = None,
|
201
|
+
partition_ttl: int = 86400,
|
202
|
+
) -> None: ...
|
203
|
+
async def aio(
|
204
|
+
self,
|
205
|
+
v: typing.Any,
|
206
|
+
block: bool = True,
|
207
|
+
timeout: typing.Optional[float] = None,
|
208
|
+
*,
|
209
|
+
partition: typing.Optional[str] = None,
|
210
|
+
partition_ttl: int = 86400,
|
211
|
+
) -> None: ...
|
154
212
|
|
155
213
|
put: __put_spec
|
156
214
|
|
157
215
|
class __put_many_spec(typing_extensions.Protocol):
|
158
|
-
def __call__(
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
216
|
+
def __call__(
|
217
|
+
self,
|
218
|
+
vs: list[typing.Any],
|
219
|
+
block: bool = True,
|
220
|
+
timeout: typing.Optional[float] = None,
|
221
|
+
*,
|
222
|
+
partition: typing.Optional[str] = None,
|
223
|
+
partition_ttl: int = 86400,
|
224
|
+
) -> None: ...
|
225
|
+
async def aio(
|
226
|
+
self,
|
227
|
+
vs: list[typing.Any],
|
228
|
+
block: bool = True,
|
229
|
+
timeout: typing.Optional[float] = None,
|
230
|
+
*,
|
231
|
+
partition: typing.Optional[str] = None,
|
232
|
+
partition_ttl: int = 86400,
|
233
|
+
) -> None: ...
|
163
234
|
|
164
235
|
put_many: __put_many_spec
|
165
236
|
|
166
237
|
class ___put_many_blocking_spec(typing_extensions.Protocol):
|
167
|
-
def __call__(
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
238
|
+
def __call__(
|
239
|
+
self,
|
240
|
+
partition: typing.Optional[str],
|
241
|
+
partition_ttl: int,
|
242
|
+
vs: list[typing.Any],
|
243
|
+
timeout: typing.Optional[float] = None,
|
244
|
+
): ...
|
245
|
+
async def aio(
|
246
|
+
self,
|
247
|
+
partition: typing.Optional[str],
|
248
|
+
partition_ttl: int,
|
249
|
+
vs: list[typing.Any],
|
250
|
+
timeout: typing.Optional[float] = None,
|
251
|
+
): ...
|
172
252
|
|
173
253
|
_put_many_blocking: ___put_many_blocking_spec
|
174
254
|
|
175
255
|
class ___put_many_nonblocking_spec(typing_extensions.Protocol):
|
176
|
-
def __call__(self, partition: typing.
|
177
|
-
|
178
|
-
|
179
|
-
async def aio(self, *args, **kwargs):
|
180
|
-
...
|
256
|
+
def __call__(self, partition: typing.Optional[str], partition_ttl: int, vs: list[typing.Any]): ...
|
257
|
+
async def aio(self, partition: typing.Optional[str], partition_ttl: int, vs: list[typing.Any]): ...
|
181
258
|
|
182
259
|
_put_many_nonblocking: ___put_many_nonblocking_spec
|
183
260
|
|
184
261
|
class __len_spec(typing_extensions.Protocol):
|
185
|
-
def __call__(self, *, partition: typing.
|
186
|
-
|
187
|
-
|
188
|
-
async def aio(self, *args, **kwargs) -> int:
|
189
|
-
...
|
262
|
+
def __call__(self, *, partition: typing.Optional[str] = None, total: bool = False) -> int: ...
|
263
|
+
async def aio(self, *, partition: typing.Optional[str] = None, total: bool = False) -> int: ...
|
190
264
|
|
191
265
|
len: __len_spec
|
192
266
|
|
193
267
|
class __iterate_spec(typing_extensions.Protocol):
|
194
|
-
def __call__(
|
195
|
-
|
196
|
-
|
197
|
-
def aio(
|
198
|
-
|
268
|
+
def __call__(
|
269
|
+
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
270
|
+
) -> typing.Generator[typing.Any, None, None]: ...
|
271
|
+
def aio(
|
272
|
+
self, *, partition: typing.Optional[str] = None, item_poll_timeout: float = 0.0
|
273
|
+
) -> collections.abc.AsyncGenerator[typing.Any, None]: ...
|
199
274
|
|
200
275
|
iterate: __iterate_spec
|
modal/requirements/2024.04.txt
CHANGED
@@ -17,10 +17,11 @@ idna==3.6
|
|
17
17
|
markdown-it-py==3.0.0
|
18
18
|
mdurl==0.1.2
|
19
19
|
multidict==6.0.5
|
20
|
-
protobuf==
|
20
|
+
protobuf==4.25.3
|
21
21
|
pydantic==2.6.4
|
22
22
|
pydantic_core==2.16.3
|
23
23
|
Pygments==2.17.2
|
24
|
+
python-multipart==0.0.9
|
24
25
|
rich==13.7.1
|
25
26
|
sniffio==1.3.1
|
26
27
|
starlette==0.36.3
|
@@ -0,0 +1,16 @@
|
|
1
|
+
aiohappyeyeballs==2.4.3
|
2
|
+
aiohttp==3.10.8
|
3
|
+
aiosignal==1.3.1
|
4
|
+
async-timeout==4.0.3 ; python_version < "3.11"
|
5
|
+
attrs==24.2.0
|
6
|
+
certifi==2024.8.30
|
7
|
+
frozenlist==1.4.1
|
8
|
+
grpclib==0.4.7
|
9
|
+
h2==4.1.0
|
10
|
+
hpack==4.0.0
|
11
|
+
hyperframe==6.0.1
|
12
|
+
idna==3.10
|
13
|
+
multidict==6.1.0
|
14
|
+
protobuf>=3.20,<6
|
15
|
+
typing_extensions==4.12.2
|
16
|
+
yarl==1.13.1
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Modal Image builder configuration
|
2
|
+
|
3
|
+
This directory contains `modal.Image` specifications that vary across
|
4
|
+
"image builder" versions.
|
5
|
+
|
6
|
+
The `base-images.json` file specifies the versions used for Modal's
|
7
|
+
various `Image` constructor methods.
|
8
|
+
|
9
|
+
The versioned requirements files enumerate the dependencies needed by
|
10
|
+
the Modal client library when it is running inside a Modal container.
|
11
|
+
|
12
|
+
The container requirements are a subset of the dependencies required by the
|
13
|
+
client for local operation (i.e., to run or deploy Modal apps). Additionally,
|
14
|
+
we aim to pin specific versions rather than allowing a range as we do for the
|
15
|
+
installation dependencies.
|
16
|
+
|
17
|
+
From version `2024.04`, the requirements specify the entire dependency tree,
|
18
|
+
and not just the first-order dependencies.
|
19
|
+
|
20
|
+
Note that for `2023.12`, there is a separate requirements file that is used for
|
21
|
+
Python 3.12.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"debian": {
|
3
|
+
"2024.10": "bookworm",
|
4
|
+
"2024.04": "bookworm",
|
5
|
+
"2023.12": "bullseye"
|
6
|
+
},
|
7
|
+
"python": {
|
8
|
+
"2024.10": ["3.9.20", "3.10.15", "3.11.10", "3.12.6", "3.13.0"],
|
9
|
+
"2024.04": ["3.9.19", "3.10.14", "3.11.8", "3.12.2"],
|
10
|
+
"2023.12": ["3.9.15", "3.10.8", "3.11.0", "3.12.1"]
|
11
|
+
},
|
12
|
+
"micromamba": {
|
13
|
+
"2024.10": "1.5.10",
|
14
|
+
"2024.04": "1.5.8",
|
15
|
+
"2023.12": "1.3.1"
|
16
|
+
},
|
17
|
+
"package_tools": {
|
18
|
+
"2024.10": "pip wheel uv",
|
19
|
+
"2024.04": "pip wheel uv",
|
20
|
+
"2023.12": "pip"
|
21
|
+
}
|
22
|
+
}
|
modal/retries.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
|
+
import asyncio
|
2
3
|
from datetime import timedelta
|
3
4
|
|
4
5
|
from modal_proto import api_pb2
|
5
6
|
|
6
7
|
from .exception import InvalidError
|
7
8
|
|
9
|
+
MIN_INPUT_RETRY_DELAY_MS = 1000
|
10
|
+
MAX_INPUT_RETRY_DELAY_MS = 24 * 60 * 60 * 1000
|
11
|
+
|
8
12
|
|
9
13
|
class Retries:
|
10
14
|
"""Adds a retry policy to a Modal function.
|
@@ -13,7 +17,7 @@ class Retries:
|
|
13
17
|
|
14
18
|
```python
|
15
19
|
import modal
|
16
|
-
app = modal.App()
|
20
|
+
app = modal.App()
|
17
21
|
|
18
22
|
# Basic configuration.
|
19
23
|
# This sets a policy of max 4 retries with 1-second delay between failures.
|
@@ -53,14 +57,16 @@ class Retries:
|
|
53
57
|
# The maximum number of retries that can be made in the presence of failures.
|
54
58
|
max_retries: int,
|
55
59
|
# Coefficent controlling how much the retry delay increases each retry attempt.
|
56
|
-
# A backoff coefficient of 1.0 creates fixed-delay
|
60
|
+
# A backoff coefficient of 1.0 creates fixed-delay where the delay period always equals the initial delay.
|
57
61
|
backoff_coefficient: float = 2.0,
|
58
62
|
# Number of seconds that must elapse before the first retry occurs.
|
59
63
|
initial_delay: float = 1.0,
|
60
64
|
# Maximum length of retry delay in seconds, preventing the delay from growing infinitely.
|
61
65
|
max_delay: float = 60.0,
|
62
66
|
):
|
63
|
-
"""
|
67
|
+
"""
|
68
|
+
Construct a new retries policy, supporting exponential and fixed-interval delays via a backoff coefficient.
|
69
|
+
"""
|
64
70
|
if max_retries < 0:
|
65
71
|
raise InvalidError(f"Invalid retries number: {max_retries}. Function retries must be non-negative.")
|
66
72
|
|
@@ -84,7 +90,8 @@ class Retries:
|
|
84
90
|
|
85
91
|
if not 1.0 <= backoff_coefficient <= 10.0:
|
86
92
|
raise InvalidError(
|
87
|
-
f"Invalid backoff_coefficient: {backoff_coefficient}.
|
93
|
+
f"Invalid backoff_coefficient: {backoff_coefficient}. "
|
94
|
+
"Coefficient must be between 1.0 (fixed-interval backoff) and 10.0"
|
88
95
|
)
|
89
96
|
|
90
97
|
self.max_retries = max_retries
|
@@ -100,3 +107,37 @@ class Retries:
|
|
100
107
|
initial_delay_ms=self.initial_delay // timedelta(milliseconds=1),
|
101
108
|
max_delay_ms=self.max_delay // timedelta(milliseconds=1),
|
102
109
|
)
|
110
|
+
|
111
|
+
|
112
|
+
class RetryManager:
|
113
|
+
"""
|
114
|
+
Helper class to apply the specified retry policy.
|
115
|
+
"""
|
116
|
+
|
117
|
+
def __init__(self, retry_policy: api_pb2.FunctionRetryPolicy):
|
118
|
+
self.retry_policy = retry_policy
|
119
|
+
self.attempt_count = 0
|
120
|
+
|
121
|
+
async def raise_or_sleep(self, exc: Exception):
|
122
|
+
"""
|
123
|
+
Raises an exception if the maximum retry count has been reached, otherwise sleeps for calculated delay.
|
124
|
+
"""
|
125
|
+
self.attempt_count += 1
|
126
|
+
if self.attempt_count > self.retry_policy.retries:
|
127
|
+
raise exc
|
128
|
+
delay_ms = self._retry_delay_ms(self.attempt_count, self.retry_policy)
|
129
|
+
await asyncio.sleep(delay_ms / 1000)
|
130
|
+
|
131
|
+
@staticmethod
|
132
|
+
def _retry_delay_ms(attempt_count: int, retry_policy: api_pb2.FunctionRetryPolicy) -> float:
|
133
|
+
"""
|
134
|
+
Computes the amount of time to sleep before retrying based on the backend_coefficient and initial_delay_ms args.
|
135
|
+
"""
|
136
|
+
if attempt_count < 1:
|
137
|
+
raise ValueError(f"Cannot compute retry delay. attempt_count must be at least 1, but was {attempt_count}")
|
138
|
+
delay_ms = retry_policy.initial_delay_ms * (retry_policy.backoff_coefficient ** (attempt_count - 1))
|
139
|
+
if delay_ms < MIN_INPUT_RETRY_DELAY_MS:
|
140
|
+
return MIN_INPUT_RETRY_DELAY_MS
|
141
|
+
if delay_ms > MAX_INPUT_RETRY_DELAY_MS:
|
142
|
+
return MAX_INPUT_RETRY_DELAY_MS
|
143
|
+
return delay_ms
|