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

Skip to content

Commit f6b16a4

Browse files
committed
Issue #14371: Support bzip2 in zipfile module.
Patch by Serhiy Storchaka.
1 parent 9acbb60 commit f6b16a4

5 files changed

Lines changed: 333 additions & 97 deletions

File tree

Doc/library/zipfile.rst

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,22 @@ The module defines the following items:
8787
.. data:: ZIP_DEFLATED
8888

8989
The numeric constant for the usual ZIP compression method. This requires the
90-
zlib module. No other compression methods are currently supported.
90+
zlib module.
91+
92+
93+
.. data:: ZIP_BZIP2
94+
95+
The numeric constant for the BZIP2 compression method. This requires the
96+
bz2 module.
97+
98+
.. versionadded:: 3.3
99+
100+
.. note::
101+
102+
The ZIP file format specification has included support for bzip2 compression
103+
since 2001. However, some tools (including older Python releases) do not
104+
support it, and may either refuse to process the ZIP file altogether, or
105+
fail to extract individual files.
91106

92107

93108
.. seealso::
@@ -118,9 +133,11 @@ ZipFile Objects
118133
adding a ZIP archive to another file (such as :file:`python.exe`). If
119134
*mode* is ``a`` and the file does not exist at all, it is created.
120135
*compression* is the ZIP compression method to use when writing the archive,
121-
and should be :const:`ZIP_STORED` or :const:`ZIP_DEFLATED`; unrecognized
122-
values will cause :exc:`RuntimeError` to be raised. If :const:`ZIP_DEFLATED`
123-
is specified but the :mod:`zlib` module is not available, :exc:`RuntimeError`
136+
and should be :const:`ZIP_STORED`, :const:`ZIP_DEFLATED`; or
137+
:const:`ZIP_DEFLATED`; unrecognized
138+
values will cause :exc:`RuntimeError` to be raised. If :const:`ZIP_DEFLATED` or
139+
:const:`ZIP_BZIP2` is specified but the corresponded module
140+
(:mod:`zlib` or :mod:`bz2`) is not available, :exc:`RuntimeError`
124141
is also raised. The default is :const:`ZIP_STORED`. If *allowZip64* is
125142
``True`` zipfile will create ZIP files that use the ZIP64 extensions when
126143
the zipfile is larger than 2 GB. If it is false (the default) :mod:`zipfile`
@@ -143,6 +160,9 @@ ZipFile Objects
143160
.. versionadded:: 3.2
144161
Added the ability to use :class:`ZipFile` as a context manager.
145162

163+
.. versionchanged:: 3.3
164+
Added support for :mod:`bzip2` compression.
165+
146166

147167
.. method:: ZipFile.close()
148168

Lib/test/support.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
except ImportError:
4141
zlib = None
4242

43+
try:
44+
import bz2
45+
except ImportError:
46+
bz2 = None
47+
4348
__all__ = [
4449
"Error", "TestFailed", "ResourceDenied", "import_module",
4550
"verbose", "use_resources", "max_memuse", "record_original_stdout",
@@ -57,7 +62,7 @@
5762
"get_attribute", "swap_item", "swap_attr", "requires_IEEE_754",
5863
"TestHandler", "Matcher", "can_symlink", "skip_unless_symlink",
5964
"import_fresh_module", "requires_zlib", "PIPE_MAX_SIZE", "failfast",
60-
"anticipate_failure", "run_with_tz"
65+
"anticipate_failure", "run_with_tz", "requires_bz2"
6166
]
6267

6368
class Error(Exception):
@@ -506,6 +511,8 @@ def _is_ipv6_enabled():
506511

507512
requires_zlib = unittest.skipUnless(zlib, 'requires zlib')
508513

514+
requires_bz2 = unittest.skipUnless(bz2, 'requires bz2')
515+
509516
is_jython = sys.platform.startswith('java')
510517

511518
# Filename used for testing

Lib/test/test_zipfile.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from random import randint, random
1414
from unittest import skipUnless
1515

16-
from test.support import TESTFN, run_unittest, findfile, unlink, requires_zlib
16+
from test.support import TESTFN, run_unittest, findfile, unlink, requires_zlib, requires_bz2
1717

