modal 0.74.43__py3-none-any.whl → 0.74.44__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.
@@ -1,7 +1,6 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  import dataclasses
4
- import functools
5
4
  import hashlib
6
5
  import os
7
6
  import platform
@@ -399,6 +398,7 @@ class FileUploadSpec2:
399
398
  async def from_path(
400
399
  filename: Path,
401
400
  mount_filename: PurePosixPath,
401
+ hash_semaphore: asyncio.Semaphore,
402
402
  mode: Optional[int] = None,
403
403
  ) -> "FileUploadSpec2":
404
404
  # Python appears to give files 0o666 bits on Windows (equal for user, group, and global),
@@ -413,6 +413,7 @@ class FileUploadSpec2:
413
413
  filename,
414
414
  mount_filename,
415
415
  mode,
416
+ hash_semaphore,
416
417
  )
417
418
 
418
419
 
@@ -420,7 +421,8 @@ class FileUploadSpec2:
420
421
  async def from_fileobj(
421
422
  source_fp: Union[BinaryIO, BytesIO],
422
423
  mount_filename: PurePosixPath,
423
- mode: int
424
+ hash_semaphore: asyncio.Semaphore,
425
+ mode: int,
424
426
  ) -> "FileUploadSpec2":
425
427
  try:
426
428
  fileno = source_fp.fileno()
@@ -442,6 +444,7 @@ class FileUploadSpec2:
442
444
  str(source),
443
445
  mount_filename,
444
446
  mode,
447
+ hash_semaphore,
445
448
  )
446
449
 
447
450
 
@@ -451,13 +454,14 @@ class FileUploadSpec2:
451
454
  source_description: Union[str, Path],
452
455
  mount_filename: PurePosixPath,
453
456
  mode: int,
457
+ hash_semaphore: asyncio.Semaphore,
454
458
  ) -> "FileUploadSpec2":
455
459
  # Current position is ignored - we always upload from position 0
456
460
  with source() as source_fp:
457
461
  source_fp.seek(0, os.SEEK_END)
458
462
  size = source_fp.tell()
459
463
 
460
- blocks_sha256 = await hash_blocks_sha256(source, size)
464
+ blocks_sha256 = await hash_blocks_sha256(source, size, hash_semaphore)
461
465
 
