1- import contextlib
21import dataclasses
32import functools
43import threading
@@ -93,10 +92,12 @@ def __init__(self, chunks_gen: Generator[_OpenedChunk, None, None]):
9392 self .idx = 0
9493 self .chunks_gen = chunks_gen
9594 self .current : Optional [_OpenedChunk ] = None
95+ self .current_read_len = 0
96+ self .current_verified = False
9697 self .reach_end = False
9798
9899 def read (self , length : int = - 1 ) -> bytes :
99- if self .reach_end :
100+ if self .reach_end or length == 0 :
100101 return b''
101102 if self .current is None :
102103 if not self .__switch_to_next ():
@@ -116,16 +117,33 @@ def read(self, length: int = -1) -> bytes:
116117 logger .get ().error (f'Failed to read { to_read } bytes from chunk { self .current .offset_chunk } : { e } ' )
117118 raise
118119 total_read += len (buf )
120+ self .current_read_len += len (buf )
119121 results .append (buf )
120- if to_read == - 1 or len ( buf ) < to_read :
121- self .current . verify_callback ()
122+ if self . current_read_len >= self . current . offset_chunk . chunk . raw_size :
123+ self .__verify_current ()
122124 if not self .__switch_to_next ():
123125 break
126+ elif to_read == - 1 or len (buf ) < to_read :
127+ raise EOFError ('chunk {} exhausted after reading {} bytes, expected {}' .format (
128+ self .current .offset_chunk , self .current_read_len , self .current .offset_chunk .chunk .raw_size ,
129+ ))
124130 return b'' .join (results )
125131
132+ def close (self ):
133+ self .chunks_gen .close ()
134+ self .current = None
135+ self .reach_end = True
136+
137+ def __verify_current (self ):
138+ if self .current is not None and not self .current_verified :
139+ self .current .verify_callback ()
140+ self .current_verified = True
141+
126142 def __switch_to_next (self ) -> bool :
127143 try :
128144 self .current = next (self .chunks_gen )
145+ self .current_read_len = 0
146+ self .current_verified = False
129147 return True
130148 except StopIteration :
131149 self .reach_end = True
@@ -217,8 +235,6 @@ def __export_as_reader_direct(self, reader_csm: Callable[[SupportsReadBytes], An
217235
218236 def __export_as_reader_chunked (self , reader_csm : Callable [[SupportsReadBytes ], Any ]):
219237 blob_chunks = self .blob_chunks_getter .get (self .blob .id )
220- exit_flag = False
221- gen_yielded = False
222238
223239 def open_chunk_gen () -> Generator [_OpenedChunk , None , None ]:
224240 hash_method = chunk_utils .get_hash_method ()
@@ -231,8 +247,6 @@ def open_chunk_gen() -> Generator[_OpenedChunk, None, None]:
231247 self .logger .error (f'Failed to peek-read chunk { oc .chunk .hash } in pack entry { oc .chunk .pack_entry .pack_id } @{ oc .chunk .pack_entry .offset } for { oc } : { e } ' )
232248 raise
233249
234- nonlocal gen_yielded
235- gen_yielded = True
236250 if self .verify_blob :
237251 def verify_callback (ck : ChunkInfo , br : BypassReader ):
238252 self .__verify_exported_chunk (ck , br .get_read_len (), br .get_hash ())
@@ -242,20 +256,13 @@ def verify_callback(ck: ChunkInfo, br: BypassReader):
242256 else :
243257 yield _OpenedChunk (oc , peek_reader , lambda : None )
244258
245- nonlocal exit_flag
246- if exit_flag :
247- break
248-
249259 with PackFileObjectPool () as pack_file_obj_pool :
250260 chunk_gen = open_chunk_gen ()
261+ reader = _CombinedChunksReader (chunk_gen )
251262 try :
252- reader_csm (_CombinedChunksReader ( chunk_gen ) )
263+ reader_csm (reader )
253264 finally :
254- # ensure possible opened ChunkIO is closed
255- exit_flag = True
256- if gen_yielded :
257- with contextlib .suppress (StopIteration ):
258- next (chunk_gen )
265+ reader .close ()
259266
260267 def __verify_exported_blob (self , written_size : int , written_hash : str ):
261268 self .__verify_exported_data (lambda : 'blob' , self .blob .raw_size , self .blob .hash , written_size , written_hash )
0 commit comments