modal 1.1.1.dev6__py3-none-any.whl → 1.1.1.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,10 @@ def get_content_length(data: BinaryIO) -> int:
188
188
  return content_length - pos
189
189
 
190
190
 
191
- async def _measure_endpoint_latency(item: str) -> int:
192
- latency_ms = 0
193
- t0 = time.monotonic_ns()
194
- async with ClientSessionRegistry.get_session().head(item) as _:
195
- latency_ms = (time.monotonic_ns() - t0) // 1_000_000
196
- return latency_ms
197
-
198
-
199
- async def _blob_upload_with_fallback(items, blob_ids: list[str], callback) -> tuple[str, bool, int]:
200
- r2_latency_ms = 0
191
+ async def _blob_upload_with_fallback(
192
+ items, blob_ids: list[str], callback, content_length: int
193
+ ) -> tuple[str, bool, int]:
194
+ r2_throughput_bytes_s = 0
201
195
  r2_failed = False
202
196
  for idx, (item, blob_id) in enumerate(zip(items, blob_ids)):
203
197
  # We want to default to R2 95% of the time and S3 5% of the time.
@@ -206,14 +200,13 @@ async def _blob_upload_with_fallback(items, blob_ids: list[str], callback) -> tu
206
200
  continue
207
201
  try:
208
202
  if blob_id.endswith(":r2"):
209
- # measure the time it takes to contact the bucket endpoint
210
- r2_latency_ms, _ = await asyncio.gather(
211
- _measure_endpoint_latency(item),
212
- callback(item),
213
- )
203
+ t0 = time.monotonic_ns()
204
+ await callback(item)
205
+ dt_ns = time.monotonic_ns() - t0
206
+ r2_throughput_bytes_s = (content_length * 1_000_000_000) // max(dt_ns, 1)
214
207
  else:
215
208
  await callback(item)
216
- return blob_id, r2_failed, r2_latency_ms
209
+ return blob_id, r2_failed, r2_throughput_bytes_s
217
210
  except Exception as _:
218
211
  if blob_id.endswith(":r2"):
219
212
  r2_failed = True
@@ -251,10 +244,11 @@ async def _blob_upload(
251
244
  progress_report_cb=progress_report_cb,
252
245
  )
253
246
 
