blob: b7bc218d17a3d966171cdd98c5750720b4f78fed [file] [log] [blame]
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02001import contextlib
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002import importlib.util
Ezio Melotti74c96ec2009-07-08 22:24:06 +00003import io
Daniel Hillierda6ce582019-10-29 18:24:18 +11004import itertools
Ezio Melotti74c96ec2009-07-08 22:24:06 +00005import os
Serhiy Storchaka8606e952017-03-08 14:37:51 +02006import pathlib
Serhiy Storchaka503f9082016-02-08 00:02:25 +02007import posixpath
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06008import string
Ezio Melotti74c96ec2009-07-08 22:24:06 +00009import struct
Gregory P. Smith3f4db4a2019-09-10 17:14:11 +010010import subprocess
11import sys
Jason R. Coombsb2758ff2019-05-08 09:45:06 -040012import time
Ezio Melotti74c96ec2009-07-08 22:24:06 +000013import unittest
Berker Peksag2f1b8572019-09-12 17:13:44 +030014import unittest.mock as mock
Jason R. Coombsb2758ff2019-05-08 09:45:06 -040015import zipfile
Ezio Melotti74c96ec2009-07-08 22:24:06 +000016
Tim Petersa45cacf2004-08-20 03:47:14 +000017
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000018from tempfile import TemporaryFile
Victor Stinner87502dd2020-04-17 22:54:38 +020019from random import randint, random, randbytes
Tim Petersa19a1682001-03-29 04:36:09 +000020
Serhiy Storchaka61c4c442016-10-23 13:07:59 +030021from test.support import script_helper
Serhiy Storchaka8606e952017-03-08 14:37:51 +020022from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir, temp_cwd,
Serhiy Storchakac5b75db2013-01-29 20:14:08 +020023 requires_zlib, requires_bz2, requires_lzma,
Victor Stinnerd6debb22017-03-27 16:05:26 +020024 captured_stdout)
Guido van Rossum368f04a2000-04-10 13:23:04 +000025
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000026TESTFN2 = TESTFN + "2"
Martin v. Löwis59e47792009-01-24 14:10:07 +000027TESTFNDIR = TESTFN + "d"
Guido van Rossumb5a755e2007-07-18 18:15:48 +000028FIXEDTEST_SIZE = 1000
Georg Brandl5ba11de2011-01-01 10:09:32 +000029DATAFILES_DIR = 'zipfile_datafiles'
Guido van Rossum368f04a2000-04-10 13:23:04 +000030
Christian Heimes790c8232008-01-07 21:14:23 +000031SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
32 ('ziptest2dir/_ziptest2', 'qawsedrftg'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -080033 ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
Christian Heimes790c8232008-01-07 21:14:23 +000034 ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]
35
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030036def get_files(test):
37 yield TESTFN2
38 with TemporaryFile() as f:
39 yield f
40 test.assertFalse(f.closed)
41 with io.BytesIO() as f:
42 yield f
43 test.assertFalse(f.closed)
Ezio Melotti76430242009-07-11 18:28:48 +000044
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030045class AbstractTestsWithSourceFile:
46 @classmethod
47 def setUpClass(cls):
48 cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" %
49 (i, random()), "ascii")
50 for i in range(FIXEDTEST_SIZE)]
51 cls.data = b''.join(cls.line_gen)
52
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000053 def setUp(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000054 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +000055 with open(TESTFN, "wb") as fp:
56 fp.write(self.data)
Tim Peters7d3bad62001-04-04 18:56:49 +000057
Bo Baylesce237c72018-01-29 23:54:07 -060058 def make_test_archive(self, f, compression, compresslevel=None):
59 kwargs = {'compression': compression, 'compresslevel': compresslevel}
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000060 # Create the ZIP archive
Bo Baylesce237c72018-01-29 23:54:07 -060061 with zipfile.ZipFile(f, "w", **kwargs) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000062 zipfp.write(TESTFN, "another.name")
63 zipfp.write(TESTFN, TESTFN)
64 zipfp.writestr("strfile", self.data)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030065 with zipfp.open('written-open-w', mode='w') as f:
66 for line in self.line_gen:
67 f.write(line)
Tim Peters7d3bad62001-04-04 18:56:49 +000068
Bo Baylesce237c72018-01-29 23:54:07 -060069 def zip_test(self, f, compression, compresslevel=None):
70 self.make_test_archive(f, compression, compresslevel)
Guido van Rossumd8faa362007-04-27 19:54:29 +000071
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000072 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000073 with zipfile.ZipFile(f, "r", compression) as zipfp:
74 self.assertEqual(zipfp.read(TESTFN), self.data)
75 self.assertEqual(zipfp.read("another.name"), self.data)
76 self.assertEqual(zipfp.read("strfile"), self.data)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000077
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000078 # Print the ZIP directory
79 fp = io.StringIO()
80 zipfp.printdir(file=fp)
81 directory = fp.getvalue()
82 lines = directory.splitlines()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030083 self.assertEqual(len(lines), 5) # Number of files + header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000084
Benjamin Peterson577473f2010-01-19 00:09:57 +000085 self.assertIn('File Name', lines[0])
86 self.assertIn('Modified', lines[0])
87 self.assertIn('Size', lines[0])
Thomas Wouters0e3f5912006-08-11 14:57:12 +000088
Ezio Melotti35386712009-12-31 13:22:41 +000089 fn, date, time_, size = lines[1].split()
90 self.assertEqual(fn, 'another.name')
91 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
92 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
93 self.assertEqual(size, str(len(self.data)))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000094
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000095 # Check the namelist
96 names = zipfp.namelist()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030097 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +000098 self.assertIn(TESTFN, names)
99 self.assertIn("another.name", names)
100 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300101 self.assertIn("written-open-w", names)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000102
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000103 # Check infolist
104 infos = zipfp.infolist()
Ezio Melotti35386712009-12-31 13:22:41 +0000105 names = [i.filename for i in infos]
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300106 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000107 self.assertIn(TESTFN, names)
108 self.assertIn("another.name", names)
109 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300110 self.assertIn("written-open-w", names)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000111 for i in infos:
Ezio Melotti35386712009-12-31 13:22:41 +0000112 self.assertEqual(i.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000113
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000114 # check getinfo
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300115 for nm in (TESTFN, "another.name", "strfile", "written-open-w"):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000116 info = zipfp.getinfo(nm)
Ezio Melotti35386712009-12-31 13:22:41 +0000117 self.assertEqual(info.filename, nm)
118 self.assertEqual(info.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000119
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000120 # Check that testzip doesn't raise an exception
121 zipfp.testzip()
Tim Peters7d3bad62001-04-04 18:56:49 +0000122
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300123 def test_basic(self):
124 for f in get_files(self):
125 self.zip_test(f, self.compression)
Raymond Hettingerc0fac962003-06-27 22:25:03 +0000126
Ezio Melottiafd0d112009-07-15 17:17:17 +0000127 def zip_open_test(self, f, compression):
128 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000129
130 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000131 with zipfile.ZipFile(f, "r", compression) as zipfp:
132 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000133 with zipfp.open(TESTFN) as zipopen1:
134 while True:
135 read_data = zipopen1.read(256)
136 if not read_data:
137 break
138 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000139
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000140 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000141 with zipfp.open("another.name") as zipopen2:
142 while True:
143 read_data = zipopen2.read(256)
144 if not read_data:
145 break
146 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000147
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000148 self.assertEqual(b''.join(zipdata1), self.data)
149 self.assertEqual(b''.join(zipdata2), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000150
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300151 def test_open(self):
152 for f in get_files(self):
153 self.zip_open_test(f, self.compression)
Georg Brandlb533e262008-05-25 18:19:30 +0000154
Serhiy Storchaka8606e952017-03-08 14:37:51 +0200155 def test_open_with_pathlike(self):
156 path = pathlib.Path(TESTFN2)
157 self.zip_open_test(path, self.compression)
158 with zipfile.ZipFile(path, "r", self.compression) as zipfp:
159 self.assertIsInstance(zipfp.filename, str)
160
Ezio Melottiafd0d112009-07-15 17:17:17 +0000161 def zip_random_open_test(self, f, compression):
162 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000163
164 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000165 with zipfile.ZipFile(f, "r", compression) as zipfp:
166 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000167 with zipfp.open(TESTFN) as zipopen1:
168 while True:
169 read_data = zipopen1.read(randint(1, 1024))
170 if not read_data:
171 break
172 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000173
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000174 self.assertEqual(b''.join(zipdata1), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000175
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300176 def test_random_open(self):
177 for f in get_files(self):
178 self.zip_random_open_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000179
Serhiy Storchakad2c07a52013-09-27 22:11:57 +0300180 def zip_read1_test(self, f, compression):
181 self.make_test_archive(f, compression)
182
183 # Read the ZIP archive
184 with zipfile.ZipFile(f, "r") as zipfp, \
185 zipfp.open(TESTFN) as zipopen:
186 zipdata = []
187 while True:
188 read_data = zipopen.read1(-1)
189 if not read_data:
190 break
191 zipdata.append(read_data)
192
193 self.assertEqual(b''.join(zipdata), self.data)
194
195 def test_read1(self):
196 for f in get_files(self):
197 self.zip_read1_test(f, self.compression)
198
199 def zip_read1_10_test(self, f, compression):
200 self.make_test_archive(f, compression)
201
202 # Read the ZIP archive
203 with zipfile.ZipFile(f, "r") as zipfp, \
204 zipfp.open(TESTFN) as zipopen:
205 zipdata = []
206 while True:
207 read_data = zipopen.read1(10)
208 self.assertLessEqual(len(read_data), 10)
209 if not read_data:
210 break
211 zipdata.append(read_data)
212
213 self.assertEqual(b''.join(zipdata), self.data)
214
215 def test_read1_10(self):
216 for f in get_files(self):
217 self.zip_read1_10_test(f, self.compression)
218
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000219 def zip_readline_read_test(self, f, compression):
220 self.make_test_archive(f, compression)
221
222 # Read the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300223 with zipfile.ZipFile(f, "r") as zipfp, \
224 zipfp.open(TESTFN) as zipopen:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000225 data = b''
226 while True:
227 read = zipopen.readline()
228 if not read:
229 break
230 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000231
Brian Curtin8fb9b862010-11-18 02:15:28 +0000232 read = zipopen.read(100)
233 if not read:
234 break
235 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000236
237 self.assertEqual(data, self.data)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300238
239 def test_readline_read(self):
240 # Issue #7610: calls to readline() interleaved with calls to read().
241 for f in get_files(self):
242 self.zip_readline_read_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000243
Ezio Melottiafd0d112009-07-15 17:17:17 +0000244 def zip_readline_test(self, f, compression):
245 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000246
247 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000248 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000249 with zipfp.open(TESTFN) as zipopen:
250 for line in self.line_gen:
251 linedata = zipopen.readline()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300252 self.assertEqual(linedata, line)
253
254 def test_readline(self):
255 for f in get_files(self):
256 self.zip_readline_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000257
Ezio Melottiafd0d112009-07-15 17:17:17 +0000258 def zip_readlines_test(self, f, compression):
259 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000260
261 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000262 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000263 with zipfp.open(TESTFN) as zipopen:
264 ziplines = zipopen.readlines()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000265 for line, zipline in zip(self.line_gen, ziplines):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300266 self.assertEqual(zipline, line)
267
268 def test_readlines(self):
269 for f in get_files(self):
270 self.zip_readlines_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000271
Ezio Melottiafd0d112009-07-15 17:17:17 +0000272 def zip_iterlines_test(self, f, compression):
273 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000274
275 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000276 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000277 with zipfp.open(TESTFN) as zipopen:
278 for line, zipline in zip(self.line_gen, zipopen):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300279 self.assertEqual(zipline, line)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000280
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300281 def test_iterlines(self):
282 for f in get_files(self):
283 self.zip_iterlines_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000284
Ezio Melottiafd0d112009-07-15 17:17:17 +0000285 def test_low_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000286 """Check for cases where compressed data is larger than original."""
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000287 # Create the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300288 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000289 zipfp.writestr("strfile", '12')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000290
291 # Get an open object for strfile
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300292 with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000293 with zipfp.open("strfile") as openobj:
294 self.assertEqual(openobj.read(1), b'1')
295 self.assertEqual(openobj.read(1), b'2')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000296
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300297 def test_writestr_compression(self):
298 zipfp = zipfile.ZipFile(TESTFN2, "w")
299 zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
300 info = zipfp.getinfo('b.txt')
301 self.assertEqual(info.compress_type, self.compression)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200302
Bo Baylesce237c72018-01-29 23:54:07 -0600303 def test_writestr_compresslevel(self):
304 zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1)
305 zipfp.writestr("a.txt", "hello world", compress_type=self.compression)
306 zipfp.writestr("b.txt", "hello world", compress_type=self.compression,
307 compresslevel=2)
308
309 # Compression level follows the constructor.
310 a_info = zipfp.getinfo('a.txt')
311 self.assertEqual(a_info.compress_type, self.compression)
312 self.assertEqual(a_info._compresslevel, 1)
313
314 # Compression level is overridden.
315 b_info = zipfp.getinfo('b.txt')
316 self.assertEqual(b_info.compress_type, self.compression)
317 self.assertEqual(b_info._compresslevel, 2)
318
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300319 def test_read_return_size(self):
320 # Issue #9837: ZipExtFile.read() shouldn't return more bytes
321 # than requested.
322 for test_size in (1, 4095, 4096, 4097, 16384):
323 file_size = test_size + 1
Victor Stinner87502dd2020-04-17 22:54:38 +0200324 junk = randbytes(file_size)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300325 with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf:
326 zipf.writestr('foo', junk)
327 with zipf.open('foo', 'r') as fp:
328 buf = fp.read(test_size)
329 self.assertEqual(len(buf), test_size)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200330
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200331 def test_truncated_zipfile(self):
332 fp = io.BytesIO()
333 with zipfile.ZipFile(fp, mode='w') as zipf:
334 zipf.writestr('strfile', self.data, compress_type=self.compression)
335 end_offset = fp.tell()
336 zipfiledata = fp.getvalue()
337
338 fp = io.BytesIO(zipfiledata)
339 with zipfile.ZipFile(fp) as zipf:
340 with zipf.open('strfile') as zipopen:
341 fp.truncate(end_offset - 20)
342 with self.assertRaises(EOFError):
343 zipopen.read()
344
345 fp = io.BytesIO(zipfiledata)
346 with zipfile.ZipFile(fp) as zipf:
347 with zipf.open('strfile') as zipopen:
348 fp.truncate(end_offset - 20)
349 with self.assertRaises(EOFError):
350 while zipopen.read(100):
351 pass
352
353 fp = io.BytesIO(zipfiledata)
354 with zipfile.ZipFile(fp) as zipf:
355 with zipf.open('strfile') as zipopen:
356 fp.truncate(end_offset - 20)
357 with self.assertRaises(EOFError):
358 while zipopen.read1(100):
359 pass
360
Serhiy Storchaka51a43702014-10-29 22:42:06 +0200361 def test_repr(self):
362 fname = 'file.name'
363 for f in get_files(self):
364 with zipfile.ZipFile(f, 'w', self.compression) as zipfp:
365 zipfp.write(TESTFN, fname)
366 r = repr(zipfp)
367 self.assertIn("mode='w'", r)
368
369 with zipfile.ZipFile(f, 'r') as zipfp:
370 r = repr(zipfp)
371 if isinstance(f, str):
372 self.assertIn('filename=%r' % f, r)
373 else:
374 self.assertIn('file=%r' % f, r)
375 self.assertIn("mode='r'", r)
376 r = repr(zipfp.getinfo(fname))
377 self.assertIn('filename=%r' % fname, r)
378 self.assertIn('filemode=', r)
379 self.assertIn('file_size=', r)
380 if self.compression != zipfile.ZIP_STORED:
381 self.assertIn('compress_type=', r)
382 self.assertIn('compress_size=', r)
383 with zipfp.open(fname) as zipopen:
384 r = repr(zipopen)
385 self.assertIn('name=%r' % fname, r)
386 self.assertIn("mode='r'", r)
387 if self.compression != zipfile.ZIP_STORED:
388 self.assertIn('compress_type=', r)
389 self.assertIn('[closed]', repr(zipopen))
390 self.assertIn('[closed]', repr(zipfp))
391
Bo Baylesce237c72018-01-29 23:54:07 -0600392 def test_compresslevel_basic(self):
393 for f in get_files(self):
394 self.zip_test(f, self.compression, compresslevel=9)
395
396 def test_per_file_compresslevel(self):
397 """Check that files within a Zip archive can have different
398 compression levels."""
399 with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp:
400 zipfp.write(TESTFN, 'compress_1')
401 zipfp.write(TESTFN, 'compress_9', compresslevel=9)
402 one_info = zipfp.getinfo('compress_1')
403 nine_info = zipfp.getinfo('compress_9')
404 self.assertEqual(one_info._compresslevel, 1)
405 self.assertEqual(nine_info._compresslevel, 9)
406
Serhiy Storchaka2524fde2019-03-30 08:25:19 +0200407 def test_writing_errors(self):
408 class BrokenFile(io.BytesIO):
409 def write(self, data):
410 nonlocal count
411 if count is not None:
412 if count == stop:
413 raise OSError
414 count += 1
415 super().write(data)
416
417 stop = 0
418 while True:
419 testfile = BrokenFile()
420 count = None
421 with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp:
422 with zipfp.open('file1', 'w') as f:
423 f.write(b'data1')
424 count = 0
425 try:
426 with zipfp.open('file2', 'w') as f:
427 f.write(b'data2')
428 except OSError:
429 stop += 1
430 else:
431 break
432 finally:
433 count = None
434 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
435 self.assertEqual(zipfp.namelist(), ['file1'])
436 self.assertEqual(zipfp.read('file1'), b'data1')
437
438 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
439 self.assertEqual(zipfp.namelist(), ['file1', 'file2'])
440 self.assertEqual(zipfp.read('file1'), b'data1')
441 self.assertEqual(zipfp.read('file2'), b'data2')
442
443
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300444 def tearDown(self):
445 unlink(TESTFN)
446 unlink(TESTFN2)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200447
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200448
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300449class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
450 unittest.TestCase):
451 compression = zipfile.ZIP_STORED
452 test_low_compression = None
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200453
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300454 def zip_test_writestr_permissions(self, f, compression):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300455 # Make sure that writestr and open(... mode='w') create files with
456 # mode 0600, when they are passed a name rather than a ZipInfo
457 # instance.
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200458
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300459 self.make_test_archive(f, compression)
460 with zipfile.ZipFile(f, "r") as zipfp:
461 zinfo = zipfp.getinfo('strfile')
462 self.assertEqual(zinfo.external_attr, 0o600 << 16)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200463
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300464 zinfo2 = zipfp.getinfo('written-open-w')
465 self.assertEqual(zinfo2.external_attr, 0o600 << 16)
466
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300467 def test_writestr_permissions(self):
468 for f in get_files(self):
469 self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +0200470
Ezio Melottiafd0d112009-07-15 17:17:17 +0000471 def test_absolute_arcnames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000472 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
473 zipfp.write(TESTFN, "/absolute")
Georg Brandl8f7c54e2006-02-20 08:40:38 +0000474
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000475 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
476 self.assertEqual(zipfp.namelist(), ["absolute"])
Tim Peters32cbc962006-02-20 21:42:18 +0000477
Ezio Melottiafd0d112009-07-15 17:17:17 +0000478 def test_append_to_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000479 """Test appending to an existing zipfile."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000480 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
481 zipfp.write(TESTFN, TESTFN)
482
483 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
484 zipfp.writestr("strfile", self.data)
485 self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"])
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000486
Ezio Melottiafd0d112009-07-15 17:17:17 +0000487 def test_append_to_non_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000488 """Test appending to an existing file that is not a zipfile."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000489 # NOTE: this test fails if len(d) < 22 because of the first
490 # line "fpin.seek(-22, 2)" in _EndRecData
Ezio Melotti35386712009-12-31 13:22:41 +0000491 data = b'I am not a ZipFile!'*10
492 with open(TESTFN2, 'wb') as f:
493 f.write(data)
494
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000495 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
496 zipfp.write(TESTFN, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000497
Ezio Melotti35386712009-12-31 13:22:41 +0000498 with open(TESTFN2, 'rb') as f:
499 f.seek(len(data))
500 with zipfile.ZipFile(f, "r") as zipfp:
501 self.assertEqual(zipfp.namelist(), [TESTFN])
Serhiy Storchaka8793b212016-10-07 22:20:50 +0300502 self.assertEqual(zipfp.read(TESTFN), self.data)
503 with open(TESTFN2, 'rb') as f:
504 self.assertEqual(f.read(len(data)), data)
505 zipfiledata = f.read()
506 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
507 self.assertEqual(zipfp.namelist(), [TESTFN])
508 self.assertEqual(zipfp.read(TESTFN), self.data)
509
510 def test_read_concatenated_zip_file(self):
511 with io.BytesIO() as bio:
512 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
513 zipfp.write(TESTFN, TESTFN)
514 zipfiledata = bio.getvalue()
515 data = b'I am not a ZipFile!'*10
516 with open(TESTFN2, 'wb') as f:
517 f.write(data)
518 f.write(zipfiledata)
519
520 with zipfile.ZipFile(TESTFN2) as zipfp:
521 self.assertEqual(zipfp.namelist(), [TESTFN])
522 self.assertEqual(zipfp.read(TESTFN), self.data)
523
524 def test_append_to_concatenated_zip_file(self):
525 with io.BytesIO() as bio:
526 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
527 zipfp.write(TESTFN, TESTFN)
528 zipfiledata = bio.getvalue()
529 data = b'I am not a ZipFile!'*1000000
530 with open(TESTFN2, 'wb') as f:
531 f.write(data)
532 f.write(zipfiledata)
533
534 with zipfile.ZipFile(TESTFN2, 'a') as zipfp:
535 self.assertEqual(zipfp.namelist(), [TESTFN])
536 zipfp.writestr('strfile', self.data)
537
538 with open(TESTFN2, 'rb') as f:
539 self.assertEqual(f.read(len(data)), data)
540 zipfiledata = f.read()
541 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
542 self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile'])
543 self.assertEqual(zipfp.read(TESTFN), self.data)
544 self.assertEqual(zipfp.read('strfile'), self.data)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000545
R David Murray4fbb9db2011-06-09 15:50:51 -0400546 def test_ignores_newline_at_end(self):
547 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
548 zipfp.write(TESTFN, TESTFN)
549 with open(TESTFN2, 'a') as f:
550 f.write("\r\n\00\00\00")
551 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
552 self.assertIsInstance(zipfp, zipfile.ZipFile)
553
554 def test_ignores_stuff_appended_past_comments(self):
555 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
556 zipfp.comment = b"this is a comment"
557 zipfp.write(TESTFN, TESTFN)
558 with open(TESTFN2, 'a') as f:
559 f.write("abcdef\r\n")
560 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
561 self.assertIsInstance(zipfp, zipfile.ZipFile)
562 self.assertEqual(zipfp.comment, b"this is a comment")
563
Ezio Melottiafd0d112009-07-15 17:17:17 +0000564 def test_write_default_name(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000565 """Check that calling ZipFile.write without arcname specified
566 produces the expected result."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000567 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
568 zipfp.write(TESTFN)
Brian Curtin8fb9b862010-11-18 02:15:28 +0000569 with open(TESTFN, "rb") as f:
570 self.assertEqual(zipfp.read(TESTFN), f.read())
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000571
Daniel Hillier8d62df62019-11-30 19:30:47 +1100572 def test_io_on_closed_zipextfile(self):
573 fname = "somefile.txt"
574 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
575 zipfp.writestr(fname, "bogus")
576
577 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
578 with zipfp.open(fname) as fid:
579 fid.close()
580 self.assertRaises(ValueError, fid.read)
581 self.assertRaises(ValueError, fid.seek, 0)
582 self.assertRaises(ValueError, fid.tell)
583 self.assertRaises(ValueError, fid.readable)
584 self.assertRaises(ValueError, fid.seekable)
585
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300586 def test_write_to_readonly(self):
587 """Check that trying to call write() on a readonly ZipFile object
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300588 raises a ValueError."""
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300589 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
590 zipfp.writestr("somefile.txt", "bogus")
591
592 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300593 self.assertRaises(ValueError, zipfp.write, TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300594
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300595 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300596 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300597 zipfp.open(TESTFN, mode='w')
598
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300599 def test_add_file_before_1980(self):
600 # Set atime and mtime to 1970-01-01
601 os.utime(TESTFN, (0, 0))
602 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
603 self.assertRaises(ValueError, zipfp.write, TESTFN)
604
Marcel Plch77b112c2018-08-31 16:43:31 +0200605 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
606 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200607 zinfo = zipfp.getinfo(TESTFN)
608 self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0))
609
610 def test_add_file_after_2107(self):
611 # Set atime and mtime to 2108-12-30
Victor Stinnerc232c912020-01-30 15:47:53 +0100612 ts = 4386268800
Marcel Plch7b41dba2018-08-03 17:59:19 +0200613 try:
Victor Stinnerc232c912020-01-30 15:47:53 +0100614 time.localtime(ts)
615 except OverflowError:
616 self.skipTest(f'time.localtime({ts}) raises OverflowError')
617 try:
618 os.utime(TESTFN, (ts, ts))
Marcel Plch7b41dba2018-08-03 17:59:19 +0200619 except OverflowError:
620 self.skipTest('Host fs cannot set timestamp to required value.')
621
Victor Stinner3cb49b62020-01-29 15:23:29 +0100622 mtime_ns = os.stat(TESTFN).st_mtime_ns
623 if mtime_ns != (4386268800 * 10**9):
624 # XFS filesystem is limited to 32-bit timestamp, but the syscall
625 # didn't fail. Moreover, there is a VFS bug which returns
626 # a cached timestamp which is different than the value on disk.
627 #
628 # Test st_mtime_ns rather than st_mtime to avoid rounding issues.
629 #
630 # https://bugzilla.redhat.com/show_bug.cgi?id=1795576
631 # https://bugs.python.org/issue39460#msg360952
632 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}")
633
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200634 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
635 self.assertRaises(struct.error, zipfp.write, TESTFN)
636
Marcel Plch77b112c2018-08-31 16:43:31 +0200637 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
638 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200639 zinfo = zipfp.getinfo(TESTFN)
640 self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59))
641
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200642
Hai Shia3ec3ad2020-05-19 06:02:57 +0800643@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300644class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile,
645 unittest.TestCase):
646 compression = zipfile.ZIP_DEFLATED
647
Ezio Melottiafd0d112009-07-15 17:17:17 +0000648 def test_per_file_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000649 """Check that files within a Zip archive can have different
650 compression options."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000651 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
652 zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED)
653 zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED)
654 sinfo = zipfp.getinfo('storeme')
655 dinfo = zipfp.getinfo('deflateme')
656 self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED)
657 self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000658
Hai Shia3ec3ad2020-05-19 06:02:57 +0800659@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300660class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile,
661 unittest.TestCase):
662 compression = zipfile.ZIP_BZIP2
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000663
Hai Shia3ec3ad2020-05-19 06:02:57 +0800664@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300665class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile,
666 unittest.TestCase):
667 compression = zipfile.ZIP_LZMA
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000668
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300669
670class AbstractTestZip64InSmallFiles:
671 # These tests test the ZIP64 functionality without using large files,
672 # see test_zipfile64 for proper tests.
673
674 @classmethod
675 def setUpClass(cls):
676 line_gen = (bytes("Test of zipfile line %d." % i, "ascii")
677 for i in range(0, FIXEDTEST_SIZE))
678 cls.data = b'\n'.join(line_gen)
679
680 def setUp(self):
681 self._limit = zipfile.ZIP64_LIMIT
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300682 self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT
683 zipfile.ZIP64_LIMIT = 1000
684 zipfile.ZIP_FILECOUNT_LIMIT = 9
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300685
686 # Make a source file with some lines
687 with open(TESTFN, "wb") as fp:
688 fp.write(self.data)
689
690 def zip_test(self, f, compression):
691 # Create the ZIP archive
692 with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp:
693 zipfp.write(TESTFN, "another.name")
694 zipfp.write(TESTFN, TESTFN)
695 zipfp.writestr("strfile", self.data)
696
697 # Read the ZIP archive
698 with zipfile.ZipFile(f, "r", compression) as zipfp:
699 self.assertEqual(zipfp.read(TESTFN), self.data)
700 self.assertEqual(zipfp.read("another.name"), self.data)
701 self.assertEqual(zipfp.read("strfile"), self.data)
702
703 # Print the ZIP directory
704 fp = io.StringIO()
705 zipfp.printdir(fp)
706
707 directory = fp.getvalue()
708 lines = directory.splitlines()
709 self.assertEqual(len(lines), 4) # Number of files + header
710
711 self.assertIn('File Name', lines[0])
712 self.assertIn('Modified', lines[0])
713 self.assertIn('Size', lines[0])
714
715 fn, date, time_, size = lines[1].split()
716 self.assertEqual(fn, 'another.name')
717 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
718 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
719 self.assertEqual(size, str(len(self.data)))
720
721 # Check the namelist
722 names = zipfp.namelist()
723 self.assertEqual(len(names), 3)
724 self.assertIn(TESTFN, names)
725 self.assertIn("another.name", names)
726 self.assertIn("strfile", names)
727
728 # Check infolist
729 infos = zipfp.infolist()
730 names = [i.filename for i in infos]
731 self.assertEqual(len(names), 3)
732 self.assertIn(TESTFN, names)
733 self.assertIn("another.name", names)
734 self.assertIn("strfile", names)
735 for i in infos:
736 self.assertEqual(i.file_size, len(self.data))
737
738 # check getinfo
739 for nm in (TESTFN, "another.name", "strfile"):
740 info = zipfp.getinfo(nm)
741 self.assertEqual(info.filename, nm)
742 self.assertEqual(info.file_size, len(self.data))
743
744 # Check that testzip doesn't raise an exception
745 zipfp.testzip()
746
747 def test_basic(self):
748 for f in get_files(self):
749 self.zip_test(f, self.compression)
750
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300751 def test_too_many_files(self):
752 # This test checks that more than 64k files can be added to an archive,
753 # and that the resulting archive can be read properly by ZipFile
754 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
755 allowZip64=True)
756 zipf.debug = 100
757 numfiles = 15
758 for i in range(numfiles):
759 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
760 self.assertEqual(len(zipf.namelist()), numfiles)
761 zipf.close()
762
763 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
764 self.assertEqual(len(zipf2.namelist()), numfiles)
765 for i in range(numfiles):
766 content = zipf2.read("foo%08d" % i).decode('ascii')
767 self.assertEqual(content, "%d" % (i**3 % 57))
768 zipf2.close()
769
770 def test_too_many_files_append(self):
771 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
772 allowZip64=False)
773 zipf.debug = 100
774 numfiles = 9
775 for i in range(numfiles):
776 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
777 self.assertEqual(len(zipf.namelist()), numfiles)
778 with self.assertRaises(zipfile.LargeZipFile):
779 zipf.writestr("foo%08d" % numfiles, b'')
780 self.assertEqual(len(zipf.namelist()), numfiles)
781 zipf.close()
782
783 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
784 allowZip64=False)
785 zipf.debug = 100
786 self.assertEqual(len(zipf.namelist()), numfiles)
787 with self.assertRaises(zipfile.LargeZipFile):
788 zipf.writestr("foo%08d" % numfiles, b'')
789 self.assertEqual(len(zipf.namelist()), numfiles)
790 zipf.close()
791
792 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
793 allowZip64=True)
794 zipf.debug = 100
795 self.assertEqual(len(zipf.namelist()), numfiles)
796 numfiles2 = 15
797 for i in range(numfiles, numfiles2):
798 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
799 self.assertEqual(len(zipf.namelist()), numfiles2)
800 zipf.close()
801
802 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
803 self.assertEqual(len(zipf2.namelist()), numfiles2)
804 for i in range(numfiles2):
805 content = zipf2.read("foo%08d" % i).decode('ascii')
806 self.assertEqual(content, "%d" % (i**3 % 57))
807 zipf2.close()
808
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300809 def tearDown(self):
810 zipfile.ZIP64_LIMIT = self._limit
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300811 zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300812 unlink(TESTFN)
813 unlink(TESTFN2)
814
815
816class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
817 unittest.TestCase):
818 compression = zipfile.ZIP_STORED
819
820 def large_file_exception_test(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200821 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300822 self.assertRaises(zipfile.LargeZipFile,
823 zipfp.write, TESTFN, "another.name")
824
825 def large_file_exception_test2(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200826 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300827 self.assertRaises(zipfile.LargeZipFile,
828 zipfp.writestr, "another.name", self.data)
829
830 def test_large_file_exception(self):
831 for f in get_files(self):
832 self.large_file_exception_test(f, zipfile.ZIP_STORED)
833 self.large_file_exception_test2(f, zipfile.ZIP_STORED)
834
835 def test_absolute_arcnames(self):
836 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
837 allowZip64=True) as zipfp:
838 zipfp.write(TESTFN, "/absolute")
839
840 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
841 self.assertEqual(zipfp.namelist(), ["absolute"])
842
Serhiy Storchaka9bdb7be2018-09-17 15:36:40 +0300843 def test_append(self):
844 # Test that appending to the Zip64 archive doesn't change
845 # extra fields of existing entries.
846 with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp:
847 zipfp.writestr("strfile", self.data)
848 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
849 zinfo = zipfp.getinfo("strfile")
850 extra = zinfo.extra
851 with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp:
852 zipfp.writestr("strfile2", self.data)
853 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
854 zinfo = zipfp.getinfo("strfile")
855 self.assertEqual(zinfo.extra, extra)
856
Daniel Hillierda6ce582019-10-29 18:24:18 +1100857 def make_zip64_file(
858 self, file_size_64_set=False, file_size_extra=False,
859 compress_size_64_set=False, compress_size_extra=False,
860 header_offset_64_set=False, header_offset_extra=False,
861 ):
862 """Generate bytes sequence for a zip with (incomplete) zip64 data.
863
864 The actual values (not the zip 64 0xffffffff values) stored in the file
865 are:
866 file_size: 8
867 compress_size: 8
868 header_offset: 0
869 """
870 actual_size = 8
871 actual_header_offset = 0
872 local_zip64_fields = []
873 central_zip64_fields = []
874
875 file_size = actual_size
876 if file_size_64_set:
877 file_size = 0xffffffff
878 if file_size_extra:
879 local_zip64_fields.append(actual_size)
880 central_zip64_fields.append(actual_size)
881 file_size = struct.pack("<L", file_size)
882
883 compress_size = actual_size
884 if compress_size_64_set:
885 compress_size = 0xffffffff
886 if compress_size_extra:
887 local_zip64_fields.append(actual_size)
888 central_zip64_fields.append(actual_size)
889 compress_size = struct.pack("<L", compress_size)
890
891 header_offset = actual_header_offset
892 if header_offset_64_set:
893 header_offset = 0xffffffff
894 if header_offset_extra:
895 central_zip64_fields.append(actual_header_offset)
896 header_offset = struct.pack("<L", header_offset)
897
898 local_extra = struct.pack(
899 '<HH' + 'Q'*len(local_zip64_fields),
900 0x0001,
901 8*len(local_zip64_fields),
902 *local_zip64_fields
903 )
904
905 central_extra = struct.pack(
906 '<HH' + 'Q'*len(central_zip64_fields),
907 0x0001,
908 8*len(central_zip64_fields),
909 *central_zip64_fields
910 )
911
912 central_dir_size = struct.pack('<Q', 58 + 8 * len(central_zip64_fields))
913 offset_to_central_dir = struct.pack('<Q', 50 + 8 * len(local_zip64_fields))
914
915 local_extra_length = struct.pack("<H", 4 + 8 * len(local_zip64_fields))
916 central_extra_length = struct.pack("<H", 4 + 8 * len(central_zip64_fields))
917
918 filename = b"test.txt"
919 content = b"test1234"
920 filename_length = struct.pack("<H", len(filename))
921 zip64_contents = (
922 # Local file header
923 b"PK\x03\x04\x14\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
924 + compress_size
925 + file_size
926 + filename_length
927 + local_extra_length
928 + filename
929 + local_extra
930 + content
931 # Central directory:
932 + b"PK\x01\x02-\x03-\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
933 + compress_size
934 + file_size
935 + filename_length
936 + central_extra_length
937 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01"
938 + header_offset
939 + filename
940 + central_extra
941 # Zip64 end of central directory
942 + b"PK\x06\x06,\x00\x00\x00\x00\x00\x00\x00-\x00-"
943 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
944 + b"\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
945 + central_dir_size
946 + offset_to_central_dir
947 # Zip64 end of central directory locator
948 + b"PK\x06\x07\x00\x00\x00\x00l\x00\x00\x00\x00\x00\x00\x00\x01"
949 + b"\x00\x00\x00"
950 # end of central directory
951 + b"PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00:\x00\x00\x002\x00"
952 + b"\x00\x00\x00\x00"
953 )
954 return zip64_contents
955
956 def test_bad_zip64_extra(self):
957 """Missing zip64 extra records raises an exception.
958
959 There are 4 fields that the zip64 format handles (the disk number is
960 not used in this module and so is ignored here). According to the zip
961 spec:
962 The order of the fields in the zip64 extended
963 information record is fixed, but the fields MUST
964 only appear if the corresponding Local or Central
965 directory record field is set to 0xFFFF or 0xFFFFFFFF.
966
967 If the zip64 extra content doesn't contain enough entries for the
968 number of fields marked with 0xFFFF or 0xFFFFFFFF, we raise an error.
969 This test mismatches the length of the zip64 extra field and the number
970 of fields set to indicate the presence of zip64 data.
971 """
972 # zip64 file size present, no fields in extra, expecting one, equals
973 # missing file size.
974 missing_file_size_extra = self.make_zip64_file(
975 file_size_64_set=True,
976 )
977 with self.assertRaises(zipfile.BadZipFile) as e:
978 zipfile.ZipFile(io.BytesIO(missing_file_size_extra))
979 self.assertIn('file size', str(e.exception).lower())
980
981 # zip64 file size present, zip64 compress size present, one field in
982 # extra, expecting two, equals missing compress size.
983 missing_compress_size_extra = self.make_zip64_file(
984 file_size_64_set=True,
985 file_size_extra=True,
986 compress_size_64_set=True,
987 )
988 with self.assertRaises(zipfile.BadZipFile) as e:
989 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
990 self.assertIn('compress size', str(e.exception).lower())
991
992 # zip64 compress size present, no fields in extra, expecting one,
993 # equals missing compress size.
994 missing_compress_size_extra = self.make_zip64_file(
995 compress_size_64_set=True,
996 )
997 with self.assertRaises(zipfile.BadZipFile) as e:
998 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
999 self.assertIn('compress size', str(e.exception).lower())
1000
1001 # zip64 file size present, zip64 compress size present, zip64 header
1002 # offset present, two fields in extra, expecting three, equals missing
1003 # header offset
1004 missing_header_offset_extra = self.make_zip64_file(
1005 file_size_64_set=True,
1006 file_size_extra=True,
1007 compress_size_64_set=True,
1008 compress_size_extra=True,
1009 header_offset_64_set=True,
1010 )
1011 with self.assertRaises(zipfile.BadZipFile) as e:
1012 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1013 self.assertIn('header offset', str(e.exception).lower())
1014
1015 # zip64 compress size present, zip64 header offset present, one field
1016 # in extra, expecting two, equals missing header offset
1017 missing_header_offset_extra = self.make_zip64_file(
1018 file_size_64_set=False,
1019 compress_size_64_set=True,
1020 compress_size_extra=True,
1021 header_offset_64_set=True,
1022 )
1023 with self.assertRaises(zipfile.BadZipFile) as e:
1024 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1025 self.assertIn('header offset', str(e.exception).lower())
1026
1027 # zip64 file size present, zip64 header offset present, one field in
1028 # extra, expecting two, equals missing header offset
1029 missing_header_offset_extra = self.make_zip64_file(
1030 file_size_64_set=True,
1031 file_size_extra=True,
1032 compress_size_64_set=False,
1033 header_offset_64_set=True,
1034 )
1035 with self.assertRaises(zipfile.BadZipFile) as e:
1036 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1037 self.assertIn('header offset', str(e.exception).lower())
1038
1039 # zip64 header offset present, no fields in extra, expecting one,
1040 # equals missing header offset
1041 missing_header_offset_extra = self.make_zip64_file(
1042 file_size_64_set=False,
1043 compress_size_64_set=False,
1044 header_offset_64_set=True,
1045 )
1046 with self.assertRaises(zipfile.BadZipFile) as e:
1047 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1048 self.assertIn('header offset', str(e.exception).lower())
1049
1050 def test_generated_valid_zip64_extra(self):
1051 # These values are what is set in the make_zip64_file method.
1052 expected_file_size = 8
1053 expected_compress_size = 8
1054 expected_header_offset = 0
1055 expected_content = b"test1234"
1056
1057 # Loop through the various valid combinations of zip64 masks
1058 # present and extra fields present.
1059 params = (
1060 {"file_size_64_set": True, "file_size_extra": True},
1061 {"compress_size_64_set": True, "compress_size_extra": True},
1062 {"header_offset_64_set": True, "header_offset_extra": True},
1063 )
1064
1065 for r in range(1, len(params) + 1):
1066 for combo in itertools.combinations(params, r):
1067 kwargs = {}
1068 for c in combo:
1069 kwargs.update(c)
1070 with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf:
1071 zinfo = zf.infolist()[0]
1072 self.assertEqual(zinfo.file_size, expected_file_size)
1073 self.assertEqual(zinfo.compress_size, expected_compress_size)
1074 self.assertEqual(zinfo.header_offset, expected_header_offset)
1075 self.assertEqual(zf.read(zinfo), expected_content)
1076
1077
Hai Shia3ec3ad2020-05-19 06:02:57 +08001078@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001079class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1080 unittest.TestCase):
1081 compression = zipfile.ZIP_DEFLATED
1082
Hai Shia3ec3ad2020-05-19 06:02:57 +08001083@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001084class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1085 unittest.TestCase):
1086 compression = zipfile.ZIP_BZIP2
1087
Hai Shia3ec3ad2020-05-19 06:02:57 +08001088@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001089class LzmaTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1090 unittest.TestCase):
1091 compression = zipfile.ZIP_LZMA
1092
1093
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001094class AbstractWriterTests:
1095
1096 def tearDown(self):
1097 unlink(TESTFN2)
1098
1099 def test_close_after_close(self):
1100 data = b'content'
1101 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1102 w = zipf.open('test', 'w')
1103 w.write(data)
1104 w.close()
1105 self.assertTrue(w.closed)
1106 w.close()
1107 self.assertTrue(w.closed)
1108 self.assertEqual(zipf.read('test'), data)
1109
1110 def test_write_after_close(self):
1111 data = b'content'
1112 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1113 w = zipf.open('test', 'w')
1114 w.write(data)
1115 w.close()
1116 self.assertTrue(w.closed)
1117 self.assertRaises(ValueError, w.write, b'')
1118 self.assertEqual(zipf.read('test'), data)
1119
1120class StoredWriterTests(AbstractWriterTests, unittest.TestCase):
1121 compression = zipfile.ZIP_STORED
1122
Hai Shia3ec3ad2020-05-19 06:02:57 +08001123@requires_zlib()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001124class DeflateWriterTests(AbstractWriterTests, unittest.TestCase):
1125 compression = zipfile.ZIP_DEFLATED
1126
Hai Shia3ec3ad2020-05-19 06:02:57 +08001127@requires_bz2()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001128class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase):
1129 compression = zipfile.ZIP_BZIP2
1130
Hai Shia3ec3ad2020-05-19 06:02:57 +08001131@requires_lzma()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001132class LzmaWriterTests(AbstractWriterTests, unittest.TestCase):
1133 compression = zipfile.ZIP_LZMA
1134
1135
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001136class PyZipFileTests(unittest.TestCase):
1137 def assertCompiledIn(self, name, namelist):
1138 if name + 'o' not in namelist:
1139 self.assertIn(name + 'c', namelist)
1140
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001141 def requiresWriteAccess(self, path):
Berker Peksage1efc072015-02-16 04:36:18 +02001142 # effective_ids unavailable on windows
1143 if not os.access(path, os.W_OK,
1144 effective_ids=os.access in os.supports_effective_ids):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001145 self.skipTest('requires write access to the installed location')
Serhiy Storchakad86a6ef2015-09-19 10:55:20 +03001146 filename = os.path.join(path, 'test_zipfile.try')
1147 try:
1148 fd = os.open(filename, os.O_WRONLY | os.O_CREAT)
1149 os.close(fd)
1150 except Exception:
1151 self.skipTest('requires write access to the installed location')
1152 unlink(filename)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001153
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001154 def test_write_pyfile(self):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001155 self.requiresWriteAccess(os.path.dirname(__file__))
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001156 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1157 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001158 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001159 path_split = fn.split(os.sep)
1160 if os.altsep is not None:
1161 path_split.extend(fn.split(os.altsep))
1162 if '__pycache__' in path_split:
Serhiy Storchaka9068e4d2013-07-22 21:02:14 +03001163 fn = importlib.util.source_from_cache(fn)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001164 else:
1165 fn = fn[:-1]
1166
1167 zipfp.writepy(fn)
1168
1169 bn = os.path.basename(fn)
1170 self.assertNotIn(bn, zipfp.namelist())
1171 self.assertCompiledIn(bn, zipfp.namelist())
1172
1173 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1174 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001175 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001176 fn = fn[:-1]
1177
1178 zipfp.writepy(fn, "testpackage")
1179
1180 bn = "%s/%s" % ("testpackage", os.path.basename(fn))
1181 self.assertNotIn(bn, zipfp.namelist())
1182 self.assertCompiledIn(bn, zipfp.namelist())
1183
1184 def test_write_python_package(self):
1185 import email
1186 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001187 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001188
1189 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1190 zipfp.writepy(packagedir)
1191
1192 # Check for a couple of modules at different levels of the
1193 # hierarchy
1194 names = zipfp.namelist()
1195 self.assertCompiledIn('email/__init__.py', names)
1196 self.assertCompiledIn('email/mime/text.py', names)
1197
Christian Tismer59202e52013-10-21 03:59:23 +02001198 def test_write_filtered_python_package(self):
1199 import test
1200 packagedir = os.path.dirname(test.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001201 self.requiresWriteAccess(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001202
1203 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1204
Christian Tismer59202e52013-10-21 03:59:23 +02001205 # first make sure that the test folder gives error messages
Georg Brandla6065422013-10-21 08:29:29 +02001206 # (on the badsyntax_... files)
1207 with captured_stdout() as reportSIO:
1208 zipfp.writepy(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001209 reportStr = reportSIO.getvalue()
1210 self.assertTrue('SyntaxError' in reportStr)
1211
Christian Tismer410d9312013-10-22 04:09:28 +02001212 # then check that the filter works on the whole package
Georg Brandla6065422013-10-21 08:29:29 +02001213 with captured_stdout() as reportSIO:
1214 zipfp.writepy(packagedir, filterfunc=lambda whatever: False)
Christian Tismer59202e52013-10-21 03:59:23 +02001215 reportStr = reportSIO.getvalue()
1216 self.assertTrue('SyntaxError' not in reportStr)
1217
Christian Tismer410d9312013-10-22 04:09:28 +02001218 # then check that the filter works on individual files
Larry Hastings7e63b362015-05-08 06:54:58 -07001219 def filter(path):
1220 return not os.path.basename(path).startswith("bad")
Serhiy Storchakac46d1fa2014-01-20 21:59:33 +02001221 with captured_stdout() as reportSIO, self.assertWarns(UserWarning):
Larry Hastings7e63b362015-05-08 06:54:58 -07001222 zipfp.writepy(packagedir, filterfunc=filter)
Christian Tismer410d9312013-10-22 04:09:28 +02001223 reportStr = reportSIO.getvalue()
1224 if reportStr:
1225 print(reportStr)
1226 self.assertTrue('SyntaxError' not in reportStr)
1227
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001228 def test_write_with_optimization(self):
1229 import email
1230 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001231 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001232 optlevel = 1 if __debug__ else 0
Brett Cannonf299abd2015-04-13 14:21:02 -04001233 ext = '.pyc'
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001234
1235 with TemporaryFile() as t, \
Christian Tismer59202e52013-10-21 03:59:23 +02001236 zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001237 zipfp.writepy(packagedir)
1238
1239 names = zipfp.namelist()
1240 self.assertIn('email/__init__' + ext, names)
1241 self.assertIn('email/mime/text' + ext, names)
1242
1243 def test_write_python_directory(self):
1244 os.mkdir(TESTFN2)
1245 try:
1246 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1247 fp.write("print(42)\n")
1248
1249 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1250 fp.write("print(42 * 42)\n")
1251
1252 with open(os.path.join(TESTFN2, "mod2.txt"), "w") as fp:
1253 fp.write("bla bla bla\n")
1254
1255 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1256 zipfp.writepy(TESTFN2)
1257
1258 names = zipfp.namelist()
1259 self.assertCompiledIn('mod1.py', names)
1260 self.assertCompiledIn('mod2.py', names)
1261 self.assertNotIn('mod2.txt', names)
1262
1263 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001264 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001265
Christian Tismer410d9312013-10-22 04:09:28 +02001266 def test_write_python_directory_filtered(self):
1267 os.mkdir(TESTFN2)
1268 try:
1269 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1270 fp.write("print(42)\n")
1271
1272 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1273 fp.write("print(42 * 42)\n")
1274
1275 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1276 zipfp.writepy(TESTFN2, filterfunc=lambda fn:
1277 not fn.endswith('mod2.py'))
1278
1279 names = zipfp.namelist()
1280 self.assertCompiledIn('mod1.py', names)
1281 self.assertNotIn('mod2.py', names)
1282
1283 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001284 rmtree(TESTFN2)
Christian Tismer410d9312013-10-22 04:09:28 +02001285
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001286 def test_write_non_pyfile(self):
1287 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1288 with open(TESTFN, 'w') as f:
1289 f.write('most definitely not a python file')
1290 self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
Victor Stinner88b215e2014-09-04 00:51:09 +02001291 unlink(TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001292
1293 def test_write_pyfile_bad_syntax(self):
1294 os.mkdir(TESTFN2)
1295 try:
1296 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1297 fp.write("Bad syntax in python file\n")
1298
1299 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1300 # syntax errors are printed to stdout
1301 with captured_stdout() as s:
1302 zipfp.writepy(os.path.join(TESTFN2, "mod1.py"))
1303
1304 self.assertIn("SyntaxError", s.getvalue())
1305
1306 # as it will not have compiled the python file, it will
Brett Cannonf299abd2015-04-13 14:21:02 -04001307 # include the .py file not .pyc
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001308 names = zipfp.namelist()
1309 self.assertIn('mod1.py', names)
1310 self.assertNotIn('mod1.pyc', names)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001311
1312 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001313 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001314
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001315 def test_write_pathlike(self):
1316 os.mkdir(TESTFN2)
1317 try:
1318 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1319 fp.write("print(42)\n")
1320
1321 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1322 zipfp.writepy(pathlib.Path(TESTFN2) / "mod1.py")
1323 names = zipfp.namelist()
1324 self.assertCompiledIn('mod1.py', names)
1325 finally:
1326 rmtree(TESTFN2)
1327
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001328
1329class ExtractTests(unittest.TestCase):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001330
1331 def make_test_file(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001332 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1333 for fpath, fdata in SMALL_TEST_DATA:
1334 zipfp.writestr(fpath, fdata)
Christian Heimes790c8232008-01-07 21:14:23 +00001335
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001336 def test_extract(self):
1337 with temp_cwd():
1338 self.make_test_file()
1339 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1340 for fpath, fdata in SMALL_TEST_DATA:
1341 writtenfile = zipfp.extract(fpath)
1342
1343 # make sure it was written to the right place
1344 correctfile = os.path.join(os.getcwd(), fpath)
1345 correctfile = os.path.normpath(correctfile)
1346
1347 self.assertEqual(writtenfile, correctfile)
1348
1349 # make sure correct data is in correct file
1350 with open(writtenfile, "rb") as f:
1351 self.assertEqual(fdata.encode(), f.read())
1352
1353 unlink(writtenfile)
1354
1355 def _test_extract_with_target(self, target):
1356 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001357 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1358 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001359 writtenfile = zipfp.extract(fpath, target)
Christian Heimes790c8232008-01-07 21:14:23 +00001360
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001361 # make sure it was written to the right place
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001362 correctfile = os.path.join(target, fpath)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001363 correctfile = os.path.normpath(correctfile)
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001364 self.assertTrue(os.path.samefile(writtenfile, correctfile), (writtenfile, target))
Christian Heimes790c8232008-01-07 21:14:23 +00001365
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001366 # make sure correct data is in correct file
Brian Curtin8fb9b862010-11-18 02:15:28 +00001367 with open(writtenfile, "rb") as f:
1368 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001369
Victor Stinner88b215e2014-09-04 00:51:09 +02001370 unlink(writtenfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001371
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001372 unlink(TESTFN2)
1373
1374 def test_extract_with_target(self):
1375 with temp_dir() as extdir:
1376 self._test_extract_with_target(extdir)
1377
1378 def test_extract_with_target_pathlike(self):
1379 with temp_dir() as extdir:
1380 self._test_extract_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001381
Ezio Melottiafd0d112009-07-15 17:17:17 +00001382 def test_extract_all(self):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001383 with temp_cwd():
1384 self.make_test_file()
1385 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1386 zipfp.extractall()
1387 for fpath, fdata in SMALL_TEST_DATA:
1388 outfile = os.path.join(os.getcwd(), fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001389
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001390 with open(outfile, "rb") as f:
1391 self.assertEqual(fdata.encode(), f.read())
1392
1393 unlink(outfile)
1394
1395 def _test_extract_all_with_target(self, target):
1396 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001397 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001398 zipfp.extractall(target)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001399 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001400 outfile = os.path.join(target, fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001401
Brian Curtin8fb9b862010-11-18 02:15:28 +00001402 with open(outfile, "rb") as f:
1403 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001404
Victor Stinner88b215e2014-09-04 00:51:09 +02001405 unlink(outfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001406
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001407 unlink(TESTFN2)
1408
1409 def test_extract_all_with_target(self):
1410 with temp_dir() as extdir:
1411 self._test_extract_all_with_target(extdir)
1412
1413 def test_extract_all_with_target_pathlike(self):
1414 with temp_dir() as extdir:
1415 self._test_extract_all_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001416
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001417 def check_file(self, filename, content):
1418 self.assertTrue(os.path.isfile(filename))
1419 with open(filename, 'rb') as f:
1420 self.assertEqual(f.read(), content)
1421
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001422 def test_sanitize_windows_name(self):
1423 san = zipfile.ZipFile._sanitize_windows_name
1424 # Passing pathsep in allows this test to work regardless of platform.
1425 self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z')
1426 self.assertEqual(san(r'a\b,c<d>e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i')
1427 self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r')
1428
1429 def test_extract_hackers_arcnames_common_cases(self):
1430 common_hacknames = [
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001431 ('../foo/bar', 'foo/bar'),
1432 ('foo/../bar', 'foo/bar'),
1433 ('foo/../../bar', 'foo/bar'),
1434 ('foo/bar/..', 'foo/bar'),
1435 ('./../foo/bar', 'foo/bar'),
1436 ('/foo/bar', 'foo/bar'),
1437 ('/foo/../bar', 'foo/bar'),
1438 ('/foo/../../bar', 'foo/bar'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001439 ]
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001440 self._test_extract_hackers_arcnames(common_hacknames)
1441
1442 @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.')
1443 def test_extract_hackers_arcnames_windows_only(self):
1444 """Test combination of path fixing and windows name sanitization."""
1445 windows_hacknames = [
Christian Tismer59202e52013-10-21 03:59:23 +02001446 (r'..\foo\bar', 'foo/bar'),
1447 (r'..\/foo\/bar', 'foo/bar'),
1448 (r'foo/\..\/bar', 'foo/bar'),
1449 (r'foo\/../\bar', 'foo/bar'),
1450 (r'C:foo/bar', 'foo/bar'),
1451 (r'C:/foo/bar', 'foo/bar'),
1452 (r'C://foo/bar', 'foo/bar'),
1453 (r'C:\foo\bar', 'foo/bar'),
1454 (r'//conky/mountpoint/foo/bar', 'foo/bar'),
1455 (r'\\conky\mountpoint\foo\bar', 'foo/bar'),
1456 (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
1457 (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
1458 (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
1459 (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
1460 (r'//?/C:/foo/bar', 'foo/bar'),
1461 (r'\\?\C:\foo\bar', 'foo/bar'),
1462 (r'C:/../C:/foo/bar', 'C_/foo/bar'),
1463 (r'a:b\c<d>e|f"g?h*i', 'b/c_d_e_f_g_h_i'),
1464 ('../../foo../../ba..r', 'foo/ba..r'),
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001465 ]
1466 self._test_extract_hackers_arcnames(windows_hacknames)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001467
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001468 @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.')
1469 def test_extract_hackers_arcnames_posix_only(self):
1470 posix_hacknames = [
1471 ('//foo/bar', 'foo/bar'),
1472 ('../../foo../../ba..r', 'foo../ba..r'),
1473 (r'foo/..\bar', r'foo/..\bar'),
1474 ]
1475 self._test_extract_hackers_arcnames(posix_hacknames)
1476
1477 def _test_extract_hackers_arcnames(self, hacknames):
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001478 for arcname, fixedname in hacknames:
1479 content = b'foobar' + arcname.encode()
1480 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp:
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001481 zinfo = zipfile.ZipInfo()
1482 # preserve backslashes
1483 zinfo.filename = arcname
1484 zinfo.external_attr = 0o600 << 16
1485 zipfp.writestr(zinfo, content)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001486
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001487 arcname = arcname.replace(os.sep, "/")
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001488 targetpath = os.path.join('target', 'subdir', 'subsub')
1489 correctfile = os.path.join(targetpath, *fixedname.split('/'))
1490
1491 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1492 writtenfile = zipfp.extract(arcname, targetpath)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001493 self.assertEqual(writtenfile, correctfile,
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001494 msg='extract %r: %r != %r' %
1495 (arcname, writtenfile, correctfile))
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001496 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001497 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001498
1499 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1500 zipfp.extractall(targetpath)
1501 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001502 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001503
1504 correctfile = os.path.join(os.getcwd(), *fixedname.split('/'))
1505
1506 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1507 writtenfile = zipfp.extract(arcname)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001508 self.assertEqual(writtenfile, correctfile,
1509 msg="extract %r" % arcname)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001510 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001511 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001512
1513 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1514 zipfp.extractall()
1515 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001516 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001517
Victor Stinner88b215e2014-09-04 00:51:09 +02001518 unlink(TESTFN2)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001519
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001520
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001521class OtherTests(unittest.TestCase):
1522 def test_open_via_zip_info(self):
1523 # Create the ZIP archive
1524 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1525 zipfp.writestr("name", "foo")
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001526 with self.assertWarns(UserWarning):
1527 zipfp.writestr("name", "bar")
1528 self.assertEqual(zipfp.namelist(), ["name"] * 2)
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001529
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001530 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1531 infos = zipfp.infolist()
1532 data = b""
1533 for info in infos:
1534 with zipfp.open(info) as zipopen:
1535 data += zipopen.read()
1536 self.assertIn(data, {b"foobar", b"barfoo"})
1537 data = b""
1538 for info in infos:
1539 data += zipfp.read(info)
1540 self.assertIn(data, {b"foobar", b"barfoo"})
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02001541
Gregory P. Smithb0d9ca92009-07-07 05:06:04 +00001542 def test_writestr_extended_local_header_issue1202(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001543 with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
1544 for data in 'abcdefghijklmnop':
1545 zinfo = zipfile.ZipInfo(data)
1546 zinfo.flag_bits |= 0x08 # Include an extended local header.
1547 orig_zip.writestr(zinfo, data)
1548
1549 def test_close(self):
1550 """Check that the zipfile is closed after the 'with' block."""
1551 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1552 for fpath, fdata in SMALL_TEST_DATA:
1553 zipfp.writestr(fpath, fdata)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001554 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1555 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001556
1557 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001558 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1559 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001560
1561 def test_close_on_exception(self):
1562 """Check that the zipfile is closed if an exception is raised in the
1563 'with' block."""
1564 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1565 for fpath, fdata in SMALL_TEST_DATA:
1566 zipfp.writestr(fpath, fdata)
1567
1568 try:
1569 with zipfile.ZipFile(TESTFN2, "r") as zipfp2:
Georg Brandl4d540882010-10-28 06:42:33 +00001570 raise zipfile.BadZipFile()
1571 except zipfile.BadZipFile:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001572 self.assertIsNone(zipfp2.fp, 'zipfp is not closed')
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00001573
Martin v. Löwisd099b562012-05-01 14:08:22 +02001574 def test_unsupported_version(self):
1575 # File has an extract_version of 120
1576 data = (b'PK\x03\x04x\x00\x00\x00\x00\x00!p\xa1@\x00\x00\x00\x00\x00\x00'
Christian Tismer59202e52013-10-21 03:59:23 +02001577 b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00'
1578 b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
1579 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06'
1580 b'\x00\x00\x00\x00\x01\x00\x01\x00/\x00\x00\x00\x1f\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001581
Martin v. Löwisd099b562012-05-01 14:08:22 +02001582 self.assertRaises(NotImplementedError, zipfile.ZipFile,
1583 io.BytesIO(data), 'r')
1584
Hai Shia3ec3ad2020-05-19 06:02:57 +08001585 @requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001586 def test_read_unicode_filenames(self):
1587 # bug #10801
1588 fname = findfile('zip_cp437_header.zip')
1589 with zipfile.ZipFile(fname) as zipfp:
1590 for name in zipfp.namelist():
1591 zipfp.open(name).close()
1592
1593 def test_write_unicode_filenames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001594 with zipfile.ZipFile(TESTFN, "w") as zf:
1595 zf.writestr("foo.txt", "Test for unicode filename")
1596 zf.writestr("\xf6.txt", "Test for unicode filename")
Ezio Melottie9615932010-01-24 19:26:24 +00001597 self.assertIsInstance(zf.infolist()[0].filename, str)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001598
1599 with zipfile.ZipFile(TESTFN, "r") as zf:
1600 self.assertEqual(zf.filelist[0].filename, "foo.txt")
1601 self.assertEqual(zf.filelist[1].filename, "\xf6.txt")
Martin v. Löwis8570f6a2008-05-05 17:44:38 +00001602
Serhiy Storchaka36ff5132020-06-22 11:24:11 +03001603 def test_read_after_write_unicode_filenames(self):
1604 with zipfile.ZipFile(TESTFN2, 'w') as zipfp:
1605 zipfp.writestr('приклад', b'sample')
1606 self.assertEqual(zipfp.read('приклад'), b'sample')
1607
Serhiy Storchaka764fc9b2015-03-25 10:09:41 +02001608 def test_exclusive_create_zip_file(self):
1609 """Test exclusive creating a new zipfile."""
1610 unlink(TESTFN2)
1611 filename = 'testfile.txt'
1612 content = b'hello, world. this is some content.'
1613 with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp:
1614 zipfp.writestr(filename, content)
1615 with self.assertRaises(FileExistsError):
1616 zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED)
1617 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1618 self.assertEqual(zipfp.namelist(), [filename])
1619 self.assertEqual(zipfp.read(filename), content)
1620
Ezio Melottiafd0d112009-07-15 17:17:17 +00001621 def test_create_non_existent_file_for_append(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00001622 if os.path.exists(TESTFN):
1623 os.unlink(TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001624
Thomas Wouterscf297e42007-02-23 15:07:44 +00001625 filename = 'testfile.txt'
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001626 content = b'hello, world. this is some content.'
Guido van Rossumd8faa362007-04-27 19:54:29 +00001627
Thomas Wouterscf297e42007-02-23 15:07:44 +00001628 try:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001629 with zipfile.ZipFile(TESTFN, 'a') as zf:
1630 zf.writestr(filename, content)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001631 except OSError:
Thomas Wouterscf297e42007-02-23 15:07:44 +00001632 self.fail('Could not append data to a non-existent zip file.')
1633
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001634 self.assertTrue(os.path.exists(TESTFN))
Thomas Wouterscf297e42007-02-23 15:07:44 +00001635
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001636 with zipfile.ZipFile(TESTFN, 'r') as zf:
1637 self.assertEqual(zf.read(filename), content)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001638
Ezio Melottiafd0d112009-07-15 17:17:17 +00001639 def test_close_erroneous_file(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001640 # This test checks that the ZipFile constructor closes the file object
Ezio Melotti35386712009-12-31 13:22:41 +00001641 # it opens if there's an error in the file. If it doesn't, the
1642 # traceback holds a reference to the ZipFile object and, indirectly,
1643 # the file object.
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001644 # On Windows, this causes the os.unlink() call to fail because the
1645 # underlying file is still open. This is SF bug #412214.
1646 #
Ezio Melotti35386712009-12-31 13:22:41 +00001647 with open(TESTFN, "w") as fp:
1648 fp.write("this is not a legal zip file\n")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001649 try:
1650 zf = zipfile.ZipFile(TESTFN)
Georg Brandl4d540882010-10-28 06:42:33 +00001651 except zipfile.BadZipFile:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001652 pass
1653
Ezio Melottiafd0d112009-07-15 17:17:17 +00001654 def test_is_zip_erroneous_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001655 """Check that is_zipfile() correctly identifies non-zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001656 # - passing a filename
1657 with open(TESTFN, "w") as fp:
1658 fp.write("this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001659 self.assertFalse(zipfile.is_zipfile(TESTFN))
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001660 # - passing a path-like object
1661 self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN)))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001662 # - passing a file object
1663 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001664 self.assertFalse(zipfile.is_zipfile(fp))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001665 # - passing a file-like object
1666 fp = io.BytesIO()
1667 fp.write(b"this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001668 self.assertFalse(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001669 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001670 self.assertFalse(zipfile.is_zipfile(fp))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001671
Serhiy Storchakad2b15272013-01-31 15:27:07 +02001672 def test_damaged_zipfile(self):
1673 """Check that zipfiles with missing bytes at the end raise BadZipFile."""
1674 # - Create a valid zip file
1675 fp = io.BytesIO()
1676 with zipfile.ZipFile(fp, mode="w") as zipf:
1677 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1678 zipfiledata = fp.getvalue()
1679
1680 # - Now create copies of it missing the last N bytes and make sure
1681 # a BadZipFile exception is raised when we try to open it
1682 for N in range(len(zipfiledata)):
1683 fp = io.BytesIO(zipfiledata[:N])
1684 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp)
1685
Ezio Melottiafd0d112009-07-15 17:17:17 +00001686 def test_is_zip_valid_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001687 """Check that is_zipfile() correctly identifies zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001688 # - passing a filename
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001689 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1690 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1691
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001692 self.assertTrue(zipfile.is_zipfile(TESTFN))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001693 # - passing a file object
1694 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001695 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001696 fp.seek(0, 0)
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001697 zip_contents = fp.read()
1698 # - passing a file-like object
1699 fp = io.BytesIO()
1700 fp.write(zip_contents)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001701 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001702 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001703 self.assertTrue(zipfile.is_zipfile(fp))
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001704
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001705 def test_non_existent_file_raises_OSError(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001706 # make sure we don't raise an AttributeError when a partially-constructed
1707 # ZipFile instance is finalized; this tests for regression on SF tracker
1708 # bug #403871.
1709
1710 # The bug we're testing for caused an AttributeError to be raised
1711 # when a ZipFile instance was created for a file that did not
1712 # exist; the .fp member was not initialized but was needed by the
1713 # __del__() method. Since the AttributeError is in the __del__(),
1714 # it is ignored, but the user should be sufficiently annoyed by
1715 # the message on the output that regression will be noticed
1716 # quickly.
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001717 self.assertRaises(OSError, zipfile.ZipFile, TESTFN)
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001718
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001719 def test_empty_file_raises_BadZipFile(self):
1720 f = open(TESTFN, 'w')
1721 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001722 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001723
Ezio Melotti35386712009-12-31 13:22:41 +00001724 with open(TESTFN, 'w') as fp:
1725 fp.write("short file")
Georg Brandl4d540882010-10-28 06:42:33 +00001726 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001727
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001728 def test_closed_zip_raises_ValueError(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001729 """Verify that testzip() doesn't swallow inappropriate exceptions."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001730 data = io.BytesIO()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001731 with zipfile.ZipFile(data, mode="w") as zipf:
1732 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001733
Andrew Svetlov737fb892012-12-18 21:14:22 +02001734 # This is correct; calling .read on a closed ZipFile should raise
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001735 # a ValueError, and so should calling .testzip. An earlier
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001736 # version of .testzip would swallow this exception (and any other)
1737 # and report that the first file in the archive was corrupt.
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001738 self.assertRaises(ValueError, zipf.read, "foo.txt")
1739 self.assertRaises(ValueError, zipf.open, "foo.txt")
1740 self.assertRaises(ValueError, zipf.testzip)
1741 self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus")
Brian Curtin8fb9b862010-11-18 02:15:28 +00001742 with open(TESTFN, 'w') as f:
1743 f.write('zipfile test data')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001744 self.assertRaises(ValueError, zipf.write, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001745
Ezio Melottiafd0d112009-07-15 17:17:17 +00001746 def test_bad_constructor_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001747 """Check that bad modes passed to ZipFile constructor are caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001748 self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001749
Ezio Melottiafd0d112009-07-15 17:17:17 +00001750 def test_bad_open_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001751 """Check that bad modes passed to ZipFile.open are caught."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001752 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1753 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1754
1755 with zipfile.ZipFile(TESTFN, mode="r") as zipf:
Serhiy Storchakae670be22016-06-11 19:32:44 +03001756 # read the data to make sure the file is there
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001757 zipf.read("foo.txt")
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001758 self.assertRaises(ValueError, zipf.open, "foo.txt", "q")
Serhiy Storchakae670be22016-06-11 19:32:44 +03001759 # universal newlines support is removed
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001760 self.assertRaises(ValueError, zipf.open, "foo.txt", "U")
1761 self.assertRaises(ValueError, zipf.open, "foo.txt", "rU")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001762
Ezio Melottiafd0d112009-07-15 17:17:17 +00001763 def test_read0(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001764 """Check that calling read(0) on a ZipExtFile object returns an empty
1765 string and doesn't advance file pointer."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001766 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1767 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1768 # read the data to make sure the file is there
Brian Curtin8fb9b862010-11-18 02:15:28 +00001769 with zipf.open("foo.txt") as f:
1770 for i in range(FIXEDTEST_SIZE):
1771 self.assertEqual(f.read(0), b'')
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001772
Brian Curtin8fb9b862010-11-18 02:15:28 +00001773 self.assertEqual(f.read(), b"O, for a Muse of Fire!")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001774
Ezio Melottiafd0d112009-07-15 17:17:17 +00001775 def test_open_non_existent_item(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001776 """Check that attempting to call open() for an item that doesn't
1777 exist in the archive raises a RuntimeError."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001778 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1779 self.assertRaises(KeyError, zipf.open, "foo.txt", "r")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001780
Ezio Melottiafd0d112009-07-15 17:17:17 +00001781 def test_bad_compression_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001782 """Check that bad compression methods passed to ZipFile.open are
1783 caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001784 self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001785
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001786 def test_unsupported_compression(self):
1787 # data is declared as shrunk, but actually deflated
1788 data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00'
Christian Tismer59202e52013-10-21 03:59:23 +02001789 b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01'
1790 b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00'
1791 b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1792 b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00'
1793 b'/\x00\x00\x00!\x00\x00\x00\x00\x00')
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001794 with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
1795 self.assertRaises(NotImplementedError, zipf.open, 'x')
1796
Ezio Melottiafd0d112009-07-15 17:17:17 +00001797 def test_null_byte_in_filename(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001798 """Check that a filename containing a null byte is properly
1799 terminated."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001800 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1801 zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!")
1802 self.assertEqual(zipf.namelist(), ['foo.txt'])
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001803
Ezio Melottiafd0d112009-07-15 17:17:17 +00001804 def test_struct_sizes(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001805 """Check that ZIP internal structure sizes are calculated correctly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001806 self.assertEqual(zipfile.sizeEndCentDir, 22)
1807 self.assertEqual(zipfile.sizeCentralDir, 46)
1808 self.assertEqual(zipfile.sizeEndCentDir64, 56)
1809 self.assertEqual(zipfile.sizeEndCentDir64Locator, 20)
1810
Ezio Melottiafd0d112009-07-15 17:17:17 +00001811 def test_comments(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001812 """Check that comments on the archive are handled properly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001813
1814 # check default comment is empty
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001815 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1816 self.assertEqual(zipf.comment, b'')
1817 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1818
1819 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1820 self.assertEqual(zipfr.comment, b'')
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001821
1822 # check a simple short comment
1823 comment = b'Bravely taking to his feet, he beat a very brave retreat.'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001824 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1825 zipf.comment = comment
1826 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1827 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1828 self.assertEqual(zipf.comment, comment)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001829
1830 # check a comment of max length
1831 comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)])
1832 comment2 = comment2.encode("ascii")
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001833 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1834 zipf.comment = comment2
1835 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1836
1837 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1838 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001839
1840 # check a comment that is too long is truncated
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001841 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001842 with self.assertWarns(UserWarning):
1843 zipf.comment = comment2 + b'oops'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001844 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1845 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1846 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001847
Antoine Pitrouc3991852012-06-30 17:31:37 +02001848 # check that comments are correctly modified in append mode
1849 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1850 zipf.comment = b"original comment"
1851 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1852 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1853 zipf.comment = b"an updated comment"
1854 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1855 self.assertEqual(zipf.comment, b"an updated comment")
1856
1857 # check that comments are correctly shortened in append mode
1858 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1859 zipf.comment = b"original comment that's longer"
1860 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1861 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1862 zipf.comment = b"shorter comment"
1863 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1864 self.assertEqual(zipf.comment, b"shorter comment")
1865
R David Murrayf50b38a2012-04-12 18:44:58 -04001866 def test_unicode_comment(self):
1867 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1868 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1869 with self.assertRaises(TypeError):
1870 zipf.comment = "this is an error"
1871
1872 def test_change_comment_in_empty_archive(self):
1873 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1874 self.assertFalse(zipf.filelist)
1875 zipf.comment = b"this is a comment"
1876 with zipfile.ZipFile(TESTFN, "r") as zipf:
1877 self.assertEqual(zipf.comment, b"this is a comment")
1878
1879 def test_change_comment_in_nonempty_archive(self):
1880 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1881 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1882 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1883 self.assertTrue(zipf.filelist)
1884 zipf.comment = b"this is a comment"
1885 with zipfile.ZipFile(TESTFN, "r") as zipf:
1886 self.assertEqual(zipf.comment, b"this is a comment")
1887
Georg Brandl268e4d42010-10-14 06:59:45 +00001888 def test_empty_zipfile(self):
1889 # Check that creating a file in 'w' or 'a' mode and closing without
1890 # adding any files to the archives creates a valid empty ZIP file
1891 zipf = zipfile.ZipFile(TESTFN, mode="w")
1892 zipf.close()
1893 try:
1894 zipf = zipfile.ZipFile(TESTFN, mode="r")
1895 except zipfile.BadZipFile:
1896 self.fail("Unable to create empty ZIP file in 'w' mode")
1897
1898 zipf = zipfile.ZipFile(TESTFN, mode="a")
1899 zipf.close()
1900 try:
1901 zipf = zipfile.ZipFile(TESTFN, mode="r")
1902 except:
1903 self.fail("Unable to create empty ZIP file in 'a' mode")
1904
1905 def test_open_empty_file(self):
1906 # Issue 1710703: Check that opening a file with less than 22 bytes
Georg Brandl4d540882010-10-28 06:42:33 +00001907 # raises a BadZipFile exception (rather than the previously unhelpful
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001908 # OSError)
Georg Brandl268e4d42010-10-14 06:59:45 +00001909 f = open(TESTFN, 'w')
1910 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001911 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r')
Georg Brandl268e4d42010-10-14 06:59:45 +00001912
Senthil Kumaran29fa9d42011-10-20 01:46:00 +08001913 def test_create_zipinfo_before_1980(self):
1914 self.assertRaises(ValueError,
1915 zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0))
1916
Mickaël Schoentgen992347d2019-09-09 15:08:54 +02001917 def test_create_empty_zipinfo_repr(self):
1918 """Before bpo-26185, repr() on empty ZipInfo object was failing."""
1919 zi = zipfile.ZipInfo(filename="empty")
1920 self.assertEqual(repr(zi), "<ZipInfo filename='empty' file_size=0>")
1921
1922 def test_create_empty_zipinfo_default_attributes(self):
1923 """Ensure all required attributes are set."""
1924 zi = zipfile.ZipInfo()
1925 self.assertEqual(zi.orig_filename, "NoName")
1926 self.assertEqual(zi.filename, "NoName")
1927 self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0))
1928 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
1929 self.assertEqual(zi.comment, b"")
1930 self.assertEqual(zi.extra, b"")
1931 self.assertIn(zi.create_system, (0, 3))
1932 self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION)
1933 self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION)
1934 self.assertEqual(zi.reserved, 0)
1935 self.assertEqual(zi.flag_bits, 0)
1936 self.assertEqual(zi.volume, 0)
1937 self.assertEqual(zi.internal_attr, 0)
1938 self.assertEqual(zi.external_attr, 0)
1939
1940 # Before bpo-26185, both were missing
1941 self.assertEqual(zi.file_size, 0)
1942 self.assertEqual(zi.compress_size, 0)
1943
Gregory P. Smith0af8a862014-05-29 23:42:14 -07001944 def test_zipfile_with_short_extra_field(self):
1945 """If an extra field in the header is less than 4 bytes, skip it."""
1946 zipdata = (
1947 b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e'
1948 b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab'
1949 b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00'
1950 b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00'
1951 b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00'
1952 b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00'
1953 b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00'
1954 )
1955 with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf:
1956 # testzip returns the name of the first corrupt file, or None
1957 self.assertIsNone(zipf.testzip())
1958
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001959 def test_open_conflicting_handles(self):
1960 # It's only possible to open one writable file handle at a time
1961 msg1 = b"It's fun to charter an accountant!"
1962 msg2 = b"And sail the wide accountant sea"
1963 msg3 = b"To find, explore the funds offshore"
1964 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf:
1965 with zipf.open('foo', mode='w') as w2:
1966 w2.write(msg1)
1967 with zipf.open('bar', mode='w') as w1:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001968 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001969 zipf.open('handle', mode='w')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001970 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001971 zipf.open('foo', mode='r')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001972 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001973 zipf.writestr('str', 'abcde')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001974 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001975 zipf.write(__file__, 'file')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001976 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001977 zipf.close()
1978 w1.write(msg2)
1979 with zipf.open('baz', mode='w') as w2:
1980 w2.write(msg3)
1981
1982 with zipfile.ZipFile(TESTFN2, 'r') as zipf:
1983 self.assertEqual(zipf.read('foo'), msg1)
1984 self.assertEqual(zipf.read('bar'), msg2)
1985 self.assertEqual(zipf.read('baz'), msg3)
1986 self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz'])
1987
John Jolly066df4f2018-01-30 01:51:35 -07001988 def test_seek_tell(self):
1989 # Test seek functionality
1990 txt = b"Where's Bruce?"
1991 bloc = txt.find(b"Bruce")
1992 # Check seek on a file
1993 with zipfile.ZipFile(TESTFN, "w") as zipf:
1994 zipf.writestr("foo.txt", txt)
1995 with zipfile.ZipFile(TESTFN, "r") as zipf:
1996 with zipf.open("foo.txt", "r") as fp:
1997 fp.seek(bloc, os.SEEK_SET)
1998 self.assertEqual(fp.tell(), bloc)
1999 fp.seek(-bloc, os.SEEK_CUR)
2000 self.assertEqual(fp.tell(), 0)
2001 fp.seek(bloc, os.SEEK_CUR)
2002 self.assertEqual(fp.tell(), bloc)
2003 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2004 fp.seek(0, os.SEEK_END)
2005 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002006 fp.seek(0, os.SEEK_SET)
2007 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002008 # Check seek on memory file
2009 data = io.BytesIO()
2010 with zipfile.ZipFile(data, mode="w") as zipf:
2011 zipf.writestr("foo.txt", txt)
2012 with zipfile.ZipFile(data, mode="r") as zipf:
2013 with zipf.open("foo.txt", "r") as fp:
2014 fp.seek(bloc, os.SEEK_SET)
2015 self.assertEqual(fp.tell(), bloc)
2016 fp.seek(-bloc, os.SEEK_CUR)
2017 self.assertEqual(fp.tell(), 0)
2018 fp.seek(bloc, os.SEEK_CUR)
2019 self.assertEqual(fp.tell(), bloc)
2020 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2021 fp.seek(0, os.SEEK_END)
2022 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002023 fp.seek(0, os.SEEK_SET)
2024 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002025
Hai Shia3ec3ad2020-05-19 06:02:57 +08002026 @requires_bz2()
Berker Peksag2f1b8572019-09-12 17:13:44 +03002027 def test_decompress_without_3rd_party_library(self):
2028 data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
2029 zip_file = io.BytesIO(data)
2030 with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf:
2031 zf.writestr('a.txt', b'a')
2032 with mock.patch('zipfile.bz2', None):
2033 with zipfile.ZipFile(zip_file) as zf:
2034 self.assertRaises(RuntimeError, zf.extract, 'a.txt')
2035
Guido van Rossumd8faa362007-04-27 19:54:29 +00002036 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002037 unlink(TESTFN)
2038 unlink(TESTFN2)
2039
Thomas Wouterscf297e42007-02-23 15:07:44 +00002040
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002041class AbstractBadCrcTests:
2042 def test_testzip_with_bad_crc(self):
2043 """Tests that files with bad CRCs return their name from testzip."""
2044 zipdata = self.zip_with_bad_crc
2045
2046 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2047 # testzip returns the name of the first corrupt file, or None
2048 self.assertEqual('afile', zipf.testzip())
2049
2050 def test_read_with_bad_crc(self):
2051 """Tests that files with bad CRCs raise a BadZipFile exception when read."""
2052 zipdata = self.zip_with_bad_crc
2053
2054 # Using ZipFile.read()
2055 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2056 self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
2057
2058 # Using ZipExtFile.read()
2059 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2060 with zipf.open('afile', 'r') as corrupt_file:
2061 self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
2062
2063 # Same with small reads (in order to exercise the buffering logic)
2064 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2065 with zipf.open('afile', 'r') as corrupt_file:
2066 corrupt_file.MIN_READ_SIZE = 2
2067 with self.assertRaises(zipfile.BadZipFile):
2068 while corrupt_file.read(2):
2069 pass
2070
2071
2072class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2073 compression = zipfile.ZIP_STORED
2074 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002075 b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
2076 b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
2077 b'ilehello,AworldP'
2078 b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
2079 b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
2080 b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
2081 b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
2082 b'\0\0/\0\0\0\0\0')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002083
Hai Shia3ec3ad2020-05-19 06:02:57 +08002084@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002085class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2086 compression = zipfile.ZIP_DEFLATED
2087 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002088 b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
2089 b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2090 b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
2091 b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
2092 b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
2093 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
2094 b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
2095 b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002096
Hai Shia3ec3ad2020-05-19 06:02:57 +08002097@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002098class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2099 compression = zipfile.ZIP_BZIP2
2100 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002101 b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
2102 b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2103 b'ileBZh91AY&SY\xd4\xa8\xca'
2104 b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
2105 b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
2106 b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
2107 b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
2108 b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
2109 b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
2110 b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
2111 b'\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002112
Hai Shia3ec3ad2020-05-19 06:02:57 +08002113@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002114class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2115 compression = zipfile.ZIP_LZMA
2116 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002117 b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2118 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2119 b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
2120 b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
2121 b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2122 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
2123 b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
2124 b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
2125 b'\x00>\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002126
2127
Thomas Wouterscf297e42007-02-23 15:07:44 +00002128class DecryptionTests(unittest.TestCase):
Ezio Melotti35386712009-12-31 13:22:41 +00002129 """Check that ZIP decryption works. Since the library does not
2130 support encryption at the moment, we use a pre-generated encrypted
2131 ZIP file."""
Thomas Wouterscf297e42007-02-23 15:07:44 +00002132
2133 data = (
Christian Tismer59202e52013-10-21 03:59:23 +02002134 b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
2135 b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
2136 b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
2137 b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
2138 b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
2139 b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
2140 b'\x00\x00L\x00\x00\x00\x00\x00' )
Christian Heimesfdab48e2008-01-20 09:06:41 +00002141 data2 = (
Christian Tismer59202e52013-10-21 03:59:23 +02002142 b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
2143 b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
2144 b'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
2145 b'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
2146 b'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
2147 b'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
2148 b'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
2149 b'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
Thomas Wouterscf297e42007-02-23 15:07:44 +00002150
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002151 plain = b'zipfile.py encryption test'
Christian Heimesfdab48e2008-01-20 09:06:41 +00002152 plain2 = b'\x00'*512
Thomas Wouterscf297e42007-02-23 15:07:44 +00002153
2154 def setUp(self):
Ezio Melotti35386712009-12-31 13:22:41 +00002155 with open(TESTFN, "wb") as fp:
2156 fp.write(self.data)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002157 self.zip = zipfile.ZipFile(TESTFN, "r")
Ezio Melotti35386712009-12-31 13:22:41 +00002158 with open(TESTFN2, "wb") as fp:
2159 fp.write(self.data2)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002160 self.zip2 = zipfile.ZipFile(TESTFN2, "r")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002161
2162 def tearDown(self):
2163 self.zip.close()
2164 os.unlink(TESTFN)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002165 self.zip2.close()
2166 os.unlink(TESTFN2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002167
Ezio Melottiafd0d112009-07-15 17:17:17 +00002168 def test_no_password(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00002169 # Reading the encrypted file without password
2170 # must generate a RunTime exception
2171 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002172 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002173
Ezio Melottiafd0d112009-07-15 17:17:17 +00002174 def test_bad_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002175 self.zip.setpassword(b"perl")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002176 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002177 self.zip2.setpassword(b"perl")
2178 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Guido van Rossumd8faa362007-04-27 19:54:29 +00002179
Hai Shia3ec3ad2020-05-19 06:02:57 +08002180 @requires_zlib()
Ezio Melottiafd0d112009-07-15 17:17:17 +00002181 def test_good_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002182 self.zip.setpassword(b"python")
Ezio Melotti35386712009-12-31 13:22:41 +00002183 self.assertEqual(self.zip.read("test.txt"), self.plain)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002184 self.zip2.setpassword(b"12345")
Ezio Melotti35386712009-12-31 13:22:41 +00002185 self.assertEqual(self.zip2.read("zero"), self.plain2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002186
R. David Murray8d855d82010-12-21 21:53:37 +00002187 def test_unicode_password(self):
2188 self.assertRaises(TypeError, self.zip.setpassword, "unicode")
2189 self.assertRaises(TypeError, self.zip.read, "test.txt", "python")
2190 self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
2191 self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")
2192
Serhiy Storchaka5c32af72019-10-27 10:22:14 +02002193 def test_seek_tell(self):
2194 self.zip.setpassword(b"python")
2195 txt = self.plain
2196 test_word = b'encryption'
2197 bloc = txt.find(test_word)
2198 bloc_len = len(test_word)
2199 with self.zip.open("test.txt", "r") as fp:
2200 fp.seek(bloc, os.SEEK_SET)
2201 self.assertEqual(fp.tell(), bloc)
2202 fp.seek(-bloc, os.SEEK_CUR)
2203 self.assertEqual(fp.tell(), 0)
2204 fp.seek(bloc, os.SEEK_CUR)
2205 self.assertEqual(fp.tell(), bloc)
2206 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2207
2208 # Make sure that the second read after seeking back beyond
2209 # _readbuffer returns the same content (ie. rewind to the start of
2210 # the file to read forward to the required position).
2211 old_read_size = fp.MIN_READ_SIZE
2212 fp.MIN_READ_SIZE = 1
2213 fp._readbuffer = b''
2214 fp._offset = 0
2215 fp.seek(0, os.SEEK_SET)
2216 self.assertEqual(fp.tell(), 0)
2217 fp.seek(bloc, os.SEEK_CUR)
2218 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2219 fp.MIN_READ_SIZE = old_read_size
2220
2221 fp.seek(0, os.SEEK_END)
2222 self.assertEqual(fp.tell(), len(txt))
2223 fp.seek(0, os.SEEK_SET)
2224 self.assertEqual(fp.tell(), 0)
2225
2226 # Read the file completely to definitely call any eof integrity
2227 # checks (crc) and make sure they still pass.
2228 fp.read()
2229
2230
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002231class AbstractTestsWithRandomBinaryFiles:
2232 @classmethod
2233 def setUpClass(cls):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002234 datacount = randint(16, 64)*1024 + randint(1, 1024)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002235 cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
2236 for i in range(datacount))
Guido van Rossumd8faa362007-04-27 19:54:29 +00002237
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002238 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002239 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +00002240 with open(TESTFN, "wb") as fp:
2241 fp.write(self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002242
2243 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002244 unlink(TESTFN)
2245 unlink(TESTFN2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002246
Ezio Melottiafd0d112009-07-15 17:17:17 +00002247 def make_test_archive(self, f, compression):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002248 # Create the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002249 with zipfile.ZipFile(f, "w", compression) as zipfp:
2250 zipfp.write(TESTFN, "another.name")
2251 zipfp.write(TESTFN, TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002252
Ezio Melottiafd0d112009-07-15 17:17:17 +00002253 def zip_test(self, f, compression):
2254 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002255
2256 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002257 with zipfile.ZipFile(f, "r", compression) as zipfp:
2258 testdata = zipfp.read(TESTFN)
2259 self.assertEqual(len(testdata), len(self.data))
2260 self.assertEqual(testdata, self.data)
2261 self.assertEqual(zipfp.read("another.name"), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002262
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002263 def test_read(self):
2264 for f in get_files(self):
2265 self.zip_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002266
Ezio Melottiafd0d112009-07-15 17:17:17 +00002267 def zip_open_test(self, f, compression):
2268 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002269
2270 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002271 with zipfile.ZipFile(f, "r", compression) as zipfp:
2272 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002273 with zipfp.open(TESTFN) as zipopen1:
2274 while True:
2275 read_data = zipopen1.read(256)
2276 if not read_data:
2277 break
2278 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002279
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002280 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002281 with zipfp.open("another.name") as zipopen2:
2282 while True:
2283 read_data = zipopen2.read(256)
2284 if not read_data:
2285 break
2286 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002287
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002288 testdata1 = b''.join(zipdata1)
2289 self.assertEqual(len(testdata1), len(self.data))
2290 self.assertEqual(testdata1, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002291
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002292 testdata2 = b''.join(zipdata2)
Ezio Melotti35386712009-12-31 13:22:41 +00002293 self.assertEqual(len(testdata2), len(self.data))
2294 self.assertEqual(testdata2, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002295
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002296 def test_open(self):
2297 for f in get_files(self):
2298 self.zip_open_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002299
Ezio Melottiafd0d112009-07-15 17:17:17 +00002300 def zip_random_open_test(self, f, compression):
2301 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002302
2303 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002304 with zipfile.ZipFile(f, "r", compression) as zipfp:
2305 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002306 with zipfp.open(TESTFN) as zipopen1:
2307 while True:
2308 read_data = zipopen1.read(randint(1, 1024))
2309 if not read_data:
2310 break
2311 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002312
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002313 testdata = b''.join(zipdata1)
2314 self.assertEqual(len(testdata), len(self.data))
2315 self.assertEqual(testdata, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002316
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002317 def test_random_open(self):
2318 for f in get_files(self):
2319 self.zip_random_open_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002320
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00002321
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002322class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2323 unittest.TestCase):
2324 compression = zipfile.ZIP_STORED
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02002325
Hai Shia3ec3ad2020-05-19 06:02:57 +08002326@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002327class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2328 unittest.TestCase):
2329 compression = zipfile.ZIP_DEFLATED
2330
Hai Shia3ec3ad2020-05-19 06:02:57 +08002331@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002332class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2333 unittest.TestCase):
2334 compression = zipfile.ZIP_BZIP2
2335
Hai Shia3ec3ad2020-05-19 06:02:57 +08002336@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002337class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2338 unittest.TestCase):
2339 compression = zipfile.ZIP_LZMA
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002340
Ezio Melotti76430242009-07-11 18:28:48 +00002341
luzpaza5293b42017-11-05 07:37:50 -06002342# Provide the tell() method but not seek()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002343class Tellable:
2344 def __init__(self, fp):
2345 self.fp = fp
2346 self.offset = 0
2347
2348 def write(self, data):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002349 n = self.fp.write(data)
2350 self.offset += n
2351 return n
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002352
2353 def tell(self):
2354 return self.offset
2355
2356 def flush(self):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002357 self.fp.flush()
2358
2359class Unseekable:
2360 def __init__(self, fp):
2361 self.fp = fp
2362
2363 def write(self, data):
2364 return self.fp.write(data)
2365
2366 def flush(self):
2367 self.fp.flush()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002368
2369class UnseekableTests(unittest.TestCase):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002370 def test_writestr(self):
2371 for wrapper in (lambda f: f), Tellable, Unseekable:
2372 with self.subTest(wrapper=wrapper):
2373 f = io.BytesIO()
2374 f.write(b'abc')
2375 bf = io.BufferedWriter(f)
2376 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2377 zipfp.writestr('ones', b'111')
2378 zipfp.writestr('twos', b'222')
2379 self.assertEqual(f.getvalue()[:5], b'abcPK')
2380 with zipfile.ZipFile(f, mode='r') as zipf:
2381 with zipf.open('ones') as zopen:
2382 self.assertEqual(zopen.read(), b'111')
2383 with zipf.open('twos') as zopen:
2384 self.assertEqual(zopen.read(), b'222')
2385
2386 def test_write(self):
2387 for wrapper in (lambda f: f), Tellable, Unseekable:
2388 with self.subTest(wrapper=wrapper):
2389 f = io.BytesIO()
2390 f.write(b'abc')
2391 bf = io.BufferedWriter(f)
2392 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2393 self.addCleanup(unlink, TESTFN)
2394 with open(TESTFN, 'wb') as f2:
2395 f2.write(b'111')
2396 zipfp.write(TESTFN, 'ones')
2397 with open(TESTFN, 'wb') as f2:
2398 f2.write(b'222')
2399 zipfp.write(TESTFN, 'twos')
2400 self.assertEqual(f.getvalue()[:5], b'abcPK')
2401 with zipfile.ZipFile(f, mode='r') as zipf:
2402 with zipf.open('ones') as zopen:
2403 self.assertEqual(zopen.read(), b'111')
2404 with zipf.open('twos') as zopen:
2405 self.assertEqual(zopen.read(), b'222')
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002406
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002407 def test_open_write(self):
2408 for wrapper in (lambda f: f), Tellable, Unseekable:
2409 with self.subTest(wrapper=wrapper):
2410 f = io.BytesIO()
2411 f.write(b'abc')
2412 bf = io.BufferedWriter(f)
2413 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf:
2414 with zipf.open('ones', 'w') as zopen:
2415 zopen.write(b'111')
2416 with zipf.open('twos', 'w') as zopen:
2417 zopen.write(b'222')
2418 self.assertEqual(f.getvalue()[:5], b'abcPK')
2419 with zipfile.ZipFile(f) as zipf:
2420 self.assertEqual(zipf.read('ones'), b'111')
2421 self.assertEqual(zipf.read('twos'), b'222')
2422
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002423
Hai Shia3ec3ad2020-05-19 06:02:57 +08002424@requires_zlib()
Guido van Rossumd8faa362007-04-27 19:54:29 +00002425class TestsWithMultipleOpens(unittest.TestCase):
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002426 @classmethod
2427 def setUpClass(cls):
Victor Stinner87502dd2020-04-17 22:54:38 +02002428 cls.data1 = b'111' + randbytes(10000)
2429 cls.data2 = b'222' + randbytes(10000)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002430
2431 def make_test_archive(self, f):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002432 # Create the ZIP archive
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002433 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp:
2434 zipfp.writestr('ones', self.data1)
2435 zipfp.writestr('twos', self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002436
Ezio Melottiafd0d112009-07-15 17:17:17 +00002437 def test_same_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002438 # Verify that (when the ZipFile is in control of creating file objects)
2439 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002440 for f in get_files(self):
2441 self.make_test_archive(f)
2442 with zipfile.ZipFile(f, mode="r") as zipf:
2443 with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2:
2444 data1 = zopen1.read(500)
2445 data2 = zopen2.read(500)
2446 data1 += zopen1.read()
2447 data2 += zopen2.read()
2448 self.assertEqual(data1, data2)
2449 self.assertEqual(data1, self.data1)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002450
Ezio Melottiafd0d112009-07-15 17:17:17 +00002451 def test_different_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002452 # Verify that (when the ZipFile is in control of creating file objects)
2453 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002454 for f in get_files(self):
2455 self.make_test_archive(f)
2456 with zipfile.ZipFile(f, mode="r") as zipf:
2457 with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2:
2458 data1 = zopen1.read(500)
2459 data2 = zopen2.read(500)
2460 data1 += zopen1.read()
2461 data2 += zopen2.read()
2462 self.assertEqual(data1, self.data1)
2463 self.assertEqual(data2, self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002464
Ezio Melottiafd0d112009-07-15 17:17:17 +00002465 def test_interleaved(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002466 # Verify that (when the ZipFile is in control of creating file objects)
2467 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002468 for f in get_files(self):
2469 self.make_test_archive(f)
2470 with zipfile.ZipFile(f, mode="r") as zipf:
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002471 with zipf.open('ones') as zopen1:
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002472 data1 = zopen1.read(500)
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002473 with zipf.open('twos') as zopen2:
2474 data2 = zopen2.read(500)
2475 data1 += zopen1.read()
2476 data2 += zopen2.read()
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002477 self.assertEqual(data1, self.data1)
2478 self.assertEqual(data2, self.data2)
2479
2480 def test_read_after_close(self):
2481 for f in get_files(self):
2482 self.make_test_archive(f)
2483 with contextlib.ExitStack() as stack:
2484 with zipfile.ZipFile(f, 'r') as zipf:
2485 zopen1 = stack.enter_context(zipf.open('ones'))
2486 zopen2 = stack.enter_context(zipf.open('twos'))
Brian Curtin8fb9b862010-11-18 02:15:28 +00002487 data1 = zopen1.read(500)
2488 data2 = zopen2.read(500)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002489 data1 += zopen1.read()
2490 data2 += zopen2.read()
2491 self.assertEqual(data1, self.data1)
2492 self.assertEqual(data2, self.data2)
2493
2494 def test_read_after_write(self):
2495 for f in get_files(self):
2496 with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf:
2497 zipf.writestr('ones', self.data1)
2498 zipf.writestr('twos', self.data2)
2499 with zipf.open('ones') as zopen1:
2500 data1 = zopen1.read(500)
2501 self.assertEqual(data1, self.data1[:500])
2502 with zipfile.ZipFile(f, 'r') as zipf:
2503 data1 = zipf.read('ones')
2504 data2 = zipf.read('twos')
2505 self.assertEqual(data1, self.data1)
2506 self.assertEqual(data2, self.data2)
2507
2508 def test_write_after_read(self):
2509 for f in get_files(self):
2510 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf:
2511 zipf.writestr('ones', self.data1)
2512 with zipf.open('ones') as zopen1:
2513 zopen1.read(500)
2514 zipf.writestr('twos', self.data2)
2515 with zipfile.ZipFile(f, 'r') as zipf:
2516 data1 = zipf.read('ones')
2517 data2 = zipf.read('twos')
2518 self.assertEqual(data1, self.data1)
2519 self.assertEqual(data2, self.data2)
2520
2521 def test_many_opens(self):
2522 # Verify that read() and open() promptly close the file descriptor,
2523 # and don't rely on the garbage collector to free resources.
2524 self.make_test_archive(TESTFN2)
2525 with zipfile.ZipFile(TESTFN2, mode="r") as zipf:
2526 for x in range(100):
2527 zipf.read('ones')
2528 with zipf.open('ones') as zopen1:
2529 pass
2530 with open(os.devnull) as f:
2531 self.assertLess(f.fileno(), 100)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002532
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002533 def test_write_while_reading(self):
2534 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf:
2535 zipf.writestr('ones', self.data1)
2536 with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf:
2537 with zipf.open('ones', 'r') as r1:
2538 data1 = r1.read(500)
2539 with zipf.open('twos', 'w') as w1:
2540 w1.write(self.data2)
2541 data1 += r1.read()
2542 self.assertEqual(data1, self.data1)
2543 with zipfile.ZipFile(TESTFN2) as zipf:
2544 self.assertEqual(zipf.read('twos'), self.data2)
2545
Guido van Rossumd8faa362007-04-27 19:54:29 +00002546 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002547 unlink(TESTFN2)
2548
Guido van Rossumd8faa362007-04-27 19:54:29 +00002549
Martin v. Löwis59e47792009-01-24 14:10:07 +00002550class TestWithDirectory(unittest.TestCase):
2551 def setUp(self):
2552 os.mkdir(TESTFN2)
2553
Ezio Melottiafd0d112009-07-15 17:17:17 +00002554 def test_extract_dir(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002555 with zipfile.ZipFile(findfile("zipdir.zip")) as zipf:
2556 zipf.extractall(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002557 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2558 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2559 self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
2560
Ezio Melottiafd0d112009-07-15 17:17:17 +00002561 def test_bug_6050(self):
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002562 # Extraction should succeed if directories already exist
2563 os.mkdir(os.path.join(TESTFN2, "a"))
Ezio Melottiafd0d112009-07-15 17:17:17 +00002564 self.test_extract_dir()
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002565
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002566 def test_write_dir(self):
2567 dirpath = os.path.join(TESTFN2, "x")
2568 os.mkdir(dirpath)
2569 mode = os.stat(dirpath).st_mode & 0xFFFF
2570 with zipfile.ZipFile(TESTFN, "w") as zipf:
2571 zipf.write(dirpath)
2572 zinfo = zipf.filelist[0]
2573 self.assertTrue(zinfo.filename.endswith("/x/"))
2574 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2575 zipf.write(dirpath, "y")
2576 zinfo = zipf.filelist[1]
2577 self.assertTrue(zinfo.filename, "y/")
2578 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2579 with zipfile.ZipFile(TESTFN, "r") as zipf:
2580 zinfo = zipf.filelist[0]
2581 self.assertTrue(zinfo.filename.endswith("/x/"))
2582 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2583 zinfo = zipf.filelist[1]
2584 self.assertTrue(zinfo.filename, "y/")
2585 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2586 target = os.path.join(TESTFN2, "target")
2587 os.mkdir(target)
2588 zipf.extractall(target)
2589 self.assertTrue(os.path.isdir(os.path.join(target, "y")))
2590 self.assertEqual(len(os.listdir(target)), 2)
2591
2592 def test_writestr_dir(self):
Martin v. Löwis59e47792009-01-24 14:10:07 +00002593 os.mkdir(os.path.join(TESTFN2, "x"))
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002594 with zipfile.ZipFile(TESTFN, "w") as zipf:
2595 zipf.writestr("x/", b'')
2596 zinfo = zipf.filelist[0]
2597 self.assertEqual(zinfo.filename, "x/")
2598 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2599 with zipfile.ZipFile(TESTFN, "r") as zipf:
2600 zinfo = zipf.filelist[0]
2601 self.assertTrue(zinfo.filename.endswith("x/"))
2602 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2603 target = os.path.join(TESTFN2, "target")
2604 os.mkdir(target)
2605 zipf.extractall(target)
2606 self.assertTrue(os.path.isdir(os.path.join(target, "x")))
2607 self.assertEqual(os.listdir(target), ["x"])
Martin v. Löwis59e47792009-01-24 14:10:07 +00002608
2609 def tearDown(self):
Victor Stinner57004c62014-09-04 00:49:01 +02002610 rmtree(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002611 if os.path.exists(TESTFN):
Ezio Melotti76430242009-07-11 18:28:48 +00002612 unlink(TESTFN)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002613
Guido van Rossumd8faa362007-04-27 19:54:29 +00002614
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002615class ZipInfoTests(unittest.TestCase):
2616 def test_from_file(self):
2617 zi = zipfile.ZipInfo.from_file(__file__)
2618 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2619 self.assertFalse(zi.is_dir())
Serhiy Storchaka8606e952017-03-08 14:37:51 +02002620 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2621
2622 def test_from_file_pathlike(self):
2623 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2624 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2625 self.assertFalse(zi.is_dir())
2626 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2627
2628 def test_from_file_bytes(self):
2629 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2630 self.assertEqual(posixpath.basename(zi.filename), 'test')
2631 self.assertFalse(zi.is_dir())
2632 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2633
2634 def test_from_file_fileno(self):
2635 with open(__file__, 'rb') as f:
2636 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2637 self.assertEqual(posixpath.basename(zi.filename), 'test')
2638 self.assertFalse(zi.is_dir())
2639 self.assertEqual(zi.file_size, os.path.getsize(__file__))
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002640
2641 def test_from_dir(self):
2642 dirpath = os.path.dirname(os.path.abspath(__file__))
2643 zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
2644 self.assertEqual(zi.filename, 'stdlib_tests/')
2645 self.assertTrue(zi.is_dir())
2646 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
2647 self.assertEqual(zi.file_size, 0)
2648
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002649
2650class CommandLineTest(unittest.TestCase):
2651
2652 def zipfilecmd(self, *args, **kwargs):
2653 rc, out, err = script_helper.assert_python_ok('-m', 'zipfile', *args,
2654 **kwargs)
2655 return out.replace(os.linesep.encode(), b'\n')
2656
2657 def zipfilecmd_failure(self, *args):
2658 return script_helper.assert_python_failure('-m', 'zipfile', *args)
2659
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002660 def test_bad_use(self):
2661 rc, out, err = self.zipfilecmd_failure()
2662 self.assertEqual(out, b'')
2663 self.assertIn(b'usage', err.lower())
2664 self.assertIn(b'error', err.lower())
2665 self.assertIn(b'required', err.lower())
2666 rc, out, err = self.zipfilecmd_failure('-l', '')
2667 self.assertEqual(out, b'')
2668 self.assertNotEqual(err.strip(), b'')
2669
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002670 def test_test_command(self):
2671 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002672 for opt in '-t', '--test':
2673 out = self.zipfilecmd(opt, zip_name)
2674 self.assertEqual(out.rstrip(), b'Done testing')
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002675 zip_name = findfile('testtar.tar')
2676 rc, out, err = self.zipfilecmd_failure('-t', zip_name)
2677 self.assertEqual(out, b'')
2678
2679 def test_list_command(self):
2680 zip_name = findfile('zipdir.zip')
2681 t = io.StringIO()
2682 with zipfile.ZipFile(zip_name, 'r') as tf:
2683 tf.printdir(t)
2684 expected = t.getvalue().encode('ascii', 'backslashreplace')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002685 for opt in '-l', '--list':
2686 out = self.zipfilecmd(opt, zip_name,
2687 PYTHONIOENCODING='ascii:backslashreplace')
2688 self.assertEqual(out, expected)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002689
Hai Shia3ec3ad2020-05-19 06:02:57 +08002690 @requires_zlib()
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002691 def test_create_command(self):
2692 self.addCleanup(unlink, TESTFN)
2693 with open(TESTFN, 'w') as f:
2694 f.write('test 1')
2695 os.mkdir(TESTFNDIR)
2696 self.addCleanup(rmtree, TESTFNDIR)
2697 with open(os.path.join(TESTFNDIR, 'file.txt'), 'w') as f:
2698 f.write('test 2')
2699 files = [TESTFN, TESTFNDIR]
2700 namelist = [TESTFN, TESTFNDIR + '/', TESTFNDIR + '/file.txt']
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002701 for opt in '-c', '--create':
2702 try:
2703 out = self.zipfilecmd(opt, TESTFN2, *files)
2704 self.assertEqual(out, b'')
2705 with zipfile.ZipFile(TESTFN2) as zf:
2706 self.assertEqual(zf.namelist(), namelist)
2707 self.assertEqual(zf.read(namelist[0]), b'test 1')
2708 self.assertEqual(zf.read(namelist[2]), b'test 2')
2709 finally:
2710 unlink(TESTFN2)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002711
2712 def test_extract_command(self):
2713 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002714 for opt in '-e', '--extract':
2715 with temp_dir() as extdir:
2716 out = self.zipfilecmd(opt, zip_name, extdir)
2717 self.assertEqual(out, b'')
2718 with zipfile.ZipFile(zip_name) as zf:
2719 for zi in zf.infolist():
2720 path = os.path.join(extdir,
2721 zi.filename.replace('/', os.sep))
2722 if zi.is_dir():
2723 self.assertTrue(os.path.isdir(path))
2724 else:
2725 self.assertTrue(os.path.isfile(path))
2726 with open(path, 'rb') as f:
2727 self.assertEqual(f.read(), zf.read(zi))
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002728
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002729
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002730class TestExecutablePrependedZip(unittest.TestCase):
2731 """Test our ability to open zip files with an executable prepended."""
2732
2733 def setUp(self):
2734 self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
2735 self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
2736
2737 def _test_zip_works(self, name):
2738 # bpo28494 sanity check: ensure is_zipfile works on these.
2739 self.assertTrue(zipfile.is_zipfile(name),
2740 f'is_zipfile failed on {name}')
2741 # Ensure we can operate on these via ZipFile.
2742 with zipfile.ZipFile(name) as zipfp:
2743 for n in zipfp.namelist():
2744 data = zipfp.read(n)
2745 self.assertIn(b'FAVORITE_NUMBER', data)
2746
2747 def test_read_zip_with_exe_prepended(self):
2748 self._test_zip_works(self.exe_zip)
2749
2750 def test_read_zip64_with_exe_prepended(self):
2751 self._test_zip_works(self.exe_zip64)
2752
2753 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2754 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2755 'Test relies on #!/bin/bash working.')
2756 def test_execute_zip2(self):
2757 output = subprocess.check_output([self.exe_zip, sys.executable])
2758 self.assertIn(b'number in executable: 5', output)
2759
2760 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2761 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2762 'Test relies on #!/bin/bash working.')
2763 def test_execute_zip64(self):
2764 output = subprocess.check_output([self.exe_zip64, sys.executable])
2765 self.assertIn(b'number in executable: 5', output)
2766
2767
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002768# Poor man's technique to consume a (smallish) iterable.
2769consume = tuple
2770
2771
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002772# from jaraco.itertools 5.0
2773class jaraco:
2774 class itertools:
2775 class Counter:
2776 def __init__(self, i):
2777 self.count = 0
2778 self._orig_iter = iter(i)
2779
2780 def __iter__(self):
2781 return self
2782
2783 def __next__(self):
2784 result = next(self._orig_iter)
2785 self.count += 1
2786 return result
2787
2788
shireenraoa4e29912019-08-24 11:26:41 -04002789def add_dirs(zf):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002790 """
shireenraoa4e29912019-08-24 11:26:41 -04002791 Given a writable zip file zf, inject directory entries for
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002792 any directories implied by the presence of children.
2793 """
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002794 for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()):
shireenraoa4e29912019-08-24 11:26:41 -04002795 zf.writestr(name, b"")
2796 return zf
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002797
2798
shireenraoa4e29912019-08-24 11:26:41 -04002799def build_alpharep_fixture():
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002800 """
2801 Create a zip file with this structure:
2802
2803 .
2804 ├── a.txt
shireenraoa4e29912019-08-24 11:26:41 -04002805 ├── b
2806 │ ├── c.txt
2807 │ ├── d
2808 │ │ └── e.txt
2809 │ └── f.txt
2810 └── g
2811 └── h
2812 └── i.txt
2813
2814 This fixture has the following key characteristics:
2815
2816 - a file at the root (a)
2817 - a file two levels deep (b/d/e)
2818 - multiple files in a directory (b/c, b/f)
2819 - a directory containing only a directory (g/h)
2820
2821 "alpha" because it uses alphabet
2822 "rep" because it's a representative example
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002823 """
2824 data = io.BytesIO()
2825 zf = zipfile.ZipFile(data, "w")
2826 zf.writestr("a.txt", b"content of a")
2827 zf.writestr("b/c.txt", b"content of c")
2828 zf.writestr("b/d/e.txt", b"content of e")
shireenraoa4e29912019-08-24 11:26:41 -04002829 zf.writestr("b/f.txt", b"content of f")
2830 zf.writestr("g/h/i.txt", b"content of i")
2831 zf.filename = "alpharep.zip"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002832 return zf
2833
2834
2835class TestPath(unittest.TestCase):
2836 def setUp(self):
2837 self.fixtures = contextlib.ExitStack()
2838 self.addCleanup(self.fixtures.close)
2839
shireenraoa4e29912019-08-24 11:26:41 -04002840 def zipfile_alpharep(self):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002841 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002842 yield build_alpharep_fixture()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002843 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002844 yield add_dirs(build_alpharep_fixture())
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002845
2846 def zipfile_ondisk(self):
2847 tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
shireenraoa4e29912019-08-24 11:26:41 -04002848 for alpharep in self.zipfile_alpharep():
2849 buffer = alpharep.fp
2850 alpharep.close()
2851 path = tmpdir / alpharep.filename
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002852 with path.open("wb") as strm:
2853 strm.write(buffer.getvalue())
2854 yield path
2855
shireenraoa4e29912019-08-24 11:26:41 -04002856 def test_iterdir_and_types(self):
2857 for alpharep in self.zipfile_alpharep():
2858 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002859 assert root.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002860 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002861 assert a.is_file()
2862 assert b.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002863 assert g.is_dir()
2864 c, f, d = b.iterdir()
2865 assert c.is_file() and f.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002866 e, = d.iterdir()
2867 assert e.is_file()
shireenraoa4e29912019-08-24 11:26:41 -04002868 h, = g.iterdir()
2869 i, = h.iterdir()
2870 assert i.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002871
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002872 def test_subdir_is_dir(self):
2873 for alpharep in self.zipfile_alpharep():
2874 root = zipfile.Path(alpharep)
2875 assert (root / 'b').is_dir()
2876 assert (root / 'b/').is_dir()
2877 assert (root / 'g').is_dir()
2878 assert (root / 'g/').is_dir()
2879
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002880 def test_open(self):
shireenraoa4e29912019-08-24 11:26:41 -04002881 for alpharep in self.zipfile_alpharep():
2882 root = zipfile.Path(alpharep)
2883 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002884 with a.open() as strm:
2885 data = strm.read()
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002886 assert data == "content of a"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002887
2888 def test_read(self):
shireenraoa4e29912019-08-24 11:26:41 -04002889 for alpharep in self.zipfile_alpharep():
2890 root = zipfile.Path(alpharep)
2891 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002892 assert a.read_text() == "content of a"
2893 assert a.read_bytes() == b"content of a"
2894
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002895 def test_joinpath(self):
shireenraoa4e29912019-08-24 11:26:41 -04002896 for alpharep in self.zipfile_alpharep():
2897 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002898 a = root.joinpath("a")
2899 assert a.is_file()
2900 e = root.joinpath("b").joinpath("d").joinpath("e.txt")
2901 assert e.read_text() == "content of e"
2902
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002903 def test_traverse_truediv(self):
shireenraoa4e29912019-08-24 11:26:41 -04002904 for alpharep in self.zipfile_alpharep():
2905 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002906 a = root / "a"
2907 assert a.is_file()
2908 e = root / "b" / "d" / "e.txt"
2909 assert e.read_text() == "content of e"
2910
2911 def test_pathlike_construction(self):
2912 """
2913 zipfile.Path should be constructable from a path-like object
2914 """
2915 for zipfile_ondisk in self.zipfile_ondisk():
2916 pathlike = pathlib.Path(str(zipfile_ondisk))
2917 zipfile.Path(pathlike)
2918
2919 def test_traverse_pathlike(self):
shireenraoa4e29912019-08-24 11:26:41 -04002920 for alpharep in self.zipfile_alpharep():
2921 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002922 root / pathlib.Path("a")
2923
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002924 def test_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002925 for alpharep in self.zipfile_alpharep():
2926 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002927 assert (root / 'a').parent.at == ''
2928 assert (root / 'a' / 'b').parent.at == 'a/'
2929
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002930 def test_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002931 for alpharep in self.zipfile_alpharep():
2932 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002933 assert (root / 'b').parent.at == ''
2934 assert (root / 'b/').parent.at == ''
2935
2936 def test_missing_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002937 for alpharep in self.zipfile_alpharep():
2938 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002939 assert (root / 'missing dir/').parent.at == ''
2940
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002941 def test_mutability(self):
2942 """
2943 If the underlying zipfile is changed, the Path object should
2944 reflect that change.
2945 """
2946 for alpharep in self.zipfile_alpharep():
2947 root = zipfile.Path(alpharep)
2948 a, b, g = root.iterdir()
2949 alpharep.writestr('foo.txt', 'foo')
2950 alpharep.writestr('bar/baz.txt', 'baz')
2951 assert any(
2952 child.name == 'foo.txt'
2953 for child in root.iterdir())
2954 assert (root / 'foo.txt').read_text() == 'foo'
2955 baz, = (root / 'bar').iterdir()
2956 assert baz.read_text() == 'baz'
2957
2958 HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
2959
2960 def huge_zipfile(self):
2961 """Create a read-only zipfile with a huge number of entries entries."""
2962 strm = io.BytesIO()
2963 zf = zipfile.ZipFile(strm, "w")
2964 for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
2965 zf.writestr(entry, entry)
2966 zf.mode = 'r'
2967 return zf
2968
2969 def test_joinpath_constant_time(self):
2970 """
2971 Ensure joinpath on items in zipfile is linear time.
2972 """
2973 root = zipfile.Path(self.huge_zipfile())
2974 entries = jaraco.itertools.Counter(root.iterdir())
2975 for entry in entries:
2976 entry.joinpath('suffix')
2977 # Check the file iterated all items
2978 assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
2979
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002980 # @func_timeout.func_set_timeout(3)
2981 def test_implied_dirs_performance(self):
2982 data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
2983 zipfile.CompleteDirs._implied_dirs(data)
2984
shireenraoa4e29912019-08-24 11:26:41 -04002985
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00002986if __name__ == "__main__":
Brett Cannond5b4e1d2013-06-12 19:57:19 -04002987 unittest.main()