|
5 | 5 |
|
6 | 6 | # based on Andrew Kuchling's minigzip.py distributed with the zlib module
|
7 | 7 |
|
8 |
| -import struct, sys, time, os |
9 |
| -import zlib |
| 8 | +import _compression |
10 | 9 | import builtins
|
11 | 10 | import io
|
12 |
| -import _compression |
| 11 | +import os |
| 12 | +import struct |
| 13 | +import sys |
| 14 | +import time |
| 15 | +import weakref |
| 16 | +import zlib |
13 | 17 |
|
14 | 18 | __all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]
|
15 | 19 |
|
@@ -125,10 +129,13 @@ class BadGzipFile(OSError):
|
125 | 129 | class _WriteBufferStream(io.RawIOBase):
|
126 | 130 | """Minimal object to pass WriteBuffer flushes into GzipFile"""
|
127 | 131 | def __init__(self, gzip_file):
|
128 |
| - self.gzip_file = gzip_file |
| 132 | + self.gzip_file = weakref.ref(gzip_file) |
129 | 133 |
|
130 | 134 | def write(self, data):
|
131 |
| - return self.gzip_file._write_raw(data) |
| 135 | + gzip_file = self.gzip_file() |
| 136 | + if gzip_file is None: |
| 137 | + raise RuntimeError("lost gzip_file") |
| 138 | + return gzip_file._write_raw(data) |
132 | 139 |
|
133 | 140 | def seekable(self):
|
134 | 141 | return False
|
@@ -190,51 +197,58 @@ def __init__(self, filename=None, mode=None,
|
190 | 197 | raise ValueError("Invalid mode: {!r}".format(mode))
|
191 | 198 | if mode and 'b' not in mode:
|
192 | 199 | mode += 'b'
|
193 |
| - if fileobj is None: |
194 |
| - fileobj = self.myfileobj = builtins.open(filename, mode or 'rb') |
195 |
| - if filename is None: |
196 |
| - filename = getattr(fileobj, 'name', '') |
197 |
| - if not isinstance(filename, (str, bytes)): |
198 |
| - filename = '' |
199 |
| - else: |
200 |
| - filename = os.fspath(filename) |
201 |
| - origmode = mode |
202 |
| - if mode is None: |
203 |
| - mode = getattr(fileobj, 'mode', 'rb') |
204 |
| - |
205 |
| - |
206 |
| - if mode.startswith('r'): |
207 |
| - self.mode = READ |
208 |
| - raw = _GzipReader(fileobj) |
209 |
| - self._buffer = io.BufferedReader(raw) |
210 |
| - self.name = filename |
211 |
| - |
212 |
| - elif mode.startswith(('w', 'a', 'x')): |
213 |
| - if origmode is None: |
214 |
| - import warnings |
215 |
| - warnings.warn( |
216 |
| - "GzipFile was opened for writing, but this will " |
217 |
| - "change in future Python releases. " |
218 |
| - "Specify the mode argument for opening it for writing.", |
219 |
| - FutureWarning, 2) |
220 |
| - self.mode = WRITE |
221 |
| - self._init_write(filename) |
222 |
| - self.compress = zlib.compressobj(compresslevel, |
223 |
| - zlib.DEFLATED, |
224 |
| - -zlib.MAX_WBITS, |
225 |
| - zlib.DEF_MEM_LEVEL, |
226 |
| - 0) |
227 |
| - self._write_mtime = mtime |
228 |
| - self._buffer_size = _WRITE_BUFFER_SIZE |
229 |
| - self._buffer = io.BufferedWriter(_WriteBufferStream(self), |
230 |
| - buffer_size=self._buffer_size) |
231 |
| - else: |
232 |
| - raise ValueError("Invalid mode: {!r}".format(mode)) |
233 | 200 |
|
234 |
| - self.fileobj = fileobj |
| 201 | + try: |
| 202 | + if fileobj is None: |
| 203 | + fileobj = self.myfileobj = builtins.open(filename, mode or 'rb') |
| 204 | + if filename is None: |
| 205 | + filename = getattr(fileobj, 'name', '') |
| 206 | + if not isinstance(filename, (str, bytes)): |
| 207 | + filename = '' |
| 208 | + else: |
| 209 | + filename = os.fspath(filename) |
| 210 | + origmode = mode |
| 211 | + if mode is None: |
| 212 | + mode = getattr(fileobj, 'mode', 'rb') |
| 213 | + |
| 214 | + |
| 215 | + if mode.startswith('r'): |
| 216 | + self.mode = READ |
| 217 | + raw = _GzipReader(fileobj) |
| 218 | + self._buffer = io.BufferedReader(raw) |
| 219 | + self.name = filename |
| 220 | + |
| 221 | + elif mode.startswith(('w', 'a', 'x')): |
| 222 | + if origmode is None: |
| 223 | + import warnings |
| 224 | + warnings.warn( |
| 225 | + "GzipFile was opened for writing, but this will " |
| 226 | + "change in future Python releases. " |
| 227 | + "Specify the mode argument for opening it for writing.", |
| 228 | + FutureWarning, 2) |
| 229 | + self.mode = WRITE |
| 230 | + self._init_write(filename) |
| 231 | + self.compress = zlib.compressobj(compresslevel, |
| 232 | + zlib.DEFLATED, |
| 233 | + -zlib.MAX_WBITS, |
| 234 | + zlib.DEF_MEM_LEVEL, |
| 235 | + 0) |
| 236 | + self._write_mtime = mtime |
| 237 | + self._buffer_size = _WRITE_BUFFER_SIZE |
| 238 | + self._buffer = io.BufferedWriter(_WriteBufferStream(self), |
| 239 | + buffer_size=self._buffer_size) |
| 240 | + else: |
| 241 | + raise ValueError("Invalid mode: {!r}".format(mode)) |
235 | 242 |
|
236 |
| - if self.mode == WRITE: |
237 |
| - self._write_gzip_header(compresslevel) |
| 243 | + self.fileobj = fileobj |
| 244 | + |
| 245 | + if self.mode == WRITE: |
| 246 | + self._write_gzip_header(compresslevel) |
| 247 | + except: |
| 248 | + # Avoid a ResourceWarning if the write fails, |
| 249 | + # eg read-only file or KeyboardInterrupt |
| 250 | + self._close() |
| 251 | + raise |
238 | 252 |
|
239 | 253 | @property
|
240 | 254 | def mtime(self):
|
@@ -363,11 +377,14 @@ def close(self):
|
363 | 377 | elif self.mode == READ:
|
364 | 378 | self._buffer.close()
|
365 | 379 | finally:
|
366 |
| - self.fileobj = None |
367 |
| - myfileobj = self.myfileobj |
368 |
| - if myfileobj: |
369 |
| - self.myfileobj = None |
370 |
| - myfileobj.close() |
| 380 | + self._close() |
| 381 | + |
| 382 | + def _close(self): |
| 383 | + self.fileobj = None |
| 384 | + myfileobj = self.myfileobj |
| 385 | + if myfileobj is not None: |
| 386 | + self.myfileobj = None |
| 387 | + myfileobj.close() |
371 | 388 |
|
372 | 389 | def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH):
|
373 | 390 | self._check_not_closed()
|
@@ -580,12 +597,12 @@ def _rewind(self):
|
580 | 597 | self._new_member = True
|
581 | 598 |
|
582 | 599 |
|
583 |
| -def compress(data, compresslevel=_COMPRESS_LEVEL_BEST, *, mtime=0): |
| 600 | +def compress(data, compresslevel=_COMPRESS_LEVEL_BEST, *, mtime=None): |
584 | 601 | """Compress data in one shot and return the compressed string.
|
585 | 602 |
|
586 | 603 | compresslevel sets the compression level in range of 0-9.
|
587 |
| - mtime can be used to set the modification time. |
588 |
| - The modification time is set to 0 by default, for reproducibility. |
| 604 | + mtime can be used to set the modification time. The modification time is |
| 605 | + set to the current time by default. |
589 | 606 | """
|
590 | 607 | # Wbits=31 automatically includes a gzip header and trailer.
|
591 | 608 | gzip_data = zlib.compress(data, level=compresslevel, wbits=31)
|
|
0 commit comments