Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/library/gzip.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ The module defines the following items:
.. versionchanged:: 3.6
Accepts a :term:`path-like object`.

.. exception:: BadGzipFile

The exception raised for invalid gzip files. It inherits :class:`OSError`.

.. versionadded:: 3.8

.. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)

Constructor for the :class:`GzipFile` class, which simulates most of the
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,11 @@ gzip
Added the *mtime* parameter to :func:`gzip.compress` for reproducible output.
(Contributed by Guo Ci Teo in :issue:`34898`.)

A :exc:`~gzip.BadGzipFile` exception is now raised in case of invalid or
corrupt gzip files instead of :exc:`OSError`.
(Contributed by Filip Gruszczyński, Michele Orrù, and Zackery Spytz in
:issue:`6584`.)


idlelib and IDLE
----------------
Expand Down
17 changes: 11 additions & 6 deletions Lib/gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import io
import _compression

__all__ = ["GzipFile", "open", "compress", "decompress"]
__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"]

FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16

Expand Down Expand Up @@ -112,6 +112,11 @@ def seek(self, off):
def seekable(self):
return True # Allows fast-forwarding even in unseekable streams


class BadGzipFile(OSError):
"""Exception raised if the processed gzip file is not valid."""


class GzipFile(_compression.BaseStream):
"""The GzipFile class simulates most of the methods of a file object with
the exception of the truncate() method.
Expand Down Expand Up @@ -413,12 +418,12 @@ def _read_gzip_header(self):
return False

if magic != b'\037\213':
raise OSError('Not a gzipped file (%r)' % magic)
raise BadGzipFile('Not a gzipped file (%r)' % magic)

(method, flag,
self._last_mtime) = struct.unpack("<BBIxx", self._read_exact(8))
if method != 8:
raise OSError('Unknown compression method')
raise BadGzipFile('Unknown compression method')

if flag & FEXTRA:
# Read & discard the extra field, if present
Expand Down Expand Up @@ -502,10 +507,10 @@ def _read_eof(self):
# stored is the true file size mod 2**32.
crc32, isize = struct.unpack("<II", self._read_exact(8))
if crc32 != self._crc:
raise OSError("CRC check failed %s != %s" % (hex(crc32),
hex(self._crc)))
raise BadGzipFile("CRC check failed %s != %s" % (hex(crc32),
hex(self._crc)))
elif isize != (self._stream_size & 0xffffffff):
raise OSError("Incorrect length of data produced")
raise BadGzipFile("Incorrect length of data produced")

# Gzip files can be padded with zeroes and still have archives.
# Consume all zero bytes and set the file position to the first
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,15 @@ def test_zero_padded_file(self):
d = f.read()
self.assertEqual(d, data1 * 50, "Incorrect data in file")

def test_gzip_BadGzipFile_exception(self):
self.assertTrue(issubclass(gzip.BadGzipFile, OSError))

def test_bad_gzip_file(self):
with open(self.filename, 'wb') as file:
file.write(data1 * 50)
with gzip.GzipFile(self.filename, 'r') as file:
self.assertRaises(gzip.BadGzipFile, file.readlines)

def test_non_seekable_file(self):
uncompressed = data1 * 50
buf = UnseekableIO()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a :exc:`~gzip.BadGzipFile` exception to the :mod:`gzip` module.