1818
TESTFN2 = TESTFN + "2"
1919
TESTFNDIR = TESTFN + "d"
@@ -313,6 +313,54 @@ def test_low_compression(self):
313313
self.assertEqual(openobj.read(1), b'1')
314314
self.assertEqual(openobj.read(1), b'2')
315315

316+
@requires_bz2
317+
def test_bzip2(self):
318+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
319+
self.zip_test(f, zipfile.ZIP_BZIP2)
320+
321+
@requires_bz2
322+
def test_open_bzip2(self):
323+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
324+
self.zip_open_test(f, zipfile.ZIP_BZIP2)
325+
326+
@requires_bz2
327+
def test_random_open_bzip2(self):
328+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
329+
self.zip_random_open_test(f, zipfile.ZIP_BZIP2)
330+
331+
@requires_bz2
332+
def test_readline_read_bzip2(self):
333+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
334+
self.zip_readline_read_test(f, zipfile.ZIP_BZIP2)
335+
336+
@requires_bz2
337+
def test_readline_bzip2(self):
338+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
339+
self.zip_readline_test(f, zipfile.ZIP_BZIP2)
340+
341+
@requires_bz2
342+
def test_readlines_bzip2(self):
343+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
344+
self.zip_readlines_test(f, zipfile.ZIP_BZIP2)
345+
346+
@requires_bz2
347+
def test_iterlines_bzip2(self):
348+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
349+
self.zip_iterlines_test(f, zipfile.ZIP_BZIP2)
350+
351+
@requires_bz2
352+
def test_low_compression_bzip2(self):
353+
"""Check for cases where compressed data is larger than original."""
354+
# Create the ZIP archive
355+
with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_BZIP2) as zipfp:
356+
zipfp.writestr("strfile", '12')
357+
358+
# Get an open object for strfile
359+
with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_BZIP2) as zipfp:
360+
with zipfp.open("strfile") as openobj:
361+
self.assertEqual(openobj.read(1), b'1')
362+
self.assertEqual(openobj.read(1), b'2')
363+
316364
def test_absolute_arcnames(self):
317365
with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
318366
zipfp.write(TESTFN, "/absolute")
@@ -453,6 +501,13 @@ def test_writestr_compression_deflated(self):
453501
info = zipfp.getinfo('b.txt')
454502
self.assertEqual(info.compress_type, zipfile.ZIP_DEFLATED)
455503

504+
@requires_bz2
505+
def test_writestr_compression_bzip2(self):
506+
zipfp = zipfile.ZipFile(TESTFN2, "w")
507+
zipfp.writestr("b.txt", "hello world", compress_type=zipfile.ZIP_BZIP2)
508+
info = zipfp.getinfo('b.txt')
509+
self.assertEqual(info.compress_type, zipfile.ZIP_BZIP2)
510+
456511
def zip_test_writestr_permissions(self, f, compression):
457512
# Make sure that writestr creates files with mode 0600,
458513
# when it is passed a name rather than a ZipInfo instance.
@@ -626,6 +681,11 @@ def test_deflated(self):
626681
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
627682
self.zip_test(f, zipfile.ZIP_DEFLATED)
628683

684+
@requires_bz2
685+
def test_bzip2(self):
686+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
687+
self.zip_test(f, zipfile.ZIP_BZIP2)
688+
629689
def test_absolute_arcnames(self):
630690
with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
631691
allowZip64=True) as zipfp:
@@ -754,6 +814,18 @@ class OtherTests(unittest.TestCase):
754814
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
755815
b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
756816
b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00'),
817+
zipfile.ZIP_BZIP2: (
818+
b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
819+
b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
820+
b'ileBZh91AY&SY\xd4\xa8\xca'
821+
b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
822+
b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
823+
b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
824+
b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
825+
b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
826+
b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
827+
b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
828+
b'\x00\x00\x00\x00'),
757829
}
758830

759831
def test_unicode_filenames(self):
@@ -1007,6 +1079,10 @@ def test_testzip_with_bad_crc_stored(self):
10071079
def test_testzip_with_bad_crc_deflated(self):
10081080
self.check_testzip_with_bad_crc(zipfile.ZIP_DEFLATED)
10091081

