modal 0.67.16__py3-none-any.whl → 0.67.18__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.
@@ -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
- return await _blob_upload(upload_hashes, payload, stub)
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
- # convenience function reading all of the downloaded file into memory
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
- return await _download_from_url(resp.download_url)
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.16"
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.18"
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.16"
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.18"
85
85
  ): ...
86
86
  def is_closed(self) -> bool: ...
87
87
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.67.16
3
+ Version: 0.67.18
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -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=IJfwJhllWxKJfV38qk43KNNHSoo3HQAhUu6uqrIsCOo,7354
22
+ modal/client.pyi,sha256=nmabQ_iRVi5HSDGw9_Bij5P8f2HyaMer8FOsGYGKPW4,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
@@ -82,7 +82,7 @@ modal/_runtime/user_code_imports.py,sha256=q_3JOYqCPDcVFZWCHEjyEqj8yzdFsQ49HzeqY
82
82
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
83
83
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
84
84
  modal/_utils/async_utils.py,sha256=CYXogDVqqUtSe-DVP2A3F-6KztjPZaW6ez2lrYBCW_Y,24917
85
- modal/_utils/blob_utils.py,sha256=XJpFr6SfNoODZEjyGm7WH9R3pKsK4yqpzZOFR3YX6fc,15856
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=1zr0aMwKHYgPfkvAjQ5hCRiWbJcuVXxwiuOlLpDU46Y,77644
146
- modal_proto/api_grpc.py,sha256=S7h8xe-msb3-Q8oSd7DUoB46z-dcRhsXGb6LjFCLNFI,99013
147
- modal_proto/api_pb2.py,sha256=PLRmPloiKDiiziRzbZrzF3Cr-Cn5uCv9USwc7nC_eeg,282809
148
- modal_proto/api_pb2.pyi,sha256=LAs_gmu_GYKzfa_8dxXiXsNejnWyWjX5hQ-6MuJmww0,378389
149
- modal_proto/api_pb2_grpc.py,sha256=g7EfCSir3xStPPjJOU2U668zz6cGdN6u7SxvTTwU9aU,214126
150
- modal_proto/api_pb2_grpc.pyi,sha256=9GhLZVRm69Qhyj_jmGqEGv1rD37Tzj6E6hGzKV08u48,49961
151
- modal_proto/modal_api_grpc.py,sha256=en48QTR5fwA7x0twtlsqLKRjjDEAKVoh6EeSznQfQ3U,13236
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=YjVTHXgFgY_BKue5ypqLKUgLAOu2RSFr5ZWkU08vbx4,149
163
- modal-0.67.16.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
- modal-0.67.16.dist-info/METADATA,sha256=1mxQSL4rxUlG1ThKNOsKPg3DOWUK-HafKMRkUO9ibXg,2329
165
- modal-0.67.16.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
- modal-0.67.16.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
- modal-0.67.16.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
- modal-0.67.16.dist-info/RECORD,,
162
+ modal_version/_version_generated.py,sha256=goG3pX_4gvPQTqRGbb26U_EVKGKJ2BSJ86M2obbDDy0,149
163
+ modal-0.67.18.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
+ modal-0.67.18.dist-info/METADATA,sha256=AkIw67Fd_w3r_nnsmLNl2C6uJHRSvg9o_MGK-PIBt2M,2329
165
+ modal-0.67.18.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
+ modal-0.67.18.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
+ modal-0.67.18.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
+ modal-0.67.18.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',