diff --git a/Doc/lib/libgzip.tex b/Doc/lib/libgzip.tex index 34ec897ac7c..aab70a19f00 100644 --- a/Doc/lib/libgzip.tex +++ b/Doc/lib/libgzip.tex @@ -21,8 +21,8 @@ The module defines the following items: \begin{classdesc}{GzipFile}{\optional{filename\optional{, mode\optional{, compresslevel\optional{, fileobj}}}}} Constructor for the \class{GzipFile} class, which simulates most of -the methods of a file object, with the exception of the -\method{seek()} and \method{tell()} methods. At least one of +the methods of a file object, with the exception of the \method{readinto()}, +\method{truncate()}, and \method{xreadlines()} methods. At least one of \var{fileobj} and \var{filename} must be given a non-trivial value. The new class instance is based on \var{fileobj}, which can be a diff --git a/Lib/gzip.py b/Lib/gzip.py index 79ca2050d2e..9df8e5d180e 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -64,6 +64,7 @@ def __init__(self, filename=None, mode=None, raise ValueError, "Mode " + mode + " not supported" self.fileobj = fileobj + self.offset = 0 if self.mode == WRITE: self._write_gzip_header() @@ -138,6 +139,7 @@ def write(self,data): self.size = self.size + len(data) self.crc = zlib.crc32(data, self.crc) self.fileobj.write( self.compress.compress(data) ) + self.offset += len(data) def writelines(self,lines): self.write(" ".join(lines)) @@ -167,11 +169,13 @@ def read(self, size=-1): self.extrabuf = self.extrabuf[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) def _read(self, size=1024): if self.fileobj is None: raise EOFError, "Reached EOF" @@ -185,7 +189,6 @@ def _read(self, size=1024): pos = self.fileobj.tell() # Save current position self.fileobj.seek(0, 2) # Seek to end of file if pos == self.fileobj.tell(): - self.fileobj = None raise EOFError, "Reached EOF" else: self.fileobj.seek( pos ) # Return to original position @@ -204,7 +207,6 @@ def _read(self, size=1024): if buf == "": uncompress = self.decompress.flush() self._read_eof() - self.fileobj = None self._add_read_data( uncompress ) raise EOFError, 'Reached EOF' @@ -270,6 +272,36 @@ def flush(self): def isatty(self): return 0 + def tell(self): + return self.offset + + def rewind(self): + '''Return the uncompressed stream file position indicator to the + beginning of the file''' + if self.mode != READ: + raise IOError("Can't rewind in write mode") + self.fileobj.seek(0) + self._new_member = 1 + self.extrabuf = "" + self.extrasize = 0 + self.offset = 0 + + def seek(self, offset): + if self.mode == WRITE: + if offset < self.offset: + raise IOError('Negative seek in write mode') + count = offset - self.offset + for i in range(count/1024): + f.write(1024*'\0') + self.write((count%1024)*'\0') + elif self.mode == READ: + if offset < self.offset: + # for negative seek, rewind and do positive seek + self.rewind() + count = offset - self.offset + for i in range(count/1024): self.read(1024) + self.read(count % 1024) + def readline(self, size=-1): if size < 0: size = sys.maxint bufs = [] diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 8ff8c337e73..6d69c3f281f 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -50,5 +50,29 @@ if L == []: break f.close() +# Try seek, read test + +f = gzip.GzipFile(filename) +while 1: + oldpos = f.tell() + line1 = f.readline() + if not line1: break + newpos = f.tell() + f.seek(oldpos) # negative seek + if len(line1)>10: + amount = 10 + else: + amount = len(line1) + line2 = f.read(amount) + verify(line1[:amount] == line2) + f.seek(newpos) # positive seek +f.close() + +# Try seek, write test +f = gzip.GzipFile(filename, 'w') +for pos in range(0, 256, 16): + f.seek(pos) + f.write('GZ\n') +f.close() os.unlink(filename)