254
- blob_id, r2_failed, r2_latency_ms = await _blob_upload_with_fallback(
247
+ blob_id, r2_failed, r2_throughput_bytes_s = await _blob_upload_with_fallback(
255
248
  resp.multiparts.items,
256
249
  resp.blob_ids,
257
250
  upload_multipart_upload,
251
+ content_length=content_length,
258
252
  )
259
253
  else:
260
254
  from .bytes_io_segment_payload import BytesIOSegmentPayload
@@ -271,16 +265,17 @@ async def _blob_upload(
271
265
  content_md5_b64=upload_hashes.md5_base64,
272
266
  )
273
267
 
274
- blob_id, r2_failed, r2_latency_ms = await _blob_upload_with_fallback(
268
+ blob_id, r2_failed, r2_throughput_bytes_s = await _blob_upload_with_fallback(
275
269
  resp.upload_urls.items,
276
270
  resp.blob_ids,
277
271
  upload_to_s3_url,
272
+ content_length=content_length,
278
273
  )
279
274
 
280
275
  if progress_report_cb:
281
276
  progress_report_cb(complete=True)
282
277
 
283
- return blob_id, r2_failed, r2_latency_ms
278
+ return blob_id, r2_failed, r2_throughput_bytes_s
284
279
 
285
280
 
286
281
  async def blob_upload_with_r2_failure_info(payload: bytes, stub: ModalClientModal) -> tuple[str, bool, int]:
@@ -291,13 +286,13 @@ async def blob_upload_with_r2_failure_info(payload: bytes, stub: ModalClientModa
291
286
  logger.warning("Blob uploading string, not bytes - auto-encoding as utf8")
292
287
  payload = payload.encode("utf8")
293
288
  upload_hashes = get_upload_hashes(payload)
294
- blob_id, r2_failed, r2_latency_ms = await _blob_upload(upload_hashes, payload, stub)
289
+ blob_id, r2_failed, r2_throughput_bytes_s = await _blob_upload(upload_hashes, payload, stub)
295
290
  dur_s = max(time.time() - t0, 0.001) # avoid division by zero
296
291
  throughput_mib_s = (size_mib) / dur_s
297
292
  logger.debug(
298
293
  f"Uploaded large blob of size {size_mib:.2f} MiB ({throughput_mib_s:.2f} MiB/s, total {dur_s:.2f}s). {blob_id}"
299
294
  )
300
- return blob_id, r2_failed, r2_latency_ms
295
+ return blob_id, r2_failed, r2_throughput_bytes_s
301
296
 
302
297
 
303
298
  async def blob_upload(payload: bytes, stub: ModalClientModal) -> str:
@@ -563,7 +563,7 @@ async def _create_input(
563
563
  args_serialized = serialize((args, kwargs))
564
564
 
565
565
  if should_upload(len(args_serialized), max_object_size_bytes, function_call_invocation_type):
566
- args_blob_id, r2_failed, r2_latency_ms = await blob_upload_with_r2_failure_info(args_serialized, stub)
566
+ args_blob_id, r2_failed, r2_throughput_bytes_s = await blob_upload_with_r2_failure_info(args_serialized, stub)
567
567
  return api_pb2.FunctionPutInputsItem(
568
568
  input=api_pb2.FunctionInput(
569
569
  args_blob_id=args_blob_id,
@@ -572,7 +572,7 @@ async def _create_input(
572
572
  ),
573
573
  idx=idx,
574
574
  r2_failed=r2_failed,
575
- r2_latency_ms=r2_latency_ms,
575
+ r2_throughput_bytes_s=r2_throughput_bytes_s,
576
576
  )
577
577
  else:
578
578
  return api_pb2.FunctionPutInputsItem(
modal/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.1.1.dev6",
36
+ version: str = "1.1.1.dev9",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.1.1.dev6",
166
+ version: str = "1.1.1.dev9",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
@@ -20,7 +20,7 @@ from ..cls import _Cls, _Obj
20
20
  from ..exception import InvalidError
21
21
  from ..image import DockerfileSpec, ImageBuilderVersion, _Image, _ImageRegistryConfig
22
22
  from ..secret import _Secret
23
- from .flash import flash_forward, flash_prometheus_autoscaler # noqa: F401
23
+ from .flash import flash_forward, flash_get_containers, flash_prometheus_autoscaler # noqa: F401
24
24
 
25
25
 
26
26
  def stop_fetching_inputs():
@@ -447,3 +447,20 @@ async def flash_prometheus_autoscaler(
447
447
  )
448
448
  await autoscaler.start()
449
449
  return autoscaler
450
+
451
+
452
+ @synchronizer.create_blocking
453
+ async def flash_get_containers(app_name: str, cls_name: str) -> list[dict[str, Any]]:
454
+ """
455
+ Return a list of flash containers for a deployed Flash service.
456
+
457
+ This is a highly experimental method that can break or be removed at any time without warning.
458
+ Do not use this method unless explicitly instructed to do so by Modal support.
459
+ """
460
+ client = await _Client.from_env()
461
+ fn = _Cls.from_name(app_name, cls_name)._class_service_function
462
+ assert fn is not None
463
+ await fn.hydrate(client=client)
464
+ req = api_pb2.FlashContainerListRequest(function_id=fn.object_id)
465
+ resp = await retry_transient_errors(client.stub.FlashContainerList, req)
466
+ return resp.containers
@@ -250,3 +250,22 @@ class __flash_prometheus_autoscaler_spec(typing_extensions.Protocol):
250
250
  ...
251
251
 
252
252
  flash_prometheus_autoscaler: __flash_prometheus_autoscaler_spec
253
+
254
+ class __flash_get_containers_spec(typing_extensions.Protocol):
255
+ def __call__(self, /, app_name: str, cls_name: str) -> list[dict[str, typing.Any]]:
256
+ """Return a list of flash containers for a deployed Flash service.
257
+
258
+ This is a highly experimental method that can break or be removed at any time without warning.
259
+ Do not use this method unless explicitly instructed to do so by Modal support.
260
+ """
261
+ ...
262
+
263
+ async def aio(self, /, app_name: str, cls_name: str) -> list[dict[str, typing.Any]]:
264
+ """Return a list of flash containers for a deployed Flash service.
265
+
266
+ This is a highly experimental method that can break or be removed at any time without warning.
267
+ Do not use this method unless explicitly instructed to do so by Modal support.
268
+ """
269
+ ...
270
+
271
+ flash_get_containers: __flash_get_containers_spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.1.dev6
3
+ Version: 1.1.1.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=BBR2NmGzZbFGfhKAmtzllD0o4TbVDBbOEs0O2ysSdQo,48277
22
22
  modal/app.pyi,sha256=h6JtBA6a7wobdZAuS3QuXrWCUZqfyKPuGV3XdjCqT3k,43753
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=pBSZ7lv5dezIL9U9H4tpE0Yz6qA1n0NoNbnJ3KCQMMA,18252
25
- modal/client.pyi,sha256=shwOM1cRURg_svEKF7KfRLQsiPdYlO_yor3R7HMfsJg,15388
25
+ modal/client.pyi,sha256=-wpRB8J01HBAFnO1zkm7WdlL5umq-d68vQZz_cFKlkg,15388
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=7A0xGnugQzm8dOfnKMjLjtqekRlRtQ0jPFRYgq6xdUM,40018
@@ -93,11 +93,11 @@ 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=ot8NiPGZ5bRJhY5ilZyjNgx24VI-1BIpCu054oLHDf0,29556
95
95
  modal/_utils/auth_token_manager.py,sha256=4YS0pBfwbuNFy5DoAIOnBNCcYjS9rNCMv4zSVGybiOw,5245
96
- modal/_utils/blob_utils.py,sha256=v2NAQVVGx1AQjHQ7-2T64x5rYtwjFFykxDXb-0grrzA,21022
96
+ modal/_utils/blob_utils.py,sha256=bySVr9M7hlFzZo-u4ikovxMdcdEE8yfGOs94Zex2k4o,20913
97
97
  modal/_utils/bytes_io_segment_payload.py,sha256=vaXPq8b52-x6G2hwE7SrjS58pg_aRm7gV3bn3yjmTzQ,4261
98
98
  modal/_utils/deprecation.py,sha256=-Bgg7jZdcJU8lROy18YyVnQYbM8hue-hVmwJqlWAGH0,5504
99
99
  modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
100
- modal/_utils/function_utils.py,sha256=9QIDlMpSKh3dH1lNZxTYIkTuGdt3WdiPrESzMhCwOFE,27320
100
+ modal/_utils/function_utils.py,sha256=EvFGDLO85iN62epvxWZrYrAqJy8_pVATryBc7V6IqVg,27344
101
101
  modal/_utils/git_utils.py,sha256=qtUU6JAttF55ZxYq51y55OR58B0tDPZsZWK5dJe6W5g,3182
102
102
  modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
103
103
  modal/_utils/grpc_utils.py,sha256=HBZdMcBHCk6uozILYTjGnR0mV8fg7WOdJldoyZ-ZhSg,10137
@@ -139,9 +139,9 @@ modal/cli/volume.py,sha256=KJ4WKQYjRGsTERkwHE1HcRia9rWzLIDDnlc89QmTLvE,10960
139
139
  modal/cli/programs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
140
140
  modal/cli/programs/run_jupyter.py,sha256=44Lpvqk2l3hH-uOkmAOzw60NEsfB5uaRDWDKVshvQhs,2682
141
141
  modal/cli/programs/vscode.py,sha256=KbTAaIXyQBVCDXxXjmBHmKpgXkUw0q4R4KkJvUjCYgk,3380
142
- modal/experimental/__init__.py,sha256=XhRr0QQds4fEAoILQFa0CQWUtQ76Gxioo88CupOxDvI,10847
143
- modal/experimental/flash.py,sha256=xqfJLpdqaa7mgW8OgAFBfZV95PKzuTzevh7SOSVesnA,19055
144
- modal/experimental/flash.pyi,sha256=1Nd31nYD8Eqi0BI63XuK6owXPGA0s9CgU_WOAzCQSQs,9957
142
+ modal/experimental/__init__.py,sha256=nuc7AL4r_Fs08DD5dciWFZhrV1nanwoClOfdTcudU0M,10869
143
+ modal/experimental/flash.py,sha256=jPvh-acBgpV-iwsGSNE_kdVdcRgGi7qkfBvrVXd8jhw,19782
144
+ modal/experimental/flash.pyi,sha256=A8_qJGtGoXEzKDdHbvhmCw7oqfneFEvJQK3ZdTOvUdU,10830
145
145
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
146
146
  modal/requirements/2023.12.312.txt,sha256=zWWUVgVQ92GXBKNYYr2-5vn9rlnXcmkqlwlX5u1eTYw,400
147
147
  modal/requirements/2023.12.txt,sha256=OjsbXFkCSdkzzryZP82Q73osr5wxQ6EUzmGcK7twfkA,502
@@ -151,7 +151,7 @@ modal/requirements/2025.06.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqr
151
151
  modal/requirements/PREVIEW.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqrs,312
152
152
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
153
153
  modal/requirements/base-images.json,sha256=JYSDAgHTl-WrV_TZW5icY-IJEnbe2eQ4CZ_KN6EOZKU,1304
154
- modal-1.1.1.dev6.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
154
+ modal-1.1.1.dev9.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
155
155
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
156
156
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
157
157
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -159,10 +159,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
159
159
  modal_docs/mdmd/mdmd.py,sha256=eW5MzrEl7mSclDo4Uv64sQ1-4IyLggldbgUJdBVLDdI,6449
160
160
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
161
161
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
162
- modal_proto/api.proto,sha256=N05BG_WD22aybgQW7u0S8cDkBpaNXaQ1sLfS_6wgKjo,100003
162
+ modal_proto/api.proto,sha256=Do5Ytjhz27H3vkJs7Y8T_GbbX2sM_KqFPq5hBXBQmes,100042
163
163
  modal_proto/api_grpc.py,sha256=F7Hu-1Yg7p5a2SbKw9yR4AgpyU0ntvgZTaVbIJMR0DE,122366
164
- modal_proto/api_pb2.py,sha256=7P2vtfAJahfBdX08tIB1vj2jV6xTewiKfMRkPBLXqkY,351422
165
- modal_proto/api_pb2.pyi,sha256=8PRc7tUO6uf7u1yrp0n_Tmjrgv3aZJLO7yDQ0WQmtYc,480096
164
+ modal_proto/api_pb2.py,sha256=hBn8XwlwHyAyrVYQBxz6nt8iAv5hy8aegT_aMmHAtJk,351460
165
+ modal_proto/api_pb2.pyi,sha256=AmmWyUYLudFvKml_rnAqXRcOaUCN_tQ2LHJUpF14e_k,480136
166
166
  modal_proto/api_pb2_grpc.py,sha256=pIFrNmCOgRRcIW8A1Ekja9Po6fHcsj54ExDZFzTpYe4,264347
167
167
  modal_proto/api_pb2_grpc.pyi,sha256=vtxrQ9xnQG6ZRXjp2uk43Mb7wV7F4qGYuVl5JUBc8jI,61968
168
168
  modal_proto/modal_api_grpc.py,sha256=Yl_fGbSIuX2FAEnURkYpKqshs7kbNqtz5HlTJEXkbhE,18487
@@ -174,10 +174,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
174
174
  modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
175
175
  modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
176
176
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
- modal_version/__init__.py,sha256=Pss9B3-pcb6UOAvatY6lxHmP4KK6ABEjGqwppssJcqU,120
177
+ modal_version/__init__.py,sha256=HoxFkFQ2_M9PtcJTVCwYnkjZyQjeRJ2tuy_f__ybbmY,120
178
178
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
179
- modal-1.1.1.dev6.dist-info/METADATA,sha256=KdpHgleqGBzAzBtAo8QqcoVOm6qGx98UPQk5U_vdDcw,2461
180
- modal-1.1.1.dev6.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
181
- modal-1.1.1.dev6.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
182
- modal-1.1.1.dev6.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
183
- modal-1.1.1.dev6.dist-info/RECORD,,
179
+ modal-1.1.1.dev9.dist-info/METADATA,sha256=qBXJU7T25hzZaod6XrxHzmxQEQrjj33FleKZJNT0wFY,2461
180
+ modal-1.1.1.dev9.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
181
+ modal-1.1.1.dev9.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
182
+ modal-1.1.1.dev9.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
183
+ modal-1.1.1.dev9.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -1816,7 +1816,8 @@ message FunctionPutInputsItem {
1816
1816
  int32 idx = 1;
1817
1817
  FunctionInput input = 2;
1818
1818
  bool r2_failed = 3;
1819
- uint64 r2_latency_ms = 4;
1819
+ reserved 4; // r2_latency_ms
1820
+ uint64 r2_throughput_bytes_s = 5;
1820
1821
  }
1821
1822
 
1822
1823
  message FunctionPutInputsRequest {