]> granicus.if.org Git - python/commitdiff
Make BZ2File's fileobj support easier to use.
authorNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 4 Jun 2012 21:31:20 +0000 (23:31 +0200)
committerNadeem Vawda <nadeem.vawda@gmail.com>
Mon, 4 Jun 2012 21:31:20 +0000 (23:31 +0200)
The fileobj argument was added during the 3.3 development cycle, so this change
does not break backward compatibility with 3.2.

Doc/library/bz2.rst
Lib/bz2.py
Lib/tarfile.py
Lib/test/test_bz2.py
Misc/NEWS

index 9577f31247bc6c82c1cf41c06ea62cc0d28667f4..de5c825b9c8bc015ea7cd99ea6e3c1785d6e2b82 100644 (file)
@@ -26,17 +26,18 @@ All of the classes in this module may safely be accessed from multiple threads.
 (De)compression of files
 ------------------------
 
-.. class:: BZ2File(filename=None, mode='r', buffering=None, compresslevel=9, \*, fileobj=None)
+.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9)
 
    Open a bzip2-compressed file.
 
-   The :class:`BZ2File` can wrap an existing :term:`file object` (given by
-   *fileobj*), or operate directly on a named file (named by *filename*).
-   Exactly one of these two parameters should be provided.
+   If *filename* is a :class:`str` or :class:`bytes` object, open the named file
+   directly. Otherwise, *filename* should be a :term:`file object`, which will
+   be used to read or write the compressed data.
 
    The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for
-   overwriting, or ``'a'`` for appending. If *fileobj* is provided, a mode of
-   ``'w'`` does not truncate the file, and is instead equivalent to ``'a'``.
+   overwriting, or ``'a'`` for appending. If *filename* is a file object (rather
+   than an actual file name), a mode of ``'w'`` does not truncate the file, and
+   is instead equivalent to ``'a'``.
 
    The *buffering* argument is ignored. Its use is deprecated.
 
@@ -69,7 +70,8 @@ All of the classes in this module may safely be accessed from multiple threads.
       :meth:`read1` and :meth:`readinto` methods were added.
 
    .. versionchanged:: 3.3
-      The *fileobj* argument to the constructor was added.
+      Support was added for *filename* being a :term:`file object` instead of an
+      actual filename.
 
    .. versionchanged:: 3.3
       The ``'a'`` (append) mode was added, along with support for reading
index 51b9ac438856aae4bba0f184a1226b0e371408aa..ae59407c1ac2d365619b606debfcae4819758ca6 100644 (file)
@@ -39,13 +39,12 @@ class BZ2File(io.BufferedIOBase):
     returned as bytes, and data to be written should be given as bytes.
     """
 
-    def __init__(self, filename=None, mode="r", buffering=None,
-                 compresslevel=9, *, fileobj=None):
+    def __init__(self, filename, mode="r", buffering=None, compresslevel=9):
         """Open a bzip2-compressed file.
 
-        If filename is given, open the named file. Otherwise, operate on
-        the file object given by fileobj. Exactly one of these two
-        parameters should be provided.
+        If filename is a str or bytes object, is gives the name of the file to
+        be opened. Otherwise, it should be a file object, which will be used to
+        read or write the compressed data.
 
         mode can be 'r' for reading (default), 'w' for (over)writing, or
         'a' for appending.
@@ -91,15 +90,15 @@ class BZ2File(io.BufferedIOBase):
         else:
             raise ValueError("Invalid mode: {!r}".format(mode))
 
-        if filename is not None and fileobj is None:
+        if isinstance(filename, (str, bytes)):
             self._fp = open(filename, mode)
             self._closefp = True
             self._mode = mode_code
-        elif fileobj is not None and filename is None:
-            self._fp = fileobj
+        elif hasattr(filename, "read") or hasattr(filename, "write"):
+            self._fp = filename
             self._mode = mode_code
         else:
