mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Issue #14099: Restored support of writing ZIP files to tellable but
non-seekable streams.
This commit is contained in:
		
							parent
							
								
									f15e524026
								
							
						
					
					
						commit
						a14f7d239f
					
				
					 3 changed files with 44 additions and 3 deletions
				
			
		| 
						 | 
					@ -1668,6 +1668,34 @@ class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
 | 
				
			||||||
    compression = zipfile.ZIP_LZMA
 | 
					    compression = zipfile.ZIP_LZMA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Privide the tell() method but not seek()
 | 
				
			||||||
 | 
					class Tellable:
 | 
				
			||||||
 | 
					    def __init__(self, fp):
 | 
				
			||||||
 | 
					        self.fp = fp
 | 
				
			||||||
 | 
					        self.offset = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, data):
 | 
				
			||||||
 | 
					        self.offset += self.fp.write(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tell(self):
 | 
				
			||||||
 | 
					        return self.offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def flush(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UnseekableTests(unittest.TestCase):
 | 
				
			||||||
 | 
					    def test_writestr_tellable(self):
 | 
				
			||||||
 | 
					        f = io.BytesIO()
 | 
				
			||||||
 | 
					        with zipfile.ZipFile(Tellable(f), 'w', zipfile.ZIP_STORED) as zipfp:
 | 
				
			||||||
 | 
					            zipfp.writestr('ones', b'111')
 | 
				
			||||||
 | 
					            zipfp.writestr('twos', b'222')
 | 
				
			||||||
 | 
					        with zipfile.ZipFile(f, mode='r') as zipf:
 | 
				
			||||||
 | 
					            with zipf.open('ones') as zopen:
 | 
				
			||||||
 | 
					                self.assertEqual(zopen.read(), b'111')
 | 
				
			||||||
 | 
					            with zipf.open('twos') as zopen:
 | 
				
			||||||
 | 
					                self.assertEqual(zopen.read(), b'222')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@requires_zlib
 | 
					@requires_zlib
 | 
				
			||||||
class TestsWithMultipleOpens(unittest.TestCase):
 | 
					class TestsWithMultipleOpens(unittest.TestCase):
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1504,7 +1504,14 @@ def writestr(self, zinfo_or_arcname, data, compress_type=None):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        zinfo.file_size = len(data)            # Uncompressed size
 | 
					        zinfo.file_size = len(data)            # Uncompressed size
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self.fp.seek(self.start_dir, 0)
 | 
					            try:
 | 
				
			||||||
 | 
					                self.fp.seek(self.start_dir)
 | 
				
			||||||
 | 
					            except (AttributeError, io.UnsupportedOperation):
 | 
				
			||||||
 | 
					                # Some file-like objects can provide tell() but not seek()
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					            zinfo.header_offset = self.fp.tell()    # Start of header data
 | 
				
			||||||
 | 
					            if compress_type is not None:
 | 
				
			||||||
 | 
					                zinfo.compress_type = compress_type
 | 
				
			||||||
            zinfo.header_offset = self.fp.tell()    # Start of header data
 | 
					            zinfo.header_offset = self.fp.tell()    # Start of header data
 | 
				
			||||||
            if compress_type is not None:
 | 
					            if compress_type is not None:
 | 
				
			||||||
                zinfo.compress_type = compress_type
 | 
					                zinfo.compress_type = compress_type
 | 
				
			||||||
| 
						 | 
					@ -1550,7 +1557,11 @@ def close(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if self.mode in ("w", "a") and self._didModify: # write ending records
 | 
					            if self.mode in ("w", "a") and self._didModify: # write ending records
 | 
				
			||||||
                with self._lock:
 | 
					                with self._lock:
 | 
				
			||||||
                    self.fp.seek(self.start_dir, 0)
 | 
					                    try:
 | 
				
			||||||
 | 
					                        self.fp.seek(self.start_dir)
 | 
				
			||||||
 | 
					                    except (AttributeError, io.UnsupportedOperation):
 | 
				
			||||||
 | 
					                        # Some file-like objects can provide tell() but not seek()
 | 
				
			||||||
 | 
					                        pass
 | 
				
			||||||
                    self._write_end_record()
 | 
					                    self._write_end_record()
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            fp = self.fp
 | 
					            fp = self.fp
 | 
				
			||||||
| 
						 | 
					@ -1558,7 +1569,6 @@ def close(self):
 | 
				
			||||||
            self._fpclose(fp)
 | 
					            self._fpclose(fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _write_end_record(self):
 | 
					    def _write_end_record(self):
 | 
				
			||||||
        self.fp.seek(self.start_dir, 0)
 | 
					 | 
				
			||||||
        for zinfo in self.filelist:         # write central directory
 | 
					        for zinfo in self.filelist:         # write central directory
 | 
				
			||||||
            dt = zinfo.date_time
 | 
					            dt = zinfo.date_time
 | 
				
			||||||
            dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
 | 
					            dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -218,6 +218,9 @@ Core and Builtins
 | 
				
			||||||
Library
 | 
					Library
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Issue #14099: Restored support of writing ZIP files to tellable but
 | 
				
			||||||
 | 
					  non-seekable streams.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is
 | 
					- Issue #14099: Writing to ZipFile and reading multiple ZipExtFiles is
 | 
				
			||||||
  threadsafe now.
 | 
					  threadsafe now.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue