2222_COMPRESS_LEVEL_BEST = 9
2323
2424READ_BUFFER_SIZE = 128 * 1024
25+ _WRITE_BUFFER_SIZE = 4 * io .DEFAULT_BUFFER_SIZE
2526
2627
2728def open (filename ,mode = "rb" ,compresslevel = _COMPRESS_LEVEL_BEST ,
@@ -120,6 +121,21 @@ class BadGzipFile(OSError):
120121"""Exception raised in some cases for invalid gzip files."""
121122
122123
124+ class _WriteBufferStream (io .RawIOBase ):
125+ """Minimal object to pass WriteBuffer flushes into GzipFile"""
126+ def __init__ (self ,gzip_file ):
127+ self .gzip_file = gzip_file
128+
129+ def write (self ,data ):
130+ return self .gzip_file ._write_raw (data )
131+
132+ def seekable (self ):
133+ return False
134+
135+ def writable (self ):
136+ return True
137+
138+
123139class GzipFile (_compression .BaseStream ):
124140"""The GzipFile class simulates most of the methods of a file object with
125141 the exception of the truncate() method.
@@ -184,6 +200,7 @@ def __init__(self, filename=None, mode=None,
184200if mode is None :
185201mode = getattr (fileobj ,'mode' ,'rb' )
186202
203+
187204if mode .startswith ('r' ):
188205self .mode = READ
189206raw = _GzipReader (fileobj )
@@ -206,6 +223,9 @@ def __init__(self, filename=None, mode=None,
206223zlib .DEF_MEM_LEVEL ,
2072240 )
208225self ._write_mtime = mtime
226+ self ._buffer_size = _WRITE_BUFFER_SIZE
227+ self ._buffer = io .BufferedWriter (_WriteBufferStream (self ),
228+ buffer_size = self ._buffer_size )
209229else :
210230raise ValueError ("Invalid mode: {!r}" .format (mode ))
211231
@@ -231,6 +251,11 @@ def _init_write(self, filename):
231251self .bufsize = 0
232252self .offset = 0 # Current file offset for seek(), tell(), etc
233253
254+ def tell (self ):
255+ self ._check_not_closed ()
256+ self ._buffer .flush ()
257+ return super ().tell ()
258+
234259def _write_gzip_header (self ,compresslevel ):
235260self .fileobj .write (b'\037 \213 ' )# magic header
236261self .fileobj .write (b'\010 ' )# compression method
@@ -272,6 +297,10 @@ def write(self,data):
272297if self .fileobj is None :
273298raise ValueError ("write() on closed GzipFile object" )
274299
300+ return self ._buffer .write (data )
301+
302+ def _write_raw (self ,data ):
303+ # Called by our self._buffer underlying WriteBufferStream.
275304if isinstance (data , (bytes ,bytearray )):
276305length = len (data )
277306else :
@@ -322,16 +351,17 @@ def close(self):
322351fileobj = self .fileobj
323352if fileobj is None :
324353return
325- self .fileobj = None
326354try :
327355if self .mode == WRITE :
356+ self ._buffer .flush ()
328357fileobj .write (self .compress .flush ())
329358write32u (fileobj ,self .crc )
330359# self.size may exceed 2 GiB, or even 4 GiB
331360write32u (fileobj ,self .size & 0xffffffff )
332361elif self .mode == READ :
333362self ._buffer .close ()
334363finally :
364+ self .fileobj = None
335365myfileobj = self .myfileobj
336366if myfileobj :
337367self .myfileobj = None
@@ -341,7 +371,7 @@ def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
341371self ._check_not_closed ()
342372if self .mode == WRITE :
343373# Ensure the compressor's buffer is flushed
344- self .fileobj . write ( self . compress . flush (zlib_mode ) )
374+ self ._buffer . flush ()
345375self .fileobj .flush ()
346376
347377def fileno (self ):
@@ -378,10 +408,10 @@ def seek(self, offset, whence=io.SEEK_SET):
378408if offset < self .offset :
379409raise OSError ('Negative seek in write mode' )
380410count = offset - self .offset
381- chunk = b'\0 ' * 1024
382- for i in range (count // 1024 ):
411+ chunk = b'\0 ' * self . _buffer_size
412+ for i in range (count // self . _buffer_size ):
383413self .write (chunk )
384- self .write (b'\0 ' * (count % 1024 ))
414+ self .write (b'\0 ' * (count % self . _buffer_size ))
385415elif self .mode == READ :
386416self ._check_not_closed ()
387417return self ._buffer .seek (offset ,whence )