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

Skip to content

Commit 8a9e99c

Browse files
committed
Issue #19223: Add support for the 'x' mode to the bz2 module.
Patch by Tim Heaney and Vajrasky Kok.
1 parent 42ca982 commit 8a9e99c

4 files changed

Lines changed: 80 additions & 48 deletions

File tree

Doc/library/bz2.rst

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ All of the classes in this module may safely be accessed from multiple threads.
3737
file object to read from or write to.
3838

3939
The *mode* argument can be any of ``'r'``, ``'rb'``, ``'w'``, ``'wb'``,
40-
``'a'``, or ``'ab'`` for binary mode, or ``'rt'``, ``'wt'``, or ``'at'`` for
41-
text mode. The default is ``'rb'``.
40+
``'x'``, ``'xb'``, ``'a'`` or ``'ab'`` for binary mode, or ``'rt'``,
41+
``'wt'``, ``'xt'``, or ``'at'`` for text mode. The default is ``'rb'``.
4242

4343
The *compresslevel* argument is an integer from 1 to 9, as for the
4444
:class:`BZ2File` constructor.
@@ -54,6 +54,9 @@ All of the classes in this module may safely be accessed from multiple threads.
5454

5555
.. versionadded:: 3.3
5656

57+
.. versionchanged:: 3.4
58+
The ``'x'`` (exclusive creation) mode was added.
59+
5760

5861
.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9)
5962

@@ -64,8 +67,9 @@ All of the classes in this module may safely be accessed from multiple threads.
6467
be used to read or write the compressed data.
6568

6669
The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for
67-
overwriting, or ``'a'`` for appending. These can equivalently be given as
68-
``'rb'``, ``'wb'``, and ``'ab'`` respectively.
70+
overwriting, ``'x'`` for exclusive creation, or ``'a'`` for appending. These
71+
can equivalently be given as ``'rb'``, ``'wb'``, ``'xb'`` and ``'ab'``
72+
respectively.
6973

7074
If *filename* is a file object (rather than an actual file name), a mode of
7175
``'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.
108112
The ``'a'`` (append) mode was added, along with support for reading
109113
multi-stream files.
110114

115+
.. versionchanged:: 3.4
116+
The ``'x'`` (exclusive creation) mode was added.
117+
111118

112119
Incremental (de)compression
113120
---------------------------

