modal 0.74.58__py3-none-any.whl → 0.74.60__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/cli/_download.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2023
2
2
  import asyncio
3
+ import functools
3
4
  import os
4
5
  import shutil
5
6
  import sys
@@ -70,21 +71,30 @@ async def _volume_download(
70
71
  if is_pipe:
71
72
  if entry.type == FileEntryType.FILE:
72
73
  progress_task_id = progress_cb(name=entry.path, size=entry.size)
74
+ file_progress_cb = functools.partial(progress_cb, task_id=progress_task_id)
75
+
73
76
  async for chunk in volume.read_file(entry.path):
74
77
  sys.stdout.buffer.write(chunk)
75
- progress_cb(task_id=progress_task_id, advance=len(chunk))
76
- progress_cb(task_id=progress_task_id, complete=True)
78
+ file_progress_cb(advance=len(chunk))
79
+
80
+ file_progress_cb(complete=True)
77
81
  else:
78
82
  if entry.type == FileEntryType.FILE:
79
83
  progress_task_id = progress_cb(name=entry.path, size=entry.size)
80
84
  output_path.parent.mkdir(parents=True, exist_ok=True)
85
+ file_progress_cb = functools.partial(progress_cb, task_id=progress_task_id)
86
+
81
87
  with output_path.open("wb") as fp:
82
- b = 0
83
- async for chunk in volume.read_file(entry.path):
84
- b += fp.write(chunk)
85
- progress_cb(task_id=progress_task_id, advance=len(chunk))
88
+ if isinstance(volume, _Volume):
89
+ b = await volume.read_file_into_fileobj(entry.path, fp, file_progress_cb)
90
+ else:
91
+ b = 0
92
+ async for chunk in volume.read_file(entry.path):
93
+ b += fp.write(chunk)
94
+ file_progress_cb(advance=len(chunk))
95
+
86
96
  logger.debug(f"Wrote {b} bytes to {output_path}")
87
- progress_cb(task_id=progress_task_id, complete=True)
97
+ file_progress_cb(complete=True)
88
98
  elif entry.type == FileEntryType.DIRECTORY:
89
99
  output_path.mkdir(parents=True, exist_ok=True)
90
100
  finally:
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.58"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.60"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -86,7 +86,7 @@ class Client:
86
86
  _snapshotted: bool
87
87
 
88
88
  def __init__(
89
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.58"
89
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.60"
90
90
  ): ...
91
91
  def is_closed(self) -> bool: ...
92
92
  @property
modal/dict.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Copyright Modal Labs 2022
2
- from collections.abc import AsyncIterator
2
+ from collections.abc import AsyncIterator, Mapping
3
3
  from typing import Any, Optional
4
4
 
5
5
  from grpclib import GRPCError
@@ -244,9 +244,16 @@ class _Dict(_Object, type_prefix="di"):
244
244
  return value
245
245
 
246
246
  @live_method
247
- async def update(self, **kwargs) -> None:
247
+ async def update(self, other: Optional[Mapping] = None, /, **kwargs) -> None:
248
248
  """Update the dictionary with additional items."""
249
- serialized = _serialize_dict(kwargs)
249
+ # Support the Python dict.update API
250
+ # https://docs.python.org/3/library/stdtypes.html#dict.update
251
+ contents = {}
252
+ if other:
253
+ contents.update({k: other[k] for k in other.keys()})
254
+ if kwargs:
255
+ contents.update(kwargs)
256
+ serialized = _serialize_dict(contents)
250
257
  req = api_pb2.DictUpdateRequest(dict_id=self.object_id, updates=serialized)
251
258
  try:
252
259
  await retry_transient_errors(self._client.stub.DictUpdate, req)
modal/dict.pyi CHANGED
@@ -48,7 +48,7 @@ class _Dict(modal._object._Object):
48
48
  async def contains(self, key: typing.Any) -> bool: ...
49
49
  async def len(self) -> int: ...
50
50
  async def __getitem__(self, key: typing.Any) -> typing.Any: ...
51
- async def update(self, **kwargs) -> None: ...
51
+ async def update(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
52
52
  async def put(self, key: typing.Any, value: typing.Any) -> None: ...
53
53
  async def __setitem__(self, key: typing.Any, value: typing.Any) -> None: ...
54
54
  async def pop(self, key: typing.Any) -> typing.Any: ...
@@ -155,8 +155,8 @@ class Dict(modal.object.Object):
155
155
  __getitem__: ____getitem___spec[typing_extensions.Self]
156
156
 
157
157
  class __update_spec(typing_extensions.Protocol[SUPERSELF]):
158
- def __call__(self, /, **kwargs) -> None: ...
159
- async def aio(self, /, **kwargs) -> None: ...
158
+ def __call__(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
159
+ async def aio(self, other: typing.Optional[collections.abc.Mapping] = None, /, **kwargs) -> None: ...
160
160
 
161
161
  update: __update_spec[typing_extensions.Self]
162
162
 
modal/functions.pyi CHANGED
@@ -227,11 +227,11 @@ class Function(
227
227
 
228
228
  _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
229
229
 
230
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
230
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
231
231
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
232
232
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
233
233
 
234
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
234
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
235
235
 
236
236
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
237
237
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -246,12 +246,12 @@ class Function(
246
246
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
247
247
  ) -> modal._functions.OriginalReturnType: ...
248
248
 
249
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
249
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
250
250
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
251
251
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
252
252
 
253
253
  _experimental_spawn: ___experimental_spawn_spec[
254
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
254
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
255
255
  ]
256
256
 
257
257
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -260,11 +260,11 @@ class Function(
260
260
 
261
261
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
262
262
 
263
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
263
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
264
264
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
265
265
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
266
266
 
267
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
267
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
268
268
 
269
269
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
270
270
 
modal/volume.py CHANGED
@@ -463,6 +463,96 @@ class _Volume(_Object, type_prefix="vo"):
463
463
  yield value
464
464
 
465
465
 
466
+ @live_method
467
+ async def read_file_into_fileobj(
468
+ self,
469
+ path: str,
470
+ fileobj: typing.IO[bytes],
471
+ progress_cb: Optional[Callable[..., Any]] = None
472
+ ) -> int:
473
+ """mdmd:hidden
474
+ Read volume file into file-like IO object.
475
+ """
476
+ if progress_cb is None:
477
+ def progress_cb(*_, **__):
478
+ pass
479
+
480
+ if self._is_v1:
481
+ return await self._read_file_into_fileobj1(path, fileobj, progress_cb)
482
+ else:
483
+ return await self._read_file_into_fileobj2(path, fileobj, progress_cb)
484
+
485
+
486
+ async def _read_file_into_fileobj1(
487
+ self,
488
+ path: str,
489
+ fileobj: typing.IO[bytes],
490
+ progress_cb: Callable[..., Any]
491
+ ) -> int:
492
+ num_bytes_written = 0
493
+
494
+ async for chunk in self._read_file1(path):
495
+ num_chunk_bytes_written = 0
496
+
497
+ while num_chunk_bytes_written < len(chunk):
498
+ # TODO(dflemstr): this is a small write, but nonetheless might block the event loop for some time:
499
+ n = fileobj.write(chunk)
500
+ num_chunk_bytes_written += n
501
+ progress_cb(advance=n)
502
+
503
+ num_bytes_written += len(chunk)
504
+
505
+ return num_bytes_written
506
+
507
+
508
+ async def _read_file_into_fileobj2(
509
+ self,
510
+ path: str,
511
+ fileobj: typing.IO[bytes],
512
+ progress_cb: Callable[..., Any]
513
+ ) -> int:
514
+ req = api_pb2.VolumeGetFile2Request(volume_id=self.object_id, path=path)
515
+
516
+ try:
517
+ response = await retry_transient_errors(self._client.stub.VolumeGetFile2, req)
518
+ except GRPCError as exc:
519
+ raise FileNotFoundError(exc.message) if exc.status == Status.NOT_FOUND else exc
520
+
521
+ # TODO(dflemstr): Sane default limit? Make configurable?
522
+ download_semaphore = asyncio.Semaphore(multiprocessing.cpu_count())
523
+ write_lock = asyncio.Lock()
524
+ start_pos = fileobj.tell()
525
+
526
+ async def download_block(idx, url) -> int:
527
+ block_start_pos = start_pos + idx * BLOCK_SIZE
528
+ num_bytes_written = 0
529
+
530
+ async with download_semaphore, ClientSessionRegistry.get_session().get(url) as get_response:
531
+ async for chunk in get_response.content:
532
+ num_chunk_bytes_written = 0
533
+
534
+ while num_chunk_bytes_written < len(chunk):
535
+ async with write_lock:
536
+ fileobj.seek(block_start_pos + num_bytes_written + num_chunk_bytes_written)
537
+ # TODO(dflemstr): this is a small write, but nonetheless might block the event loop for some
538
+ # time:
539
+ n = fileobj.write(chunk)
540
+
541
+ num_chunk_bytes_written += n
542
+ progress_cb(advance=n)
543
+
544
+ num_bytes_written += len(chunk)
545
+
546
+ return num_bytes_written
547
+
548
+ coros = [download_block(idx, url) for idx, url in enumerate(response.get_urls)]
549
+
550
+ total_size = sum(await asyncio.gather(*coros))
551
+ fileobj.seek(start_pos + total_size)
552
+
553
+ return total_size
554
+
555
+
466
556
  @live_method
467
557
  async def remove_file(self, path: str, recursive: bool = False) -> None:
468
558
  """Remove a file or directory from a volume."""
modal/volume.pyi CHANGED
@@ -87,6 +87,18 @@ class _Volume(modal._object._Object):
87
87
  def read_file(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
88
88
  def _read_file1(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
89
89
  def _read_file2(self, path: str) -> collections.abc.AsyncIterator[bytes]: ...
90
+ async def read_file_into_fileobj(
91
+ self,
92
+ path: str,
93
+ fileobj: typing.IO[bytes],
94
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
95
+ ) -> int: ...
96
+ async def _read_file_into_fileobj1(
97
+ self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
98
+ ) -> int: ...
99
+ async def _read_file_into_fileobj2(
100
+ self, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
101
+ ) -> int: ...
90
102
  async def remove_file(self, path: str, recursive: bool = False) -> None: ...
91
103
  async def copy_files(self, src_paths: collections.abc.Sequence[str], dst_path: str) -> None: ...
92
104
  async def batch_upload(self, force: bool = False) -> _AbstractVolumeUploadContextManager: ...
@@ -234,6 +246,44 @@ class Volume(modal.object.Object):
234
246
 
235
247
  _read_file2: ___read_file2_spec[typing_extensions.Self]
236
248
 
249
+ class __read_file_into_fileobj_spec(typing_extensions.Protocol[SUPERSELF]):
250
+ def __call__(
251
+ self,
252
+ /,
253
+ path: str,
254
+ fileobj: typing.IO[bytes],
255
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
256
+ ) -> int: ...
257
+ async def aio(
258
+ self,
259
+ /,
260
+ path: str,
261
+ fileobj: typing.IO[bytes],
262
+ progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
263
+ ) -> int: ...
264
+
265
+ read_file_into_fileobj: __read_file_into_fileobj_spec[typing_extensions.Self]
266
+
267
+ class ___read_file_into_fileobj1_spec(typing_extensions.Protocol[SUPERSELF]):
268
+ def __call__(
269
+ self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
270
+ ) -> int: ...
271
+ async def aio(
272
+ self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
273
+ ) -> int: ...
274
+
275
+ _read_file_into_fileobj1: ___read_file_into_fileobj1_spec[typing_extensions.Self]
276
+
277
+ class ___read_file_into_fileobj2_spec(typing_extensions.Protocol[SUPERSELF]):
278
+ def __call__(
279
+ self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
280
+ ) -> int: ...
281
+ async def aio(
282
+ self, /, path: str, fileobj: typing.IO[bytes], progress_cb: collections.abc.Callable[..., typing.Any]
283
+ ) -> int: ...
284
+
285
+ _read_file_into_fileobj2: ___read_file_into_fileobj2_spec[typing_extensions.Self]
286
+
237
287
  class __remove_file_spec(typing_extensions.Protocol[SUPERSELF]):
238
288
  def __call__(self, /, path: str, recursive: bool = False) -> None: ...
239
289
  async def aio(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.58
3
+ Version: 0.74.60
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 @@ Requires-Dist: click>=8.1.0
22
22
  Requires-Dist: grpclib==0.4.7
23
23
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
24
24
  Requires-Dist: rich>=12.0.0
25
- Requires-Dist: synchronicity~=0.9.10
25
+ Requires-Dist: synchronicity~=0.9.12
26
26
  Requires-Dist: toml
27
27
  Requires-Dist: typer>=0.9
28
28
  Requires-Dist: types-certifi
@@ -22,7 +22,7 @@ modal/app.py,sha256=xojuGZv4LaQwZU5ntj7WbmMjeNuB9Gll8Mzqe2LyiEs,51323
22
22
  modal/app.pyi,sha256=zNwR1_2LpmQc9AhemuAeVdk90XNYDw9keOkXAwAATeA,28732
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=o-aQThHpvDHUzg_kUafyhWzACViUBhY2WLZ2EitnSHA,16787
25
- modal/client.pyi,sha256=s35e9PyIePi0LYczptJLzW634xvENbqmQwwyVmWGqqc,8385
25
+ modal/client.pyi,sha256=fpRp6NnR_PtaJ9_h2ZcqK1bApZtvdA2zarVQ-yCKxg0,8385
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
@@ -30,8 +30,8 @@ modal/cls.pyi,sha256=klQkXHXhl5gTjRo_XDV6PGecnJeFOudSMXid_kanumI,12090
30
30
  modal/config.py,sha256=OOMEJ5LHNFbHRW5wUpuhl0TH6EPW8D1XV9I3OJXrZrk,12668
31
31
  modal/container_process.py,sha256=vvyK3DVPUMsuqvkKdUiQ49cDLF9JawGrxpglLk5vfgI,6208
32
32
  modal/container_process.pyi,sha256=cR4aRHTbcVvmxGCc1_k2Ey8JllJIAQYq9PBKx0_1TuI,2916
33
- modal/dict.py,sha256=7NJVI05hisF9gTuJMYestH9X0LIOaE9PPqbCeYtwSRs,13365
34
- modal/dict.pyi,sha256=X4BhtAYry2TOW6fEj2t3nklHLJJpoh8fQw77lXQvfB4,7850
33
+ modal/dict.py,sha256=aQp4sDBUPlfAK85487Tjf8YUyy9C06SV9uynnPpcSf8,13687
34
+ modal/dict.pyi,sha256=ou2rospKvSNl0PffrsSABruTjspUlqwbkjJ22fzg-yE,8021
35
35
  modal/environments.py,sha256=t_TdUyORfIjlEAOS7I4My1qHi0cVsjPxwKloLmAAZMc,6935
36
36
  modal/environments.pyi,sha256=4HbI0kywveaUVI7HqDtZ4HphCTGXYi_wie2hz87up5A,3425
37
37
  modal/exception.py,sha256=4JyO-SACaLNDe2QC48EjsK8GMkZ8AgEurZ8j1YdRu8E,5263
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
39
39
  modal/file_io.pyi,sha256=oB7x-rKq7bmm8cA7Z7W9C9yeko7KK9m9i5GidFnkGK4,9569
40
40
  modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
41
41
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
42
- modal/functions.pyi,sha256=1GV493uk0Q7baSSPK4HtadK-1qSGMwM_OxlmgVvWP00,16742
42
+ modal/functions.pyi,sha256=aicTlhQAcQEiDHblXWR177nl21Qsau051hMUkrG-nOo,16742
43
43
  modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
44
44
  modal/image.py,sha256=ZCghS6l1O7pezXcdMHk6RoJpW3qWszfWGJTW38lNXaU,92797
45
45
  modal/image.pyi,sha256=MDq7tNJevElK78VxFYrZRe_00kz9gPdg98MN5c6fFoE,25644
@@ -78,8 +78,8 @@ modal/snapshot.pyi,sha256=dIEBdTPb7O3VwkQ8TMPjfyU17RLuS9i0DnACxxHy8X4,676
78
78
  modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
79
79
  modal/token_flow.py,sha256=0_4KabXKsuE4OXTJ1OuLOtA-b1sesShztMZkkRFK7tA,7605
80
80
  modal/token_flow.pyi,sha256=ILbRv6JsZq-jK8jcJM7eB74e0PsbzwBm7hyPcV9lBlQ,2121
81
- modal/volume.py,sha256=4njGgpmC7bVAxwDgn88XJNtaCk4mrz1qLcHBXXCXklg,41085
82
- modal/volume.pyi,sha256=-rcgdhGCaQV5v--hgkABcuYH1VbtElKPUC4AnxonNtk,18970
81
+ modal/volume.py,sha256=DFkdjipvjmWvedd1FL_CjIwyfT7QN-VxusJ9O3fVCw4,44206
82
+ modal/volume.pyi,sha256=9hPIMRBzGZycVL8uRfGpjSmNu_pCbkGAOyrnE86bU2Y,21113
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
@@ -115,7 +115,7 @@ modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,
115
115
  modal/_vendor/cloudpickle.py,sha256=avxOIgNKqL9KyPNuIOVQzBm0D1l9ipeB4RrcUMUGmeQ,55216
116
116
  modal/_vendor/tblib.py,sha256=g1O7QUDd3sDoLd8YPFltkXkih7r_fyZOjgmGuligv3s,9722
117
117
  modal/cli/__init__.py,sha256=6FRleWQxBDT19y7OayO4lBOzuL6Bs9r0rLINYYYbHwQ,769
118
- modal/cli/_download.py,sha256=t6BXZwjTd9MgznDvbsV8rp0FZWggdzC-lUAGZU4xx1g,3984
118
+ modal/cli/_download.py,sha256=tV8JFkncTtQKh85bSguQg6AW5aRRlynf-rvyN7ruigc,4337
119
119
  modal/cli/_traceback.py,sha256=4ywtmFcmPnY3tqb4-3fA061N2tRiM01xs8fSagtkwhE,7293
120
120
  modal/cli/app.py,sha256=87LWg3bTQQIHFOqs8iiJYD_X03omXBZ6lFYR0rMJV-I,8433
121
121
  modal/cli/cluster.py,sha256=EBDhkzfOtPSbwknYdYPBGYvRAwl4Gm7OJkD6_zxrcus,3106
@@ -146,7 +146,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
146
146
  modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
147
147
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
148
148
  modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
149
- modal-0.74.58.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
149
+ modal-0.74.60.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
150
150
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
151
151
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
152
152
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -171,9 +171,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
171
171
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
172
  modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
173
173
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
174
- modal_version/_version_generated.py,sha256=ty9vlBouwII9j47zM8FqtqP8f3FZEMvofJhfF5qojIY,149
175
- modal-0.74.58.dist-info/METADATA,sha256=8v0JtnopQExL5NG0TFZwr7Q8NY9tYSsGUOH_XTYa-qU,2451
176
- modal-0.74.58.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
- modal-0.74.58.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
- modal-0.74.58.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
- modal-0.74.58.dist-info/RECORD,,
174
+ modal_version/_version_generated.py,sha256=TSSCaOkZv-q4iC_Fm5y5PLPnUcJbRWTS8f6W8MrU2VE,149
175
+ modal-0.74.60.dist-info/METADATA,sha256=qXT5Yds-8kmsxuFsaCcZkhCiJFEzt-nMGl7l-iu6Xqk,2451
176
+ modal-0.74.60.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
177
+ modal-0.74.60.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-0.74.60.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-0.74.60.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 58 # git: ea8f826
4
+ build_number = 60 # git: 7229b14