From: Christian Herdtweck Date: Thu, 24 May 2018 10:29:49 +0000 (+0200) Subject: Fix error wrapping stdout when redirecting X-Git-Tag: v1.3~14^2 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=667e02f48d1f633f3213143d76bbf593198e8102;p=pyi2ncommon Fix error wrapping stdout when redirecting In some cases (e.g. redirecting stdout to shell), the automatic determination in ZipFile constructor whether its file argument can seek() and tell() fails. stdout seems to support both but returns bogus values for tell(), resulting in negative sizes when writing the archive's end record. Provide a fix with additional arg force_wrap Also ensure that CRCs are always correct as suggested in python docs and stackoverflow --- diff --git a/src/zip_stream.py b/src/zip_stream.py index 0bda371..d4adf4a 100644 --- a/src/zip_stream.py +++ b/src/zip_stream.py @@ -78,9 +78,55 @@ def _get_compressor(compress_type): return None +class BytesTellWrapper: + """ + Wrapper around a write-only stream that supports tell but not seek + + copy of zipfile._Tellable + """ + def __init__(self, fp): + self.fp = fp + self.offset = 0 + + def write(self, data): + n = self.fp.write(data) + self.offset += n + return n + + def tell(self): + return self.offset + + def flush(self): + self.fp.flush() + + def close(self): + self.fp.close() + + class ZipStream(ZipFile): """Subclass of ZipFile that supports non-seekable input and output""" + def __init__(self, file, *args, **kwargs): + """ + Create ZipStream instance which is like a ZipFile plus functions + create_zipinfo and write_stream. + + ZipFile determines whether output stream can seek() and tell(). + Unfortunately some streams (like sys.stdout.buffer when redirecting + output) seem to support these methods but only return 0 from tell. + + :param bool force_wrap: force wrapping of output stream with + BytesTellWrapper. + """ + if 'force_wrap' in kwargs and kwargs['force_wrap']: + if isstr(file): + raise ValueError('force_wrap only makes sense for streams') + del kwargs['force_wrap'] + super(ZipStream, self).__init__(BytesTellWrapper(file), *args, + **kwargs) + else: + super(ZipStream, self).__init__(file, *args, **kwargs) + def create_zipinfo(self, filename, arcname=None): """ Create ZipInfo for given file @@ -168,7 +214,7 @@ class ZipStream(ZipFile): if not buf: break file_size = file_size + len(buf) - CRC = crc32(buf, CRC) + CRC = crc32(buf, CRC) & 0xffffffff if cmpr: buf = cmpr.compress(buf) compress_size = compress_size + len(buf)