1082+
@requires_bz2
1083+
def test_testzip_with_bad_crc_bzip2(self):
1084+
self.check_testzip_with_bad_crc(zipfile.ZIP_BZIP2)
1085+
10101086
def check_read_with_bad_crc(self, compression):
10111087
"""Tests that files with bad CRCs raise a BadZipFile exception when read."""
10121088
zipdata = self.zips_with_bad_crc[compression]
@@ -1035,6 +1111,10 @@ def test_read_with_bad_crc_stored(self):
10351111
def test_read_with_bad_crc_deflated(self):
10361112
self.check_read_with_bad_crc(zipfile.ZIP_DEFLATED)
10371113

1114+
@requires_bz2
1115+
def test_read_with_bad_crc_bzip2(self):
1116+
self.check_read_with_bad_crc(zipfile.ZIP_BZIP2)
1117+
10381118
def check_read_return_size(self, compression):
10391119
# Issue #9837: ZipExtFile.read() shouldn't return more bytes
10401120
# than requested.
@@ -1055,6 +1135,10 @@ def test_read_return_size_stored(self):
10551135
def test_read_return_size_deflated(self):
10561136
self.check_read_return_size(zipfile.ZIP_DEFLATED)
10571137

1138+
@requires_bz2
1139+
def test_read_return_size_bzip2(self):
1140+
self.check_read_return_size(zipfile.ZIP_BZIP2)
1141+
10581142
def test_empty_zipfile(self):
10591143
# Check that creating a file in 'w' or 'a' mode and closing without
10601144
# adding any files to the archives creates a valid empty ZIP file
@@ -1196,6 +1280,11 @@ def test_deflated(self):
11961280
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
11971281
self.zip_test(f, zipfile.ZIP_DEFLATED)
11981282

1283+
@requires_bz2
1284+
def test_bzip2(self):
1285+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1286+
self.zip_test(f, zipfile.ZIP_BZIP2)
1287+
11991288
def zip_open_test(self, f, compression):
12001289
self.make_test_archive(f, compression)
12011290

@@ -1236,6 +1325,11 @@ def test_open_deflated(self):
12361325
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
12371326
self.zip_open_test(f, zipfile.ZIP_DEFLATED)
12381327

1328+
@requires_bz2
1329+
def test_open_bzip2(self):
1330+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1331+
self.zip_open_test(f, zipfile.ZIP_BZIP2)
1332+
12391333
def zip_random_open_test(self, f, compression):
12401334
self.make_test_archive(f, compression)
12411335

@@ -1264,6 +1358,11 @@ def test_random_open_deflated(self):
12641358
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
12651359
self.zip_random_open_test(f, zipfile.ZIP_DEFLATED)
12661360

1361+
@requires_bz2
1362+
def test_random_open_bzip2(self):
1363+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1364+
self.zip_random_open_test(f, zipfile.ZIP_BZIP2)
1365+
12671366

12681367
@requires_zlib
12691368
class TestsWithMultipleOpens(unittest.TestCase):
@@ -1483,6 +1582,31 @@ def test_iterlines_deflated(self):
14831582
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
14841583
self.iterlines_test(f, zipfile.ZIP_DEFLATED)
14851584

1585+
@requires_bz2
1586+
def test_read_bzip2(self):
1587+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1588+
self.read_test(f, zipfile.ZIP_BZIP2)
1589+
1590+
@requires_bz2
1591+
def test_readline_read_bzip2(self):
1592+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1593+
self.readline_read_test(f, zipfile.ZIP_BZIP2)
1594+
1595+
@requires_bz2
1596+
def test_readline_bzip2(self):
1597+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1598+
self.readline_test(f, zipfile.ZIP_BZIP2)
1599+
1600+
@requires_bz2
1601+
def test_readlines_bzip2(self):
1602+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1603+
self.readlines_test(f, zipfile.ZIP_BZIP2)
1604+
1605+
@requires_bz2
1606+
def test_iterlines_bzip2(self):
1607+
for f in (TESTFN2, TemporaryFile(), io.BytesIO()):
1608+
self.iterlines_test(f, zipfile.ZIP_BZIP2)
1609+
14861610
def tearDown(self):
14871611
for sep, fn in self.arcfiles.items():
14881612
os.remove(fn)

0 commit comments

Comments
 (0)