mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
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.
This commit is contained in:
parent
68721019ef
commit
aebcdba829
5 changed files with 49 additions and 31 deletions
|
|
@ -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
|
||||
|
|
|
|||
17
Lib/bz2.py
17
Lib/bz2.py
|
|
@ -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 @@ def __init__(self, filename=None, mode="r", buffering=None,
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -1657,8 +1657,8 @@ def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,20 @@ def createTempFile(self, streams=1):
|
|||
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 @@ def testSeekPreStartMultiStream(self):
|
|||
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 @@ def testFileno(self):
|
|||
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 @@ def testSeekable(self):
|
|||
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 @@ def testSeekable(self):
|
|||
|
||||
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 @@ def testSeekable(self):
|
|||
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 @@ def testReadable(self):
|
|||
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 @@ def testReadable(self):
|
|||
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 @@ def testWritable(self):
|
|||
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 @@ def testMultiStreamOrdering(self):
|
|||
|
||||
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 @@ def testPeekBytesIO(self):
|
|||
|
||||
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 @@ def testWriteBytesIO(self):
|
|||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue