Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aebcdba

Browse files
committed
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.
1 parent 6872101 commit aebcdba

5 files changed

Lines changed: 49 additions & 31 deletions

File tree

Doc/library/bz2.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@ All of the classes in this module may safely be accessed from multiple threads.
2626
(De)compression of files
2727
------------------------
2828

29-
.. class:: BZ2File(filename=None, mode='r', buffering=None, compresslevel=9, \*, fileobj=None)
29+
.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9)
3030

3131
Open a bzip2-compressed file.
3232

33-
The :class:`BZ2File` can wrap an existing :term:`file object` (given by
34-
*fileobj*), or operate directly on a named file (named by *filename*).
35-
Exactly one of these two parameters should be provided.
33+
If *filename* is a :class:`str` or :class:`bytes` object, open the named file
34+
directly. Otherwise, *filename* should be a :term:`file object`, which will
35+
be used to read or write the compressed data.
3636

3737
The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for
38-
overwriting, or ``'a'`` for appending. If *fileobj* is provided, a mode of
39-
``'w'`` does not truncate the file, and is instead equivalent to ``'a'``.
38+
overwriting, or ``'a'`` for appending. If *filename* is a file object (rather
39+
than an actual file name), a mode of ``'w'`` does not truncate the file, and
40+
is instead equivalent to ``'a'``.
4041

4142
The *buffering* argument is ignored. Its use is deprecated.
4243

@@ -69,7 +70,8 @@ All of the classes in this module may safely be accessed from multiple threads.
6970
:meth:`read1` and :meth:`readinto` methods were added.
7071

7172
.. versionchanged:: 3.3
72-
The *fileobj* argument to the constructor was added.
73+
Support was added for *filename* being a :term:`file object` instead of an
74+
actual filename.
7375

7476
.. versionchanged:: 3.3
7577
The ``'a'`` (append) mode was added, along with support for reading

