summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNadeem Vawda <nadeem.vawda@gmail.com>2013-10-18 22:11:06 (GMT)
committerNadeem Vawda <nadeem.vawda@gmail.com>2013-10-18 22:11:06 (GMT)
commit8a9e99cffcb579c208ccf92454be931e8a26dc39 (patch)
tree94d3e3b07c08dd9db8c6765c0693c6dc0e23fe09
parent42ca98217ca544220fdf4d33875c811f342edc56 (diff)
downloadcpython-8a9e99cffcb579c208ccf92454be931e8a26dc39.zip
cpython-8a9e99cffcb579c208ccf92454be931e8a26dc39.tar.gz
cpython-8a9e99cffcb579c208ccf92454be931e8a26dc39.tar.bz2
Issue #19223: Add support for the 'x' mode to the bz2 module.
Patch by Tim Heaney and Vajrasky Kok.
-rw-r--r--Doc/library/bz2.rst15
-rw-r--r--Lib/bz2.py16
-rw-r--r--Lib/test/test_bz2.py93
-rw-r--r--Misc/NEWS4
4 files changed, 80 insertions, 48 deletions
diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst
index d06a39a..44e1331 100644
--- a/Doc/library/bz2.rst
+++ b/Doc/library/bz2.rst
@@ -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
---------------------------
diff --git a/Lib/bz2.py b/Lib/bz2.py
index 6e6a2b9..6bc611e 100644
--- a/Lib/bz2.py
+++ b/Lib/bz2.py
@@ -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,
diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py
index 7090cd6..d087cc3 100644
--- a/Lib/test/test_bz2.py
+++ b/Lib/test/test_bz2.py
@@ -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:
@@ -773,6 +792,8 @@ class OpenTest(BaseTest):
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,
self.open, self.filename, "rb", errors="ignore")
diff --git a/Misc/NEWS b/Misc/NEWS
index 4d514f5..33ce184 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.