Lib/bz2.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ def __init__(self, filename, mode="r", buffering=None, compresslevel=9):
4949
which will be used to read or write the compressed data.
5050
5151
mode can be 'r' for reading (default), 'w' for (over)writing,
52-
or 'a' for appending. These can equivalently be given as 'rb',
53-
'wb', and 'ab'.
52+
'x' for creating exclusively, or 'a' for appending. These can
53+
equivalently be given as 'rb', 'wb', 'xb', and 'ab'.
5454
5555
buffering is ignored. Its use is deprecated.
5656
57-
If mode is 'w' or 'a', compresslevel can be a number between 1
57+
If mode is 'w', 'x' or 'a', compresslevel can be a number between 1
5858
and 9 specifying the level of compression: 1 produces the least
5959
compression, and 9 (default) produces the most compression.
6060
@@ -87,6 +87,10 @@ def __init__(self, filename, mode="r", buffering=None, compresslevel=9):
8787
mode = "wb"
8888
mode_code = _MODE_WRITE
8989
self._compressor = BZ2Compressor(compresslevel)
90+
elif mode in ("x", "xb"):
91+
mode = "xb"
92+
mode_code = _MODE_WRITE
93+
self._compressor = BZ2Compressor(compresslevel)
9094
elif mode in ("a", "ab"):
9195
mode = "ab"
9296
mode_code = _MODE_WRITE
@@ -443,9 +447,9 @@ def open(filename, mode="rb", compresslevel=9,
443447
The filename argument can be an actual filename (a str or bytes
444448
object), or an existing file object to read from or write to.
445449
446-
The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for
447-
binary mode, or "rt", "wt" or "at" for text mode. The default mode
448-
is "rb", and the default compresslevel is 9.
450+
The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or
451+
"ab" for binary mode, or "rt", "wt", "xt" or "at" for text mode.
452+
The default mode is "rb", and the default compresslevel is 9.
449453
450454
For binary mode, this function is equivalent to the BZ2File
451455
constructor: BZ2File(filename, mode, compresslevel). In this case,

Lib/test/test_bz2.py

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import random
99
import subprocess
1010
import sys
11+
from test.support import unlink
1112

1213
try:
1314
import threading
@@ -715,49 +716,67 @@ def open(self, *args, **kwargs):
715716
return bz2.open(*args, **kwargs)
716717

717718
def test_binary_modes(self):
718-
with self.open(self.filename, "wb") as f:
719-
f.write(self.TEXT)
720-
with open(self.filename, "rb") as f:
721-
file_data = self.decompress(f.read())
722-
self.assertEqual(file_data, self.TEXT)
723-
with self.open(self.filename, "rb") as f:
724-
self.assertEqual(f.read(), self.TEXT)
725-
with self.open(self.filename, "ab") as f:
726-
f.write(self.TEXT)
727-
with open(self.filename, "rb") as f:
728-
file_data = self.decompress(f.read())
729-
self.assertEqual(file_data, self.TEXT * 2)
719+
for mode in ("wb", "xb"):
720+
if mode == "xb":
721+
unlink(self.filename)
722+
with self.open(self.filename, mode) as f:
723+
f.write(self.TEXT)
724+
with open(self.filename, "rb") as f:
725+
file_data = self.decompress(f.read())
726+
self.assertEqual(file_data, self.TEXT)
727+
with self.open(self.filename, "rb") as f:
728+
self.assertEqual(f.read(), self.TEXT)
729+
with self.open(self.filename, "ab") as f:
730+
f.write(self.TEXT)
731+
with open(self.filename, "rb") as f:
732+
file_data = self.decompress(f.read())
733+
self.assertEqual(file_data, self.TEXT * 2)
730734

731735
def test_implicit_binary_modes(self):
732736
# Test implicit binary modes (no "b" or "t" in mode string).
733-
with self.open(self.filename, "w") as f:
734-
f.write(self.TEXT)
735-
with open(self.filename, "rb") as f:
736-
file_data = self.decompress(f.read())
737-
self.assertEqual(file_data, self.TEXT)
738-
with self.open(self.filename, "r") as f:
739-
self.assertEqual(f.read(), self.TEXT)
740-
with self.open(self.filename, "a") as f:
741-
f.write(self.TEXT)
742-
with open(self.filename, "rb") as f:
743-
file_data = self.decompress(f.read())
744-
self.assertEqual(file_data, self.TEXT * 2)
737+
for mode in ("w", "x"):
738+
if mode == "x":
739+
unlink(self.filename)
740+
with self.open(self.filename, mode) as f:
741+
f.write(self.TEXT)
742+
with open(self.filename, "rb") as f:
743+
file_data = self.decompress(f.read())
744+
self.assertEqual(file_data, self.TEXT)
745+
with self.open(self.filename, "r") as f:
746+
self.assertEqual(f.read(), self.TEXT)
747+
with self.open(self.filename, "a") as f:
748+
f.write(self.TEXT)
749+
with open(self.filename, "rb") as f:
750+
file_data = self.decompress(f.read())
751+
self.assertEqual(file_data, self.TEXT * 2)
745752

746753
def test_text_modes(self):
747754
text = self.TEXT.decode("ascii")
748755
text_native_eol = text.replace("\n", os.linesep)
749-
with self.open(self.filename, "wt") as f:
750-
f.write(text)
751-
with open(self.filename, "rb") as f:
752-
file_data = self.decompress(f.read()).decode("ascii")
753-
self.assertEqual(file_data, text_native_eol)
754-
with self.open(self.filename, "rt") as f:
755-
self.assertEqual(f.read(), text)
756-
with self.open(self.filename, "at") as f:
757-
f.write(text)
758-
with open(self.filename, "rb") as f:
759-
file_data = self.decompress(f.read()).decode("ascii")
760-
self.assertEqual(file_data, text_native_eol * 2)
756+
for mode in ("wt", "xt"):
757+
if mode == "xt":
758+
unlink(self.filename)
759+
with self.open(self.filename, mode) as f:
760+
f.write(text)
761+
with open(self.filename, "rb") as f:
762+
file_data = self.decompress(f.read()).decode("ascii")
763+
self.assertEqual(file_data, text_native_eol)
764+
with self.open(self.filename, "rt") as f:
765+
self.assertEqual(f.read(), text)
766+
with self.open(self.filename, "at") as f:
767+
f.write(text)
768+
with open(self.filename, "rb") as f:
769+
file_data = self.decompress(f.read()).decode("ascii")
770+
self.assertEqual(file_data, text_native_eol * 2)
771+
772+
def test_x_mode(self):
773+
for mode in ("x", "xb", "xt"):
774+
unlink(self.filename)
775+
with self.open(self.filename, mode) as f:
776+
pass
777+
with self.assertRaises(FileExistsError):
778+
with self.open(self.filename, mode) as f:
779+
pass
761780

762781
def test_fileobj(self):
763782
with self.open(BytesIO(self.DATA), "r") as f:
@@ -772,6 +791,8 @@ def test_bad_params(self):
772791
# Test invalid parameter combinations.
773792
self.assertRaises(ValueError,
774793
self.open, self.filename, "wbt")
794+
self.assertRaises(ValueError,
795+
self.open, self.filename, "xbt")
775796
self.assertRaises(ValueError,
776797
self.open, self.filename, "rb", encoding="utf-8")
777798
self.assertRaises(ValueError,

Misc/NEWS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ Core and Builtins
5454
Library
5555
-------
5656

57-
- Issue #19201: Add "x" mode (exclusive creation) in opening file to lzma
58-
module. Patch by Tim Heaney and Vajrasky Kok.
57+
- Issues #19201, #19223: Add "x" mode (exclusive creation) in opening file to
58+
bz2 and lzma modules. Patches by Tim Heaney and Vajrasky Kok.
5959

6060
- Fix a reference count leak in _sre.
6161

0 commit comments

Comments
 (0)