Lib/bz2.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ class BZ2File(io.BufferedIOBase):
3939
returned as bytes, and data to be written should be given as bytes.
4040
"""
4141

42-
def __init__(self, filename=None, mode="r", buffering=None,
43-
compresslevel=9, *, fileobj=None):
42+
def __init__(self, filename, mode="r", buffering=None, compresslevel=9):
4443
"""Open a bzip2-compressed file.
4544
46-
If filename is given, open the named file. Otherwise, operate on
47-
the file object given by fileobj. Exactly one of these two
48-
parameters should be provided.
45+
If filename is a str or bytes object, is gives the name of the file to
46+
be opened. Otherwise, it should be a file object, which will be used to
47+
read or write the compressed data.
4948
5049
mode can be 'r' for reading (default), 'w' for (over)writing, or
5150
'a' for appending.
@@ -91,15 +90,15 @@ def __init__(self, filename=None, mode="r", buffering=None,
9190
else:
9291
raise ValueError("Invalid mode: {!r}".format(mode))
9392

94-
if filename is not None and fileobj is None:
93+
if isinstance(filename, (str, bytes)):
9594
self._fp = open(filename, mode)
9695
self._closefp = True
9796
self._mode = mode_code
98-
elif fileobj is not None and filename is None:
99-
self._fp = fileobj
97+
elif hasattr(filename, "read") or hasattr(filename, "write"):
98+
self._fp = filename
10099
self._mode = mode_code
101100
else:
102-
raise ValueError("Must give exactly one of filename and fileobj")
101+
raise TypeError("filename must be a str or bytes object, or a file")
103102

104103
def close(self):
105104
"""Flush and close the file.

Lib/tarfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,8 +1657,8 @@ def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs):
16571657
except ImportError:
16581658
raise CompressionError("bz2 module is not available")
16591659

1660-
fileobj = bz2.BZ2File(filename=name if fileobj is None else None,
1661-
mode=mode, fileobj=fileobj, compresslevel=compresslevel)
1660+
fileobj = bz2.BZ2File(fileobj or name, mode,
1661+
compresslevel=compresslevel)
16621662

16631663
try:
16641664
t = cls.taropen(name, mode, fileobj, **kwargs)

Lib/test/test_bz2.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ def createTempFile(self, streams=1):
8181
with open(self.filename, "wb") as f:
8282
f.write(self.DATA * streams)
8383

84+
def testBadArgs(self):
85+
with self.assertRaises(TypeError):
86+
BZ2File(123.456)
87+
with self.assertRaises(ValueError):
88+
BZ2File("/dev/null", "z")
89+
with self.assertRaises(ValueError):
90+
BZ2File("/dev/null", "rx")
91+
with self.assertRaises(ValueError):
92+
BZ2File("/dev/null", "rbt")
93+
with self.assertRaises(ValueError):
94+
BZ2File("/dev/null", compresslevel=0)
95+
with self.assertRaises(ValueError):
96+
BZ2File("/dev/null", compresslevel=10)
97+
8498
def testRead(self):
8599
self.createTempFile()
86100
with BZ2File(self.filename) as bz2f:
@@ -348,15 +362,15 @@ def testSeekPreStartMultiStream(self):
348362
def testFileno(self):
349363
self.createTempFile()
350364
with open(self.filename, 'rb') as rawf:
351-
bz2f = BZ2File(fileobj=rawf)
365+
bz2f = BZ2File(rawf)
352366
try:
353367
self.assertEqual(bz2f.fileno(), rawf.fileno())
354368
finally:
355369
bz2f.close()
356370
self.assertRaises(ValueError, bz2f.fileno)
357371

358372
def testSeekable(self):
359-
bz2f = BZ2File(fileobj=BytesIO(self.DATA))
373+
bz2f = BZ2File(BytesIO(self.DATA))
360374
try:
361375
self.assertTrue(bz2f.seekable())
362376
bz2f.read()
@@ -365,7 +379,7 @@ def testSeekable(self):
365379
bz2f.close()
366380
self.assertRaises(ValueError, bz2f.seekable)
367381

368-
bz2f = BZ2File(fileobj=BytesIO(), mode="w")
382+
bz2f = BZ2File(BytesIO(), mode="w")
369383
try:
370384
self.assertFalse(bz2f.seekable())
371385
finally:
@@ -374,15 +388,15 @@ def testSeekable(self):
374388

375389
src = BytesIO(self.DATA)
376390
src.seekable = lambda: False
377-
bz2f = BZ2File(fileobj=src)
391+
bz2f = BZ2File(src)
378392
try:
379393
self.assertFalse(bz2f.seekable())
380394
finally:
381395
bz2f.close()
382396
self.assertRaises(ValueError, bz2f.seekable)
383397

384398
def testReadable(self):
385-
bz2f = BZ2File(fileobj=BytesIO(self.DATA))
399+
bz2f = BZ2File(BytesIO(self.DATA))
386400
try:
387401
self.assertTrue(bz2f.readable())
388402
bz2f.read()
@@ -391,15 +405,15 @@ def testReadable(self):
391405
bz2f.close()
392406
self.assertRaises(ValueError, bz2f.readable)
393407

394-
bz2f = BZ2File(fileobj=BytesIO(), mode="w")
408+
bz2f = BZ2File(BytesIO(), mode="w")
395409
try:
396410
self.assertFalse(bz2f.readable())
397411
finally:
398412
bz2f.close()
399413
self.assertRaises(ValueError, bz2f.readable)
400414

401415
def testWritable(self):
402-
bz2f = BZ2File(fileobj=BytesIO(self.DATA))
416+
bz2f = BZ2File(BytesIO(self.DATA))
403417
try:
404418
self.assertFalse(bz2f.writable())
405419
bz2f.read()
@@ -408,7 +422,7 @@ def testWritable(self):
408422
bz2f.close()
409423
self.assertRaises(ValueError, bz2f.writable)
410424

411-
bz2f = BZ2File(fileobj=BytesIO(), mode="w")
425+
bz2f = BZ2File(BytesIO(), mode="w")
412426
try:
413427
self.assertTrue(bz2f.writable())
414428
finally:
@@ -512,37 +526,37 @@ def testMultiStreamOrdering(self):
512526

513527
def testReadBytesIO(self):
514528
with BytesIO(self.DATA) as bio:
515-
with BZ2File(fileobj=bio) as bz2f:
529+
with BZ2File(bio) as bz2f:
516530
self.assertRaises(TypeError, bz2f.read, None)
517531
self.assertEqual(bz2f.read(), self.TEXT)
518532
self.assertFalse(bio.closed)
519533

520534
def testPeekBytesIO(self):
521535
with BytesIO(self.DATA) as bio:
522-
with BZ2File(fileobj=bio) as bz2f:
536+
with BZ2File(bio) as bz2f:
523537
pdata = bz2f.peek()
524538
self.assertNotEqual(len(pdata), 0)
525539
self.assertTrue(self.TEXT.startswith(pdata))
526540
self.assertEqual(bz2f.read(), self.TEXT)
527541

528542
def testWriteBytesIO(self):
529543
with BytesIO() as bio:
530-
with BZ2File(fileobj=bio, mode="w") as bz2f:
544+
with BZ2File(bio, "w") as bz2f:
531545
self.assertRaises(TypeError, bz2f.write)
532546
bz2f.write(self.TEXT)
533547
self.assertEqual(self.decompress(bio.getvalue()), self.TEXT)
534548
self.assertFalse(bio.closed)
535549

536550
def testSeekForwardBytesIO(self):
537551
with BytesIO(self.DATA) as bio:
538-
with BZ2File(fileobj=bio) as bz2f:
552+
with BZ2File(bio) as bz2f:
539553
self.assertRaises(TypeError, bz2f.seek)
540554
bz2f.seek(150)
541555
self.assertEqual(bz2f.read(), self.TEXT[150:])
542556

543557
def testSeekBackwardsBytesIO(self):
544558
with BytesIO(self.DATA) as bio:
545-
with BZ2File(fileobj=bio) as bz2f:
559+
with BZ2File(bio) as bz2f:
546560
bz2f.read(500)
547561
bz2f.seek(-150, 1)
548562
self.assertEqual(bz2f.read(), self.TEXT[500-150:])

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ Core and Builtins
1515
Library
1616
-------
1717

18+
- BZ2File.__init__() now accepts a file object as its first argument, rather
19+
than requiring a separate "fileobj" argument.
20+
1821
- gzip.open() now accepts file objects as well as filenames.
1922

2023
- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError

0 commit comments

Comments
 (0)