-            raise ValueError("Must give exactly one of filename and fileobj")
+            raise TypeError("filename must be a str or bytes object, or a file")
 
     def close(self):
         """Flush and close the file.
index 854967751d512910289fc02b8ad4ee1e1e4905e2..6d735699fcc98fa5063ea534724d658bb816f8be 100644 (file)
@@ -1657,8 +1657,8 @@ class TarFile(object):
         except ImportError:
             raise CompressionError("bz2 module is not available")
 
-        fileobj = bz2.BZ2File(filename=name if fileobj is None else None,
-                mode=mode, fileobj=fileobj, compresslevel=compresslevel)
+        fileobj = bz2.BZ2File(fileobj or name, mode,
+                              compresslevel=compresslevel)
 
         try:
             t = cls.taropen(name, mode, fileobj, **kwargs)
index cc416ed301d1d67b4771c7c9014cd91cde4a6985..2f2fbd384856c94239f6de3929f107200d0c4376 100644 (file)
@@ -81,6 +81,20 @@ class BZ2FileTest(BaseTest):
         with open(self.filename, "wb") as f:
             f.write(self.DATA * streams)
 
+    def testBadArgs(self):
+        with self.assertRaises(TypeError):
+            BZ2File(123.456)
+        with self.assertRaises(ValueError):
+            BZ2File("/dev/null", "z")
+        with self.assertRaises(ValueError):
+            BZ2File("/dev/null", "rx")
+        with self.assertRaises(ValueError):
+            BZ2File("/dev/null", "rbt")
+        with self.assertRaises(ValueError):
+            BZ2File("/dev/null", compresslevel=0)
+        with self.assertRaises(ValueError):
+            BZ2File("/dev/null", compresslevel=10)
+
     def testRead(self):
         self.createTempFile()
         with BZ2File(self.filename) as bz2f:
@@ -348,7 +362,7 @@ class BZ2FileTest(BaseTest):
     def testFileno(self):
         self.createTempFile()
         with open(self.filename, 'rb') as rawf:
-            bz2f = BZ2File(fileobj=rawf)
+            bz2f = BZ2File(rawf)
             try:
                 self.assertEqual(bz2f.fileno(), rawf.fileno())
             finally:
@@ -356,7 +370,7 @@ class BZ2FileTest(BaseTest):
         self.assertRaises(ValueError, bz2f.fileno)
 
     def testSeekable(self):
-        bz2f = BZ2File(fileobj=BytesIO(self.DATA))
+        bz2f = BZ2File(BytesIO(self.DATA))
         try:
             self.assertTrue(bz2f.seekable())
             bz2f.read()
@@ -365,7 +379,7 @@ class BZ2FileTest(BaseTest):
             bz2f.close()
         self.assertRaises(ValueError, bz2f.seekable)
 
-        bz2f = BZ2File(fileobj=BytesIO(), mode="w")
+        bz2f = BZ2File(BytesIO(), mode="w")
         try:
             self.assertFalse(bz2f.seekable())
         finally:
@@ -374,7 +388,7 @@ class BZ2FileTest(BaseTest):
 
         src = BytesIO(self.DATA)
         src.seekable = lambda: False
-        bz2f = BZ2File(fileobj=src)
+        bz2f = BZ2File(src)
         try:
             self.assertFalse(bz2f.seekable())
         finally:
@@ -382,7 +396,7 @@ class BZ2FileTest(BaseTest):
         self.assertRaises(ValueError, bz2f.seekable)
 
     def testReadable(self):
-        bz2f = BZ2File(fileobj=BytesIO(self.DATA))
+        bz2f = BZ2File(BytesIO(self.DATA))
         try:
             self.assertTrue(bz2f.readable())
             bz2f.read()
@@ -391,7 +405,7 @@ class BZ2FileTest(BaseTest):
             bz2f.close()
         self.assertRaises(ValueError, bz2f.readable)
 
-        bz2f = BZ2File(fileobj=BytesIO(), mode="w")
+        bz2f = BZ2File(BytesIO(), mode="w")
         try:
             self.assertFalse(bz2f.readable())
         finally:
@@ -399,7 +413,7 @@ class BZ2FileTest(BaseTest):
         self.assertRaises(ValueError, bz2f.readable)
 
     def testWritable(self):
-        bz2f = BZ2File(fileobj=BytesIO(self.DATA))
+        bz2f = BZ2File(BytesIO(self.DATA))
         try:
             self.assertFalse(bz2f.writable())
             bz2f.read()
@@ -408,7 +422,7 @@ class BZ2FileTest(BaseTest):
             bz2f.close()
         self.assertRaises(ValueError, bz2f.writable)
 
-        bz2f = BZ2File(fileobj=BytesIO(), mode="w")
+        bz2f = BZ2File(BytesIO(), mode="w")
         try:
             self.assertTrue(bz2f.writable())
         finally:
@@ -512,14 +526,14 @@ class BZ2FileTest(BaseTest):
 
     def testReadBytesIO(self):
         with BytesIO(self.DATA) as bio:
-            with BZ2File(fileobj=bio) as bz2f:
+            with BZ2File(bio) as bz2f:
                 self.assertRaises(TypeError, bz2f.read, None)
                 self.assertEqual(bz2f.read(), self.TEXT)
             self.assertFalse(bio.closed)
 
     def testPeekBytesIO(self):
         with BytesIO(self.DATA) as bio:
-            with BZ2File(fileobj=bio) as bz2f:
+            with BZ2File(bio) as bz2f:
                 pdata = bz2f.peek()
                 self.assertNotEqual(len(pdata), 0)
                 self.assertTrue(self.TEXT.startswith(pdata))
@@ -527,7 +541,7 @@ class BZ2FileTest(BaseTest):
 
     def testWriteBytesIO(self):
         with BytesIO() as bio:
-            with BZ2File(fileobj=bio, mode="w") as bz2f:
+            with BZ2File(bio, "w") as bz2f:
                 self.assertRaises(TypeError, bz2f.write)
                 bz2f.write(self.TEXT)
             self.assertEqual(self.decompress(bio.getvalue()), self.TEXT)
@@ -535,14 +549,14 @@ class BZ2FileTest(BaseTest):
 
     def testSeekForwardBytesIO(self):
         with BytesIO(self.DATA) as bio:
-            with BZ2File(fileobj=bio) as bz2f:
+            with BZ2File(bio) as bz2f:
                 self.assertRaises(TypeError, bz2f.seek)
                 bz2f.seek(150)
                 self.assertEqual(bz2f.read(), self.TEXT[150:])
 
     def testSeekBackwardsBytesIO(self):
         with BytesIO(self.DATA) as bio:
-            with BZ2File(fileobj=bio) as bz2f:
+            with BZ2File(bio) as bz2f:
                 bz2f.read(500)
                 bz2f.seek(-150, 1)
                 self.assertEqual(bz2f.read(), self.TEXT[500-150:])
index 46f3fd9cd26908547ac28a468d3c6bedf1f3c289..1dc039192ec58317eafa29755bc61afc34286cf1 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,9 @@ Core and Builtins
 Library
 -------
 
+- BZ2File.__init__() now accepts a file object as its first argument, rather
+  than requiring a separate "fileobj" argument.
+
 - gzip.open() now accepts file objects as well as filenames.
 
 - Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError