aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib-python/3/gzip.py')
-rw-r--r--lib-python/3/gzip.py127
1 files changed, 89 insertions, 38 deletions
diff --git a/lib-python/3/gzip.py b/lib-python/3/gzip.py
index 1de23b6972..2fd03fa747 100644
--- a/lib-python/3/gzip.py
+++ b/lib-python/3/gzip.py
@@ -16,17 +16,49 @@ FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
READ, WRITE = 1, 2
-def U32(i):
- """Return i as an unsigned integer, assuming it fits in 32 bits.
- If it's >= 2GB when viewed as a 32-bit unsigned int, return a long.
- """
- if i < 0:
- i += 1 << 32
- return i
+def open(filename, mode="rb", compresslevel=9,
+ encoding=None, errors=None, newline=None):
+ """Open a gzip-compressed file in binary or text mode.
+
+ The filename argument can be an actual filename (a str or bytes object), or
+ an existing file object to read from or write to.
+
+ The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for binary mode,
+ or "rt", "wt" or "at" for text mode. The default mode is "rb", and the
+ default compresslevel is 9.
+
+ For binary mode, this function is equivalent to the GzipFile constructor:
+ GzipFile(filename, mode, compresslevel). In this case, the encoding, errors
+ and newline arguments must not be provided.
-def LOWU32(i):
- """Return the low-order 32 bits, as a non-negative int"""
- return i & 0xFFFFFFFF
+ For text mode, a GzipFile object is created, and wrapped in an
+ io.TextIOWrapper instance with the specified encoding, error handling
+ behavior, and line ending(s).
+
+ """
+ if "t" in mode:
+ if "b" in mode:
+ raise ValueError("Invalid mode: %r" % (mode,))
+ else:
+ if encoding is not None:
+ raise ValueError("Argument 'encoding' not supported in binary mode")
+ if errors is not None:
+ raise ValueError("Argument 'errors' not supported in binary mode")
+ if newline is not None:
+ raise ValueError("Argument 'newline' not supported in binary mode")
+
+ gz_mode = mode.replace("t", "")
+ if isinstance(filename, (str, bytes)):
+ binary_file = GzipFile(filename, gz_mode, compresslevel)
+ elif hasattr(filename, "read") or hasattr(filename, "write"):
+ binary_file = GzipFile(None, gz_mode, compresslevel, filename)
+ else:
+ raise TypeError("filename must be a str or bytes object, or a file")
+
+ if "t" in mode:
+ return io.TextIOWrapper(binary_file, encoding, errors, newline)
+ else:
+ return binary_file
def write32u(output, value):
# The L format writes the bit pattern correctly whether signed
@@ -36,15 +68,6 @@ def write32u(output, value):
def read32(input):
return struct.unpack("<I", input.read(4))[0]
-def open(filename, mode="rb", compresslevel=9):
- """Shorthand for GzipFile(filename, mode, compresslevel).
-
- The filename argument is required; mode defaults to 'rb'
- and compresslevel defaults to 9.
-
- """
- return GzipFile(filename, mode, compresslevel)
-
class _PaddedFile:
"""Minimal read-only file object that prepends a string to the contents
of an actual file. Shouldn't be used outside of gzip.py, as it lacks
@@ -106,7 +129,7 @@ class GzipFile(io.BufferedIOBase):
the exception of the readinto() and truncate() methods.
This class only supports opening files in binary mode. If you need to open a
- compressed file in text mode, wrap your GzipFile with an io.TextIOWrapper.
+ compressed file in text mode, use the gzip.open() function.
"""
@@ -121,7 +144,7 @@ class GzipFile(io.BufferedIOBase):
non-trivial value.
The new class instance is based on fileobj, which can be a regular
- file, a StringIO object, or any other object which simulates a file.
+ file, an io.BytesIO object, or any other object which simulates a file.
It defaults to None, in which case filename is opened to provide
a file object.
@@ -137,9 +160,10 @@ class GzipFile(io.BufferedIOBase):
A mode of 'r' is equivalent to one of 'rb', and similarly for 'w' and
'wb', and 'a' and 'ab'.
- The compresslevel argument is an integer from 1 to 9 controlling the
+ The compresslevel argument is an integer from 0 to 9 controlling the
level of compression; 1 is fastest and produces the least compression,
- and 9 is slowest and produces the most compression. The default is 9.
+ and 9 is slowest and produces the most compression. 0 is no compression
+ at all. The default is 9.
The mtime argument is an optional numeric timestamp to be written
to the stream when compressing. All gzip compressed streams
@@ -153,21 +177,19 @@ class GzipFile(io.BufferedIOBase):
"""
if mode and ('t' in mode or 'U' in mode):
- raise IOError("Mode " + mode + " not supported")
+ raise ValueError("Invalid mode: {!r}".format(mode))
if mode and 'b' not in mode:
mode += 'b'
if fileobj is None:
fileobj = self.myfileobj = builtins.open(filename, mode or 'rb')
if filename is None:
- if hasattr(fileobj, 'name') and isinstance(fileobj.name, str):
- filename = fileobj.name
- else:
+ filename = getattr(fileobj, 'name', '')
+ if not isinstance(filename, (str, bytes)):
filename = ''
if mode is None:
- if hasattr(fileobj, 'mode'): mode = fileobj.mode
- else: mode = 'rb'
+ mode = getattr(fileobj, 'mode', 'rb')
- if mode[0:1] == 'r':
+ if mode.startswith('r'):
self.mode = READ
# Set flag indicating start of a new member
self._new_member = True
@@ -182,7 +204,7 @@ class GzipFile(io.BufferedIOBase):
self.min_readsize = 100
fileobj = _PaddedFile(fileobj)
- elif mode[0:1] == 'w' or mode[0:1] == 'a':
+ elif mode.startswith(('w', 'a')):
self.mode = WRITE
self._init_write(filename)
self.compress = zlib.compressobj(compresslevel,
@@ -191,7 +213,7 @@ class GzipFile(io.BufferedIOBase):
zlib.DEF_MEM_LEVEL,
0)
else:
- raise IOError("Mode " + mode + " not supported")
+ raise ValueError("Invalid mode: {!r}".format(mode))
self.fileobj = fileobj
self.offset = 0
@@ -236,7 +258,8 @@ class GzipFile(io.BufferedIOBase):
# RFC 1952 requires the FNAME field to be Latin-1. Do not
# include filenames that cannot be represented that way.
fname = os.path.basename(self.name)
- fname = fname.encode('latin-1')
+ if not isinstance(fname, bytes):
+ fname = fname.encode('latin-1')
if fname.endswith(b'.gz'):
fname = fname[:-3]
except UnicodeEncodeError:
@@ -265,6 +288,7 @@ class GzipFile(io.BufferedIOBase):
if magic != b'\037\213':
raise IOError('Not a gzipped file')
+
method = ord( self.fileobj.read(1) )
if method != 8:
raise IOError('Unknown compression method')
@@ -353,6 +377,31 @@ class GzipFile(io.BufferedIOBase):
self.offset += size
return chunk
+ def read1(self, size=-1):
+ self._check_closed()
+ if self.mode != READ:
+ import errno
+ raise IOError(errno.EBADF, "read1() on write-only GzipFile object")
+
+ if self.extrasize <= 0 and self.fileobj is None:
+ return b''
+
+ try:
+ # For certain input data, a single call to _read() may not return
+ # any data. In this case, retry until we get some data or reach EOF.
+ while self.extrasize <= 0:
+ self._read()
+ except EOFError:
+ pass
+ if size < 0 or size > self.extrasize:
+ size = self.extrasize
+
+ offset = self.offset - self.extrastart
+ chunk = self.extrabuf[offset: offset + size]
+ self.extrasize -= size
+ self.offset += size
+ return chunk
+
def peek(self, n):
if self.mode != READ:
import errno
@@ -366,8 +415,10 @@ class GzipFile(io.BufferedIOBase):
if self.fileobj is None:
return b''
try:
- # 1024 is the same buffering heuristic used in read()
- self._read(max(n, 1024))
+ # Ensure that we don't return b"" if we haven't reached EOF.
+ while self.extrasize == 0:
+ # 1024 is the same buffering heuristic used in read()
+ self._read(max(n, 1024))
except EOFError:
pass
offset = self.offset - self.extrastart
@@ -573,7 +624,7 @@ class GzipFile(io.BufferedIOBase):
def compress(data, compresslevel=9):
"""Compress data in one shot and return the compressed string.
- Optional argument is the compression level, in range of 1-9.
+ Optional argument is the compression level, in range of 0-9.
"""
buf = io.BytesIO()
with GzipFile(fileobj=buf, mode='wb', compresslevel=compresslevel) as f:
@@ -621,9 +672,9 @@ def _test():
if not chunk:
break
g.write(chunk)
- if g is not sys.stdout:
+ if g is not sys.stdout.buffer:
g.close()
- if f is not sys.stdin:
+ if f is not sys.stdin.buffer:
f.close()
if __name__ == '__main__':