modal 1.0.6.dev8__py3-none-any.whl → 1.0.6.dev9__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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

@@ -188,16 +188,23 @@ def get_content_length(data: BinaryIO) -> int:
188
188
  return content_length - pos
189
189
 
190
190
 
191
- async def _blob_upload_with_fallback(items, blob_ids, callback):
191
+ async def _blob_upload_with_fallback(items, blob_ids: list[str], callback) -> tuple[str, bool, int]:
192
+ r2_latency_ms = 0
193
+ r2_failed = False
192
194
  for idx, (item, blob_id) in enumerate(zip(items, blob_ids)):
193
195
  # We want to default to R2 95% of the time and S3 5% of the time.
194
196
  # To ensure the failure path is continuously exercised.
195
197
  if idx == 0 and len(items) > 1 and random.random() > HEALTHY_R2_UPLOAD_PERCENTAGE:
196
198
  continue
197
199
  try:
200
+ init_time = time.monotonic_ns()
198
201
  await callback(item)
199
- return blob_id
202
+ if blob_id.endswith(":r2"):
203
+ r2_latency_ms = (time.monotonic_ns() - init_time) // 1_000_000
204
+ return blob_id, r2_failed, r2_latency_ms
200
205
  except Exception as _:
206
+ if blob_id.endswith(":r2"):
207
+ r2_failed = True
201
208
  # Ignore all errors except the last one, since we're out of fallback options.
202
209
  if idx == len(items) - 1:
203
210
  raise
@@ -206,7 +213,7 @@ async def _blob_upload_with_fallback(items, blob_ids, callback):
206
213
 
207
214
  async def _blob_upload(
208
215
  upload_hashes: UploadHashes, data: Union[bytes, BinaryIO], stub, progress_report_cb: Optional[Callable] = None
209
- ) -> str:
216
+ ) -> tuple[str, bool, int]:
210
217
  if isinstance(data, bytes):
211
218
  data = BytesIO(data)
212
219
 
@@ -232,7 +239,7 @@ async def _blob_upload(
232
239
  progress_report_cb=progress_report_cb,
233
240
  )
234
241
 
