modal 0.74.43__py3-none-any.whl → 0.74.45__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/_utils/blob_utils.py +14 -5
- modal/client.pyi +2 -2
- modal/functions.pyi +6 -6
- modal/secret.py +3 -3
- modal/secret.pyi +6 -6
- modal/volume.py +78 -23
- modal/volume.pyi +17 -4
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/METADATA +1 -1
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/RECORD +14 -14
- modal_version/_version_generated.py +1 -1
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/WHEEL +0 -0
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/entry_points.txt +0 -0
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/licenses/LICENSE +0 -0
- {modal-0.74.43.dist-info → modal-0.74.45.dist-info}/top_level.txt +0 -0
modal/_utils/blob_utils.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Copyright Modal Labs 2022
|
2
2
|
import asyncio
|
3
3
|
import dataclasses
|
4
|
-
import functools
|
5
4
|
import hashlib
|
6
5
|
import os
|
7
6
|
import platform
|
@@ -399,6 +398,7 @@ class FileUploadSpec2:
|
|
399
398
|
async def from_path(
|
400
399
|
filename: Path,
|
401
400
|
mount_filename: PurePosixPath,
|
401
|
+
hash_semaphore: asyncio.Semaphore,
|
402
402
|
mode: Optional[int] = None,
|
403
403
|
) -> "FileUploadSpec2":
|
404
404
|
# Python appears to give files 0o666 bits on Windows (equal for user, group, and global),
|
@@ -413,6 +413,7 @@ class FileUploadSpec2:
|
|
413
413
|
filename,
|
414
414
|
mount_filename,
|
415
415
|
mode,
|
416
|
+
hash_semaphore,
|
416
417
|
)
|
417
418
|
|
418
419
|
|
@@ -420,7 +421,8 @@ class FileUploadSpec2:
|
|
420
421
|
async def from_fileobj(
|
421
422
|
source_fp: Union[BinaryIO, BytesIO],
|
422
423
|
mount_filename: PurePosixPath,
|
423
|
-
|
424
|
+
hash_semaphore: asyncio.Semaphore,
|
425
|
+
mode: int,
|
424
426
|
) -> "FileUploadSpec2":
|
425
427
|
try:
|
426
428
|
fileno = source_fp.fileno()
|
@@ -442,6 +444,7 @@ class FileUploadSpec2:
|
|
442
444
|
str(source),
|
443
445
|
mount_filename,
|
444
446
|
mode,
|
447
|
+
hash_semaphore,
|
445
448
|
)
|
446
449
|
|
447
450
|
|
@@ -451,13 +454,14 @@ class FileUploadSpec2:
|
|
451
454
|
source_description: Union[str, Path],
|
452
455
|
mount_filename: PurePosixPath,
|
453
456
|
mode: int,
|
457
|
+
hash_semaphore: asyncio.Semaphore,
|
454
458
|
) -> "FileUploadSpec2":
|
455
459
|
# Current position is ignored - we always upload from position 0
|
456
460
|
with source() as source_fp:
|
457
461
|
source_fp.seek(0, os.SEEK_END)
|
458
462
|
size = source_fp.tell()
|
459
463
|
|
460
|
-
blocks_sha256 = await hash_blocks_sha256(source, size)
|
464
|
+
blocks_sha256 = await hash_blocks_sha256(source, size, hash_semaphore)
|
461
465
|
|
462
466
|
return FileUploadSpec2(
|
463
467
|
source=source,
|
@@ -472,13 +476,14 @@ class FileUploadSpec2:
|
|
472
476
|
async def hash_blocks_sha256(
|
473
477
|
source: _FileUploadSource2,
|
474
478
|
size: int,
|
479
|
+
hash_semaphore: asyncio.Semaphore,
|
475
480
|
) -> list[bytes]:
|
476
481
|
def ceildiv(a: int, b: int) -> int:
|
477
482
|
return -(a // -b)
|
478
483
|
|
479
484
|
num_blocks = ceildiv(size, BLOCK_SIZE)
|
480
485
|
|
481
|
-
def
|
486
|
+
def blocking_hash_block_sha256(block_idx: int) -> bytes:
|
482
487
|
sha256_hash = hashlib.sha256()
|
483
488
|
block_start = block_idx * BLOCK_SIZE
|
484
489
|
|
@@ -497,7 +502,11 @@ async def hash_blocks_sha256(
|
|
497
502
|
|
498
503
|
return sha256_hash.digest()
|
499
504
|
|
500
|
-
|
505
|
+
async def hash_block_sha256(block_idx: int) -> bytes:
|
506
|
+
async with hash_semaphore:
|
507
|
+
return await asyncio.to_thread(blocking_hash_block_sha256, block_idx)
|
508
|
+
|
509
|
+
tasks = (hash_block_sha256(idx) for idx in range(num_blocks))
|
501
510
|
return await asyncio.gather(*tasks)
|
502
511
|
|
503
512
|
|
modal/client.pyi
CHANGED
@@ -27,7 +27,7 @@ class _Client:
|
|
27
27
|
_snapshotted: bool
|
28
28
|
|
29
29
|
def __init__(
|
30
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.
|
30
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.45"
|
31
31
|
): ...
|
32
32
|
def is_closed(self) -> bool: ...
|
33
33
|
@property
|
@@ -85,7 +85,7 @@ class Client:
|
|
85
85
|
_snapshotted: bool
|
86
86
|
|
87
87
|
def __init__(
|
88
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.
|
88
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.45"
|
89
89
|
): ...
|
90
90
|
def is_closed(self) -> bool: ...
|
91
91
|
@property
|
modal/functions.pyi
CHANGED
@@ -198,11 +198,11 @@ class Function(
|
|
198
198
|
|
199
199
|
_call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
|
200
200
|
|
201
|
-
class __remote_spec(typing_extensions.Protocol[
|
201
|
+
class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
202
202
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
203
203
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
204
204
|
|
205
|
-
remote: __remote_spec[modal._functions.
|
205
|
+
remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
206
206
|
|
207
207
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
208
208
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -217,19 +217,19 @@ class Function(
|
|
217
217
|
self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
|
218
218
|
) -> modal._functions.OriginalReturnType: ...
|
219
219
|
|
220
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
220
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
221
221
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
222
222
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
223
223
|
|
224
224
|
_experimental_spawn: ___experimental_spawn_spec[
|
225
|
-
modal._functions.
|
225
|
+
modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
|
226
226
|
]
|
227
227
|
|
228
|
-
class __spawn_spec(typing_extensions.Protocol[
|
228
|
+
class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
|
229
229
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
230
230
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
231
231
|
|
232
|
-
spawn: __spawn_spec[modal._functions.
|
232
|
+
spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
|
233
233
|
|
234
234
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
|
235
235
|
|
modal/secret.py
CHANGED
@@ -34,7 +34,7 @@ class _Secret(_Object, type_prefix="st"):
|
|
34
34
|
env_dict: dict[
|
35
35
|
str, Union[str, None]
|
36
36
|
] = {}, # dict of entries to be inserted as environment variables in functions using the secret
|
37
|
-
):
|
37
|
+
) -> "_Secret":
|
38
38
|
"""Create a secret from a str-str dictionary. Values can also be `None`, which is ignored.
|
39
39
|
|
40
40
|
Usage:
|
@@ -81,7 +81,7 @@ class _Secret(_Object, type_prefix="st"):
|
|
81
81
|
@staticmethod
|
82
82
|
def from_local_environ(
|
83
83
|
env_keys: list[str], # list of local env vars to be included for remote execution
|
84
|
-
):
|
84
|
+
) -> "_Secret":
|
85
85
|
"""Create secrets from local environment variables automatically."""
|
86
86
|
|
87
87
|
if is_local():
|
@@ -96,7 +96,7 @@ class _Secret(_Object, type_prefix="st"):
|
|
96
96
|
return _Secret.from_dict({})
|
97
97
|
|
98
98
|
@staticmethod
|
99
|
-
def from_dotenv(path=None, *, filename=".env"):
|
99
|
+
def from_dotenv(path=None, *, filename=".env") -> "_Secret":
|
100
100
|
"""Create secrets from a .env file automatically.
|
101
101
|
|
102
102
|
If no argument is provided, it will use the current working directory as the starting
|
modal/secret.pyi
CHANGED
@@ -6,11 +6,11 @@ import typing_extensions
|
|
6
6
|
|
7
7
|
class _Secret(modal._object._Object):
|
8
8
|
@staticmethod
|
9
|
-
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}): ...
|
9
|
+
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> _Secret: ...
|
10
10
|
@staticmethod
|
11
|
-
def from_local_environ(env_keys: list[str]): ...
|
11
|
+
def from_local_environ(env_keys: list[str]) -> _Secret: ...
|
12
12
|
@staticmethod
|
13
|
-
def from_dotenv(path=None, *, filename=".env"): ...
|
13
|
+
def from_dotenv(path=None, *, filename=".env") -> _Secret: ...
|
14
14
|
@staticmethod
|
15
15
|
def from_name(
|
16
16
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
|
@@ -36,11 +36,11 @@ class _Secret(modal._object._Object):
|
|
36
36
|
class Secret(modal.object.Object):
|
37
37
|
def __init__(self, *args, **kwargs): ...
|
38
38
|
@staticmethod
|
39
|
-
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}): ...
|
39
|
+
def from_dict(env_dict: dict[str, typing.Optional[str]] = {}) -> Secret: ...
|
40
40
|
@staticmethod
|
41
|
-
def from_local_environ(env_keys: list[str]): ...
|
41
|
+
def from_local_environ(env_keys: list[str]) -> Secret: ...
|
42
42
|
@staticmethod
|
43
|
-
def from_dotenv(path=None, *, filename=".env"): ...
|
43
|
+
def from_dotenv(path=None, *, filename=".env") -> Secret: ...
|
44
44
|
@staticmethod
|
45
45
|
def from_name(
|
46
46
|
name: str, *, namespace=1, environment_name: typing.Optional[str] = None, required_keys: list[str] = []
|
modal/volume.py
CHANGED
@@ -3,6 +3,7 @@ import asyncio
|
|
3
3
|
import concurrent.futures
|
4
4
|
import enum
|
5
5
|
import functools
|
6
|
+
import multiprocessing
|
6
7
|
import os
|
7
8
|
import platform
|
8
9
|
import re
|
@@ -755,19 +756,27 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
|
|
755
756
|
|
756
757
|
VolumeUploadContextManager = synchronize_api(_VolumeUploadContextManager)
|
757
758
|
|
758
|
-
_FileUploader2 = Callable[[], Awaitable[FileUploadSpec2]]
|
759
|
+
_FileUploader2 = Callable[[asyncio.Semaphore], Awaitable[FileUploadSpec2]]
|
759
760
|
|
760
761
|
class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
761
762
|
"""Context manager for batch-uploading files to a Volume version 2."""
|
762
763
|
|
763
764
|
_volume_id: str
|
764
765
|
_client: _Client
|
765
|
-
_force: bool
|
766
766
|
_progress_cb: Callable[..., Any]
|
767
|
+
_force: bool
|
768
|
+
_hash_concurrency: int
|
769
|
+
_put_concurrency: int
|
767
770
|
_uploader_generators: list[Generator[_FileUploader2]]
|
768
771
|
|
769
772
|
def __init__(
|
770
|
-
self,
|
773
|
+
self,
|
774
|
+
volume_id: str,
|
775
|
+
client: _Client,
|
776
|
+
progress_cb: Optional[Callable[..., Any]] = None,
|
777
|
+
force: bool = False,
|
778
|
+
hash_concurrency: int = multiprocessing.cpu_count(),
|
779
|
+
put_concurrency: int = multiprocessing.cpu_count(),
|
771
780
|
):
|
772
781
|
"""mdmd:hidden"""
|
773
782
|
self._volume_id = volume_id
|
@@ -775,6 +784,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
775
784
|
self._uploader_generators = []
|
776
785
|
self._progress_cb = progress_cb or (lambda *_, **__: None)
|
777
786
|
self._force = force
|
787
|
+
self._hash_concurrency = hash_concurrency
|
788
|
+
self._put_concurrency = put_concurrency
|
778
789
|
|
779
790
|
async def __aenter__(self):
|
780
791
|
return self
|
@@ -787,7 +798,9 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
787
798
|
yield from gen
|
788
799
|
|
789
800
|
async def gen_file_upload_specs() -> list[FileUploadSpec2]:
|
790
|
-
|
801
|
+
hash_semaphore = asyncio.Semaphore(self._hash_concurrency)
|
802
|
+
|
803
|
+
uploads = [asyncio.create_task(fut(hash_semaphore)) for fut in gen_upload_providers()]
|
791
804
|
logger.debug(f"Computing checksums for {len(uploads)} files")
|
792
805
|
|
793
806
|
file_specs = []
|
@@ -817,9 +830,19 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
817
830
|
|
818
831
|
def gen():
|
819
832
|
if isinstance(local_file, str) or isinstance(local_file, Path):
|
820
|
-
yield lambda: FileUploadSpec2.from_path(
|
833
|
+
yield lambda hash_semaphore: FileUploadSpec2.from_path(
|
834
|
+
local_file,
|
835
|
+
PurePosixPath(remote_path),
|
836
|
+
hash_semaphore,
|
837
|
+
mode
|
838
|
+
)
|
821
839
|
else:
|
822
|
-
yield lambda: FileUploadSpec2.from_fileobj(
|
840
|
+
yield lambda hash_semaphore: FileUploadSpec2.from_fileobj(
|
841
|
+
local_file,
|
842
|
+
PurePosixPath(remote_path),
|
843
|
+
hash_semaphore,
|
844
|
+
mode or 0o644
|
845
|
+
)
|
823
846
|
|
824
847
|
self._uploader_generators.append(gen())
|
825
848
|
|
@@ -840,7 +863,7 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
840
863
|
|
841
864
|
def create_spec(subpath):
|
842
865
|
relpath_str = subpath.relative_to(local_path)
|
843
|
-
return lambda: FileUploadSpec2.from_path(subpath, remote_path / relpath_str)
|
866
|
+
return lambda hash_semaphore: FileUploadSpec2.from_path(subpath, remote_path / relpath_str, hash_semaphore)
|
844
867
|
|
845
868
|
def gen():
|
846
869
|
glob = local_path.rglob("*") if recursive else local_path.glob("*")
|
@@ -855,6 +878,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
855
878
|
put_responses = {}
|
856
879
|
# num_blocks_total = sum(len(file_spec.blocks_sha256) for file_spec in file_specs)
|
857
880
|
|
881
|
+
logger.debug(f"Ensuring {len(file_specs)} files are uploaded...")
|
882
|
+
|
858
883
|
# We should only need two iterations: Once to possibly get some missing_blocks; the second time we should have
|
859
884
|
# all blocks uploaded
|
860
885
|
for _ in range(2):
|
@@ -888,7 +913,13 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
888
913
|
if not response.missing_blocks:
|
889
914
|
break
|
890
915
|
|
891
|
-
await _put_missing_blocks(
|
916
|
+
await _put_missing_blocks(
|
917
|
+
file_specs,
|
918
|
+
response.missing_blocks,
|
919
|
+
put_responses,
|
920
|
+
self._put_concurrency,
|
921
|
+
self._progress_cb
|
922
|
+
)
|
892
923
|
else:
|
893
924
|
raise RuntimeError("Did not succeed at uploading all files despite supplying all missing blocks")
|
894
925
|
|
@@ -904,8 +935,19 @@ async def _put_missing_blocks(
|
|
904
935
|
# by the nested class (?)
|
905
936
|
missing_blocks: list,
|
906
937
|
put_responses: dict[bytes, bytes],
|
938
|
+
put_concurrency: int,
|
907
939
|
progress_cb: Callable[..., Any]
|
908
940
|
):
|
941
|
+
@dataclass
|
942
|
+
class FileProgress:
|
943
|
+
task_id: str
|
944
|
+
pending_blocks: set[int]
|
945
|
+
|
946
|
+
put_semaphore = asyncio.Semaphore(put_concurrency)
|
947
|
+
file_progresses: dict[str, FileProgress] = dict()
|
948
|
+
|
949
|
+
logger.debug(f"Uploading {len(missing_blocks)} missing blocks...")
|
950
|
+
|
909
951
|
async def put_missing_block(
|
910
952
|
# TODO(dflemstr): Type is `api_pb2.VolumePutFiles2Response.MissingBlock` but synchronicity gets confused
|
911
953
|
# by the nested class (?)
|
@@ -923,23 +965,36 @@ async def _put_missing_blocks(
|
|
923
965
|
block_start = missing_block.block_index * BLOCK_SIZE
|
924
966
|
block_length = min(BLOCK_SIZE, file_spec.size - block_start)
|
925
967
|
|
926
|
-
|
927
|
-
|
968
|
+
if file_spec.path not in file_progresses:
|
969
|
+
file_task_id = progress_cb(name=file_spec.path, size=file_spec.size)
|
970
|
+
file_progresses[file_spec.path] = FileProgress(task_id=file_task_id, pending_blocks=set())
|
971
|
+
|
972
|
+
file_progress = file_progresses[file_spec.path]
|
973
|
+
file_progress.pending_blocks.add(missing_block.block_index)
|
974
|
+
task_progress_cb = functools.partial(progress_cb, task_id=file_progress.task_id)
|
975
|
+
|
976
|
+
async with put_semaphore:
|
977
|
+
with file_spec.source() as source_fp:
|
978
|
+
payload = BytesIOSegmentPayload(
|
979
|
+
source_fp,
|
980
|
+
block_start,
|
981
|
+
block_length,
|
982
|
+
# limit chunk size somewhat to not keep event loop busy for too long
|
983
|
+
chunk_size=256*1024,
|
984
|
+
progress_report_cb=task_progress_cb
|
985
|
+
)
|
986
|
+
|
987
|
+
async with ClientSessionRegistry.get_session().put(
|
988
|
+
missing_block.put_url,
|
989
|
+
data=payload,
|
990
|
+
) as response:
|
991
|
+
response.raise_for_status()
|
992
|
+
resp_data = await response.content.read()
|
928
993
|
|
929
|
-
|
930
|
-
payload = BytesIOSegmentPayload(
|
931
|
-
source_fp,
|
932
|
-
block_start,
|
933
|
-
block_length,
|
934
|
-
progress_report_cb=functools.partial(progress_cb, progress_task_id)
|
935
|
-
)
|
994
|
+
file_progress.pending_blocks.remove(missing_block.block_index)
|
936
995
|
|
937
|
-
|
938
|
-
|
939
|
-
data=payload,
|
940
|
-
) as response:
|
941
|
-
response.raise_for_status()
|
942
|
-
resp_data = await response.content.read()
|
996
|
+
if len(file_progress.pending_blocks) == 0:
|
997
|
+
task_progress_cb(complete=True)
|
943
998
|
|
944
999
|
return block_sha256, resp_data
|
945
1000
|
|
modal/volume.pyi
CHANGED
@@ -406,11 +406,15 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
|
|
406
406
|
class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
407
407
|
_volume_id: str
|
408
408
|
_client: modal.client._Client
|
409
|
-
_force: bool
|
410
409
|
_progress_cb: collections.abc.Callable[..., typing.Any]
|
410
|
+
_force: bool
|
411
|
+
_hash_concurrency: int
|
412
|
+
_put_concurrency: int
|
411
413
|
_uploader_generators: list[
|
412
414
|
collections.abc.Generator[
|
413
|
-
collections.abc.Callable[
|
415
|
+
collections.abc.Callable[
|
416
|
+
[asyncio.locks.Semaphore], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]
|
417
|
+
]
|
414
418
|
]
|
415
419
|
]
|
416
420
|
|
@@ -420,6 +424,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
420
424
|
client: modal.client._Client,
|
421
425
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
422
426
|
force: bool = False,
|
427
|
+
hash_concurrency: int = 4,
|
428
|
+
put_concurrency: int = 4,
|
423
429
|
): ...
|
424
430
|
async def __aenter__(self): ...
|
425
431
|
async def __aexit__(self, exc_type, exc_val, exc_tb): ...
|
@@ -440,11 +446,15 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
|
|
440
446
|
class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
441
447
|
_volume_id: str
|
442
448
|
_client: modal.client.Client
|
443
|
-
_force: bool
|
444
449
|
_progress_cb: collections.abc.Callable[..., typing.Any]
|
450
|
+
_force: bool
|
451
|
+
_hash_concurrency: int
|
452
|
+
_put_concurrency: int
|
445
453
|
_uploader_generators: list[
|
446
454
|
collections.abc.Generator[
|
447
|
-
collections.abc.Callable[
|
455
|
+
collections.abc.Callable[
|
456
|
+
[asyncio.locks.Semaphore], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]
|
457
|
+
]
|
448
458
|
]
|
449
459
|
]
|
450
460
|
|
@@ -454,6 +464,8 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
|
|
454
464
|
client: modal.client.Client,
|
455
465
|
progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
|
456
466
|
force: bool = False,
|
467
|
+
hash_concurrency: int = 4,
|
468
|
+
put_concurrency: int = 4,
|
457
469
|
): ...
|
458
470
|
def __enter__(self): ...
|
459
471
|
async def __aenter__(self): ...
|
@@ -482,6 +494,7 @@ async def _put_missing_blocks(
|
|
482
494
|
file_specs: list[modal._utils.blob_utils.FileUploadSpec2],
|
483
495
|
missing_blocks: list,
|
484
496
|
put_responses: dict[bytes, bytes],
|
497
|
+
put_concurrency: int,
|
485
498
|
progress_cb: collections.abc.Callable[..., typing.Any],
|
486
499
|
): ...
|
487
500
|
def _open_files_error_annotation(mount_path: str) -> typing.Optional[str]: ...
|
@@ -22,7 +22,7 @@ modal/app.py,sha256=r-9vVU1lrR1CWtJEo60fuaianvxY_oOXZyv1Qx1DEkI,51231
|
|
22
22
|
modal/app.pyi,sha256=0QNtnUpAFbOPcbwCt119ge7OmoBqMFw5SajLgdE5eOw,28600
|
23
23
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
24
24
|
modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
|
25
|
-
modal/client.pyi,sha256=
|
25
|
+
modal/client.pyi,sha256=K5Vbh3SDRcb_4QaSIVEOuMqD_v6nkXwaAt6Fd1gzMNU,7593
|
26
26
|
modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
|
27
27
|
modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
|
28
28
|
modal/cls.py,sha256=pfEKqzNldQbtoM45CApRhSd8jJ7hD6o-83SLX8DTXXk,33549
|
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
|
|
39
39
|
modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
|
40
40
|
modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
|
41
41
|
modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
|
42
|
-
modal/functions.pyi,sha256=
|
42
|
+
modal/functions.pyi,sha256=GhzuTwVqKJzcj9bsCJZ1p_yXiOSQg5cOjtruRSdfbGQ,14870
|
43
43
|
modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
|
44
44
|
modal/image.py,sha256=ZCghS6l1O7pezXcdMHk6RoJpW3qWszfWGJTW38lNXaU,92797
|
45
45
|
modal/image.pyi,sha256=ddbegF532pDLiVANOJdtJiYoDbbF3mAFrsCiyvIu7jU,25632
|
@@ -69,8 +69,8 @@ modal/sandbox.py,sha256=aLW1hCMrk9ndfXaWjDcknS0U-ei-GhUpSjo-pURrnW8,32722
|
|
69
69
|
modal/sandbox.pyi,sha256=cLmSwI1ab-2DgEuXNf6S1PiK63wfUR9dHtxlZtSOuX8,22719
|
70
70
|
modal/schedule.py,sha256=ewa7hb9NKYnoeSCW2PujZAbGGJL8btX6X3KalCFpc_M,2626
|
71
71
|
modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
|
72
|
-
modal/secret.py,sha256=
|
73
|
-
modal/secret.pyi,sha256=
|
72
|
+
modal/secret.py,sha256=oczsOxMDifDusYA6_w49bGTDujHbxefyWNGspq62LRE,10505
|
73
|
+
modal/secret.pyi,sha256=b1jcMx9Z4bSIGOcrbyRPc68lzkFQBEauUP71gbnZeL4,3043
|
74
74
|
modal/serving.py,sha256=orZjhyikqk7U77My7GedbVKy65j0_CF7J7mqye86dRw,4650
|
75
75
|
modal/serving.pyi,sha256=KGSaZhg0qwygLmDkhgJedUfWeNSkXsyoOipq10vYffU,1978
|
76
76
|
modal/snapshot.py,sha256=6rQvDP3iX9hdiAudKTy0-m0JESt4kk0q2gusXbaRA-8,1279
|
@@ -78,8 +78,8 @@ modal/snapshot.pyi,sha256=Ypd4NKsjOTnnnqXyTGGLKq5lkocRrUURYjY5Pi67_qA,670
|
|
78
78
|
modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
|
79
79
|
modal/token_flow.py,sha256=ZYCa-uVP6jrJmUKA6wAG4lngqNem8Mgm2HXz1DR19BM,7355
|
80
80
|
modal/token_flow.pyi,sha256=0XV3d-9CGQL3qjPdw3RgwIFVqqxo8Z-u044_mkgAM3o,2064
|
81
|
-
modal/volume.py,sha256=
|
82
|
-
modal/volume.pyi,sha256=
|
81
|
+
modal/volume.py,sha256=197mYPFbjJfQWpLEvE3vinTwNpmbh0CC_B-NkOHzKo8,41979
|
82
|
+
modal/volume.pyi,sha256=_gE9ED_aO-o3_f9MDCkJc6OM8qU1w5ULJ4aKoJkhlgk,18356
|
83
83
|
modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
84
84
|
modal/_runtime/asgi.py,sha256=_2xSTsDD27Cit7xnMs4lzkJA2wzer2_N4Oa3BkXFzVA,22521
|
85
85
|
modal/_runtime/container_io_manager.py,sha256=6j0jO2-s9ShckM4SK45OapoQxWW9HQwQjFaBkXPJPwU,44763
|
@@ -92,7 +92,7 @@ modal/_runtime/user_code_imports.py,sha256=78wJyleqY2RVibqcpbDQyfWVBVT9BjyHPeoV9
|
|
92
92
|
modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
|
93
93
|
modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
|
94
94
|
modal/_utils/async_utils.py,sha256=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
|
95
|
-
modal/_utils/blob_utils.py,sha256=
|
95
|
+
modal/_utils/blob_utils.py,sha256=IexC2Jbtqp_Tkmy62ayfgzTYte0UPCNufB_v-DO21g8,18585
|
96
96
|
modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
|
97
97
|
modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
|
98
98
|
modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
|
@@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
|
|
145
145
|
modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
|
146
146
|
modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
|
147
147
|
modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
|
148
|
-
modal-0.74.
|
148
|
+
modal-0.74.45.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
149
149
|
modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
150
150
|
modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
|
151
151
|
modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
|
@@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
170
170
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
171
171
|
modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
|
172
172
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
173
|
-
modal_version/_version_generated.py,sha256=
|
174
|
-
modal-0.74.
|
175
|
-
modal-0.74.
|
176
|
-
modal-0.74.
|
177
|
-
modal-0.74.
|
178
|
-
modal-0.74.
|
173
|
+
modal_version/_version_generated.py,sha256=c7BL2IepKJIiMHNLgjdxfiQ-AeguBvJTyJNBflKJHlE,149
|
174
|
+
modal-0.74.45.dist-info/METADATA,sha256=hSlxaVTtPVF6N-naZ0l-52XOAVz-oWkrQS4dFvzBncg,2451
|
175
|
+
modal-0.74.45.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
176
|
+
modal-0.74.45.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
177
|
+
modal-0.74.45.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
178
|
+
modal-0.74.45.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|