diff options
author | Nadeem Vawda <nadeem.vawda@gmail.com> | 2012-06-04 21:31:20 (GMT) |
---|---|---|
committer | Nadeem Vawda <nadeem.vawda@gmail.com> | 2012-06-04 21:31:20 (GMT) |
commit | aebcdba8297bf5174ecf4e8687ad23883e35b14b (patch) | |
tree | b9b55d8bb72a27f439da47f98d676f56876d8237 | |
parent | 68721019efb16ba8acad036c331a9a195d6f7da0 (diff) | |
download | cpython-aebcdba8297bf5174ecf4e8687ad23883e35b14b.zip cpython-aebcdba8297bf5174ecf4e8687ad23883e35b14b.tar.gz cpython-aebcdba8297bf5174ecf4e8687ad23883e35b14b.tar.bz2 |
Make BZ2File's fileobj support easier to use.
The fileobj argument was added during the 3.3 development cycle, so this change
does not break backward compatibility with 3.2.
-rw-r--r-- | Doc/library/bz2.rst | 16 | ||||
-rw-r--r-- | Lib/bz2.py | 17 | ||||
-rw-r--r-- | Lib/tarfile.py | 4 | ||||
-rw-r--r-- | Lib/test/test_bz2.py | 40 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
5 files changed, 49 insertions, 31 deletions
diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst index 9577f31..de5c825 100644 --- a/Doc/library/bz2.rst +++ b/Doc/library/bz2.rst @@ -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 @@ -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. diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 8549677..6d73569 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -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) diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index cc416ed..2f2fbd3 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -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:]) @@ -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 |