modal 0.74.49__py3-none-any.whl → 0.74.51__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/client.pyi +2 -2
- modal/volume.py +23 -43
- modal/volume.pyi +16 -5
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/METADATA +2 -2
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/RECORD +10 -10
- modal_version/_version_generated.py +1 -1
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/WHEEL +0 -0
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/entry_points.txt +0 -0
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/licenses/LICENSE +0 -0
- {modal-0.74.49.dist-info → modal-0.74.51.dist-info}/top_level.txt +0 -0
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.51"
|
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.51"
|
89
89
|
): ...
|
90
90
|
def is_closed(self) -> bool: ...
|
91
91
|
@property
|
modal/volume.py
CHANGED
@@ -14,7 +14,6 @@ from dataclasses import dataclass
|
|
14
14
|
from io import BytesIO
|
15
15
|
from pathlib import Path, PurePosixPath
|
16
16
|
from typing import (
|
17
|
-
IO,
|
18
17
|
Any,
|
19
18
|
Awaitable,
|
20
19
|
BinaryIO,
|
@@ -195,6 +194,16 @@ class _Volume(_Object, type_prefix="vo"):
|
|
195
194
|
def _get_metadata(self) -> Optional[Message]:
|
196
195
|
return self._metadata
|
197
196
|
|
197
|
+
|
198
|
+
@property
|
199
|
+
def _is_v1(self) -> bool:
|
200
|
+
return self._metadata.version in [
|
201
|
+
None,
|
202
|
+
api_pb2.VolumeFsVersion.VOLUME_FS_VERSION_UNSPECIFIED,
|
203
|
+
api_pb2.VolumeFsVersion.VOLUME_FS_VERSION_V1
|
204
|
+
]
|
205
|
+
|
206
|
+
|
198
207
|
@classmethod
|
199
208
|
@asynccontextmanager
|
200
209
|
async def ephemeral(
|
@@ -391,7 +400,7 @@ class _Volume(_Object, type_prefix="vo"):
|
|
391
400
|
return [entry async for entry in self.iterdir(path, recursive=recursive)]
|
392
401
|
|
393
402
|
@live_method_gen
|
394
|
-
|
403
|
+
def read_file(self, path: str) -> AsyncIterator[bytes]:
|
395
404
|
"""
|
396
405
|
Read a file from the modal.Volume.
|
397
406
|
|
@@ -405,6 +414,10 @@ class _Volume(_Object, type_prefix="vo"):
|
|
405
414
|
print(len(data)) # == 1024 * 1024
|
406
415
|
```
|
407
416
|
"""
|
417
|
+
return self._read_file1(path) if self._is_v1 else self._read_file2(path)
|
418
|
+
|
419
|
+
|
420
|
+
async def _read_file1(self, path: str) -> AsyncIterator[bytes]:
|
408
421
|
req = api_pb2.VolumeGetFileRequest(volume_id=self.object_id, path=path)
|
409
422
|
try:
|
410
423
|
response = await retry_transient_errors(self._client.stub.VolumeGetFile, req)
|
@@ -418,53 +431,20 @@ class _Volume(_Object, type_prefix="vo"):
|
|
418
431
|
async for data in blob_iter(response.data_blob_id, self._client.stub):
|
419
432
|
yield data
|
420
433
|
|
421
|
-
@live_method
|
422
|
-
async def read_file_into_fileobj(self, path: str, fileobj: IO[bytes]) -> int:
|
423
|
-
"""mdmd:hidden
|
424
434
|
|
425
|
-
|
426
|
-
|
427
|
-
"""
|
435
|
+
async def _read_file2(self, path: str) -> AsyncIterator[bytes]:
|
436
|
+
req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
|
428
437
|
|
429
|
-
chunk_size_bytes = 8 * 1024 * 1024
|
430
|
-
start = 0
|
431
|
-
req = api_pb2.VolumeGetFileRequest(volume_id=self.object_id, path=path, start=start, len=chunk_size_bytes)
|
432
438
|
try:
|
433
|
-
response = await retry_transient_errors(self._client.stub.
|
439
|
+
response = await retry_transient_errors(self._client.stub.VolumeGetFile2, req)
|
434
440
|
except GRPCError as exc:
|
435
441
|
raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
|
436
|
-
if response.WhichOneof("data_oneof") != "data":
|
437
|
-
raise RuntimeError("expected to receive 'data' in response")
|
438
|
-
|
439
|
-
n = fileobj.write(response.data)
|
440
|
-
if n != len(response.data):
|
441
|
-
raise OSError(f"failed to write {len(response.data)} bytes to output. Wrote {n}.")
|
442
|
-
elif n == response.size:
|
443
|
-
return response.size
|
444
|
-
elif n > response.size:
|
445
|
-
raise RuntimeError(f"length of returned data exceeds reported filesize: {n} > {response.size}")
|
446
|
-
# else: there's more data to read. continue reading with further ranged GET requests.
|
447
|
-
file_size = response.size
|
448
|
-
written = n
|
449
|
-
|
450
|
-
while True:
|
451
|
-
req = api_pb2.VolumeGetFileRequest(volume_id=self.object_id, path=path, start=written, len=chunk_size_bytes)
|
452
|
-
response = await retry_transient_errors(self._client.stub.VolumeGetFile, req)
|
453
|
-
if response.WhichOneof("data_oneof") != "data":
|
454
|
-
raise RuntimeError("expected to receive 'data' in response")
|
455
|
-
if len(response.data) > chunk_size_bytes:
|
456
|
-
raise RuntimeError(f"received more data than requested: {len(response.data)} > {chunk_size_bytes}")
|
457
|
-
elif (written + len(response.data)) > file_size:
|
458
|
-
raise RuntimeError(f"received data exceeds filesize of {chunk_size_bytes}")
|
459
|
-
|
460
|
-
n = fileobj.write(response.data)
|
461
|
-
if n != len(response.data):
|
462
|
-
raise OSError(f"failed to write {len(response.data)} bytes to output. Wrote {n}.")
|
463
|
-
written += n
|
464
|
-
if written == file_size:
|
465
|
-
break
|
466
442
|
|
467
|
-
|
443
|
+
for url in response.get_urls:
|
444
|
+
async with ClientSessionRegistry.get_session().get(url) as get_response:
|
445
|
+
async for data in get_response.content.iter_any():
|
446
|
+
yield data
|
447
|
+
|
468
448
|
|
469
449
|
@live_method
|
470
450
|
async def remove_file(self, path: str, recursive: bool = False) -> None:
|
modal/volume.pyi
CHANGED
@@ -52,6 +52,8 @@ class _Volume(modal._object._Object):
|
|
52
52
|
) -> _Volume: ...
|
53
53
|
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
54
54
|
def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
|
55
|
+
@property
|
56
|
+
def _is_v1(self) -> bool: ...
|
55
57
|
@classmethod
|
56
58
|
def ephemeral(
|
57
59
|
cls: type[_Volume],
|
@@ -83,7 +85,8 @@ class _Volume(modal._object._Object):
|
|
83
85
|
def iterdir(self, path: str, *, recursive: bool = True) -> collections.abc.AsyncIterator[FileEntry]: ...
|
84
86
|
async def listdir(self, path: str, *, recursive: bool = False) -> list[FileEntry]: ...
|
85
87
|
def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
86
|
-
|
88
|
+
def _read_file1(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
89
|
+
def _read_file2(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
|
87
90
|
async def remove_file(self, path: str, recursive: bool = False) -> None: ...
|
88
91
|
async def copy_files(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
|
89
92
|
async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager: ...
|
@@ -126,6 +129,8 @@ class Volume(modal.object.Object):
|
|
126
129
|
) -> Volume: ...
|
127
130
|
def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
|
128
131
|
def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
|
132
|
+
@property
|
133
|
+
def _is_v1(self) -> bool: ...
|
129
134
|
@classmethod
|
130
135
|
def ephemeral(
|
131
136
|
cls: type[Volume],
|
@@ -213,11 +218,17 @@ class Volume(modal.object.Object):
|
|
213
218
|
|
214
219
|
read_file: __read_file_spec[typing_extensions.Self]
|
215
220
|
|
216
|
-
class
|
217
|
-
def __call__(self, path: str
|
218
|
-
|
221
|
+
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]: ...
|
224
|
+
|
225
|
+
_read_file1: ___read_file1_spec[typing_extensions.Self]
|
226
|
+
|
227
|
+
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]: ...
|
219
230
|
|
220
|
-
|
231
|
+
_read_file2: ___read_file2_spec[typing_extensions.Self]
|
221
232
|
|
222
233
|
class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
|
223
234
|
def __call__(self, path: str, recursive: bool = False) -> None: ...
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: modal
|
3
|
-
Version: 0.74.
|
3
|
+
Version: 0.74.51
|
4
4
|
Summary: Python client library for Modal
|
5
5
|
Author-email: Modal Labs <support@modal.com>
|
6
6
|
License: Apache-2.0
|
@@ -20,7 +20,7 @@ Requires-Dist: aiohttp
|
|
20
20
|
Requires-Dist: certifi
|
21
21
|
Requires-Dist: click>=8.1.0
|
22
22
|
Requires-Dist: grpclib==0.4.7
|
23
|
-
Requires-Dist: protobuf!=4.24.0,<
|
23
|
+
Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
|
24
24
|
Requires-Dist: rich>=12.0.0
|
25
25
|
Requires-Dist: synchronicity~=0.9.10
|
26
26
|
Requires-Dist: toml
|
@@ -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=ogVEBy4PU2drB-wMNRCDKprT_A5VwKKO5GdfXBeEHvg,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=aHoMEWMZUN7bOezs3tRPxzS1FP3gTxZBORVjbPmtxyg,35338
|
@@ -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=d8x6i2qJgacJQ5qbNMvzeNFrK7ap4jPtjxZEpWstpCw,40624
|
82
|
+
modal/volume.pyi,sha256=rjlkEt9zuCy7GLe1TuVDSTiGJliFX9v6_KGKqL8HaeM,18760
|
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
|
@@ -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.51.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=XWG6Z6Rycy47HH88imPv1R6cURBY7Kq4Khuy3MbKuh4,149
|
174
|
+
modal-0.74.51.dist-info/METADATA,sha256=ub5SsraMgh2uqk6809lzYrCsp0T6NEke1jYNdAsUezQ,2451
|
175
|
+
modal-0.74.51.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
176
|
+
modal-0.74.51.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
177
|
+
modal-0.74.51.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
178
|
+
modal-0.74.51.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|