modal 1.0.5.dev1__py3-none-any.whl → 1.0.5.dev3__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/_clustered_functions.pyi +13 -3
- modal/_functions.py +44 -35
- modal/_runtime/container_io_manager.pyi +222 -40
- modal/_runtime/execution_context.pyi +60 -6
- modal/_tunnel.pyi +380 -12
- modal/_utils/function_utils.py +3 -3
- modal/app.pyi +658 -48
- modal/client.pyi +224 -28
- modal/cloud_bucket_mount.pyi +192 -4
- modal/cls.pyi +442 -35
- modal/container_process.pyi +103 -14
- modal/dict.pyi +453 -51
- modal/environments.pyi +41 -9
- modal/exception.py +4 -0
- modal/file_io.pyi +236 -45
- modal/functions.pyi +571 -65
- modal/image.pyi +1256 -74
- modal/io_streams.pyi +342 -39
- modal/mount.pyi +261 -31
- modal/network_file_system.pyi +307 -26
- modal/object.pyi +48 -9
- modal/parallel_map.py +38 -6
- modal/parallel_map.pyi +155 -15
- modal/partial_function.pyi +255 -14
- modal/proxy.pyi +28 -3
- modal/queue.pyi +447 -30
- modal/runner.pyi +160 -22
- modal/sandbox.pyi +310 -50
- modal/secret.pyi +164 -15
- modal/snapshot.pyi +25 -4
- modal/token_flow.pyi +28 -8
- modal/volume.pyi +649 -59
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/METADATA +1 -1
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/RECORD +39 -39
- modal_version/__init__.py +1 -1
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/WHEEL +0 -0
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/entry_points.txt +0 -0
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.5.dev1.dist-info → modal-1.0.5.dev3.dist-info}/top_level.txt +0 -0
modal/network_file_system.pyi
CHANGED
@@ -14,17 +14,84 @@ def network_file_system_mount_protos(
|
|
14
14
|
) -> list[modal_proto.api_pb2.SharedVolumeMount]: ...
|
15
15
|
|
16
16
|
class _NetworkFileSystem(modal._object._Object):
|
17
|
+
"""A shared, writable file system accessible by one or more Modal functions.
|
18
|
+
|
19
|
+
By attaching this file system as a mount to one or more functions, they can
|
20
|
+
share and persist data with each other.
|
21
|
+
|
22
|
+
**Usage**
|
23
|
+
|
24
|
+
```python
|
25
|
+
import modal
|
26
|
+
|
27
|
+
nfs = modal.NetworkFileSystem.from_name("my-nfs", create_if_missing=True)
|
28
|
+
app = modal.App()
|
29
|
+
|
30
|
+
@app.function(network_file_systems={"/root/foo": nfs})
|
31
|
+
def f():
|
32
|
+
pass
|
33
|
+
|
34
|
+
@app.function(network_file_systems={"/root/goo": nfs})
|
35
|
+
def g():
|
36
|
+
pass
|
37
|
+
```
|
38
|
+
|
39
|
+
Also see the CLI methods for accessing network file systems:
|
40
|
+
|
41
|
+
```
|
42
|
+
modal nfs --help
|
43
|
+
```
|
44
|
+
|
45
|
+
A `NetworkFileSystem` can also be useful for some local scripting scenarios, e.g.:
|
46
|
+
|
47
|
+
```python notest
|
48
|
+
nfs = modal.NetworkFileSystem.from_name("my-network-file-system")
|
49
|
+
for chunk in nfs.read_file("my_db_dump.csv"):
|
50
|
+
...
|
51
|
+
```
|
52
|
+
"""
|
17
53
|
@staticmethod
|
18
54
|
def from_name(
|
19
55
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
20
|
-
) -> _NetworkFileSystem:
|
56
|
+
) -> _NetworkFileSystem:
|
57
|
+
"""Reference a NetworkFileSystem by its name, creating if necessary.
|
58
|
+
|
59
|
+
In contrast to `modal.NetworkFileSystem.lookup`, this is a lazy method
|
60
|
+
that defers hydrating the local object with metadata from Modal servers
|
61
|
+
until the first time it is actually used.
|
62
|
+
|
63
|
+
```python notest
|
64
|
+
nfs = NetworkFileSystem.from_name("my-nfs", create_if_missing=True)
|
65
|
+
|
66
|
+
@app.function(network_file_systems={"/data": nfs})
|
67
|
+
def f():
|
68
|
+
pass
|
69
|
+
```
|
70
|
+
"""
|
71
|
+
...
|
72
|
+
|
21
73
|
@classmethod
|
22
74
|
def ephemeral(
|
23
75
|
cls: type[_NetworkFileSystem],
|
24
76
|
client: typing.Optional[modal.client._Client] = None,
|
25
77
|
environment_name: typing.Optional[str] = None,
|
26
78
|
_heartbeat_sleep: float = 300,
|
27
|
-
) -> typing.AsyncContextManager[_NetworkFileSystem]:
|
79
|
+
) -> typing.AsyncContextManager[_NetworkFileSystem]:
|
80
|
+
"""Creates a new ephemeral network filesystem within a context manager:
|
81
|
+
|
82
|
+
Usage:
|
83
|
+
```python
|
84
|
+
with modal.NetworkFileSystem.ephemeral() as nfs:
|
85
|
+
assert nfs.listdir("/") == []
|
86
|
+
```
|
87
|
+
|
88
|
+
```python notest
|
89
|
+
async with modal.NetworkFileSystem.ephemeral() as nfs:
|
90
|
+
assert await nfs.listdir("/") == []
|
91
|
+
```
|
92
|
+
"""
|
93
|
+
...
|
94
|
+
|
28
95
|
@staticmethod
|
29
96
|
async def lookup(
|
30
97
|
name: str,
|
@@ -32,14 +99,32 @@ class _NetworkFileSystem(modal._object._Object):
|
|
32
99
|
client: typing.Optional[modal.client._Client] = None,
|
33
100
|
environment_name: typing.Optional[str] = None,
|
34
101
|
create_if_missing: bool = False,
|
35
|
-
) -> _NetworkFileSystem:
|
102
|
+
) -> _NetworkFileSystem:
|
103
|
+
"""mdmd:hidden
|
104
|
+
Lookup a named NetworkFileSystem.
|
105
|
+
|
106
|
+
DEPRECATED: This method is deprecated in favor of `modal.NetworkFileSystem.from_name`.
|
107
|
+
|
108
|
+
In contrast to `modal.NetworkFileSystem.from_name`, this is an eager method
|
109
|
+
that will hydrate the local object with metadata from Modal servers.
|
110
|
+
|
111
|
+
```python notest
|
112
|
+
nfs = modal.NetworkFileSystem.lookup("my-nfs")
|
113
|
+
print(nfs.listdir("/"))
|
114
|
+
```
|
115
|
+
"""
|
116
|
+
...
|
117
|
+
|
36
118
|
@staticmethod
|
37
119
|
async def create_deployed(
|
38
120
|
deployment_name: str,
|
39
121
|
namespace=1,
|
40
122
|
client: typing.Optional[modal.client._Client] = None,
|
41
123
|
environment_name: typing.Optional[str] = None,
|
42
|
-
) -> str:
|
124
|
+
) -> str:
|
125
|
+
"""mdmd:hidden"""
|
126
|
+
...
|
127
|
+
|
43
128
|
@staticmethod
|
44
129
|
async def delete(
|
45
130
|
name: str, client: typing.Optional[modal.client._Client] = None, environment_name: typing.Optional[str] = None
|
@@ -49,9 +134,30 @@ class _NetworkFileSystem(modal._object._Object):
|
|
49
134
|
remote_path: str,
|
50
135
|
fp: typing.BinaryIO,
|
51
136
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
52
|
-
) -> int:
|
53
|
-
|
54
|
-
|
137
|
+
) -> int:
|
138
|
+
"""Write from a file object to a path on the network file system, atomically.
|
139
|
+
|
140
|
+
Will create any needed parent directories automatically.
|
141
|
+
|
142
|
+
If remote_path ends with `/` it's assumed to be a directory and the
|
143
|
+
file will be uploaded with its current name to that directory.
|
144
|
+
"""
|
145
|
+
...
|
146
|
+
|
147
|
+
def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]:
|
148
|
+
"""Read a file from the network file system"""
|
149
|
+
...
|
150
|
+
|
151
|
+
def iterdir(self, path: str) -> collections.abc.AsyncIterator[modal.volume.FileEntry]:
|
152
|
+
"""Iterate over all files in a directory in the network file system.
|
153
|
+
|
154
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
155
|
+
* Passing a file path returns a list containing only that file's listing description
|
156
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
157
|
+
that glob path (using absolute paths)
|
158
|
+
"""
|
159
|
+
...
|
160
|
+
|
55
161
|
async def add_local_file(
|
56
162
|
self,
|
57
163
|
local_path: typing.Union[pathlib.Path, str],
|
@@ -64,24 +170,104 @@ class _NetworkFileSystem(modal._object._Object):
|
|
64
170
|
remote_path: typing.Union[str, pathlib.PurePosixPath, None] = None,
|
65
171
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
66
172
|
): ...
|
67
|
-
async def listdir(self, path: str) -> list[modal.volume.FileEntry]:
|
68
|
-
|
173
|
+
async def listdir(self, path: str) -> list[modal.volume.FileEntry]:
|
174
|
+
"""List all files in a directory in the network file system.
|
175
|
+
|
176
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
177
|
+
* Passing a file path returns a list containing only that file's listing description
|
178
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
179
|
+
that glob path (using absolute paths)
|
180
|
+
"""
|
181
|
+
...
|
182
|
+
|
183
|
+
async def remove_file(self, path: str, recursive=False):
|
184
|
+
"""Remove a file in a network file system."""
|
185
|
+
...
|
69
186
|
|
70
187
|
SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
|
71
188
|
|
72
189
|
class NetworkFileSystem(modal.object.Object):
|
73
|
-
|
190
|
+
"""A shared, writable file system accessible by one or more Modal functions.
|
191
|
+
|
192
|
+
By attaching this file system as a mount to one or more functions, they can
|
193
|
+
share and persist data with each other.
|
194
|
+
|
195
|
+
**Usage**
|
196
|
+
|
197
|
+
```python
|
198
|
+
import modal
|
199
|
+
|
200
|
+
nfs = modal.NetworkFileSystem.from_name("my-nfs", create_if_missing=True)
|
201
|
+
app = modal.App()
|
202
|
+
|
203
|
+
@app.function(network_file_systems={"/root/foo": nfs})
|
204
|
+
def f():
|
205
|
+
pass
|
206
|
+
|
207
|
+
@app.function(network_file_systems={"/root/goo": nfs})
|
208
|
+
def g():
|
209
|
+
pass
|
210
|
+
```
|
211
|
+
|
212
|
+
Also see the CLI methods for accessing network file systems:
|
213
|
+
|
214
|
+
```
|
215
|
+
modal nfs --help
|
216
|
+
```
|
217
|
+
|
218
|
+
A `NetworkFileSystem` can also be useful for some local scripting scenarios, e.g.:
|
219
|
+
|
220
|
+
```python notest
|
221
|
+
nfs = modal.NetworkFileSystem.from_name("my-network-file-system")
|
222
|
+
for chunk in nfs.read_file("my_db_dump.csv"):
|
223
|
+
...
|
224
|
+
```
|
225
|
+
"""
|
226
|
+
def __init__(self, *args, **kwargs):
|
227
|
+
"""mdmd:hidden"""
|
228
|
+
...
|
229
|
+
|
74
230
|
@staticmethod
|
75
231
|
def from_name(
|
76
232
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, create_if_missing: bool = False
|
77
|
-
) -> NetworkFileSystem:
|
233
|
+
) -> NetworkFileSystem:
|
234
|
+
"""Reference a NetworkFileSystem by its name, creating if necessary.
|
235
|
+
|
236
|
+
In contrast to `modal.NetworkFileSystem.lookup`, this is a lazy method
|
237
|
+
that defers hydrating the local object with metadata from Modal servers
|
238
|
+
until the first time it is actually used.
|
239
|
+
|
240
|
+
```python notest
|
241
|
+
nfs = NetworkFileSystem.from_name("my-nfs", create_if_missing=True)
|
242
|
+
|
243
|
+
@app.function(network_file_systems={"/data": nfs})
|
244
|
+
def f():
|
245
|
+
pass
|
246
|
+
```
|
247
|
+
"""
|
248
|
+
...
|
249
|
+
|
78
250
|
@classmethod
|
79
251
|
def ephemeral(
|
80
252
|
cls: type[NetworkFileSystem],
|
81
253
|
client: typing.Optional[modal.client.Client] = None,
|
82
254
|
environment_name: typing.Optional[str] = None,
|
83
255
|
_heartbeat_sleep: float = 300,
|
84
|
-
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[NetworkFileSystem]:
|
256
|
+
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[NetworkFileSystem]:
|
257
|
+
"""Creates a new ephemeral network filesystem within a context manager:
|
258
|
+
|
259
|
+
Usage:
|
260
|
+
```python
|
261
|
+
with modal.NetworkFileSystem.ephemeral() as nfs:
|
262
|
+
assert nfs.listdir("/") == []
|
263
|
+
```
|
264
|
+
|
265
|
+
```python notest
|
266
|
+
async with modal.NetworkFileSystem.ephemeral() as nfs:
|
267
|
+
assert await nfs.listdir("/") == []
|
268
|
+
```
|
269
|
+
"""
|
270
|
+
...
|
85
271
|
|
86
272
|
class __lookup_spec(typing_extensions.Protocol):
|
87
273
|
def __call__(
|
@@ -92,7 +278,22 @@ class NetworkFileSystem(modal.object.Object):
|
|
92
278
|
client: typing.Optional[modal.client.Client] = None,
|
93
279
|
environment_name: typing.Optional[str] = None,
|
94
280
|
create_if_missing: bool = False,
|
95
|
-
) -> NetworkFileSystem:
|
281
|
+
) -> NetworkFileSystem:
|
282
|
+
"""mdmd:hidden
|
283
|
+
Lookup a named NetworkFileSystem.
|
284
|
+
|
285
|
+
DEPRECATED: This method is deprecated in favor of `modal.NetworkFileSystem.from_name`.
|
286
|
+
|
287
|
+
In contrast to `modal.NetworkFileSystem.from_name`, this is an eager method
|
288
|
+
that will hydrate the local object with metadata from Modal servers.
|
289
|
+
|
290
|
+
```python notest
|
291
|
+
nfs = modal.NetworkFileSystem.lookup("my-nfs")
|
292
|
+
print(nfs.listdir("/"))
|
293
|
+
```
|
294
|
+
"""
|
295
|
+
...
|
296
|
+
|
96
297
|
async def aio(
|
97
298
|
self,
|
98
299
|
/,
|
@@ -101,7 +302,21 @@ class NetworkFileSystem(modal.object.Object):
|
|
101
302
|
client: typing.Optional[modal.client.Client] = None,
|
102
303
|
environment_name: typing.Optional[str] = None,
|
103
304
|
create_if_missing: bool = False,
|
104
|
-
) -> NetworkFileSystem:
|
305
|
+
) -> NetworkFileSystem:
|
306
|
+
"""mdmd:hidden
|
307
|
+
Lookup a named NetworkFileSystem.
|
308
|
+
|
309
|
+
DEPRECATED: This method is deprecated in favor of `modal.NetworkFileSystem.from_name`.
|
310
|
+
|
311
|
+
In contrast to `modal.NetworkFileSystem.from_name`, this is an eager method
|
312
|
+
that will hydrate the local object with metadata from Modal servers.
|
313
|
+
|
314
|
+
```python notest
|
315
|
+
nfs = modal.NetworkFileSystem.lookup("my-nfs")
|
316
|
+
print(nfs.listdir("/"))
|
317
|
+
```
|
318
|
+
"""
|
319
|
+
...
|
105
320
|
|
106
321
|
lookup: __lookup_spec
|
107
322
|
|
@@ -113,7 +328,10 @@ class NetworkFileSystem(modal.object.Object):
|
|
113
328
|
namespace=1,
|
114
329
|
client: typing.Optional[modal.client.Client] = None,
|
115
330
|
environment_name: typing.Optional[str] = None,
|
116
|
-
) -> str:
|
331
|
+
) -> str:
|
332
|
+
"""mdmd:hidden"""
|
333
|
+
...
|
334
|
+
|
117
335
|
async def aio(
|
118
336
|
self,
|
119
337
|
/,
|
@@ -121,7 +339,9 @@ class NetworkFileSystem(modal.object.Object):
|
|
121
339
|
namespace=1,
|
122
340
|
client: typing.Optional[modal.client.Client] = None,
|
123
341
|
environment_name: typing.Optional[str] = None,
|
124
|
-
) -> str:
|
342
|
+
) -> str:
|
343
|
+
"""mdmd:hidden"""
|
344
|
+
...
|
125
345
|
|
126
346
|
create_deployed: __create_deployed_spec
|
127
347
|
|
@@ -150,26 +370,65 @@ class NetworkFileSystem(modal.object.Object):
|
|
150
370
|
remote_path: str,
|
151
371
|
fp: typing.BinaryIO,
|
152
372
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
153
|
-
) -> int:
|
373
|
+
) -> int:
|
374
|
+
"""Write from a file object to a path on the network file system, atomically.
|
375
|
+
|
376
|
+
Will create any needed parent directories automatically.
|
377
|
+
|
378
|
+
If remote_path ends with `/` it's assumed to be a directory and the
|
379
|
+
file will be uploaded with its current name to that directory.
|
380
|
+
"""
|
381
|
+
...
|
382
|
+
|
154
383
|
async def aio(
|
155
384
|
self,
|
156
385
|
/,
|
157
386
|
remote_path: str,
|
158
387
|
fp: typing.BinaryIO,
|
159
388
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
160
|
-
) -> int:
|
389
|
+
) -> int:
|
390
|
+
"""Write from a file object to a path on the network file system, atomically.
|
391
|
+
|
392
|
+
Will create any needed parent directories automatically.
|
393
|
+
|
394
|
+
If remote_path ends with `/` it's assumed to be a directory and the
|
395
|
+
file will be uploaded with its current name to that directory.
|
396
|
+
"""
|
397
|
+
...
|
161
398
|
|
162
399
|
write_file: __write_file_spec[typing_extensions.Self]
|
163
400
|
|
164
401
|
class __read_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
165
|
-
def __call__(self, /, path: str) -> typing.Iterator[bytes]:
|
166
|
-
|
402
|
+
def __call__(self, /, path: str) -> typing.Iterator[bytes]:
|
403
|
+
"""Read a file from the network file system"""
|
404
|
+
...
|
405
|
+
|
406
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]:
|
407
|
+
"""Read a file from the network file system"""
|
408
|
+
...
|
167
409
|
|
168
410
|
read_file: __read_file_spec[typing_extensions.Self]
|
169
411
|
|
170
412
|
class __iterdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
171
|
-
def __call__(self, /, path: str) -> typing.Iterator[modal.volume.FileEntry]:
|
172
|
-
|
413
|
+
def __call__(self, /, path: str) -> typing.Iterator[modal.volume.FileEntry]:
|
414
|
+
"""Iterate over all files in a directory in the network file system.
|
415
|
+
|
416
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
417
|
+
* Passing a file path returns a list containing only that file's listing description
|
418
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
419
|
+
that glob path (using absolute paths)
|
420
|
+
"""
|
421
|
+
...
|
422
|
+
|
423
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[modal.volume.FileEntry]:
|
424
|
+
"""Iterate over all files in a directory in the network file system.
|
425
|
+
|
426
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
427
|
+
* Passing a file path returns a list containing only that file's listing description
|
428
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
429
|
+
that glob path (using absolute paths)
|
430
|
+
"""
|
431
|
+
...
|
173
432
|
|
174
433
|
iterdir: __iterdir_spec[typing_extensions.Self]
|
175
434
|
|
@@ -210,13 +469,35 @@ class NetworkFileSystem(modal.object.Object):
|
|
210
469
|
add_local_dir: __add_local_dir_spec[typing_extensions.Self]
|
211
470
|
|
212
471
|
class __listdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
213
|
-
def __call__(self, /, path: str) -> list[modal.volume.FileEntry]:
|
214
|
-
|
472
|
+
def __call__(self, /, path: str) -> list[modal.volume.FileEntry]:
|
473
|
+
"""List all files in a directory in the network file system.
|
474
|
+
|
475
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
476
|
+
* Passing a file path returns a list containing only that file's listing description
|
477
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
478
|
+
that glob path (using absolute paths)
|
479
|
+
"""
|
480
|
+
...
|
481
|
+
|
482
|
+
async def aio(self, /, path: str) -> list[modal.volume.FileEntry]:
|
483
|
+
"""List all files in a directory in the network file system.
|
484
|
+
|
485
|
+
* Passing a directory path lists all files in the directory (names are relative to the directory)
|
486
|
+
* Passing a file path returns a list containing only that file's listing description
|
487
|
+
* Passing a glob path (including at least one * or ** sequence) returns all files matching
|
488
|
+
that glob path (using absolute paths)
|
489
|
+
"""
|
490
|
+
...
|
215
491
|
|
216
492
|
listdir: __listdir_spec[typing_extensions.Self]
|
217
493
|
|
218
494
|
class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
219
|
-
def __call__(self, /, path: str, recursive=False):
|
220
|
-
|
495
|
+
def __call__(self, /, path: str, recursive=False):
|
496
|
+
"""Remove a file in a network file system."""
|
497
|
+
...
|
498
|
+
|
499
|
+
async def aio(self, /, path: str, recursive=False):
|
500
|
+
"""Remove a file in a network file system."""
|
501
|
+
...
|
221
502
|
|
222
503
|
remove_file: __remove_file_spec[typing_extensions.Self]
|
modal/object.pyi
CHANGED
@@ -32,7 +32,10 @@ class Object:
|
|
32
32
|
_is_hydrated: bool
|
33
33
|
_is_rehydrated: bool
|
34
34
|
|
35
|
-
def __init__(self, *args, **kwargs):
|
35
|
+
def __init__(self, *args, **kwargs):
|
36
|
+
"""mdmd:hidden"""
|
37
|
+
...
|
38
|
+
|
36
39
|
@classmethod
|
37
40
|
def __init_subclass__(cls, type_prefix: typing.Optional[str] = None): ...
|
38
41
|
|
@@ -85,7 +88,10 @@ class Object:
|
|
85
88
|
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
86
89
|
def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
|
87
90
|
def _validate_is_hydrated(self): ...
|
88
|
-
def clone(self) -> typing_extensions.Self:
|
91
|
+
def clone(self) -> typing_extensions.Self:
|
92
|
+
"""mdmd:hidden Clone a given hydrated object."""
|
93
|
+
...
|
94
|
+
|
89
95
|
@classmethod
|
90
96
|
def _from_loader(
|
91
97
|
cls,
|
@@ -114,18 +120,51 @@ class Object:
|
|
114
120
|
def _hydrate_from_other(self, other: typing_extensions.Self): ...
|
115
121
|
def __repr__(self): ...
|
116
122
|
@property
|
117
|
-
def local_uuid(self):
|
123
|
+
def local_uuid(self):
|
124
|
+
"""mdmd:hidden"""
|
125
|
+
...
|
126
|
+
|
118
127
|
@property
|
119
|
-
def object_id(self) -> str:
|
128
|
+
def object_id(self) -> str:
|
129
|
+
"""mdmd:hidden"""
|
130
|
+
...
|
131
|
+
|
120
132
|
@property
|
121
|
-
def client(self) -> modal.client.Client:
|
133
|
+
def client(self) -> modal.client.Client:
|
134
|
+
"""mdmd:hidden"""
|
135
|
+
...
|
136
|
+
|
122
137
|
@property
|
123
|
-
def is_hydrated(self) -> bool:
|
138
|
+
def is_hydrated(self) -> bool:
|
139
|
+
"""mdmd:hidden"""
|
140
|
+
...
|
141
|
+
|
124
142
|
@property
|
125
|
-
def deps(self) -> collections.abc.Callable[..., collections.abc.Sequence[Object]]:
|
143
|
+
def deps(self) -> collections.abc.Callable[..., collections.abc.Sequence[Object]]:
|
144
|
+
"""mdmd:hidden"""
|
145
|
+
...
|
126
146
|
|
127
147
|
class __hydrate_spec(typing_extensions.Protocol[SUPERSELF]):
|
128
|
-
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> SUPERSELF:
|
129
|
-
|
148
|
+
def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> SUPERSELF:
|
149
|
+
"""Synchronize the local object with its identity on the Modal server.
|
150
|
+
|
151
|
+
It is rarely necessary to call this method explicitly, as most operations
|
152
|
+
will lazily hydrate when needed. The main use case is when you need to
|
153
|
+
access object metadata, such as its ID.
|
154
|
+
|
155
|
+
*Added in v0.72.39*: This method replaces the deprecated `.resolve()` method.
|
156
|
+
"""
|
157
|
+
...
|
158
|
+
|
159
|
+
async def aio(self, /, client: typing.Optional[modal.client.Client] = None) -> SUPERSELF:
|
160
|
+
"""Synchronize the local object with its identity on the Modal server.
|
161
|
+
|
162
|
+
It is rarely necessary to call this method explicitly, as most operations
|
163
|
+
will lazily hydrate when needed. The main use case is when you need to
|
164
|
+
access object metadata, such as its ID.
|
165
|
+
|
166
|
+
*Added in v0.72.39*: This method replaces the deprecated `.resolve()` method.
|
167
|
+
"""
|
168
|
+
...
|
130
169
|
|
131
170
|
hydrate: __hydrate_spec[typing_extensions.Self]
|
modal/parallel_map.py
CHANGED
@@ -9,6 +9,7 @@ from typing import Any, Callable, Optional
|
|
9
9
|
|
10
10
|
from grpclib import Status
|
11
11
|
|
12
|
+
import modal.exception
|
12
13
|
from modal._runtime.execution_context import current_input_id
|
13
14
|
from modal._utils.async_utils import (
|
14
15
|
AsyncOrSyncIterable,
|
@@ -89,6 +90,7 @@ async def _map_invocation(
|
|
89
90
|
client: "modal.client._Client",
|
90
91
|
order_outputs: bool,
|
91
92
|
return_exceptions: bool,
|
93
|
+
wrap_returned_exceptions: bool,
|
92
94
|
count_update_callback: Optional[Callable[[int, int], None]],
|
93
95
|
function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType",
|
94
96
|
):
|
@@ -341,7 +343,13 @@ async def _map_invocation(
|
|
341
343
|
output = await _process_result(item.result, item.data_format, client.stub, client)
|
342
344
|
except Exception as e:
|
343
345
|
if return_exceptions:
|
344
|
-
|
346
|
+
if wrap_returned_exceptions:
|
347
|
+
# Prior to client 1.0.4 there was a bug where return_exceptions would wrap
|
348
|
+
# any returned exceptions in a synchronicity.UserCodeException. This adds
|
349
|
+
# deprecated non-breaking compatibility bandaid for migrating away from that:
|
350
|
+
output = modal.exception.UserCodeException(e)
|
351
|
+
else:
|
352
|
+
output = e
|
345
353
|
else:
|
346
354
|
raise e
|
347
355
|
return (item.idx, output)
|
@@ -413,6 +421,7 @@ async def _map_helper(
|
|
413
421
|
kwargs={}, # any extra keyword arguments for the function
|
414
422
|
order_outputs: bool = True, # return outputs in order
|
415
423
|
return_exceptions: bool = False, # propagate exceptions (False) or aggregate them in the results list (True)
|
424
|
+
wrap_returned_exceptions: bool = True,
|
416
425
|
) -> typing.AsyncGenerator[Any, None]:
|
417
426
|
"""Core implementation that supports `_map_async()`, `_starmap_async()` and `_for_each_async()`.
|
418
427
|
|
@@ -443,7 +452,9 @@ async def _map_helper(
|
|
443
452
|
# synchronicity-wrapped, since they accept executable code in the form of iterators that we don't want to run inside
|
444
453
|
# the synchronicity thread. Instead, we delegate to `._map()` with a safer Queue as input.
|
445
454
|
async with aclosing(
|
446
|
-
async_merge(
|
455
|
+
async_merge(
|
456
|
+
self._map.aio(raw_input_queue, order_outputs, return_exceptions, wrap_returned_exceptions), feed_queue()
|
457
|
+
)
|
447
458
|
) as map_output_stream:
|
448
459
|
async for output in map_output_stream:
|
449
460
|
yield output
|
@@ -458,10 +469,16 @@ async def _map_async(
|
|
458
469
|
kwargs={}, # any extra keyword arguments for the function
|
459
470
|
order_outputs: bool = True, # return outputs in order
|
460
471
|
return_exceptions: bool = False, # propagate exceptions (False) or aggregate them in the results list (True)
|
472
|
+
wrap_returned_exceptions: bool = True, # wrap returned exceptions in modal.exception.UserCodeException
|
461
473
|
) -> typing.AsyncGenerator[Any, None]:
|
462
474
|
async_input_gen = async_zip(*[sync_or_async_iter(it) for it in input_iterators])
|
463
475
|
async for output in _map_helper(
|
464
|
-
self,
|
476
|
+
self,
|
477
|
+
async_input_gen,
|
478
|
+
kwargs=kwargs,
|
479
|
+
order_outputs=order_outputs,
|
480
|
+
return_exceptions=return_exceptions,
|
481
|
+
wrap_returned_exceptions=wrap_returned_exceptions,
|
465
482
|
):
|
466
483
|
yield output
|
467
484
|
|
@@ -474,6 +491,7 @@ async def _starmap_async(
|
|
474
491
|
kwargs={},
|
475
492
|
order_outputs: bool = True,
|
476
493
|
return_exceptions: bool = False,
|
494
|
+
wrap_returned_exceptions: bool = True,
|
477
495
|
) -> typing.AsyncIterable[Any]:
|
478
496
|
async for output in _map_helper(
|
479
497
|
self,
|
@@ -481,6 +499,7 @@ async def _starmap_async(
|
|
481
499
|
kwargs=kwargs,
|
482
500
|
order_outputs=order_outputs,
|
483
501
|
return_exceptions=return_exceptions,
|
502
|
+
wrap_returned_exceptions=wrap_returned_exceptions,
|
484
503
|
):
|
485
504
|
yield output
|
486
505
|
|
@@ -502,6 +521,7 @@ def _map_sync(
|
|
502
521
|
kwargs={}, # any extra keyword arguments for the function
|
503
522
|
order_outputs: bool = True, # return outputs in order
|
504
523
|
return_exceptions: bool = False, # propagate exceptions (False) or aggregate them in the results list (True)
|
524
|
+
wrap_returned_exceptions: bool = True,
|
505
525
|
) -> AsyncOrSyncIterable:
|
506
526
|
"""Parallel map over a set of inputs.
|
507
527
|
|
@@ -542,7 +562,12 @@ def _map_sync(
|
|
542
562
|
|
543
563
|
return AsyncOrSyncIterable(
|
544
564
|
_map_async(
|
545
|
-
self,
|
565
|
+
self,
|
566
|
+
*input_iterators,
|
567
|
+
kwargs=kwargs,
|
568
|
+
order_outputs=order_outputs,
|
569
|
+
return_exceptions=return_exceptions,
|
570
|
+
wrap_returned_exceptions=wrap_returned_exceptions,
|
546
571
|
),
|
547
572
|
nested_async_message=(
|
548
573
|
"You can't iter(Function.map()) from an async function. Use async for ... in Function.map.aio() instead."
|
@@ -622,6 +647,7 @@ def _starmap_sync(
|
|
622
647
|
kwargs={},
|
623
648
|
order_outputs: bool = True,
|
624
649
|
return_exceptions: bool = False,
|
650
|
+
wrap_returned_exceptions: bool = True,
|
625
651
|
) -> AsyncOrSyncIterable:
|
626
652
|
"""Like `map`, but spreads arguments over multiple function arguments.
|
627
653
|
|
@@ -641,7 +667,12 @@ def _starmap_sync(
|
|
641
667
|
"""
|
642
668
|
return AsyncOrSyncIterable(
|
643
669
|
_starmap_async(
|
644
|
-
self,
|
670
|
+
self,
|
671
|
+
input_iterator,
|
672
|
+
kwargs=kwargs,
|
673
|
+
order_outputs=order_outputs,
|
674
|
+
return_exceptions=return_exceptions,
|
675
|
+
wrap_returned_exceptions=wrap_returned_exceptions,
|
645
676
|
),
|
646
677
|
nested_async_message=(
|
647
678
|
"You can't `iter(Function.starmap())` from an async function. "
|
@@ -764,11 +795,12 @@ class _MapItemContext:
|
|
764
795
|
delay_ms = 0
|
765
796
|
|
766
797
|
# None means the maximum number of retries has been reached, so output the error
|
767
|
-
if delay_ms is None:
|
798
|
+
if delay_ms is None or item.result.status == api_pb2.GenericResult.GENERIC_STATUS_TERMINATED:
|
768
799
|
self.state = _MapItemState.COMPLETE
|
769
800
|
return _OutputType.FAILED_COMPLETION
|
770
801
|
|
771
802
|
self.state = _MapItemState.WAITING_TO_RETRY
|
803
|
+
|
772
804
|
await retry_queue.put(now_seconds + (delay_ms / 1000), item.idx)
|
773
805
|
|
774
806
|
return _OutputType.RETRYING
|