Skip to content

Commit

Permalink
Add fileobj support to gzip.open().
Browse files Browse the repository at this point in the history
  • Loading branch information
nvawda committed Jun 4, 2012
1 parent d7b7c74 commit 6872101
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 10 deletions.
20 changes: 11 additions & 9 deletions Doc/library/gzip.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ like the GNU programs :program:`gzip` and :program:`gunzip` would.
The data compression is provided by the :mod:`zlib` module.

The :mod:`gzip` module provides the :class:`GzipFile` class, as well as the
:func:`gzip.open`, :func:`compress` and :func:`decompress` convenience
functions. The :class:`GzipFile` class reads and writes :program:`gzip`\ -format
files, automatically compressing or decompressing the data so that it looks like
an ordinary :term:`file object`.
:func:`.open`, :func:`compress` and :func:`decompress` convenience functions.
The :class:`GzipFile` class reads and writes :program:`gzip`\ -format files,
automatically compressing or decompressing the data so that it looks like an
ordinary :term:`file object`.

Note that additional file formats which can be decompressed by the
:program:`gzip` and :program:`gunzip` programs, such as those produced by
Expand All @@ -28,9 +28,11 @@ The module defines the following items:

.. function:: open(filename, mode='rb', compresslevel=9, encoding=None, errors=None, newline=None)

Open *filename* as a gzip-compressed file in binary or text mode.
Open a gzip-compressed file in binary or text mode, returning a :term:`file
object`.

Returns a :term:`file object`.
The *filename* argument can be an actual filename (a :class:`str` or
:class:`bytes` object), or an existing file object to read from or write to.

The *mode* argument can be any of ``'r'``, ``'rb'``, ``'a'``, ``'ab'``,
``'w'``, or ``'wb'`` for binary mode, or ``'rt'``, ``'at'``, or ``'wt'`` for
Expand All @@ -48,8 +50,8 @@ The module defines the following items:
handling behavior, and line ending(s).

.. versionchanged:: 3.3
Support for text mode was added, along with the *encoding*, *errors* and
*newline* arguments.
Added support for *filename* being a file object, support for text mode,
and the *encoding*, *errors* and *newline* arguments.


.. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)
Expand All @@ -75,7 +77,7 @@ The module defines the following items:
is the mode of *fileobj* if discernible; otherwise, the default is ``'rb'``.

Note that the file is always opened in binary mode. To open a compressed file
in text mode, use :func:`gzip.open` (or wrap your :class:`GzipFile` with an
in text mode, use :func:`.open` (or wrap your :class:`GzipFile` with an
:class:`io.TextIOWrapper`).

The *compresslevel* argument is an integer from ``1`` to ``9`` controlling the
Expand Down
13 changes: 12 additions & 1 deletion Lib/gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ 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.
Expand All @@ -43,7 +46,15 @@ def open(filename, mode="rb", compresslevel=9,
raise ValueError("Argument 'errors' not supported in binary mode")
if newline is not None:
raise ValueError("Argument 'newline' not supported in binary mode")
binary_file = GzipFile(filename, mode.replace("t", ""), compresslevel)

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:
Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_gzip.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,21 @@ def test_text_modes(self):
file_data = gzip.decompress(f.read()).decode("ascii")
self.assertEqual(file_data, uncompressed_raw * 2)

def test_fileobj(self):
uncompressed_bytes = data1 * 50
uncompressed_str = uncompressed_bytes.decode("ascii")
compressed = gzip.compress(uncompressed_bytes)
with gzip.open(io.BytesIO(compressed), "r") as f:
self.assertEqual(f.read(), uncompressed_bytes)
with gzip.open(io.BytesIO(compressed), "rb") as f:
self.assertEqual(f.read(), uncompressed_bytes)
with gzip.open(io.BytesIO(compressed), "rt") as f:
self.assertEqual(f.read(), uncompressed_str)

def test_bad_params(self):
# Test invalid parameter combinations.
with self.assertRaises(TypeError):
gzip.open(123.456)
with self.assertRaises(ValueError):
gzip.open(self.filename, "wbt")
with self.assertRaises(ValueError):
Expand Down
2 changes: 2 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Core and Builtins
Library
-------

- gzip.open() now accepts file objects as well as filenames.

- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError
when the path existed and had the S_ISGID mode bit set when it was
not explicitly asked for. This is no longer an exception as mkdir
Expand Down

0 comments on commit 6872101

Please sign in to comment.