modal 0.67.16__py3-none-any.whl → 0.67.19__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/async_utils.py +6 -3
- modal/_utils/blob_utils.py +18 -4
- modal/client.pyi +2 -2
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/METADATA +1 -1
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/RECORD +17 -17
- modal_proto/api.proto +13 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +241 -221
- modal_proto/api_pb2.pyi +41 -0
- modal_proto/api_pb2_grpc.py +33 -0
- modal_proto/api_pb2_grpc.pyi +10 -0
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/LICENSE +0 -0
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/WHEEL +0 -0
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/entry_points.txt +0 -0
- {modal-0.67.16.dist-info → modal-0.67.19.dist-info}/top_level.txt +0 -0
modal/_utils/async_utils.py
CHANGED
@@ -6,7 +6,7 @@ import inspect
|
|
6
6
|
import itertools
|
7
7
|
import time
|
8
8
|
import typing
|
9
|
-
from collections.abc import AsyncGenerator, Awaitable, Iterable, Iterator
|
9
|
+
from collections.abc import AsyncGenerator, AsyncIterable, Awaitable, Iterable, Iterator
|
10
10
|
from contextlib import asynccontextmanager
|
11
11
|
from dataclasses import dataclass
|
12
12
|
from typing import (
|
@@ -484,14 +484,17 @@ class aclosing(typing.Generic[T]): # noqa
|
|
484
484
|
await self.agen.aclose()
|
485
485
|
|
486
486
|
|
487
|
-
async def sync_or_async_iter(iter: Union[Iterable[T],
|
487
|
+
async def sync_or_async_iter(iter: Union[Iterable[T], AsyncIterable[T]]) -> AsyncGenerator[T, None]:
|
488
488
|
if hasattr(iter, "__aiter__"):
|
489
489
|
agen = typing.cast(AsyncGenerator[T, None], iter)
|
490
490
|
try:
|
491
491
|
async for item in agen:
|
492
492
|
yield item
|
493
493
|
finally:
|
494
|
-
|
494
|
+
if hasattr(agen, "aclose"):
|
495
|
+
# All AsyncGenerator's have an aclose method
|
496
|
+
# but some AsyncIterable's don't necessarily
|
497
|
+
await agen.aclose()
|
495
498
|
else:
|
496
499
|
assert hasattr(iter, "__iter__"), "sync_or_async_iter requires an Iterable or AsyncGenerator"
|
497
500
|
# This intentionally could block the event loop for the duration of calling __iter__ and __next__,
|
modal/_utils/blob_utils.py
CHANGED
@@ -5,6 +5,7 @@ import hashlib
|
|
5
5
|
import io
|
6
6
|
import os
|
7
7
|
import platform
|
8
|
+
import time
|
8
9
|
from collections.abc import AsyncIterator
|
9
10
|
from contextlib import AbstractContextManager, contextmanager
|
10
11
|
from pathlib import Path, PurePosixPath
|
@@ -289,11 +290,18 @@ async def _blob_upload(
|
|
289
290
|
|
290
291
|
|
291
292
|
async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
|
293
|
+
size_mib = len(payload) / 1024 / 1024
|
294
|
+
logger.debug(f"Uploading large blob of size {size_mib:.2f} MiB")
|
295
|
+
t0 = time.time()
|
292
296
|
if isinstance(payload, str):
|
293
297
|
logger.warning("Blob uploading string, not bytes - auto-encoding as utf8")
|
294
298
|
payload = payload.encode("utf8")
|
295
299
|
upload_hashes = get_upload_hashes(payload)
|
296
|
-
|
300
|
+
blob_id = await _blob_upload(upload_hashes, payload, stub)
|
301
|
+
dur_s = max(time.time() - t0, 0.001) # avoid division by zero
|
302
|
+
throughput_mib_s = (size_mib) / dur_s
|
303
|
+
logger.debug(f"Uploaded large blob of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s)." f" {blob_id}")
|
304
|
+
return blob_id
|
297
305
|
|
298
306
|
|
299
307
|
async def blob_upload_file(
|
@@ -318,11 +326,17 @@ async def _download_from_url(download_url: str) -> bytes:
|
|
318
326
|
|
319
327
|
|
320
328
|
async def blob_download(blob_id: str, stub: ModalClientModal) -> bytes:
|
321
|
-
|
329
|
+
"""Convenience function for reading all of the downloaded file into memory."""
|
330
|
+
logger.debug(f"Downloading large blob {blob_id}")
|
331
|
+
t0 = time.time()
|
322
332
|
req = api_pb2.BlobGetRequest(blob_id=blob_id)
|
323
333
|
resp = await retry_transient_errors(stub.BlobGet, req)
|
324
|
-
|
325
|
-
|
334
|
+
data = await _download_from_url(resp.download_url)
|
335
|
+
size_mib = len(data) / 1024 / 1024
|
336
|
+
dur_s = max(time.time() - t0, 0.001) # avoid division by zero
|
337
|
+
throughput_mib_s = size_mib / dur_s
|
338
|
+
logger.debug(f"Downloaded large blob {blob_id} of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s)")
|
339
|
+
return data
|
326
340
|
|
327
341
|
|
328
342
|
async def blob_iter(blob_id: str, stub: ModalClientModal) -> AsyncIterator[bytes]:
|
modal/client.pyi
CHANGED
@@ -26,7 +26,7 @@ class _Client:
|
|
26
26
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
27
27
|
|
28
28
|
def __init__(
|
29
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.19"
|
30
30
|
): ...
|
31
31
|
def is_closed(self) -> bool: ...
|
32
32
|
@property
|
@@ -81,7 +81,7 @@ class Client:
|
|
81
81
|
_stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
|
82
82
|
|
83
83
|
def __init__(
|
84
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.19"
|
85
85
|
): ...
|
86
86
|
def is_closed(self) -> bool: ...
|
87
87
|
@property
|
@@ -19,7 +19,7 @@ modal/app.py,sha256=EJ7FUN6rWnSwLJoYJh8nmKg_t-8hdN8_rt0OrkP7JvQ,46084
|
|
19
19
|
modal/app.pyi,sha256=BE5SlR5tRECuc6-e2lUuOknDdov3zxgZ4N0AsLb5ZVQ,25270
|
20
20
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
21
21
|
modal/client.py,sha256=VMg_aIuo_LOEe2ttxBHEND3PLhTp5lo-onH4wELhIyY,16375
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=thOQnTpBj0M-83tyx4cAzEtK1hyiaunf2CNsegg9DjM,7354
|
23
23
|
modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
|
25
25
|
modal/cls.py,sha256=F2jk5zFCAA8h-GfM0dbdBG3Mu5wiG9k9Z9JLYRYuT2Q,24758
|
@@ -81,8 +81,8 @@ modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5
|
|
81
81
|
modal/_runtime/user_code_imports.py,sha256=q_3JOYqCPDcVFZWCHEjyEqj8yzdFsQ49HzeqYmFDLbk,14521
|
82
82
|
modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
|
83
83
|
modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
|
84
|
-
modal/_utils/async_utils.py,sha256=
|
85
|
-
modal/_utils/blob_utils.py,sha256=
|
84
|
+
modal/_utils/async_utils.py,sha256=9ubwMkwiDB4gzOYG2jL9j7Fs-5dxHjcifZe3r7JRg-k,25091
|
85
|
+
modal/_utils/blob_utils.py,sha256=0k_qUpO5GHnz538wjRhyRw4NdJ5O322N7QSilIu32jw,16601
|
86
86
|
modal/_utils/function_utils.py,sha256=SkT5emqGJ8NNASk0BlBmgDfDBUYAkUM851K74qCHL98,24641
|
87
87
|
modal/_utils/grpc_testing.py,sha256=iqM9n5M0cWUUIIWNaEDer_pIfPnzXdZBO4L8FVbNepQ,8309
|
88
88
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
@@ -142,13 +142,13 @@ modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0
|
|
142
142
|
modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
|
143
143
|
modal_global_objects/mounts/python_standalone.py,sha256=_vTEX3PECUsatzhDs8lyJmDK0LbFetT1sJB6MIDfFAo,1870
|
144
144
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
145
|
-
modal_proto/api.proto,sha256=
|
146
|
-
modal_proto/api_grpc.py,sha256=
|
147
|
-
modal_proto/api_pb2.py,sha256=
|
148
|
-
modal_proto/api_pb2.pyi,sha256=
|
149
|
-
modal_proto/api_pb2_grpc.py,sha256=
|
150
|
-
modal_proto/api_pb2_grpc.pyi,sha256=
|
151
|
-
modal_proto/modal_api_grpc.py,sha256=
|
145
|
+
modal_proto/api.proto,sha256=I36DzPZ4fs045HqQCEdgEiiJr1Dcd6T6fFA1RbQW7aE,78014
|
146
|
+
modal_proto/api_grpc.py,sha256=cQOfwiGd2Nyj9esTgtu39EK1QKGZJXharISgWG7_lQ8,99814
|
147
|
+
modal_proto/api_pb2.py,sha256=rk-8yxgiqsevrf8ZOQl7Elr5xKKz0nYdI46Yd6S0iJI,284335
|
148
|
+
modal_proto/api_pb2.pyi,sha256=FCMnndhn2lqdFo1kr49PjfOQzxacPg2UHM9hGpatdS8,380051
|
149
|
+
modal_proto/api_pb2_grpc.py,sha256=VTrD72cWvA2SscVVpR7w0swwGJ2O6pY-uASamt9Qm7M,215824
|
150
|
+
modal_proto/api_pb2_grpc.pyi,sha256=SbWtkTeCztsSOz8WaM07UZ5fxfKn7nQG4Dxbp5PQUz8,50356
|
151
|
+
modal_proto/modal_api_grpc.py,sha256=AUfZ2n1xOaCv-NzMp5w91fgsQi5zs79IDLH7RGOg1-o,13340
|
152
152
|
modal_proto/modal_options_grpc.py,sha256=qJ1cuwA54oRqrdTyPTbvfhFZYd9HhJKK5UCwt523r3Y,120
|
153
153
|
modal_proto/options.proto,sha256=a-siq4swVbZPfaFRXAipRZzGP2bq8OsdUvjlyzAeodQ,488
|
154
154
|
modal_proto/options_grpc.py,sha256=M18X3d-8F_cNYSVM3I25dUTO5rZ0rd-vCCfynfh13Nc,125
|
@@ -159,10 +159,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
159
159
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
160
160
|
modal_version/__init__.py,sha256=3IY-AWLH55r35_mQXIaut0jrJvoPuf1NZJBQQfSbPuo,470
|
161
161
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
162
|
-
modal_version/_version_generated.py,sha256=
|
163
|
-
modal-0.67.
|
164
|
-
modal-0.67.
|
165
|
-
modal-0.67.
|
166
|
-
modal-0.67.
|
167
|
-
modal-0.67.
|
168
|
-
modal-0.67.
|
162
|
+
modal_version/_version_generated.py,sha256=zYKLeeniwCulLrYJ4NLXigHdUUd3VY_DsZP7BG5Ck7A,149
|
163
|
+
modal-0.67.19.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
164
|
+
modal-0.67.19.dist-info/METADATA,sha256=AsTc53FDpIG8sHxoyYC0LDTwQGt3ff6rMtMUuVlV3cw,2329
|
165
|
+
modal-0.67.19.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
166
|
+
modal-0.67.19.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
167
|
+
modal-0.67.19.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
168
|
+
modal-0.67.19.dist-info/RECORD,,
|
modal_proto/api.proto
CHANGED
@@ -2146,6 +2146,18 @@ message SandboxListResponse {
|
|
2146
2146
|
repeated SandboxInfo sandboxes = 1;
|
2147
2147
|
}
|
2148
2148
|
|
2149
|
+
message SandboxSnapshotFsRequest {
|
2150
|
+
string sandbox_id = 1;
|
2151
|
+
float timeout = 2;
|
2152
|
+
}
|
2153
|
+
|
2154
|
+
message SandboxSnapshotFsResponse {
|
2155
|
+
string image_id = 1;
|
2156
|
+
GenericResult result = 2;
|
2157
|
+
// Metadata may be empty since we may skip it for performance reasons.
|
2158
|
+
ImageMetadata image_metadata = 3;
|
2159
|
+
}
|
2160
|
+
|
2149
2161
|
message SandboxStdinWriteRequest {
|
2150
2162
|
string sandbox_id = 1;
|
2151
2163
|
bytes input = 2;
|
@@ -2745,6 +2757,7 @@ service ModalClient {
|
|
2745
2757
|
rpc SandboxGetTaskId(SandboxGetTaskIdRequest) returns (SandboxGetTaskIdResponse); // needed for modal container exec
|
2746
2758
|
rpc SandboxGetTunnels(SandboxGetTunnelsRequest) returns (SandboxGetTunnelsResponse);
|
2747
2759
|
rpc SandboxList(SandboxListRequest) returns (SandboxListResponse);
|
2760
|
+
rpc SandboxSnapshotFs(SandboxSnapshotFsRequest) returns (SandboxSnapshotFsResponse);
|
2748
2761
|
rpc SandboxStdinWrite(SandboxStdinWriteRequest) returns (SandboxStdinWriteResponse);
|
2749
2762
|
rpc SandboxTagsSet(SandboxTagsSetRequest) returns (google.protobuf.Empty);
|
2750
2763
|
rpc SandboxTerminate(SandboxTerminateRequest) returns (SandboxTerminateResponse);
|
modal_proto/api_grpc.py
CHANGED
@@ -389,6 +389,10 @@ class ModalClientBase(abc.ABC):
|
|
389
389
|
async def SandboxList(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxListRequest, modal_proto.api_pb2.SandboxListResponse]') -> None:
|
390
390
|
pass
|
391
391
|
|
392
|
+
@abc.abstractmethod
|
393
|
+
async def SandboxSnapshotFs(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxSnapshotFsRequest, modal_proto.api_pb2.SandboxSnapshotFsResponse]') -> None:
|
394
|
+
pass
|
395
|
+
|
392
396
|
@abc.abstractmethod
|
393
397
|
async def SandboxStdinWrite(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.SandboxStdinWriteRequest, modal_proto.api_pb2.SandboxStdinWriteResponse]') -> None:
|
394
398
|
pass
|
@@ -1093,6 +1097,12 @@ class ModalClientBase(abc.ABC):
|
|
1093
1097
|
modal_proto.api_pb2.SandboxListRequest,
|
1094
1098
|
modal_proto.api_pb2.SandboxListResponse,
|
1095
1099
|
),
|
1100
|
+
'/modal.client.ModalClient/SandboxSnapshotFs': grpclib.const.Handler(
|
1101
|
+
self.SandboxSnapshotFs,
|
1102
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
1103
|
+
modal_proto.api_pb2.SandboxSnapshotFsRequest,
|
1104
|
+
modal_proto.api_pb2.SandboxSnapshotFsResponse,
|
1105
|
+
),
|
1096
1106
|
'/modal.client.ModalClient/SandboxStdinWrite': grpclib.const.Handler(
|
1097
1107
|
self.SandboxStdinWrite,
|
1098
1108
|
grpclib.const.Cardinality.UNARY_UNARY,
|
@@ -1873,6 +1883,12 @@ class ModalClientStub:
|
|
1873
1883
|
modal_proto.api_pb2.SandboxListRequest,
|
1874
1884
|
modal_proto.api_pb2.SandboxListResponse,
|
1875
1885
|
)
|
1886
|
+
self.SandboxSnapshotFs = grpclib.client.UnaryUnaryMethod(
|
1887
|
+
channel,
|
1888
|
+
'/modal.client.ModalClient/SandboxSnapshotFs',
|
1889
|
+
modal_proto.api_pb2.SandboxSnapshotFsRequest,
|
1890
|
+
modal_proto.api_pb2.SandboxSnapshotFsResponse,
|
1891
|
+
)
|
1876
1892
|
self.SandboxStdinWrite = grpclib.client.UnaryUnaryMethod(
|
1877
1893
|
channel,
|
1878
1894
|
'/modal.client.ModalClient/SandboxStdinWrite',
|