Issue #19223: Add support for the 'x' mode to the bz2 module.

Patch by Tim Heaney and Vajrasky Kok.
This commit is contained in:
Nadeem Vawda 2013-10-19 00:11:06 +02:00
parent 42ca98217c
commit 8a9e99cffc
4 changed files with 80 additions and 48 deletions

View File

@ -37,8 +37,8 @@ All of the classes in this module may safely be accessed from multiple threads.
file object to read from or write to.
The *mode* argument can be any of ``'r'``, ``'rb'``, ``'w'``, ``'wb'``,
``'a'``, or ``'ab'`` for binary mode, or ``'rt'``, ``'wt'``, or ``'at'`` for
text mode. The default is ``'rb'``.
``'x'``, ``'xb'``, ``'a'`` or ``'ab'`` for binary mode, or ``'rt'``,
``'wt'``, ``'xt'``, or ``'at'`` for text mode. The default is ``'rb'``.
The *compresslevel* argument is an integer from 1 to 9, as for the
:class:`BZ2File` constructor.
@ -54,6 +54,9 @@ All of the classes in this module may safely be accessed from multiple threads.
.. versionadded:: 3.3
.. versionchanged:: 3.4
The ``'x'`` (exclusive creation) mode was added.
.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9)
@ -64,8 +67,9 @@ All of the classes in this module may safely be accessed from multiple threads.
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. These can equivalently be given as
``'rb'``, ``'wb'``, and ``'ab'`` respectively.
overwriting, ``'x'`` for exclusive creation, or ``'a'`` for appending. These
can equivalently be given as ``'rb'``, ``'wb'``, ``'xb'`` and ``'ab'``
respectively.
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'``.
@ -108,6 +112,9 @@ All of the classes in this module may safely be accessed from multiple threads.
The ``'a'`` (append) mode was added, along with support for reading
multi-stream files.
.. versionchanged:: 3.4
The ``'x'`` (exclusive creation) mode was added.
Incremental (de)compression
---------------------------

View File

@ -49,12 +49,12 @@ class BZ2File(io.BufferedIOBase):
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. These can equivalently be given as 'rb',
'wb', and 'ab'.
'x' for creating exclusively, or 'a' for appending. These can
equivalently be given as 'rb', 'wb', 'xb', and 'ab'.
buffering is ignored. Its use is deprecated.
If mode is 'w' or 'a', compresslevel can be a number between 1
If mode is 'w', 'x' or 'a', compresslevel can be a number between 1
and 9 specifying the level of compression: 1 produces the least
compression, and 9 (default) produces the most compression.
@ -87,6 +87,10 @@ class BZ2File(io.BufferedIOBase):
mode = "wb"
mode_code = _MODE_WRITE
self._compressor = BZ2Compressor(compresslevel)
elif mode in ("x", "xb"):
mode = "xb"
mode_code = _MODE_WRITE
self._compressor = BZ2Compressor(compresslevel)
elif mode in ("a", "ab"):
mode = "ab"
mode_code = _MODE_WRITE
@ -443,9 +447,9 @@ def open(filename, mode="rb", compresslevel=9,
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.
The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or
"ab" for binary mode, or "rt", "wt", "xt" or "at" for text mode.
The default mode is "rb", and the default compresslevel is 9.
For binary mode, this function is equivalent to the BZ2File
constructor: BZ2File(filename, mode, compresslevel). In this case,

View File

@ -8,6 +8,7 @@ import os
import random
import subprocess
import sys
from test.support import unlink
try:
import threading
@ -715,49 +716,67 @@ class OpenTest(BaseTest):
return bz2.open(*args, **kwargs)
def test_binary_modes(self):
with self.open(self.filename, "wb") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT)
with self.open(self.filename, "rb") as f:
self.assertEqual(f.read(), self.TEXT)
with self.open(self.filename, "ab") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT * 2)
for mode in ("wb", "xb"):
if mode == "xb":
unlink(self.filename)
with self.open(self.filename, mode) as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT)
with self.open(self.filename, "rb") as f:
self.assertEqual(f.read(), self.TEXT)
with self.open(self.filename, "ab") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT * 2)
def test_implicit_binary_modes(self):
# Test implicit binary modes (no "b" or "t" in mode string).
with self.open(self.filename, "w") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT)
with self.open(self.filename, "r") as f:
self.assertEqual(f.read(), self.TEXT)
with self.open(self.filename, "a") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT * 2)
for mode in ("w", "x"):
if mode == "x":
unlink(self.filename)
with self.open(self.filename, mode) as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT)
with self.open(self.filename, "r") as f:
self.assertEqual(f.read(), self.TEXT)
with self.open(self.filename, "a") as f:
f.write(self.TEXT)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read())
self.assertEqual(file_data, self.TEXT * 2)
def test_text_modes(self):
text = self.TEXT.decode("ascii")
text_native_eol = text.replace("\n", os.linesep)
with self.open(self.filename, "wt") as f:
f.write(text)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read()).decode("ascii")
self.assertEqual(file_data, text_native_eol)
with self.open(self.filename, "rt") as f:
self.assertEqual(f.read(), text)
with self.open(self.filename, "at") as f:
f.write(text)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read()).decode("ascii")
self.assertEqual(file_data, text_native_eol * 2)
for mode in ("wt", "xt"):
if mode == "xt":
unlink(self.filename)
with self.open(self.filename, mode) as f:
f.write(text)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read()).decode("ascii")
self.assertEqual(file_data, text_native_eol)
with self.open(self.filename, "rt") as f:
self.assertEqual(f.read(), text)
with self.open(self.filename, "at") as f:
f.write(text)
with open(self.filename, "rb") as f:
file_data = self.decompress(f.read()).decode("ascii")
self.assertEqual(file_data, text_native_eol * 2)
def test_x_mode(self):
for mode in ("x", "xb", "xt"):
unlink(self.filename)
with self.open(self.filename, mode) as f:
pass
with self.assertRaises(FileExistsError):
with self.open(self.filename, mode) as f:
pass
def test_fileobj(self):
with self.open(BytesIO(self.DATA), "r") as f:
@ -772,6 +791,8 @@ class OpenTest(BaseTest):
# Test invalid parameter combinations.
self.assertRaises(ValueError,
self.open, self.filename, "wbt")
self.assertRaises(ValueError,
self.open, self.filename, "xbt")
self.assertRaises(ValueError,
self.open, self.filename, "rb", encoding="utf-8")
self.assertRaises(ValueError,

View File

@ -54,8 +54,8 @@ Core and Builtins
Library
-------
- Issue #19201: Add "x" mode (exclusive creation) in opening file to lzma
module. Patch by Tim Heaney and Vajrasky Kok.
- Issues #19201, #19223: Add "x" mode (exclusive creation) in opening file to
bz2 and lzma modules. Patches by Tim Heaney and Vajrasky Kok.
- Fix a reference count leak in _sre.