22
22
_COMPRESS_LEVEL_BEST = 9
23
23
24
24
READ_BUFFER_SIZE = 128 * 1024
25
+ _WRITE_BUFFER_SIZE = 4 * io .DEFAULT_BUFFER_SIZE
25
26
26
27
27
28
def open (filename , mode = "rb" , compresslevel = _COMPRESS_LEVEL_BEST ,
@@ -120,6 +121,21 @@ class BadGzipFile(OSError):
120
121
"""Exception raised in some cases for invalid gzip files."""
121
122
122
123
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
+
123
139
class GzipFile (_compression .BaseStream ):
124
140
"""The GzipFile class simulates most of the methods of a file object with
125
141
the exception of the truncate() method.
@@ -184,6 +200,7 @@ def __init__(self, filename=None, mode=None,
184
200
if mode is None :
185
201
mode = getattr (fileobj , 'mode' , 'rb' )
186
202
203
+
187
204
if mode .startswith ('r' ):
188
205
self .mode = READ
189
206
raw = _GzipReader (fileobj )
@@ -206,6 +223,9 @@ def __init__(self, filename=None, mode=None,
206
223
zlib .DEF_MEM_LEVEL ,
207
224
0 )
208
225
self ._write_mtime = mtime
226
+ self ._buffer_size = _WRITE_BUFFER_SIZE
227
+ self ._buffer = io .BufferedWriter (_WriteBufferStream (self ),
228
+ buffer_size = self ._buffer_size )
209
229
else :
210
230
raise ValueError ("Invalid mode: {!r}" .format (mode ))
211
231
@@ -231,6 +251,11 @@ def _init_write(self, filename):
231
251
self .bufsize = 0
232
252
self .offset = 0 # Current file offset for seek(), tell(), etc
233
253
254
+ def tell (self ):
255
+ self ._check_not_closed ()
256
+ self ._buffer .flush ()
257
+ return super ().tell ()
258
+
234
259
def _write_gzip_header (self , compresslevel ):
235
260
self .fileobj .write (b'\037 \213 ' ) # magic header
236
261
self .fileobj .write (b'\010 ' ) # compression method
@@ -272,6 +297,10 @@ def write(self,data):
272
297
if self .fileobj is None :
273
298
raise ValueError ("write() on closed GzipFile object" )
274
299
300
+ return self ._buffer .write (data )
301
+
302
+ def _write_raw (self , data ):
303
+ # Called by our self._buffer underlying WriteBufferStream.
275
304
if isinstance (data , (bytes , bytearray )):
276
305
length = len (data )
277
306
else :
@@ -322,16 +351,17 @@ def close(self):
322
351
fileobj = self .fileobj
323
352
if fileobj is None :
324
353
return
325
- self .fileobj = None
326
354
try :
327
355
if self .mode == WRITE :
356
+ self ._buffer .flush ()
328
357
fileobj .write (self .compress .flush ())
329
358
write32u (fileobj , self .crc )
330
359
# self.size may exceed 2 GiB, or even 4 GiB
331
360
write32u (fileobj , self .size & 0xffffffff )
332
361
elif self .mode == READ :
333
362
self ._buffer .close ()
334
363
finally :
364
+ self .fileobj = None
335
365
myfileobj = self .myfileobj
336
366
if myfileobj :
337
367
self .myfileobj = None
@@ -341,7 +371,7 @@ def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
341
371
self ._check_not_closed ()
342
372
if self .mode == WRITE :
343
373
# Ensure the compressor's buffer is flushed
344
- self .fileobj . write ( self . compress . flush (zlib_mode ) )
374
+ self ._buffer . flush ()
345
375
self .fileobj .flush ()
346
376
347
377
def fileno (self ):
@@ -378,10 +408,10 @@ def seek(self, offset, whence=io.SEEK_SET):
378
408
if offset < self .offset :
379
409
raise OSError ('Negative seek in write mode' )
380
410
count = 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 ):
383
413
self .write (chunk )
384
- self .write (b'\0 ' * (count % 1024 ))
414
+ self .write (b'\0 ' * (count % self . _buffer_size ))
385
415
elif self .mode == READ :
386
416
self ._check_not_closed ()
387
417
return self ._buffer .seek (offset , whence )
0 commit comments