]> granicus.if.org Git - python/commitdiff
Add fileobj support to gzip.open().
authorNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 4 Jun 2012 21:21:38 +0000 (23:21 +0200)
committerNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 4 Jun 2012 21:21:38 +0000 (23:21 +0200)
Doc/library/gzip.rst
Lib/gzip.py
Lib/test/test_gzip.py
Misc/NEWS

index 861a59cd4503ddc76384e115818e90349b39a6ca..50d04627e777069d0fcd3acf22c93cd5e928498f 100644 (file)
@@ -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
@@ -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
@@ -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)
@@ -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
index 2f53aa8aacfeb61d4c477a2472b2eea62b8dc2b1..412bf05495a2a27384c81dc1066d1ccaf1d669ae 100644 (file)
@@ -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.
@@ -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:
index 270411bd80c4e03e176d149e5c5b69506eb45287..bb9709776fd4fe1a4aed28853fa4c29a2c06b083 100644 (file)
@@ -424,8 +424,21 @@ class TestOpen(BaseTest):
             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):
index 479090f665cba26b1b3a6e2f0dc00d80dd0f52d5..46f3fd9cd26908547ac28a468d3c6bedf1f3c289 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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