235
- blob_id = await _blob_upload_with_fallback(
242
+ blob_id, r2_failed, r2_latency_ms = await _blob_upload_with_fallback(
236
243
  resp.multiparts.items,
237
244
  resp.blob_ids,
238
245
  upload_multipart_upload,
@@ -252,7 +259,7 @@ async def _blob_upload(
252
259
  content_md5_b64=upload_hashes.md5_base64,
253
260
  )
254
261
 
255
- blob_id = await _blob_upload_with_fallback(
262
+ blob_id, r2_failed, r2_latency_ms = await _blob_upload_with_fallback(
256
263
  resp.upload_urls.items,
257
264
  resp.blob_ids,
258
265
  upload_to_s3_url,
@@ -261,10 +268,10 @@ async def _blob_upload(
261
268
  if progress_report_cb:
262
269
  progress_report_cb(complete=True)
263
270
 
264
- return blob_id
271
+ return blob_id, r2_failed, r2_latency_ms
265
272
 
266
273
 
267
- async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
274
+ async def blob_upload_with_r2_failure_info(payload: bytes, stub: ModalClientModal) -> tuple[str, bool, int]:
268
275
  size_mib = len(payload) / 1024 / 1024
269
276
  logger.debug(f"Uploading large blob of size {size_mib:.2f} MiB")
270
277
  t0 = time.time()
@@ -272,12 +279,17 @@ async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
272
279
  logger.warning("Blob uploading string, not bytes - auto-encoding as utf8")
273
280
  payload = payload.encode("utf8")
274
281
  upload_hashes = get_upload_hashes(payload)
275
- blob_id = await _blob_upload(upload_hashes, payload, stub)
282
+ blob_id, r2_failed, r2_latency_ms = await _blob_upload(upload_hashes, payload, stub)
276
283
  dur_s = max(time.time() - t0, 0.001) # avoid division by zero
277
284
  throughput_mib_s = (size_mib) / dur_s
278
285
  logger.debug(
279
286
  f"Uploaded large blob of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s, total {dur_s:.2f}s). {blob_id}"
280
287
  )
288
+ return blob_id, r2_failed, r2_latency_ms
289
+
290
+
291
+ async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
292
+ blob_id, _, _ = await blob_upload_with_r2_failure_info(payload, stub)
281
293
  return blob_id
282
294
 
283
295
 
@@ -289,7 +301,8 @@ async def blob_upload_file(
289
301
  md5_hex: Optional[str] = None,
290
302
  ) -> str:
291
303
  upload_hashes = get_upload_hashes(file_obj, sha256_hex=sha256_hex, md5_hex=md5_hex)
292
- return await _blob_upload(upload_hashes, file_obj, stub, progress_report_cb)
304
+ blob_id, _, _ = await _blob_upload(upload_hashes, file_obj, stub, progress_report_cb)
305
+ return blob_id
293
306
 
294
307
 
295
308
  @retry(n_attempts=5, base_delay=0.1, timeout=None)
@@ -32,7 +32,12 @@ from ..exception import (
32
32
  RemoteError,
33
33
  )
34
34
  from ..mount import ROOT_DIR, _is_modal_path, _Mount
35
- from .blob_utils import MAX_ASYNC_OBJECT_SIZE_BYTES, MAX_OBJECT_SIZE_BYTES, blob_download, blob_upload
35
+ from .blob_utils import (
36
+ MAX_ASYNC_OBJECT_SIZE_BYTES,
37
+ MAX_OBJECT_SIZE_BYTES,
38
+ blob_download,
39
+ blob_upload_with_r2_failure_info,
40
+ )
36
41
  from .grpc_utils import RETRYABLE_GRPC_STATUS_CODES
37
42
 
38
43
 
@@ -544,7 +549,7 @@ async def _create_input(
544
549
  args_serialized = serialize((args, kwargs))
545
550
 
546
551
  if should_upload(len(args_serialized), function_call_invocation_type):
547
- args_blob_id = await blob_upload(args_serialized, stub)
552
+ args_blob_id, r2_failed, r2_latency_ms = await blob_upload_with_r2_failure_info(args_serialized, stub)
548
553
  return api_pb2.FunctionPutInputsItem(
549
554
  input=api_pb2.FunctionInput(
550
555
  args_blob_id=args_blob_id,
@@ -552,6 +557,8 @@ async def _create_input(
552
557
  method_name=method_name,
553
558
  ),
554
559
  idx=idx,
560
+ r2_failed=r2_failed,
561
+ r2_latency_ms=r2_latency_ms,
555
562
  )
556
563
  else:
557
564
  return api_pb2.FunctionPutInputsItem(
modal/client.pyi CHANGED
@@ -31,7 +31,7 @@ class _Client:
31
31
  server_url: str,
32
32
  client_type: int,
33
33
  credentials: typing.Optional[tuple[str, str]],
34
- version: str = "1.0.6.dev8",
34
+ version: str = "1.0.6.dev9",
35
35
  ):
36
36
  """mdmd:hidden
37
37
  The Modal client object is not intended to be instantiated directly by users.
@@ -160,7 +160,7 @@ class Client:
160
160
  server_url: str,
161
161
  client_type: int,
162
162
  credentials: typing.Optional[tuple[str, str]],
163
- version: str = "1.0.6.dev8",
163
+ version: str = "1.0.6.dev9",
164
164
  ):
165
165
  """mdmd:hidden
166
166
  The Modal client object is not intended to be instantiated directly by users.
modal/functions.pyi CHANGED
@@ -428,7 +428,7 @@ class Function(
428
428
 
429
429
  _call_generator: ___call_generator_spec[typing_extensions.Self]
430
430
 
431
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
431
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
432
432
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
433
433
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
434
434
  ...
@@ -437,7 +437,7 @@ class Function(
437
437
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
438
438
  ...
439
439
 
440
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
440
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
441
441
 
442
442
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
443
443
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -464,7 +464,7 @@ class Function(
464
464
  """
465
465
  ...
466
466
 
467
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
467
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
468
468
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
469
469
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
470
470
 
@@ -488,7 +488,7 @@ class Function(
488
488
  ...
489
489
 
490
490
  _experimental_spawn: ___experimental_spawn_spec[
491
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
491
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
492
492
  ]
493
493
 
494
494
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -497,7 +497,7 @@ class Function(
497
497
 
498
498
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
499
499
 
500
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
500
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
501
501
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
502
502
  """Calls the function with the given arguments, without waiting for the results.
503
503
 
@@ -518,7 +518,7 @@ class Function(
518
518
  """
519
519
  ...
520
520
 
521
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
521
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
522
522
 
523
523
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
524
524
  """Return the inner Python object wrapped by this Modal Function."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.6.dev8
3
+ Version: 1.0.6.dev9
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -22,7 +22,7 @@ modal/app.py,sha256=fCKq3TJ2Y5LB2WKNs6pp_5XECNH5avUL01jQljuoYRU,46603
22
22
  modal/app.pyi,sha256=Z6wi_dkXywiaM2rvAvguj2Wgu9ZgPjMSLl1nH1a7EYI,42243
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=OwISJvkgMb-rHm9Gc4i-7YcDgGiZgwJ7F_PzwZH7a6Q,16847
25
- modal/client.pyi,sha256=F0JvLqFF2KdeFO-kDVo--6m5sW_J97l0agyaUV4T54U,15079
25
+ modal/client.pyi,sha256=37fmB7AmR6Cz7dhvKHykM-W9pMI7X9GTFeDfrXW1F1M,15079
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=-qSfYAQvIoO_l2wsCCGTG5ZUwQieNKXdAO00yP1-LYU,7394
28
28
  modal/cls.py,sha256=EFrM949jNXJpmwB2G_1d28b8IpHShfKIEIaiPkZqeOU,39881
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=SCBfLk5gRieqdTVlA_f-2YHHtRp7Iy_sA6iR1zPsO3c,21100
39
39
  modal/file_io.pyi,sha256=_Hm-59MrppfuBYxtzdJkA2Jf9zI5LlbPh_0gURk0_7s,15222
40
40
  modal/file_pattern_matcher.py,sha256=urAue8es8jxqX94k9EYoZxxhtfgOlsEES8lbFHOorzc,7734
41
41
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
42
- modal/functions.pyi,sha256=FJe_91dSrMCRNVT-YV1UhtxFKzIvL_C5q8xdk08-wT8,34840
42
+ modal/functions.pyi,sha256=ffW_kkU8AxMuV77ltmjK3nslXW_2iwEjKsT-Cgd4Trs,34840
43
43
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
44
44
  modal/image.py,sha256=W0Xg-nhsuLDO6weC1lk07MWVZFgZAxRdFqaA-Lt6_AI,94068
45
45
  modal/image.pyi,sha256=ha8QhMPDRmfTSoRlQ8CgV4-AZ7kYKK_-R2UXLyGPgQU,68172
@@ -92,11 +92,11 @@ 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=MhSCsCL8GqIVFWoHubU_899IH-JBZAiiqadG9Wri2l4,29361
95
- modal/_utils/blob_utils.py,sha256=NViIia3A5rdCpN712UDwiIiWtCZFoK_9DGq-xNX1z6A,19906
95
+ modal/_utils/blob_utils.py,sha256=4R-X3VNUJkc8EDSyGNfgcR5fAAkdpQ9W0O5Fy3PyOlU,20628
96
96
  modal/_utils/bytes_io_segment_payload.py,sha256=vaXPq8b52-x6G2hwE7SrjS58pg_aRm7gV3bn3yjmTzQ,4261
97
97
  modal/_utils/deprecation.py,sha256=-Bgg7jZdcJU8lROy18YyVnQYbM8hue-hVmwJqlWAGH0,5504
98
98
  modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
99
- modal/_utils/function_utils.py,sha256=7rFHglocBOUEmbZDEwgVAA2pnhq-7JHcpppQmgAZLoQ,27907
99
+ modal/_utils/function_utils.py,sha256=bBfmypTmFnxuo0XyT-hH-6N1CD-NSZVoX7ip6L4mvKA,28070
100
100
  modal/_utils/git_utils.py,sha256=qtUU6JAttF55ZxYq51y55OR58B0tDPZsZWK5dJe6W5g,3182
101
101
  modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
102
102
  modal/_utils/grpc_utils.py,sha256=aFDJIK3Idn9r0iqLRmQqCKsPhRCueyeaA64mZUvDNKA,11118
@@ -147,7 +147,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
147
147
  modal/requirements/PREVIEW.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqrs,312
148
148
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
149
149
  modal/requirements/base-images.json,sha256=3oKVHov9vE88hMQGnn1OqDQK-ohxNF_TEL2DNPKg09s,1051
150
- modal-1.0.6.dev8.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
150
+ modal-1.0.6.dev9.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
151
151
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
152
152
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
153
153
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -155,10 +155,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
155
155
  modal_docs/mdmd/mdmd.py,sha256=eW5MzrEl7mSclDo4Uv64sQ1-4IyLggldbgUJdBVLDdI,6449
156
156
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
157
157
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
158
- modal_proto/api.proto,sha256=1MXS-aHrFzxalwWfZGz-9BeaKWXjh6I2Y5lj5gMjHKs,97877
158
+ modal_proto/api.proto,sha256=9omXWIlKv263VKaw8itVaWzPMfy0LHUtv4xAVT5H6YQ,97927
159
159
  modal_proto/api_grpc.py,sha256=GYwNovPwj07sOoE8dC-cYZNYDPBOMT3dIkUb62aLrVY,119952
160
- modal_proto/api_pb2.py,sha256=2XBowKXGHytALBHPrX0aPY2UDKrjyq8VDZfb4mmpnPQ,344980
161
- modal_proto/api_pb2.pyi,sha256=E0CL0q0-ycQYn3fvFCdieDapMdYSSVtzMcNN5WR5g9g,470454
160
+ modal_proto/api_pb2.py,sha256=Z2cALXXN8ujwjmMWwWW5AB-_fcv7PSusjHB84re6S9E,345062
161
+ modal_proto/api_pb2.pyi,sha256=4nJzjsUBIWU-M_oCmELzai_DTnZP0KBENWH8e3VqvAw,470746
162
162
  modal_proto/api_pb2_grpc.py,sha256=YBLQpJSxBVK846jPPRSbbA4acUEqRMBeAUXsgwh_xI8,259277
163
163
  modal_proto/api_pb2_grpc.pyi,sha256=4W_ahZAhumFE-ugs8agFWSQoobs6BdjUkiO3BcBX7ew,60735
164
164
  modal_proto/modal_api_grpc.py,sha256=D8nUUUId-x98-IzPGWh76ERt5COhdeAd4NtoGL5dBpI,18137
@@ -170,10 +170,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
170
170
  modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
171
171
  modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
172
172
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
- modal_version/__init__.py,sha256=paHgvAiEToJxaQReFX1MRZqzvG9f7EZRuVDrZufTweg,120
173
+ modal_version/__init__.py,sha256=0igdmjj_TA_bu_64Kf8GYfCa9PCkUlOhBpHWCe5o69w,120
174
174
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
175
- modal-1.0.6.dev8.dist-info/METADATA,sha256=DbJAb6s9SLb-ldcQXECfBSbOkUauxxbciiHmGsGqpPk,2461
176
- modal-1.0.6.dev8.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
- modal-1.0.6.dev8.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
- modal-1.0.6.dev8.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
- modal-1.0.6.dev8.dist-info/RECORD,,
175
+ modal-1.0.6.dev9.dist-info/METADATA,sha256=dOUwnaRvK0CURlA8BhR66VV12WLjuBA3cr5gcUh_-E8,2461
176
+ modal-1.0.6.dev9.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
+ modal-1.0.6.dev9.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-1.0.6.dev9.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-1.0.6.dev9.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -1790,6 +1790,8 @@ message FunctionPrecreateResponse {
1790
1790
  message FunctionPutInputsItem {
1791
1791
  int32 idx = 1;
1792
1792
  FunctionInput input = 2;
1793
+ bool r2_failed = 3;
1794
+ uint64 r2_latency_ms = 4;
1793
1795
  }
1794
1796
 
1795
1797
  message FunctionPutInputsRequest {