modal 0.74.57__py3-none-any.whl → 0.74.59__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 +2 -2
- modal/_runtime/container_io_manager.pyi +46 -40
- modal/_runtime/execution_context.pyi +2 -2
- modal/_tunnel.pyi +2 -2
- modal/app.pyi +12 -4
- modal/cli/_download.py +17 -7
- modal/client.pyi +16 -16
- modal/cls.pyi +10 -6
- modal/container_process.pyi +6 -6
- modal/dict.pyi +32 -28
- modal/environments.pyi +10 -8
- modal/file_io.pyi +36 -32
- modal/functions.pyi +60 -45
- modal/image.pyi +4 -4
- modal/io_streams.pyi +12 -12
- modal/mount.pyi +6 -4
- modal/network_file_system.pyi +20 -8
- modal/object.pyi +6 -4
- modal/parallel_map.pyi +6 -6
- modal/queue.pyi +36 -14
- modal/runner.pyi +8 -4
- modal/sandbox.pyi +40 -30
- modal/secret.pyi +4 -0
- modal/serving.pyi +2 -0
- modal/snapshot.pyi +2 -2
- modal/token_flow.pyi +4 -4
- modal/volume.py +112 -5
- modal/volume.pyi +88 -30
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/METADATA +1 -1
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/RECORD +35 -35
- modal_version/_version_generated.py +1 -1
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/WHEEL +0 -0
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/entry_points.txt +0 -0
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/licenses/LICENSE +0 -0
- {modal-0.74.57.dist-info → modal-0.74.59.dist-info}/top_level.txt +0 -0
modal/sandbox.pyi
CHANGED
@@ -208,6 +208,7 @@ class Sandbox(modal.object.Object):
|
|
208
208
|
class __create_spec(typing_extensions.Protocol):
|
209
209
|
def __call__(
|
210
210
|
self,
|
211
|
+
/,
|
211
212
|
*entrypoint_args: str,
|
212
213
|
app: typing.Optional[modal.app.App] = None,
|
213
214
|
environment_name: typing.Optional[str] = None,
|
@@ -240,6 +241,7 @@ class Sandbox(modal.object.Object):
|
|
240
241
|
) -> Sandbox: ...
|
241
242
|
async def aio(
|
242
243
|
self,
|
244
|
+
/,
|
243
245
|
*entrypoint_args: str,
|
244
246
|
app: typing.Optional[modal.app.App] = None,
|
245
247
|
environment_name: typing.Optional[str] = None,
|
@@ -276,50 +278,50 @@ class Sandbox(modal.object.Object):
|
|
276
278
|
def _hydrate_metadata(self, handle_metadata: typing.Optional[google.protobuf.message.Message]): ...
|
277
279
|
|
278
280
|
class __from_id_spec(typing_extensions.Protocol):
|
279
|
-
def __call__(self, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
|
280
|
-
async def aio(self, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
|
281
|
+
def __call__(self, /, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
|
282
|
+
async def aio(self, /, sandbox_id: str, client: typing.Optional[modal.client.Client] = None) -> Sandbox: ...
|
281
283
|
|
282
284
|
from_id: __from_id_spec
|
283
285
|
|
284
286
|
class __set_tags_spec(typing_extensions.Protocol[SUPERSELF]):
|
285
|
-
def __call__(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
|
286
|
-
async def aio(self, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
|
287
|
+
def __call__(self, /, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
|
288
|
+
async def aio(self, /, tags: dict[str, str], *, client: typing.Optional[modal.client.Client] = None): ...
|
287
289
|
|
288
290
|
set_tags: __set_tags_spec[typing_extensions.Self]
|
289
291
|
|
290
292
|
class __snapshot_filesystem_spec(typing_extensions.Protocol[SUPERSELF]):
|
291
|
-
def __call__(self, timeout: int = 55) -> modal.image.Image: ...
|
292
|
-
async def aio(self, timeout: int = 55) -> modal.image.Image: ...
|
293
|
+
def __call__(self, /, timeout: int = 55) -> modal.image.Image: ...
|
294
|
+
async def aio(self, /, timeout: int = 55) -> modal.image.Image: ...
|
293
295
|
|
294
296
|
snapshot_filesystem: __snapshot_filesystem_spec[typing_extensions.Self]
|
295
297
|
|
296
298
|
class __wait_spec(typing_extensions.Protocol[SUPERSELF]):
|
297
|
-
def __call__(self, raise_on_termination: bool = True): ...
|
298
|
-
async def aio(self, raise_on_termination: bool = True): ...
|
299
|
+
def __call__(self, /, raise_on_termination: bool = True): ...
|
300
|
+
async def aio(self, /, raise_on_termination: bool = True): ...
|
299
301
|
|
300
302
|
wait: __wait_spec[typing_extensions.Self]
|
301
303
|
|
302
304
|
class __tunnels_spec(typing_extensions.Protocol[SUPERSELF]):
|
303
|
-
def __call__(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
|
304
|
-
async def aio(self, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
|
305
|
+
def __call__(self, /, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
|
306
|
+
async def aio(self, /, timeout: int = 50) -> dict[int, modal._tunnel.Tunnel]: ...
|
305
307
|
|
306
308
|
tunnels: __tunnels_spec[typing_extensions.Self]
|
307
309
|
|
308
310
|
class __terminate_spec(typing_extensions.Protocol[SUPERSELF]):
|
309
|
-
def __call__(self): ...
|
310
|
-
async def aio(self): ...
|
311
|
+
def __call__(self, /): ...
|
312
|
+
async def aio(self, /): ...
|
311
313
|
|
312
314
|
terminate: __terminate_spec[typing_extensions.Self]
|
313
315
|
|
314
316
|
class __poll_spec(typing_extensions.Protocol[SUPERSELF]):
|
315
|
-
def __call__(self) -> typing.Optional[int]: ...
|
316
|
-
async def aio(self) -> typing.Optional[int]: ...
|
317
|
+
def __call__(self, /) -> typing.Optional[int]: ...
|
318
|
+
async def aio(self, /) -> typing.Optional[int]: ...
|
317
319
|
|
318
320
|
poll: __poll_spec[typing_extensions.Self]
|
319
321
|
|
320
322
|
class ___get_task_id_spec(typing_extensions.Protocol[SUPERSELF]):
|
321
|
-
def __call__(self): ...
|
322
|
-
async def aio(self): ...
|
323
|
+
def __call__(self, /): ...
|
324
|
+
async def aio(self, /): ...
|
323
325
|
|
324
326
|
_get_task_id: ___get_task_id_spec[typing_extensions.Self]
|
325
327
|
|
@@ -327,6 +329,7 @@ class Sandbox(modal.object.Object):
|
|
327
329
|
@typing.overload
|
328
330
|
def __call__(
|
329
331
|
self,
|
332
|
+
/,
|
330
333
|
*cmds: str,
|
331
334
|
pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
|
332
335
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
@@ -341,6 +344,7 @@ class Sandbox(modal.object.Object):
|
|
341
344
|
@typing.overload
|
342
345
|
def __call__(
|
343
346
|
self,
|
347
|
+
/,
|
344
348
|
*cmds: str,
|
345
349
|
pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
|
346
350
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
@@ -355,6 +359,7 @@ class Sandbox(modal.object.Object):
|
|
355
359
|
@typing.overload
|
356
360
|
async def aio(
|
357
361
|
self,
|
362
|
+
/,
|
358
363
|
*cmds: str,
|
359
364
|
pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
|
360
365
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
@@ -369,6 +374,7 @@ class Sandbox(modal.object.Object):
|
|
369
374
|
@typing.overload
|
370
375
|
async def aio(
|
371
376
|
self,
|
377
|
+
/,
|
372
378
|
*cmds: str,
|
373
379
|
pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
|
374
380
|
stdout: modal.stream_type.StreamType = modal.stream_type.StreamType.PIPE,
|
@@ -384,54 +390,55 @@ class Sandbox(modal.object.Object):
|
|
384
390
|
exec: __exec_spec[typing_extensions.Self]
|
385
391
|
|
386
392
|
class ___experimental_snapshot_spec(typing_extensions.Protocol[SUPERSELF]):
|
387
|
-
def __call__(self) -> modal.snapshot.SandboxSnapshot: ...
|
388
|
-
async def aio(self) -> modal.snapshot.SandboxSnapshot: ...
|
393
|
+
def __call__(self, /) -> modal.snapshot.SandboxSnapshot: ...
|
394
|
+
async def aio(self, /) -> modal.snapshot.SandboxSnapshot: ...
|
389
395
|
|
390
396
|
_experimental_snapshot: ___experimental_snapshot_spec[typing_extensions.Self]
|
391
397
|
|
392
398
|
class ___experimental_from_snapshot_spec(typing_extensions.Protocol):
|
393
399
|
def __call__(
|
394
|
-
self, snapshot: modal.snapshot.SandboxSnapshot, client: typing.Optional[modal.client.Client] = None
|
400
|
+
self, /, snapshot: modal.snapshot.SandboxSnapshot, client: typing.Optional[modal.client.Client] = None
|
395
401
|
): ...
|
396
402
|
async def aio(
|
397
|
-
self, snapshot: modal.snapshot.SandboxSnapshot, client: typing.Optional[modal.client.Client] = None
|
403
|
+
self, /, snapshot: modal.snapshot.SandboxSnapshot, client: typing.Optional[modal.client.Client] = None
|
398
404
|
): ...
|
399
405
|
|
400
406
|
_experimental_from_snapshot: ___experimental_from_snapshot_spec
|
401
407
|
|
402
408
|
class __open_spec(typing_extensions.Protocol[SUPERSELF]):
|
403
409
|
@typing.overload
|
404
|
-
def __call__(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
|
410
|
+
def __call__(self, /, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
|
405
411
|
@typing.overload
|
406
|
-
def __call__(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
|
412
|
+
def __call__(self, /, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
|
407
413
|
@typing.overload
|
408
|
-
async def aio(self, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
|
414
|
+
async def aio(self, /, path: str, mode: _typeshed.OpenTextMode) -> modal.file_io.FileIO[str]: ...
|
409
415
|
@typing.overload
|
410
|
-
async def aio(self, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
|
416
|
+
async def aio(self, /, path: str, mode: _typeshed.OpenBinaryMode) -> modal.file_io.FileIO[bytes]: ...
|
411
417
|
|
412
418
|
open: __open_spec[typing_extensions.Self]
|
413
419
|
|
414
420
|
class __ls_spec(typing_extensions.Protocol[SUPERSELF]):
|
415
|
-
def __call__(self, path: str) -> list[str]: ...
|
416
|
-
async def aio(self, path: str) -> list[str]: ...
|
421
|
+
def __call__(self, /, path: str) -> list[str]: ...
|
422
|
+
async def aio(self, /, path: str) -> list[str]: ...
|
417
423
|
|
418
424
|
ls: __ls_spec[typing_extensions.Self]
|
419
425
|
|
420
426
|
class __mkdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
421
|
-
def __call__(self, path: str, parents: bool = False) -> None: ...
|
422
|
-
async def aio(self, path: str, parents: bool = False) -> None: ...
|
427
|
+
def __call__(self, /, path: str, parents: bool = False) -> None: ...
|
428
|
+
async def aio(self, /, path: str, parents: bool = False) -> None: ...
|
423
429
|
|
424
430
|
mkdir: __mkdir_spec[typing_extensions.Self]
|
425
431
|
|
426
432
|
class __rm_spec(typing_extensions.Protocol[SUPERSELF]):
|
427
|
-
def __call__(self, path: str, recursive: bool = False) -> None: ...
|
428
|
-
async def aio(self, path: str, recursive: bool = False) -> None: ...
|
433
|
+
def __call__(self, /, path: str, recursive: bool = False) -> None: ...
|
434
|
+
async def aio(self, /, path: str, recursive: bool = False) -> None: ...
|
429
435
|
|
430
436
|
rm: __rm_spec[typing_extensions.Self]
|
431
437
|
|
432
438
|
class __watch_spec(typing_extensions.Protocol[SUPERSELF]):
|
433
439
|
def __call__(
|
434
440
|
self,
|
441
|
+
/,
|
435
442
|
path: str,
|
436
443
|
filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
|
437
444
|
recursive: typing.Optional[bool] = None,
|
@@ -439,6 +446,7 @@ class Sandbox(modal.object.Object):
|
|
439
446
|
) -> typing.Iterator[modal.file_io.FileWatchEvent]: ...
|
440
447
|
def aio(
|
441
448
|
self,
|
449
|
+
/,
|
442
450
|
path: str,
|
443
451
|
filter: typing.Optional[list[modal.file_io.FileWatchEventType]] = None,
|
444
452
|
recursive: typing.Optional[bool] = None,
|
@@ -459,6 +467,7 @@ class Sandbox(modal.object.Object):
|
|
459
467
|
class __list_spec(typing_extensions.Protocol):
|
460
468
|
def __call__(
|
461
469
|
self,
|
470
|
+
/,
|
462
471
|
*,
|
463
472
|
app_id: typing.Optional[str] = None,
|
464
473
|
tags: typing.Optional[dict[str, str]] = None,
|
@@ -466,6 +475,7 @@ class Sandbox(modal.object.Object):
|
|
466
475
|
) -> typing.Generator[Sandbox, None, None]: ...
|
467
476
|
def aio(
|
468
477
|
self,
|
478
|
+
/,
|
469
479
|
*,
|
470
480
|
app_id: typing.Optional[str] = None,
|
471
481
|
tags: typing.Optional[dict[str, str]] = None,
|
modal/secret.pyi
CHANGED
@@ -49,6 +49,7 @@ class Secret(modal.object.Object):
|
|
49
49
|
class __lookup_spec(typing_extensions.Protocol):
|
50
50
|
def __call__(
|
51
51
|
self,
|
52
|
+
/,
|
52
53
|
name: str,
|
53
54
|
namespace=1,
|
54
55
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -57,6 +58,7 @@ class Secret(modal.object.Object):
|
|
57
58
|
) -> Secret: ...
|
58
59
|
async def aio(
|
59
60
|
self,
|
61
|
+
/,
|
60
62
|
name: str,
|
61
63
|
namespace=1,
|
62
64
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -69,6 +71,7 @@ class Secret(modal.object.Object):
|
|
69
71
|
class __create_deployed_spec(typing_extensions.Protocol):
|
70
72
|
def __call__(
|
71
73
|
self,
|
74
|
+
/,
|
72
75
|
deployment_name: str,
|
73
76
|
env_dict: dict[str, str],
|
74
77
|
namespace=1,
|
@@ -78,6 +81,7 @@ class Secret(modal.object.Object):
|
|
78
81
|
) -> str: ...
|
79
82
|
async def aio(
|
80
83
|
self,
|
84
|
+
/,
|
81
85
|
deployment_name: str,
|
82
86
|
env_dict: dict[str, str],
|
83
87
|
namespace=1,
|
modal/serving.pyi
CHANGED
@@ -38,6 +38,7 @@ def _serve_stub(*args, **kwargs): ...
|
|
38
38
|
class __serve_app_spec(typing_extensions.Protocol):
|
39
39
|
def __call__(
|
40
40
|
self,
|
41
|
+
/,
|
41
42
|
app: _App,
|
42
43
|
import_ref: modal.cli.import_refs.ImportRef,
|
43
44
|
*,
|
@@ -46,6 +47,7 @@ class __serve_app_spec(typing_extensions.Protocol):
|
|
46
47
|
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]: ...
|
47
48
|
def aio(
|
48
49
|
self,
|
50
|
+
/,
|
49
51
|
app: _App,
|
50
52
|
import_ref: modal.cli.import_refs.ImportRef,
|
51
53
|
*,
|
modal/snapshot.pyi
CHANGED
@@ -12,7 +12,7 @@ class SandboxSnapshot(modal.object.Object):
|
|
12
12
|
def __init__(self, *args, **kwargs): ...
|
13
13
|
|
14
14
|
class __from_id_spec(typing_extensions.Protocol):
|
15
|
-
def __call__(self, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
|
16
|
-
async def aio(self, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
|
15
|
+
def __call__(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
|
16
|
+
async def aio(self, /, sandbox_snapshot_id: str, client: typing.Optional[modal.client.Client] = None): ...
|
17
17
|
|
18
18
|
from_id: __from_id_spec
|
modal/token_flow.pyi
CHANGED
@@ -20,20 +20,20 @@ class TokenFlow:
|
|
20
20
|
|
21
21
|
class __start_spec(typing_extensions.Protocol[SUPERSELF]):
|
22
22
|
def __call__(
|
23
|
-
self, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
23
|
+
self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
24
24
|
) -> synchronicity.combined_types.AsyncAndBlockingContextManager[tuple[str, str, str]]: ...
|
25
25
|
def aio(
|
26
|
-
self, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
26
|
+
self, /, utm_source: typing.Optional[str] = None, next_url: typing.Optional[str] = None
|
27
27
|
) -> typing.AsyncContextManager[tuple[str, str, str]]: ...
|
28
28
|
|
29
29
|
start: __start_spec[typing_extensions.Self]
|
30
30
|
|
31
31
|
class __finish_spec(typing_extensions.Protocol[SUPERSELF]):
|
32
32
|
def __call__(
|
33
|
-
self, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
33
|
+
self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
34
34
|
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]: ...
|
35
35
|
async def aio(
|
36
|
-
self, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
36
|
+
self, /, timeout: float = 40.0, grpc_extra_timeout: float = 5.0
|
37
37
|
) -> typing.Optional[modal_proto.api_pb2.TokenFlowWaitResponse]: ...
|
38
38
|
|
39
39
|
finish: __finish_spec[typing_extensions.Self]
|
modal/volume.py
CHANGED
@@ -32,7 +32,14 @@ from modal_proto import api_pb2
|
|
32
32
|
|
33
33
|
from ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
|
34
34
|
from ._resolver import Resolver
|
35
|
-
from ._utils.async_utils import
|
35
|
+
from ._utils.async_utils import (
|
36
|
+
TaskContext,
|
37
|
+
aclosing,
|
38
|
+
async_map,
|
39
|
+
async_map_ordered,
|
40
|
+
asyncnullcontext,
|
41
|
+
synchronize_api,
|
42
|
+
)
|
36
43
|
from ._utils.blob_utils import (
|
37
44
|
BLOCK_SIZE,
|
38
45
|
FileUploadSpec,
|
@@ -440,10 +447,110 @@ class _Volume(_Object, type_prefix="vo"):
|
|
440
447
|
except GRPCError as exc:
|
441
448
|
raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
|
442
449
|
|
443
|
-
|
444
|
-
async with ClientSessionRegistry.get_session().get(
|
445
|
-
|
446
|
-
|
450
|
+
async def read_block(block_url: str) -> bytes:
|
451
|
+
async with ClientSessionRegistry.get_session().get(block_url) as get_response:
|
452
|
+
return await get_response.content.read()
|
453
|
+
|
454
|
+
async def iter_urls() -> AsyncGenerator[str]:
|
455
|
+
for url in response.get_urls:
|
456
|
+
yield url
|
457
|
+
|
458
|
+
# TODO(dflemstr): Reasonable default? Make configurable?
|
459
|
+
prefetch_num_blocks = multiprocessing.cpu_count()
|
460
|
+
|
461
|
+
async with aclosing(async_map_ordered(iter_urls(), read_block, concurrency=prefetch_num_blocks)) as stream:
|
462
|
+
async for value in stream:
|
463
|
+
yield value
|
464
|
+
|
465
|
+
|
466
|
+
@live_method
|
467
|
+
async def read_file_into_fileobj(
|
468
|
+
self,
|
469
|
+
path: str,
|
470
|
+
fileobj: typing.IO[bytes],
|
471
|
+
progress_cb: Optional[Callable[..., Any]] = None
|
472
|
+
) -> int:
|
473
|
+
"""mdmd:hidden
|
474
|
+
Read volume file into file-like IO object.
|
475
|
+
"""
|
476
|
+
if progress_cb is None:
|
477
|
+
def progress_cb(*_, **__):
|
478
|
+
pass
|
479
|
+
|
480
|
+
if self._is_v1:
|
481
|
+
return await self._read_file_into_fileobj1(path, fileobj, progress_cb)
|
482
|
+
else:
|
483
|
+
return await self._read_file_into_fileobj2(path, fileobj, progress_cb)
|
484
|
+
|
485
|
+
|
486
|
+
async def _read_file_into_fileobj1(
|
487
|
+
self,
|
488
|
+
path: str,
|
489
|
+
fileobj: typing.IO[bytes],
|
490
|
+
progress_cb: Callable[..., Any]
|
491
|
+
) -> int:
|
492
|
+
num_bytes_written = 0
|
493
|
+
|
494
|
+
async for chunk in self._read_file1(path):
|
495
|
+
num_chunk_bytes_written = 0
|
496
|
+
|
497
|
+
while num_chunk_bytes_written < len(chunk):
|
498
|
+
# TODO(dflemstr): this is a small write, but nonetheless might block the event loop for some time:
|
499
|
+
n = fileobj.write(chunk)
|
500
|
+
num_chunk_bytes_written += n
|
501
|
+
progress_cb(advance=n)
|
502
|
+
|
503
|
+
num_bytes_written += len(chunk)
|
504
|
+
|
505
|
+
return num_bytes_written
|
506
|
+
|
507
|
+
|
508
|
+
async def _read_file_into_fileobj2(
|
509
|
+
self,
|
510
|
+
path: str,
|
511
|
+
fileobj: typing.IO[bytes],
|
512
|
+
progress_cb: Callable[..., Any]
|
513
|
+
) -> int:
|
514
|
+
req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
|
515
|
+
|
516
|
+
try:
|
517
|
+
response = await retry_transient_errors(self._client.stub.VolumeGetFile2, req)
|
518
|
+
except GRPCError as exc:
|
519
|
+
raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
|
520
|
+
|
521
|
+
# TODO(dflemstr): Sane default limit? Make configurable?
|
522
|
+
download_semaphore = asyncio.Semaphore(multiprocessing.cpu_count())
|
523
|
+
write_lock = asyncio.Lock()
|
524
|
+
start_pos = fileobj.tell()
|
525
|
+
|
526
|
+
async def download_block(idx, url) -> int:
|
527
|
+
block_start_pos = start_pos + idx * BLOCK_SIZE
|
528
|
+
num_bytes_written = 0
|
529
|
+
|
530
|
+
async with download_semaphore, ClientSessionRegistry.get_session().get(url) as get_response:
|
531
|
+
async for chunk in get_response.content:
|
532
|
+
num_chunk_bytes_written = 0
|
533
|
+
|
534
|
+
while num_chunk_bytes_written < len(chunk):
|
535
|
+
async with write_lock:
|
536
|
+
fileobj.seek(block_start_pos + num_bytes_written + num_chunk_bytes_written)
|
537
|
+
# TODO(dflemstr): this is a small write, but nonetheless might block the event loop for some
|
538
|
+
# time:
|
539
|
+
n = fileobj.write(chunk)
|
540
|
+
|
541
|
+
num_chunk_bytes_written += n
|
542
|
+
progress_cb(advance=n)
|
543
|
+
|
544
|
+
num_bytes_written += len(chunk)
|
545
|
+
|
546
|
+
return num_bytes_written
|
547
|
+
|
548
|
+
coros = [download_block(idx, url) for idx, url in enumerate(response.get_urls)]
|
549
|
+
|
550
|
+
total_size = sum(await asyncio.gather(*coros))
|
551
|
+
fileobj.seek(start_pos + total_size)
|
552
|
+
|
553
|
+
return total_size
|
447
554
|
|
448
555
|
|
449
556
|
@live_method
|
modal/volume.pyi
CHANGED
@@ -87,6 +87,18 @@ class _Volume(modal._object._Object):
|
|
87
87
|
def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
88
88
|
def _read_file1(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
89
89
|
def _read_file2(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
90
|
+
async def read_file_into_fileobj(
|
91
|
+
self,
|
92
|
+
path: str,
|
93
|
+
fileobj: typing.IO[bytes],
|
94
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
95
|
+
) -> int: ...
|
96
|
+
async def _read_file_into_fileobj1(
|
97
|
+
self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
98
|
+
) -> int: ...
|
99
|
+
async def _read_file_into_fileobj2(
|
100
|
+
self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
101
|
+
) -> int: ...
|
90
102
|
async def remove_file(self, path: str, recursive: bool = False) -> None: ...
|
91
103
|
async def copy_files(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
92
104
|
async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager: ...
|
@@ -113,8 +125,8 @@ class Volume(modal.object.Object):
|
|
113
125
|
def __init__(self, *args, **kwargs): ...
|
114
126
|
|
115
127
|
class ___get_lock_spec(typing_extensions.Protocol[SUPERSELF]):
|
116
|
-
def __call__(self): ...
|
117
|
-
async def aio(self): ...
|
128
|
+
def __call__(self, /): ...
|
129
|
+
async def aio(self, /): ...
|
118
130
|
|
119
131
|
_get_lock: ___get_lock_spec[typing_extensions.Self]
|
120
132
|
|
@@ -143,6 +155,7 @@ class Volume(modal.object.Object):
|
|
143
155
|
class __lookup_spec(typing_extensions.Protocol):
|
144
156
|
def __call__(
|
145
157
|
self,
|
158
|
+
/,
|
146
159
|
name: str,
|
147
160
|
namespace=1,
|
148
161
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -152,6 +165,7 @@ class Volume(modal.object.Object):
|
|
152
165
|
) -> Volume: ...
|
153
166
|
async def aio(
|
154
167
|
self,
|
168
|
+
/,
|
155
169
|
name: str,
|
156
170
|
namespace=1,
|
157
171
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -165,6 +179,7 @@ class Volume(modal.object.Object):
|
|
165
179
|
class __create_deployed_spec(typing_extensions.Protocol):
|
166
180
|
def __call__(
|
167
181
|
self,
|
182
|
+
/,
|
168
183
|
deployment_name: str,
|
169
184
|
namespace=1,
|
170
185
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -173,6 +188,7 @@ class Volume(modal.object.Object):
|
|
173
188
|
) -> str: ...
|
174
189
|
async def aio(
|
175
190
|
self,
|
191
|
+
/,
|
176
192
|
deployment_name: str,
|
177
193
|
namespace=1,
|
178
194
|
client: typing.Optional[modal.client.Client] = None,
|
@@ -183,86 +199,126 @@ class Volume(modal.object.Object):
|
|
183
199
|
create_deployed: __create_deployed_spec
|
184
200
|
|
185
201
|
class ___do_reload_spec(typing_extensions.Protocol[SUPERSELF]):
|
186
|
-
def __call__(self, lock=True): ...
|
187
|
-
async def aio(self, lock=True): ...
|
202
|
+
def __call__(self, /, lock=True): ...
|
203
|
+
async def aio(self, /, lock=True): ...
|
188
204
|
|
189
205
|
_do_reload: ___do_reload_spec[typing_extensions.Self]
|
190
206
|
|
191
207
|
class __commit_spec(typing_extensions.Protocol[SUPERSELF]):
|
192
|
-
def __call__(self): ...
|
193
|
-
async def aio(self): ...
|
208
|
+
def __call__(self, /): ...
|
209
|
+
async def aio(self, /): ...
|
194
210
|
|
195
211
|
commit: __commit_spec[typing_extensions.Self]
|
196
212
|
|
197
213
|
class __reload_spec(typing_extensions.Protocol[SUPERSELF]):
|
198
|
-
def __call__(self): ...
|
199
|
-
async def aio(self): ...
|
214
|
+
def __call__(self, /): ...
|
215
|
+
async def aio(self, /): ...
|
200
216
|
|
201
217
|
reload: __reload_spec[typing_extensions.Self]
|
202
218
|
|
203
219
|
class __iterdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
204
|
-
def __call__(self, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]: ...
|
205
|
-
def aio(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
|
220
|
+
def __call__(self, /, path: str, *, recursive: bool = True) -> typing.Iterator[FileEntry]: ...
|
221
|
+
def aio(self, /, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
|
206
222
|
|
207
223
|
iterdir: __iterdir_spec[typing_extensions.Self]
|
208
224
|
|
209
225
|
class __listdir_spec(typing_extensions.Protocol[SUPERSELF]):
|
210
|
-
def __call__(self, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
211
|
-
async def aio(self, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
226
|
+
def __call__(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
227
|
+
async def aio(self, /, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
212
228
|
|
213
229
|
listdir: __listdir_spec[typing_extensions.Self]
|
214
230
|
|
215
231
|
class __read_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
216
|
-
def __call__(self, path: str) -> typing.Iterator[bytes]: ...
|
217
|
-
def aio(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
232
|
+
def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
|
233
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
218
234
|
|
219
235
|
read_file: __read_file_spec[typing_extensions.Self]
|
220
236
|
|
221
237
|
class ___read_file1_spec(typing_extensions.Protocol[SUPERSELF]):
|
222
|
-
def __call__(self, path: str) -> typing.Iterator[bytes]: ...
|
223
|
-
def aio(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
238
|
+
def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
|
239
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
224
240
|
|
225
241
|
_read_file1: ___read_file1_spec[typing_extensions.Self]
|
226
242
|
|
227
243
|
class ___read_file2_spec(typing_extensions.Protocol[SUPERSELF]):
|
228
|
-
def __call__(self, path: str) -> typing.Iterator[bytes]: ...
|
229
|
-
def aio(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
244
|
+
def __call__(self, /, path: str) -> typing.Iterator[bytes]: ...
|
245
|
+
def aio(self, /, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
230
246
|
|
231
247
|
_read_file2: ___read_file2_spec[typing_extensions.Self]
|
232
248
|
|
249
|
+
class __read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
|
250
|
+
def __call__(
|
251
|
+
self,
|
252
|
+
/,
|
253
|
+
path: str,
|
254
|
+
fileobj: typing.IO[bytes],
|
255
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
256
|
+
) -> int: ...
|
257
|
+
async def aio(
|
258
|
+
self,
|
259
|
+
/,
|
260
|
+
path: str,
|
261
|
+
fileobj: typing.IO[bytes],
|
262
|
+
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
263
|
+
) -> int: ...
|
264
|
+
|
265
|
+
read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
|
266
|
+
|
267
|
+
class ___read_file_into_fileobj1_spec(typing_extensions.Protocol[SUPERSELF]):
|
268
|
+
def __call__(
|
269
|
+
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
270
|
+
) -> int: ...
|
271
|
+
async def aio(
|
272
|
+
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
273
|
+
) -> int: ...
|
274
|
+
|
275
|
+
_read_file_into_fileobj1: ___read_file_into_fileobj1_spec[typing_extensions.Self]
|
276
|
+
|
277
|
+
class ___read_file_into_fileobj2_spec(typing_extensions.Protocol[SUPERSELF]):
|
278
|
+
def __call__(
|
279
|
+
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
280
|
+
) -> int: ...
|
281
|
+
async def aio(
|
282
|
+
self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
|
283
|
+
) -> int: ...
|
284
|
+
|
285
|
+
_read_file_into_fileobj2: ___read_file_into_fileobj2_spec[typing_extensions.Self]
|
286
|
+
|
233
287
|
class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
234
|
-
def __call__(self, path: str, recursive: bool = False) -> None: ...
|
235
|
-
async def aio(self, path: str, recursive: bool = False) -> None: ...
|
288
|
+
def __call__(self, /, path: str, recursive: bool = False) -> None: ...
|
289
|
+
async def aio(self, /, path: str, recursive: bool = False) -> None: ...
|
236
290
|
|
237
291
|
remove_file: __remove_file_spec[typing_extensions.Self]
|
238
292
|
|
239
293
|
class __copy_files_spec(typing_extensions.Protocol[SUPERSELF]):
|
240
|
-
def __call__(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
241
|
-
async def aio(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
294
|
+
def __call__(self, /, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
295
|
+
async def aio(self, /, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
242
296
|
|
243
297
|
copy_files: __copy_files_spec[typing_extensions.Self]
|
244
298
|
|
245
299
|
class __batch_upload_spec(typing_extensions.Protocol[SUPERSELF]):
|
246
|
-
def __call__(self, force: bool = False) -> AbstractVolumeUploadContextManager: ...
|
247
|
-
async def aio(self, force: bool = False) -> AbstractVolumeUploadContextManager: ...
|
300
|
+
def __call__(self, /, force: bool = False) -> AbstractVolumeUploadContextManager: ...
|
301
|
+
async def aio(self, /, force: bool = False) -> AbstractVolumeUploadContextManager: ...
|
248
302
|
|
249
303
|
batch_upload: __batch_upload_spec[typing_extensions.Self]
|
250
304
|
|
251
305
|
class ___instance_delete_spec(typing_extensions.Protocol[SUPERSELF]):
|
252
|
-
def __call__(self): ...
|
253
|
-
async def aio(self): ...
|
306
|
+
def __call__(self, /): ...
|
307
|
+
async def aio(self, /): ...
|
254
308
|
|
255
309
|
_instance_delete: ___instance_delete_spec[typing_extensions.Self]
|
256
310
|
|
257
311
|
class __delete_spec(typing_extensions.Protocol):
|
258
312
|
def __call__(
|
259
313
|
self,
|
314
|
+
/,
|
260
315
|
name: str,
|
261
316
|
client: typing.Optional[modal.client.Client] = None,
|
262
317
|
environment_name: typing.Optional[str] = None,
|
263
318
|
): ...
|
264
319
|
async def aio(
|
265
320
|
self,
|
321
|
+
/,
|
266
322
|
name: str,
|
267
323
|
client: typing.Optional[modal.client.Client] = None,
|
268
324
|
environment_name: typing.Optional[str] = None,
|
@@ -273,6 +329,7 @@ class Volume(modal.object.Object):
|
|
273
329
|
class __rename_spec(typing_extensions.Protocol):
|
274
330
|
def __call__(
|
275
331
|
self,
|
332
|
+
/,
|
276
333
|
old_name: str,
|
277
334
|
new_name: str,
|
278
335
|
*,
|
@@ -281,6 +338,7 @@ class Volume(modal.object.Object):
|
|
281
338
|
): ...
|
282
339
|
async def aio(
|
283
340
|
self,
|
341
|
+
/,
|
284
342
|
old_name: str,
|
285
343
|
new_name: str,
|
286
344
|
*,
|
@@ -409,8 +467,8 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
409
467
|
): ...
|
410
468
|
|
411
469
|
class ___upload_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
412
|
-
def __call__(self, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
|
413
|
-
async def aio(self, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
|
470
|
+
def __call__(self, /, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
|
471
|
+
async def aio(self, /, file_spec: modal._utils.blob_utils.FileUploadSpec) -> modal_proto.api_pb2.MountFile: ...
|
414
472
|
|
415
473
|
_upload_file: ___upload_file_spec[typing_extensions.Self]
|
416
474
|
|
@@ -496,8 +554,8 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
|
496
554
|
): ...
|
497
555
|
|
498
556
|
class ___put_file_specs_spec(typing_extensions.Protocol[SUPERSELF]):
|
499
|
-
def __call__(self, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|
500
|
-
async def aio(self, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|
557
|
+
def __call__(self, /, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|
558
|
+
async def aio(self, /, file_specs: list[modal._utils.blob_utils.FileUploadSpec2]): ...
|
501
559
|
|
502
560
|
_put_file_specs: ___put_file_specs_spec[typing_extensions.Self]
|
503
561
|
|