Index: Lib/gzip.py =================================================================== --- Lib/gzip.py (revision 76872) +++ Lib/gzip.py (working copy) @@ -97,8 +97,12 @@ self.mode = READ # Set flag indicating start of a new member self._new_member = True + # Buffer data read from gzip file. extrastart is offset in + # stream where buffer starts. extrasize is number of + # bytes remaining in buffer from current stream position. self.extrabuf = "" self.extrasize = 0 + self.extrastart = 0 self.name = filename # Starts small, scales exponentially self.min_readsize = 100 @@ -204,12 +208,28 @@ if self.fileobj is None: raise ValueError, "write() on closed GzipFile object" + + # Convert data type if called by io.BufferedWriter. + if type(data) is memoryview: + data = data.tobytes() + if len(data) > 0: self.size = self.size + len(data) self.crc = zlib.crc32(data, self.crc) & 0xffffffffL self.fileobj.write( self.compress.compress(data) ) self.offset += len(data) + return len(data) + + def readinto(self, b): + """Read up to len(b) bytes into b. + + Returns number of bytes read (0 for EOF). + """ + chunk = self.read(len(b)) + b[: len(chunk)] = chunk + return len(chunk) + def read(self, size=-1): if self.mode != READ: import errno @@ -235,15 +255,14 @@ if size > self.extrasize: size = self.extrasize - chunk = self.extrabuf[:size] - self.extrabuf = self.extrabuf[size:] + offset = self.offset - self.extrastart + chunk = self.extrabuf[offset: offset + size] self.extrasize = self.extrasize - size self.offset += size return chunk def _unread(self, buf): - self.extrabuf = buf + self.extrabuf self.extrasize = len(buf) + self.extrasize self.offset -= len(buf) @@ -299,8 +318,10 @@ def _add_read_data(self, data): self.crc = zlib.crc32(data, self.crc) & 0xffffffffL - self.extrabuf = self.extrabuf + data + offset = self.offset - self.extrastart + self.extrabuf = self.extrabuf[offset:] + data self.extrasize = self.extrasize + len(data) + self.extrastart = self.offset self.size = self.size + len(data) def _read_eof(self): @@ -318,6 +339,10 @@ elif isize != (self.size & 0xffffffffL): raise IOError, "Incorrect length of data produced" + @property + def closed(self): + return self.fileobj is None + def close(self): if self.fileobj is None: return @@ -371,8 +396,18 @@ self._new_member = True self.extrabuf = "" self.extrasize = 0 + self.extrastart = 0 self.offset = 0 + def readable(self): + return self.mode == READ + + def writable(self): + return self.mode == WRITE + + def seekable(self): + return True + def seek(self, offset, whence=0): if whence: if whence == 1: @@ -395,8 +430,18 @@ self.read(1024) self.read(count % 1024) + return self.offset + def readline(self, size=-1): if size < 0: + # Shortcut common case - newline found in buffer. + offset = self.offset - self.extrastart + i = self.extrabuf.find('\n', offset) + 1 + if i > 0: + self.extrasize -= i - offset + self.offset += i - offset + return self.extrabuf[offset: i] + size = sys.maxint readsize = self.min_readsize else: Index: Lib/test/test_gzip.py =================================================================== --- Lib/test/test_gzip.py (revision 76872) +++ Lib/test/test_gzip.py (working copy) @@ -5,6 +5,7 @@ import unittest from test import test_support import os +import io import struct gzip = test_support.import_module('gzip') @@ -80,7 +81,14 @@ zgfile.close() self.assertEquals(contents, 'a'*201) + def test_buffered_reader_7471(self): + self.test_write() + f = gzip.GzipFile(self.filename, 'rb') + r = io.BufferedReader(f) + lines = [line for line in r] + r.close() + def test_readline(self): self.test_write() # Try .readline() with varying line lengths