462
466
  return FileUploadSpec2(
463
467
  source=source,
@@ -472,13 +476,14 @@ class FileUploadSpec2:
472
476
  async def hash_blocks_sha256(
473
477
  source: _FileUploadSource2,
474
478
  size: int,
479
+ hash_semaphore: asyncio.Semaphore,
475
480
  ) -> list[bytes]:
476
481
  def ceildiv(a: int, b: int) -> int:
477
482
  return -(a // -b)
478
483
 
479
484
  num_blocks = ceildiv(size, BLOCK_SIZE)
480
485
 
481
- def hash_block_sha256(block_idx: int) -> bytes:
486
+ def blocking_hash_block_sha256(block_idx: int) -> bytes:
482
487
  sha256_hash = hashlib.sha256()
483
488
  block_start = block_idx * BLOCK_SIZE
484
489
 
@@ -497,7 +502,11 @@ async def hash_blocks_sha256(
497
502
 
498
503
  return sha256_hash.digest()
499
504
 
500
- tasks = (asyncio.to_thread(functools.partial(hash_block_sha256, idx)) for idx in range(num_blocks))
505
+ async def hash_block_sha256(block_idx: int) -> bytes:
506
+ async with hash_semaphore:
507
+ return await asyncio.to_thread(blocking_hash_block_sha256, block_idx)
508
+
509
+ tasks = (hash_block_sha256(idx) for idx in range(num_blocks))
501
510
  return await asyncio.gather(*tasks)
502
511
 
503
512
 
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.43"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.44"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -85,7 +85,7 @@ class Client:
85
85
  _snapshotted: bool
86
86
 
87
87
  def __init__(
88
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.43"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.44"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
modal/volume.py CHANGED
@@ -3,6 +3,7 @@ import asyncio
3
3
  import concurrent.futures
4
4
  import enum
5
5
  import functools
6
+ import multiprocessing
6
7
  import os
7
8
  import platform
8
9
  import re
@@ -755,19 +756,27 @@ class _VolumeUploadContextManager(_AbstractVolumeUploadContextManager):
755
756
 
756
757
  VolumeUploadContextManager = synchronize_api(_VolumeUploadContextManager)
757
758
 
758
- _FileUploader2 = Callable[[], Awaitable[FileUploadSpec2]]
759
+ _FileUploader2 = Callable[[asyncio.Semaphore], Awaitable[FileUploadSpec2]]
759
760
 
760
761
  class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
761
762
  """Context manager for batch-uploading files to a Volume version 2."""
762
763
 
763
764
  _volume_id: str
764
765
  _client: _Client
765
- _force: bool
766
766
  _progress_cb: Callable[..., Any]
767
+ _force: bool
768
+ _hash_concurrency: int
769
+ _put_concurrency: int
767
770
  _uploader_generators: list[Generator[_FileUploader2]]
768
771
 
769
772
  def __init__(
770
- self, volume_id: str, client: _Client, progress_cb: Optional[Callable[..., Any]] = None, force: bool = False
773
+ self,
774
+ volume_id: str,
775
+ client: _Client,
776
+ progress_cb: Optional[Callable[..., Any]] = None,
777
+ force: bool = False,
778
+ hash_concurrency: int = multiprocessing.cpu_count(),
779
+ put_concurrency: int = multiprocessing.cpu_count(),
771
780
  ):
772
781
  """mdmd:hidden"""
773
782
  self._volume_id = volume_id
@@ -775,6 +784,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
775
784
  self._uploader_generators = []
776
785
  self._progress_cb = progress_cb or (lambda *_, **__: None)
777
786
  self._force = force
787
+ self._hash_concurrency = hash_concurrency
788
+ self._put_concurrency = put_concurrency
778
789
 
779
790
  async def __aenter__(self):
780
791
  return self
@@ -787,7 +798,9 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
787
798
  yield from gen
788
799
 
789
800
  async def gen_file_upload_specs() -> list[FileUploadSpec2]:
790
- uploads = [asyncio.create_task(fut()) for fut in gen_upload_providers()]
801
+ hash_semaphore = asyncio.Semaphore(self._hash_concurrency)
802
+
803
+ uploads = [asyncio.create_task(fut(hash_semaphore)) for fut in gen_upload_providers()]
791
804
  logger.debug(f"Computing checksums for {len(uploads)} files")
792
805
 
793
806
  file_specs = []
@@ -817,9 +830,19 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
817
830
 
818
831
  def gen():
819
832
  if isinstance(local_file, str) or isinstance(local_file, Path):
820
- yield lambda: FileUploadSpec2.from_path(local_file, PurePosixPath(remote_path), mode)
833
+ yield lambda hash_semaphore: FileUploadSpec2.from_path(
834
+ local_file,
835
+ PurePosixPath(remote_path),
836
+ hash_semaphore,
837
+ mode
838
+ )
821
839
  else:
822
- yield lambda: FileUploadSpec2.from_fileobj(local_file, PurePosixPath(remote_path), mode or 0o644)
840
+ yield lambda hash_semaphore: FileUploadSpec2.from_fileobj(
841
+ local_file,
842
+ PurePosixPath(remote_path),
843
+ hash_semaphore,
844
+ mode or 0o644
845
+ )
823
846
 
824
847
  self._uploader_generators.append(gen())
825
848
 
@@ -840,7 +863,7 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
840
863
 
841
864
  def create_spec(subpath):
842
865
  relpath_str = subpath.relative_to(local_path)
843
- return lambda: FileUploadSpec2.from_path(subpath, remote_path / relpath_str)
866
+ return lambda hash_semaphore: FileUploadSpec2.from_path(subpath, remote_path / relpath_str, hash_semaphore)
844
867
 
845
868
  def gen():
846
869
  glob = local_path.rglob("*") if recursive else local_path.glob("*")
@@ -855,6 +878,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
855
878
  put_responses = {}
856
879
  # num_blocks_total = sum(len(file_spec.blocks_sha256) for file_spec in file_specs)
857
880
 
881
+ logger.debug(f"Ensuring {len(file_specs)} files are uploaded...")
882
+
858
883
  # We should only need two iterations: Once to possibly get some missing_blocks; the second time we should have
859
884
  # all blocks uploaded
860
885
  for _ in range(2):
@@ -888,7 +913,13 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
888
913
  if not response.missing_blocks:
889
914
  break
890
915
 
891
- await _put_missing_blocks(file_specs, response.missing_blocks, put_responses, self._progress_cb)
916
+ await _put_missing_blocks(
917
+ file_specs,
918
+ response.missing_blocks,
919
+ put_responses,
920
+ self._put_concurrency,
921
+ self._progress_cb
922
+ )
892
923
  else:
893
924
  raise RuntimeError("Did not succeed at uploading all files despite supplying all missing blocks")
894
925
 
@@ -904,8 +935,19 @@ async def _put_missing_blocks(
904
935
  # by the nested class (?)
905
936
  missing_blocks: list,
906
937
  put_responses: dict[bytes, bytes],
938
+ put_concurrency: int,
907
939
  progress_cb: Callable[..., Any]
908
940
  ):
941
+ @dataclass
942
+ class FileProgress:
943
+ task_id: str
944
+ pending_blocks: set[int]
945
+
946
+ put_semaphore = asyncio.Semaphore(put_concurrency)
947
+ file_progresses: dict[str, FileProgress] = dict()
948
+
949
+ logger.debug(f"Uploading {len(missing_blocks)} missing blocks...")
950
+
909
951
  async def put_missing_block(
910
952
  # TODO(dflemstr): Type is `api_pb2.VolumePutFiles2Response.MissingBlock` but synchronicity gets confused
911
953
  # by the nested class (?)
@@ -923,23 +965,36 @@ async def _put_missing_blocks(
923
965
  block_start = missing_block.block_index * BLOCK_SIZE
924
966
  block_length = min(BLOCK_SIZE, file_spec.size - block_start)
925
967
 
926
- progress_name = f"{file_spec.path} block {missing_block.block_index + 1} / {len(file_spec.blocks_sha256)}"
927
- progress_task_id = progress_cb(name=progress_name, size=file_spec.size)
968
+ if file_spec.path not in file_progresses:
969
+ file_task_id = progress_cb(name=file_spec.path, size=file_spec.size)
970
+ file_progresses[file_spec.path] = FileProgress(task_id=file_task_id, pending_blocks=set())
971
+
972
+ file_progress = file_progresses[file_spec.path]
973
+ file_progress.pending_blocks.add(missing_block.block_index)
974
+ task_progress_cb = functools.partial(progress_cb, task_id=file_progress.task_id)
975
+
976
+ async with put_semaphore:
977
+ with file_spec.source() as source_fp:
978
+ payload = BytesIOSegmentPayload(
979
+ source_fp,
980
+ block_start,
981
+ block_length,
982
+ # limit chunk size somewhat to not keep event loop busy for too long
983
+ chunk_size=256*1024,
984
+ progress_report_cb=task_progress_cb
985
+ )
986
+
987
+ async with ClientSessionRegistry.get_session().put(
988
+ missing_block.put_url,
989
+ data=payload,
990
+ ) as response:
991
+ response.raise_for_status()
992
+ resp_data = await response.content.read()
928
993
 
929
- with file_spec.source() as source_fp:
930
- payload = BytesIOSegmentPayload(
931
- source_fp,
932
- block_start,
933
- block_length,
934
- progress_report_cb=functools.partial(progress_cb, progress_task_id)
935
- )
994
+ file_progress.pending_blocks.remove(missing_block.block_index)
936
995
 
937
- async with ClientSessionRegistry.get_session().put(
938
- missing_block.put_url,
939
- data=payload,
940
- ) as response:
941
- response.raise_for_status()
942
- resp_data = await response.content.read()
996
+ if len(file_progress.pending_blocks) == 0:
997
+ task_progress_cb(complete=True)
943
998
 
944
999
  return block_sha256, resp_data
945
1000
 
modal/volume.pyi CHANGED
@@ -406,11 +406,15 @@ class VolumeUploadContextManager(AbstractVolumeUploadContextManager):
406
406
  class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
407
407
  _volume_id: str
408
408
  _client: modal.client._Client
409
- _force: bool
410
409
  _progress_cb: collections.abc.Callable[..., typing.Any]
410
+ _force: bool
411
+ _hash_concurrency: int
412
+ _put_concurrency: int
411
413
  _uploader_generators: list[
412
414
  collections.abc.Generator[
413
- collections.abc.Callable[[], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]]
415
+ collections.abc.Callable[
416
+ [asyncio.locks.Semaphore], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]
417
+ ]
414
418
  ]
415
419
  ]
416
420
 
@@ -420,6 +424,8 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
420
424
  client: modal.client._Client,
421
425
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
422
426
  force: bool = False,
427
+ hash_concurrency: int = 4,
428
+ put_concurrency: int = 4,
423
429
  ): ...
424
430
  async def __aenter__(self): ...
425
431
  async def __aexit__(self, exc_type, exc_val, exc_tb): ...
@@ -440,11 +446,15 @@ class _VolumeUploadContextManager2(_AbstractVolumeUploadContextManager):
440
446
  class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
441
447
  _volume_id: str
442
448
  _client: modal.client.Client
443
- _force: bool
444
449
  _progress_cb: collections.abc.Callable[..., typing.Any]
450
+ _force: bool
451
+ _hash_concurrency: int
452
+ _put_concurrency: int
445
453
  _uploader_generators: list[
446
454
  collections.abc.Generator[
447
- collections.abc.Callable[[], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]]
455
+ collections.abc.Callable[
456
+ [asyncio.locks.Semaphore], typing.Awaitable[modal._utils.blob_utils.FileUploadSpec2]
457
+ ]
448
458
  ]
449
459
  ]
450
460
 
@@ -454,6 +464,8 @@ class VolumeUploadContextManager2(AbstractVolumeUploadContextManager):
454
464
  client: modal.client.Client,
455
465
  progress_cb: typing.Optional[collections.abc.Callable[..., typing.Any]] = None,
456
466
  force: bool = False,
467
+ hash_concurrency: int = 4,
468
+ put_concurrency: int = 4,
457
469
  ): ...
458
470
  def __enter__(self): ...
459
471
  async def __aenter__(self): ...
@@ -482,6 +494,7 @@ async def _put_missing_blocks(
482
494
  file_specs: list[modal._utils.blob_utils.FileUploadSpec2],
483
495
  missing_blocks: list,
484
496
  put_responses: dict[bytes, bytes],
497
+ put_concurrency: int,
485
498
  progress_cb: collections.abc.Callable[..., typing.Any],
486
499
  ): ...
487
500
  def _open_files_error_annotation(mount_path: str) -> typing.Optional[str]: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.43
3
+ Version: 0.74.44
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=r-9vVU1lrR1CWtJEo60fuaianvxY_oOXZyv1Qx1DEkI,51231
22
22
  modal/app.pyi,sha256=0QNtnUpAFbOPcbwCt119ge7OmoBqMFw5SajLgdE5eOw,28600
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
25
- modal/client.pyi,sha256=rPfz_p9AUkyobZGg5_yGRjA1RFzdHk97BrdExJ_57KE,7593
25
+ modal/client.pyi,sha256=cwIyiWSCJzcoX33hLR2JME97LUoPaLRaNaLHvljlR1M,7593
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=pfEKqzNldQbtoM45CApRhSd8jJ7hD6o-83SLX8DTXXk,33549
@@ -78,8 +78,8 @@ modal/snapshot.pyi,sha256=Ypd4NKsjOTnnnqXyTGGLKq5lkocRrUURYjY5Pi67_qA,670
78
78
  modal/stream_type.py,sha256=A6320qoAAWhEfwOCZfGtymQTu5AfLfJXXgARqooTPvY,417
79
79
  modal/token_flow.py,sha256=ZYCa-uVP6jrJmUKA6wAG4lngqNem8Mgm2HXz1DR19BM,7355
80
80
  modal/token_flow.pyi,sha256=0XV3d-9CGQL3qjPdw3RgwIFVqqxo8Z-u044_mkgAM3o,2064
81
- modal/volume.py,sha256=4iCJURTNb-10T4HXAEXOCsRys1VLapytjLRrE_tErtM,40156
82
- modal/volume.pyi,sha256=Mq6w6lI2RTrilzQna1z2DwHznSC-Jte2-sCh1R6mp9s,17980
81
+ modal/volume.py,sha256=197mYPFbjJfQWpLEvE3vinTwNpmbh0CC_B-NkOHzKo8,41979
82
+ modal/volume.pyi,sha256=_gE9ED_aO-o3_f9MDCkJc6OM8qU1w5ULJ4aKoJkhlgk,18356
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
@@ -92,7 +92,7 @@ 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=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
95
- modal/_utils/blob_utils.py,sha256=WhWaSFLcffVDchMyfKys-j0EFVbm9l7rebzeoa7Z2jM,18214
95
+ modal/_utils/blob_utils.py,sha256=IexC2Jbtqp_Tkmy62ayfgzTYte0UPCNufB_v-DO21g8,18585
96
96
  modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
97
97
  modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
98
98
  modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
@@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
145
145
  modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
146
146
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
147
147
  modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
148
- modal-0.74.43.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
148
+ modal-0.74.44.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
149
149
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
150
150
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
151
151
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
170
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
172
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
173
- modal_version/_version_generated.py,sha256=2hJCmnOxsmCyxzg5ap4nElf3c9LDoeofF0nLLlnsOGw,149
174
- modal-0.74.43.dist-info/METADATA,sha256=lWjSSt9FvZhtmQQVZkKRNBjRGF_BDnGSJGdQgL2neps,2451
175
- modal-0.74.43.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
- modal-0.74.43.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
- modal-0.74.43.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
- modal-0.74.43.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=A2au9WWp8OSJGELFBGHBaU8Q48nDv5wD_3RhTsz8opQ,149
174
+ modal-0.74.44.dist-info/METADATA,sha256=07r7G26kX3keUDnSBhVjtaCEBrktd04tpkxSiC3A7Z0,2451
175
+ modal-0.74.44.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
+ modal-0.74.44.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
+ modal-0.74.44.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
+ modal-0.74.44.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 = 43 # git: a61b2c8
4
+ build_number = 44 # git: 58b258f