blob: 29d98c8092d30c59a1ef36824f10e9d0bdef13d6 [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
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300643@requires_zlib
644class 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
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300659@requires_bz2
660class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile,
661 unittest.TestCase):
662 compression = zipfile.ZIP_BZIP2
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000663
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300664@requires_lzma
665class 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
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001078@requires_zlib
1079class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1080 unittest.TestCase):
1081 compression = zipfile.ZIP_DEFLATED
1082
1083@requires_bz2
1084class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1085 unittest.TestCase):
1086 compression = zipfile.ZIP_BZIP2
1087
1088@requires_lzma
1089class 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
1123@requires_zlib
1124class DeflateWriterTests(AbstractWriterTests, unittest.TestCase):
1125 compression = zipfile.ZIP_DEFLATED
1126
1127@requires_bz2
1128class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase):
1129 compression = zipfile.ZIP_BZIP2
1130
1131@requires_lzma
1132class 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
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001585 @requires_zlib
1586 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 Storchaka764fc9b2015-03-25 10:09:41 +02001603 def test_exclusive_create_zip_file(self):
1604 """Test exclusive creating a new zipfile."""
1605 unlink(TESTFN2)
1606 filename = 'testfile.txt'
1607 content = b'hello, world. this is some content.'
1608 with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp:
1609 zipfp.writestr(filename, content)
1610 with self.assertRaises(FileExistsError):
1611 zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED)
1612 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1613 self.assertEqual(zipfp.namelist(), [filename])
1614 self.assertEqual(zipfp.read(filename), content)
1615
Ezio Melottiafd0d112009-07-15 17:17:17 +00001616 def test_create_non_existent_file_for_append(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00001617 if os.path.exists(TESTFN):
1618 os.unlink(TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001619
Thomas Wouterscf297e42007-02-23 15:07:44 +00001620 filename = 'testfile.txt'
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001621 content = b'hello, world. this is some content.'
Guido van Rossumd8faa362007-04-27 19:54:29 +00001622
Thomas Wouterscf297e42007-02-23 15:07:44 +00001623 try:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001624 with zipfile.ZipFile(TESTFN, 'a') as zf:
1625 zf.writestr(filename, content)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001626 except OSError:
Thomas Wouterscf297e42007-02-23 15:07:44 +00001627 self.fail('Could not append data to a non-existent zip file.')
1628
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001629 self.assertTrue(os.path.exists(TESTFN))
Thomas Wouterscf297e42007-02-23 15:07:44 +00001630
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001631 with zipfile.ZipFile(TESTFN, 'r') as zf:
1632 self.assertEqual(zf.read(filename), content)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001633
Ezio Melottiafd0d112009-07-15 17:17:17 +00001634 def test_close_erroneous_file(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001635 # This test checks that the ZipFile constructor closes the file object
Ezio Melotti35386712009-12-31 13:22:41 +00001636 # it opens if there's an error in the file. If it doesn't, the
1637 # traceback holds a reference to the ZipFile object and, indirectly,
1638 # the file object.
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001639 # On Windows, this causes the os.unlink() call to fail because the
1640 # underlying file is still open. This is SF bug #412214.
1641 #
Ezio Melotti35386712009-12-31 13:22:41 +00001642 with open(TESTFN, "w") as fp:
1643 fp.write("this is not a legal zip file\n")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001644 try:
1645 zf = zipfile.ZipFile(TESTFN)
Georg Brandl4d540882010-10-28 06:42:33 +00001646 except zipfile.BadZipFile:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001647 pass
1648
Ezio Melottiafd0d112009-07-15 17:17:17 +00001649 def test_is_zip_erroneous_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001650 """Check that is_zipfile() correctly identifies non-zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001651 # - passing a filename
1652 with open(TESTFN, "w") as fp:
1653 fp.write("this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001654 self.assertFalse(zipfile.is_zipfile(TESTFN))
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001655 # - passing a path-like object
1656 self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN)))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001657 # - passing a file object
1658 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001659 self.assertFalse(zipfile.is_zipfile(fp))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001660 # - passing a file-like object
1661 fp = io.BytesIO()
1662 fp.write(b"this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001663 self.assertFalse(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001664 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001665 self.assertFalse(zipfile.is_zipfile(fp))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001666
Serhiy Storchakad2b15272013-01-31 15:27:07 +02001667 def test_damaged_zipfile(self):
1668 """Check that zipfiles with missing bytes at the end raise BadZipFile."""
1669 # - Create a valid zip file
1670 fp = io.BytesIO()
1671 with zipfile.ZipFile(fp, mode="w") as zipf:
1672 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1673 zipfiledata = fp.getvalue()
1674
1675 # - Now create copies of it missing the last N bytes and make sure
1676 # a BadZipFile exception is raised when we try to open it
1677 for N in range(len(zipfiledata)):
1678 fp = io.BytesIO(zipfiledata[:N])
1679 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp)
1680
Ezio Melottiafd0d112009-07-15 17:17:17 +00001681 def test_is_zip_valid_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001682 """Check that is_zipfile() correctly identifies zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001683 # - passing a filename
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001684 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1685 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1686
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001687 self.assertTrue(zipfile.is_zipfile(TESTFN))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001688 # - passing a file object
1689 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001690 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001691 fp.seek(0, 0)
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001692 zip_contents = fp.read()
1693 # - passing a file-like object
1694 fp = io.BytesIO()
1695 fp.write(zip_contents)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001696 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001697 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001698 self.assertTrue(zipfile.is_zipfile(fp))
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001699
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001700 def test_non_existent_file_raises_OSError(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001701 # make sure we don't raise an AttributeError when a partially-constructed
1702 # ZipFile instance is finalized; this tests for regression on SF tracker
1703 # bug #403871.
1704
1705 # The bug we're testing for caused an AttributeError to be raised
1706 # when a ZipFile instance was created for a file that did not
1707 # exist; the .fp member was not initialized but was needed by the
1708 # __del__() method. Since the AttributeError is in the __del__(),
1709 # it is ignored, but the user should be sufficiently annoyed by
1710 # the message on the output that regression will be noticed
1711 # quickly.
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001712 self.assertRaises(OSError, zipfile.ZipFile, TESTFN)
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001713
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001714 def test_empty_file_raises_BadZipFile(self):
1715 f = open(TESTFN, 'w')
1716 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001717 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001718
Ezio Melotti35386712009-12-31 13:22:41 +00001719 with open(TESTFN, 'w') as fp:
1720 fp.write("short file")
Georg Brandl4d540882010-10-28 06:42:33 +00001721 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001722
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001723 def test_closed_zip_raises_ValueError(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001724 """Verify that testzip() doesn't swallow inappropriate exceptions."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001725 data = io.BytesIO()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001726 with zipfile.ZipFile(data, mode="w") as zipf:
1727 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001728
Andrew Svetlov737fb892012-12-18 21:14:22 +02001729 # This is correct; calling .read on a closed ZipFile should raise
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001730 # a ValueError, and so should calling .testzip. An earlier
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001731 # version of .testzip would swallow this exception (and any other)
1732 # and report that the first file in the archive was corrupt.
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001733 self.assertRaises(ValueError, zipf.read, "foo.txt")
1734 self.assertRaises(ValueError, zipf.open, "foo.txt")
1735 self.assertRaises(ValueError, zipf.testzip)
1736 self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus")
Brian Curtin8fb9b862010-11-18 02:15:28 +00001737 with open(TESTFN, 'w') as f:
1738 f.write('zipfile test data')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001739 self.assertRaises(ValueError, zipf.write, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001740
Ezio Melottiafd0d112009-07-15 17:17:17 +00001741 def test_bad_constructor_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001742 """Check that bad modes passed to ZipFile constructor are caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001743 self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001744
Ezio Melottiafd0d112009-07-15 17:17:17 +00001745 def test_bad_open_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001746 """Check that bad modes passed to ZipFile.open are caught."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001747 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1748 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1749
1750 with zipfile.ZipFile(TESTFN, mode="r") as zipf:
Serhiy Storchakae670be22016-06-11 19:32:44 +03001751 # read the data to make sure the file is there
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001752 zipf.read("foo.txt")
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001753 self.assertRaises(ValueError, zipf.open, "foo.txt", "q")
Serhiy Storchakae670be22016-06-11 19:32:44 +03001754 # universal newlines support is removed
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001755 self.assertRaises(ValueError, zipf.open, "foo.txt", "U")
1756 self.assertRaises(ValueError, zipf.open, "foo.txt", "rU")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001757
Ezio Melottiafd0d112009-07-15 17:17:17 +00001758 def test_read0(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001759 """Check that calling read(0) on a ZipExtFile object returns an empty
1760 string and doesn't advance file pointer."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001761 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1762 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1763 # read the data to make sure the file is there
Brian Curtin8fb9b862010-11-18 02:15:28 +00001764 with zipf.open("foo.txt") as f:
1765 for i in range(FIXEDTEST_SIZE):
1766 self.assertEqual(f.read(0), b'')
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001767
Brian Curtin8fb9b862010-11-18 02:15:28 +00001768 self.assertEqual(f.read(), b"O, for a Muse of Fire!")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001769
Ezio Melottiafd0d112009-07-15 17:17:17 +00001770 def test_open_non_existent_item(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001771 """Check that attempting to call open() for an item that doesn't
1772 exist in the archive raises a RuntimeError."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001773 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1774 self.assertRaises(KeyError, zipf.open, "foo.txt", "r")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001775
Ezio Melottiafd0d112009-07-15 17:17:17 +00001776 def test_bad_compression_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001777 """Check that bad compression methods passed to ZipFile.open are
1778 caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001779 self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001780
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001781 def test_unsupported_compression(self):
1782 # data is declared as shrunk, but actually deflated
1783 data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00'
Christian Tismer59202e52013-10-21 03:59:23 +02001784 b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01'
1785 b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00'
1786 b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1787 b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00'
1788 b'/\x00\x00\x00!\x00\x00\x00\x00\x00')
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001789 with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
1790 self.assertRaises(NotImplementedError, zipf.open, 'x')
1791
Ezio Melottiafd0d112009-07-15 17:17:17 +00001792 def test_null_byte_in_filename(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001793 """Check that a filename containing a null byte is properly
1794 terminated."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001795 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1796 zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!")
1797 self.assertEqual(zipf.namelist(), ['foo.txt'])
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001798
Ezio Melottiafd0d112009-07-15 17:17:17 +00001799 def test_struct_sizes(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001800 """Check that ZIP internal structure sizes are calculated correctly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001801 self.assertEqual(zipfile.sizeEndCentDir, 22)
1802 self.assertEqual(zipfile.sizeCentralDir, 46)
1803 self.assertEqual(zipfile.sizeEndCentDir64, 56)
1804 self.assertEqual(zipfile.sizeEndCentDir64Locator, 20)
1805
Ezio Melottiafd0d112009-07-15 17:17:17 +00001806 def test_comments(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001807 """Check that comments on the archive are handled properly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001808
1809 # check default comment is empty
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001810 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1811 self.assertEqual(zipf.comment, b'')
1812 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1813
1814 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1815 self.assertEqual(zipfr.comment, b'')
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001816
1817 # check a simple short comment
1818 comment = b'Bravely taking to his feet, he beat a very brave retreat.'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001819 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1820 zipf.comment = comment
1821 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1822 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1823 self.assertEqual(zipf.comment, comment)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001824
1825 # check a comment of max length
1826 comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)])
1827 comment2 = comment2.encode("ascii")
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001828 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1829 zipf.comment = comment2
1830 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1831
1832 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1833 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001834
1835 # check a comment that is too long is truncated
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001836 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001837 with self.assertWarns(UserWarning):
1838 zipf.comment = comment2 + b'oops'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001839 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1840 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1841 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001842
Antoine Pitrouc3991852012-06-30 17:31:37 +02001843 # check that comments are correctly modified in append mode
1844 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1845 zipf.comment = b"original comment"
1846 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1847 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1848 zipf.comment = b"an updated comment"
1849 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1850 self.assertEqual(zipf.comment, b"an updated comment")
1851
1852 # check that comments are correctly shortened in append mode
1853 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1854 zipf.comment = b"original comment that's longer"
1855 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1856 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1857 zipf.comment = b"shorter comment"
1858 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1859 self.assertEqual(zipf.comment, b"shorter comment")
1860
R David Murrayf50b38a2012-04-12 18:44:58 -04001861 def test_unicode_comment(self):
1862 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1863 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1864 with self.assertRaises(TypeError):
1865 zipf.comment = "this is an error"
1866
1867 def test_change_comment_in_empty_archive(self):
1868 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1869 self.assertFalse(zipf.filelist)
1870 zipf.comment = b"this is a comment"
1871 with zipfile.ZipFile(TESTFN, "r") as zipf:
1872 self.assertEqual(zipf.comment, b"this is a comment")
1873
1874 def test_change_comment_in_nonempty_archive(self):
1875 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1876 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1877 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1878 self.assertTrue(zipf.filelist)
1879 zipf.comment = b"this is a comment"
1880 with zipfile.ZipFile(TESTFN, "r") as zipf:
1881 self.assertEqual(zipf.comment, b"this is a comment")
1882
Georg Brandl268e4d42010-10-14 06:59:45 +00001883 def test_empty_zipfile(self):
1884 # Check that creating a file in 'w' or 'a' mode and closing without
1885 # adding any files to the archives creates a valid empty ZIP file
1886 zipf = zipfile.ZipFile(TESTFN, mode="w")
1887 zipf.close()
1888 try:
1889 zipf = zipfile.ZipFile(TESTFN, mode="r")
1890 except zipfile.BadZipFile:
1891 self.fail("Unable to create empty ZIP file in 'w' mode")
1892
1893 zipf = zipfile.ZipFile(TESTFN, mode="a")
1894 zipf.close()
1895 try:
1896 zipf = zipfile.ZipFile(TESTFN, mode="r")
1897 except:
1898 self.fail("Unable to create empty ZIP file in 'a' mode")
1899
1900 def test_open_empty_file(self):
1901 # Issue 1710703: Check that opening a file with less than 22 bytes
Georg Brandl4d540882010-10-28 06:42:33 +00001902 # raises a BadZipFile exception (rather than the previously unhelpful
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001903 # OSError)
Georg Brandl268e4d42010-10-14 06:59:45 +00001904 f = open(TESTFN, 'w')
1905 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001906 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r')
Georg Brandl268e4d42010-10-14 06:59:45 +00001907
Senthil Kumaran29fa9d42011-10-20 01:46:00 +08001908 def test_create_zipinfo_before_1980(self):
1909 self.assertRaises(ValueError,
1910 zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0))
1911
Mickaël Schoentgen992347d2019-09-09 15:08:54 +02001912 def test_create_empty_zipinfo_repr(self):
1913 """Before bpo-26185, repr() on empty ZipInfo object was failing."""
1914 zi = zipfile.ZipInfo(filename="empty")
1915 self.assertEqual(repr(zi), "<ZipInfo filename='empty' file_size=0>")
1916
1917 def test_create_empty_zipinfo_default_attributes(self):
1918 """Ensure all required attributes are set."""
1919 zi = zipfile.ZipInfo()
1920 self.assertEqual(zi.orig_filename, "NoName")
1921 self.assertEqual(zi.filename, "NoName")
1922 self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0))
1923 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
1924 self.assertEqual(zi.comment, b"")
1925 self.assertEqual(zi.extra, b"")
1926 self.assertIn(zi.create_system, (0, 3))
1927 self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION)
1928 self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION)
1929 self.assertEqual(zi.reserved, 0)
1930 self.assertEqual(zi.flag_bits, 0)
1931 self.assertEqual(zi.volume, 0)
1932 self.assertEqual(zi.internal_attr, 0)
1933 self.assertEqual(zi.external_attr, 0)
1934
1935 # Before bpo-26185, both were missing
1936 self.assertEqual(zi.file_size, 0)
1937 self.assertEqual(zi.compress_size, 0)
1938
Gregory P. Smith0af8a862014-05-29 23:42:14 -07001939 def test_zipfile_with_short_extra_field(self):
1940 """If an extra field in the header is less than 4 bytes, skip it."""
1941 zipdata = (
1942 b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e'
1943 b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab'
1944 b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00'
1945 b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00'
1946 b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00'
1947 b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00'
1948 b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00'
1949 )
1950 with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf:
1951 # testzip returns the name of the first corrupt file, or None
1952 self.assertIsNone(zipf.testzip())
1953
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001954 def test_open_conflicting_handles(self):
1955 # It's only possible to open one writable file handle at a time
1956 msg1 = b"It's fun to charter an accountant!"
1957 msg2 = b"And sail the wide accountant sea"
1958 msg3 = b"To find, explore the funds offshore"
1959 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf:
1960 with zipf.open('foo', mode='w') as w2:
1961 w2.write(msg1)
1962 with zipf.open('bar', mode='w') as w1:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001963 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001964 zipf.open('handle', mode='w')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001965 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001966 zipf.open('foo', mode='r')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001967 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001968 zipf.writestr('str', 'abcde')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001969 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001970 zipf.write(__file__, 'file')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001971 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001972 zipf.close()
1973 w1.write(msg2)
1974 with zipf.open('baz', mode='w') as w2:
1975 w2.write(msg3)
1976
1977 with zipfile.ZipFile(TESTFN2, 'r') as zipf:
1978 self.assertEqual(zipf.read('foo'), msg1)
1979 self.assertEqual(zipf.read('bar'), msg2)
1980 self.assertEqual(zipf.read('baz'), msg3)
1981 self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz'])
1982
John Jolly066df4f2018-01-30 01:51:35 -07001983 def test_seek_tell(self):
1984 # Test seek functionality
1985 txt = b"Where's Bruce?"
1986 bloc = txt.find(b"Bruce")
1987 # Check seek on a file
1988 with zipfile.ZipFile(TESTFN, "w") as zipf:
1989 zipf.writestr("foo.txt", txt)
1990 with zipfile.ZipFile(TESTFN, "r") as zipf:
1991 with zipf.open("foo.txt", "r") as fp:
1992 fp.seek(bloc, os.SEEK_SET)
1993 self.assertEqual(fp.tell(), bloc)
1994 fp.seek(-bloc, os.SEEK_CUR)
1995 self.assertEqual(fp.tell(), 0)
1996 fp.seek(bloc, os.SEEK_CUR)
1997 self.assertEqual(fp.tell(), bloc)
1998 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
1999 fp.seek(0, os.SEEK_END)
2000 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002001 fp.seek(0, os.SEEK_SET)
2002 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002003 # Check seek on memory file
2004 data = io.BytesIO()
2005 with zipfile.ZipFile(data, mode="w") as zipf:
2006 zipf.writestr("foo.txt", txt)
2007 with zipfile.ZipFile(data, mode="r") as zipf:
2008 with zipf.open("foo.txt", "r") as fp:
2009 fp.seek(bloc, os.SEEK_SET)
2010 self.assertEqual(fp.tell(), bloc)
2011 fp.seek(-bloc, os.SEEK_CUR)
2012 self.assertEqual(fp.tell(), 0)
2013 fp.seek(bloc, os.SEEK_CUR)
2014 self.assertEqual(fp.tell(), bloc)
2015 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2016 fp.seek(0, os.SEEK_END)
2017 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002018 fp.seek(0, os.SEEK_SET)
2019 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002020
Berker Peksag2f1b8572019-09-12 17:13:44 +03002021 @requires_bz2
2022 def test_decompress_without_3rd_party_library(self):
2023 data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
2024 zip_file = io.BytesIO(data)
2025 with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf:
2026 zf.writestr('a.txt', b'a')
2027 with mock.patch('zipfile.bz2', None):
2028 with zipfile.ZipFile(zip_file) as zf:
2029 self.assertRaises(RuntimeError, zf.extract, 'a.txt')
2030
Guido van Rossumd8faa362007-04-27 19:54:29 +00002031 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002032 unlink(TESTFN)
2033 unlink(TESTFN2)
2034
Thomas Wouterscf297e42007-02-23 15:07:44 +00002035
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002036class AbstractBadCrcTests:
2037 def test_testzip_with_bad_crc(self):
2038 """Tests that files with bad CRCs return their name from testzip."""
2039 zipdata = self.zip_with_bad_crc
2040
2041 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2042 # testzip returns the name of the first corrupt file, or None
2043 self.assertEqual('afile', zipf.testzip())
2044
2045 def test_read_with_bad_crc(self):
2046 """Tests that files with bad CRCs raise a BadZipFile exception when read."""
2047 zipdata = self.zip_with_bad_crc
2048
2049 # Using ZipFile.read()
2050 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2051 self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
2052
2053 # Using ZipExtFile.read()
2054 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2055 with zipf.open('afile', 'r') as corrupt_file:
2056 self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
2057
2058 # Same with small reads (in order to exercise the buffering logic)
2059 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2060 with zipf.open('afile', 'r') as corrupt_file:
2061 corrupt_file.MIN_READ_SIZE = 2
2062 with self.assertRaises(zipfile.BadZipFile):
2063 while corrupt_file.read(2):
2064 pass
2065
2066
2067class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2068 compression = zipfile.ZIP_STORED
2069 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002070 b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
2071 b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
2072 b'ilehello,AworldP'
2073 b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
2074 b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
2075 b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
2076 b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
2077 b'\0\0/\0\0\0\0\0')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002078
2079@requires_zlib
2080class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2081 compression = zipfile.ZIP_DEFLATED
2082 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002083 b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
2084 b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2085 b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
2086 b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
2087 b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
2088 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
2089 b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
2090 b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002091
2092@requires_bz2
2093class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2094 compression = zipfile.ZIP_BZIP2
2095 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002096 b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
2097 b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2098 b'ileBZh91AY&SY\xd4\xa8\xca'
2099 b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
2100 b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
2101 b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
2102 b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
2103 b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
2104 b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
2105 b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
2106 b'\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002107
2108@requires_lzma
2109class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2110 compression = zipfile.ZIP_LZMA
2111 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002112 b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2113 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2114 b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
2115 b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
2116 b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2117 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
2118 b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
2119 b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
2120 b'\x00>\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002121
2122
Thomas Wouterscf297e42007-02-23 15:07:44 +00002123class DecryptionTests(unittest.TestCase):
Ezio Melotti35386712009-12-31 13:22:41 +00002124 """Check that ZIP decryption works. Since the library does not
2125 support encryption at the moment, we use a pre-generated encrypted
2126 ZIP file."""
Thomas Wouterscf297e42007-02-23 15:07:44 +00002127
2128 data = (
Christian Tismer59202e52013-10-21 03:59:23 +02002129 b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
2130 b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
2131 b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
2132 b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
2133 b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
2134 b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
2135 b'\x00\x00L\x00\x00\x00\x00\x00' )
Christian Heimesfdab48e2008-01-20 09:06:41 +00002136 data2 = (
Christian Tismer59202e52013-10-21 03:59:23 +02002137 b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
2138 b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
2139 b'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
2140 b'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
2141 b'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
2142 b'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
2143 b'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
2144 b'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
Thomas Wouterscf297e42007-02-23 15:07:44 +00002145
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002146 plain = b'zipfile.py encryption test'
Christian Heimesfdab48e2008-01-20 09:06:41 +00002147 plain2 = b'\x00'*512
Thomas Wouterscf297e42007-02-23 15:07:44 +00002148
2149 def setUp(self):
Ezio Melotti35386712009-12-31 13:22:41 +00002150 with open(TESTFN, "wb") as fp:
2151 fp.write(self.data)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002152 self.zip = zipfile.ZipFile(TESTFN, "r")
Ezio Melotti35386712009-12-31 13:22:41 +00002153 with open(TESTFN2, "wb") as fp:
2154 fp.write(self.data2)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002155 self.zip2 = zipfile.ZipFile(TESTFN2, "r")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002156
2157 def tearDown(self):
2158 self.zip.close()
2159 os.unlink(TESTFN)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002160 self.zip2.close()
2161 os.unlink(TESTFN2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002162
Ezio Melottiafd0d112009-07-15 17:17:17 +00002163 def test_no_password(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00002164 # Reading the encrypted file without password
2165 # must generate a RunTime exception
2166 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002167 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002168
Ezio Melottiafd0d112009-07-15 17:17:17 +00002169 def test_bad_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002170 self.zip.setpassword(b"perl")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002171 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002172 self.zip2.setpassword(b"perl")
2173 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Guido van Rossumd8faa362007-04-27 19:54:29 +00002174
Ezio Melotti975077a2011-05-19 22:03:22 +03002175 @requires_zlib
Ezio Melottiafd0d112009-07-15 17:17:17 +00002176 def test_good_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002177 self.zip.setpassword(b"python")
Ezio Melotti35386712009-12-31 13:22:41 +00002178 self.assertEqual(self.zip.read("test.txt"), self.plain)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002179 self.zip2.setpassword(b"12345")
Ezio Melotti35386712009-12-31 13:22:41 +00002180 self.assertEqual(self.zip2.read("zero"), self.plain2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002181
R. David Murray8d855d82010-12-21 21:53:37 +00002182 def test_unicode_password(self):
2183 self.assertRaises(TypeError, self.zip.setpassword, "unicode")
2184 self.assertRaises(TypeError, self.zip.read, "test.txt", "python")
2185 self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
2186 self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")
2187
Serhiy Storchaka5c32af72019-10-27 10:22:14 +02002188 def test_seek_tell(self):
2189 self.zip.setpassword(b"python")
2190 txt = self.plain
2191 test_word = b'encryption'
2192 bloc = txt.find(test_word)
2193 bloc_len = len(test_word)
2194 with self.zip.open("test.txt", "r") as fp:
2195 fp.seek(bloc, os.SEEK_SET)
2196 self.assertEqual(fp.tell(), bloc)
2197 fp.seek(-bloc, os.SEEK_CUR)
2198 self.assertEqual(fp.tell(), 0)
2199 fp.seek(bloc, os.SEEK_CUR)
2200 self.assertEqual(fp.tell(), bloc)
2201 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2202
2203 # Make sure that the second read after seeking back beyond
2204 # _readbuffer returns the same content (ie. rewind to the start of
2205 # the file to read forward to the required position).
2206 old_read_size = fp.MIN_READ_SIZE
2207 fp.MIN_READ_SIZE = 1
2208 fp._readbuffer = b''
2209 fp._offset = 0
2210 fp.seek(0, os.SEEK_SET)
2211 self.assertEqual(fp.tell(), 0)
2212 fp.seek(bloc, os.SEEK_CUR)
2213 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2214 fp.MIN_READ_SIZE = old_read_size
2215
2216 fp.seek(0, os.SEEK_END)
2217 self.assertEqual(fp.tell(), len(txt))
2218 fp.seek(0, os.SEEK_SET)
2219 self.assertEqual(fp.tell(), 0)
2220
2221 # Read the file completely to definitely call any eof integrity
2222 # checks (crc) and make sure they still pass.
2223 fp.read()
2224
2225
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002226class AbstractTestsWithRandomBinaryFiles:
2227 @classmethod
2228 def setUpClass(cls):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002229 datacount = randint(16, 64)*1024 + randint(1, 1024)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002230 cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
2231 for i in range(datacount))
Guido van Rossumd8faa362007-04-27 19:54:29 +00002232
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002233 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002234 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +00002235 with open(TESTFN, "wb") as fp:
2236 fp.write(self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002237
2238 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002239 unlink(TESTFN)
2240 unlink(TESTFN2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002241
Ezio Melottiafd0d112009-07-15 17:17:17 +00002242 def make_test_archive(self, f, compression):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002243 # Create the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002244 with zipfile.ZipFile(f, "w", compression) as zipfp:
2245 zipfp.write(TESTFN, "another.name")
2246 zipfp.write(TESTFN, TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002247
Ezio Melottiafd0d112009-07-15 17:17:17 +00002248 def zip_test(self, f, compression):
2249 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002250
2251 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002252 with zipfile.ZipFile(f, "r", compression) as zipfp:
2253 testdata = zipfp.read(TESTFN)
2254 self.assertEqual(len(testdata), len(self.data))
2255 self.assertEqual(testdata, self.data)
2256 self.assertEqual(zipfp.read("another.name"), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002257
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002258 def test_read(self):
2259 for f in get_files(self):
2260 self.zip_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002261
Ezio Melottiafd0d112009-07-15 17:17:17 +00002262 def zip_open_test(self, f, compression):
2263 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002264
2265 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002266 with zipfile.ZipFile(f, "r", compression) as zipfp:
2267 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002268 with zipfp.open(TESTFN) as zipopen1:
2269 while True:
2270 read_data = zipopen1.read(256)
2271 if not read_data:
2272 break
2273 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002274
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002275 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002276 with zipfp.open("another.name") as zipopen2:
2277 while True:
2278 read_data = zipopen2.read(256)
2279 if not read_data:
2280 break
2281 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002282
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002283 testdata1 = b''.join(zipdata1)
2284 self.assertEqual(len(testdata1), len(self.data))
2285 self.assertEqual(testdata1, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002286
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002287 testdata2 = b''.join(zipdata2)
Ezio Melotti35386712009-12-31 13:22:41 +00002288 self.assertEqual(len(testdata2), len(self.data))
2289 self.assertEqual(testdata2, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002290
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002291 def test_open(self):
2292 for f in get_files(self):
2293 self.zip_open_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002294
Ezio Melottiafd0d112009-07-15 17:17:17 +00002295 def zip_random_open_test(self, f, compression):
2296 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002297
2298 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002299 with zipfile.ZipFile(f, "r", compression) as zipfp:
2300 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002301 with zipfp.open(TESTFN) as zipopen1:
2302 while True:
2303 read_data = zipopen1.read(randint(1, 1024))
2304 if not read_data:
2305 break
2306 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002307
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002308 testdata = b''.join(zipdata1)
2309 self.assertEqual(len(testdata), len(self.data))
2310 self.assertEqual(testdata, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002311
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002312 def test_random_open(self):
2313 for f in get_files(self):
2314 self.zip_random_open_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002315
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00002316
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002317class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2318 unittest.TestCase):
2319 compression = zipfile.ZIP_STORED
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02002320
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002321@requires_zlib
2322class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2323 unittest.TestCase):
2324 compression = zipfile.ZIP_DEFLATED
2325
2326@requires_bz2
2327class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2328 unittest.TestCase):
2329 compression = zipfile.ZIP_BZIP2
2330
2331@requires_lzma
2332class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2333 unittest.TestCase):
2334 compression = zipfile.ZIP_LZMA
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002335
Ezio Melotti76430242009-07-11 18:28:48 +00002336
luzpaza5293b42017-11-05 07:37:50 -06002337# Provide the tell() method but not seek()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002338class Tellable:
2339 def __init__(self, fp):
2340 self.fp = fp
2341 self.offset = 0
2342
2343 def write(self, data):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002344 n = self.fp.write(data)
2345 self.offset += n
2346 return n
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002347
2348 def tell(self):
2349 return self.offset
2350
2351 def flush(self):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002352 self.fp.flush()
2353
2354class Unseekable:
2355 def __init__(self, fp):
2356 self.fp = fp
2357
2358 def write(self, data):
2359 return self.fp.write(data)
2360
2361 def flush(self):
2362 self.fp.flush()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002363
2364class UnseekableTests(unittest.TestCase):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002365 def test_writestr(self):
2366 for wrapper in (lambda f: f), Tellable, Unseekable:
2367 with self.subTest(wrapper=wrapper):
2368 f = io.BytesIO()
2369 f.write(b'abc')
2370 bf = io.BufferedWriter(f)
2371 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2372 zipfp.writestr('ones', b'111')
2373 zipfp.writestr('twos', b'222')
2374 self.assertEqual(f.getvalue()[:5], b'abcPK')
2375 with zipfile.ZipFile(f, mode='r') as zipf:
2376 with zipf.open('ones') as zopen:
2377 self.assertEqual(zopen.read(), b'111')
2378 with zipf.open('twos') as zopen:
2379 self.assertEqual(zopen.read(), b'222')
2380
2381 def test_write(self):
2382 for wrapper in (lambda f: f), Tellable, Unseekable:
2383 with self.subTest(wrapper=wrapper):
2384 f = io.BytesIO()
2385 f.write(b'abc')
2386 bf = io.BufferedWriter(f)
2387 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2388 self.addCleanup(unlink, TESTFN)
2389 with open(TESTFN, 'wb') as f2:
2390 f2.write(b'111')
2391 zipfp.write(TESTFN, 'ones')
2392 with open(TESTFN, 'wb') as f2:
2393 f2.write(b'222')
2394 zipfp.write(TESTFN, 'twos')
2395 self.assertEqual(f.getvalue()[:5], b'abcPK')
2396 with zipfile.ZipFile(f, mode='r') as zipf:
2397 with zipf.open('ones') as zopen:
2398 self.assertEqual(zopen.read(), b'111')
2399 with zipf.open('twos') as zopen:
2400 self.assertEqual(zopen.read(), b'222')
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002401
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002402 def test_open_write(self):
2403 for wrapper in (lambda f: f), Tellable, Unseekable:
2404 with self.subTest(wrapper=wrapper):
2405 f = io.BytesIO()
2406 f.write(b'abc')
2407 bf = io.BufferedWriter(f)
2408 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf:
2409 with zipf.open('ones', 'w') as zopen:
2410 zopen.write(b'111')
2411 with zipf.open('twos', 'w') as zopen:
2412 zopen.write(b'222')
2413 self.assertEqual(f.getvalue()[:5], b'abcPK')
2414 with zipfile.ZipFile(f) as zipf:
2415 self.assertEqual(zipf.read('ones'), b'111')
2416 self.assertEqual(zipf.read('twos'), b'222')
2417
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002418
Ezio Melotti975077a2011-05-19 22:03:22 +03002419@requires_zlib
Guido van Rossumd8faa362007-04-27 19:54:29 +00002420class TestsWithMultipleOpens(unittest.TestCase):
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002421 @classmethod
2422 def setUpClass(cls):
Victor Stinner87502dd2020-04-17 22:54:38 +02002423 cls.data1 = b'111' + randbytes(10000)
2424 cls.data2 = b'222' + randbytes(10000)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002425
2426 def make_test_archive(self, f):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002427 # Create the ZIP archive
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002428 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp:
2429 zipfp.writestr('ones', self.data1)
2430 zipfp.writestr('twos', self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002431
Ezio Melottiafd0d112009-07-15 17:17:17 +00002432 def test_same_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002433 # Verify that (when the ZipFile is in control of creating file objects)
2434 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002435 for f in get_files(self):
2436 self.make_test_archive(f)
2437 with zipfile.ZipFile(f, mode="r") as zipf:
2438 with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2:
2439 data1 = zopen1.read(500)
2440 data2 = zopen2.read(500)
2441 data1 += zopen1.read()
2442 data2 += zopen2.read()
2443 self.assertEqual(data1, data2)
2444 self.assertEqual(data1, self.data1)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002445
Ezio Melottiafd0d112009-07-15 17:17:17 +00002446 def test_different_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002447 # Verify that (when the ZipFile is in control of creating file objects)
2448 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002449 for f in get_files(self):
2450 self.make_test_archive(f)
2451 with zipfile.ZipFile(f, mode="r") as zipf:
2452 with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2:
2453 data1 = zopen1.read(500)
2454 data2 = zopen2.read(500)
2455 data1 += zopen1.read()
2456 data2 += zopen2.read()
2457 self.assertEqual(data1, self.data1)
2458 self.assertEqual(data2, self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002459
Ezio Melottiafd0d112009-07-15 17:17:17 +00002460 def test_interleaved(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002461 # Verify that (when the ZipFile is in control of creating file objects)
2462 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002463 for f in get_files(self):
2464 self.make_test_archive(f)
2465 with zipfile.ZipFile(f, mode="r") as zipf:
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002466 with zipf.open('ones') as zopen1:
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002467 data1 = zopen1.read(500)
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002468 with zipf.open('twos') as zopen2:
2469 data2 = zopen2.read(500)
2470 data1 += zopen1.read()
2471 data2 += zopen2.read()
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002472 self.assertEqual(data1, self.data1)
2473 self.assertEqual(data2, self.data2)
2474
2475 def test_read_after_close(self):
2476 for f in get_files(self):
2477 self.make_test_archive(f)
2478 with contextlib.ExitStack() as stack:
2479 with zipfile.ZipFile(f, 'r') as zipf:
2480 zopen1 = stack.enter_context(zipf.open('ones'))
2481 zopen2 = stack.enter_context(zipf.open('twos'))
Brian Curtin8fb9b862010-11-18 02:15:28 +00002482 data1 = zopen1.read(500)
2483 data2 = zopen2.read(500)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002484 data1 += zopen1.read()
2485 data2 += zopen2.read()
2486 self.assertEqual(data1, self.data1)
2487 self.assertEqual(data2, self.data2)
2488
2489 def test_read_after_write(self):
2490 for f in get_files(self):
2491 with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf:
2492 zipf.writestr('ones', self.data1)
2493 zipf.writestr('twos', self.data2)
2494 with zipf.open('ones') as zopen1:
2495 data1 = zopen1.read(500)
2496 self.assertEqual(data1, self.data1[:500])
2497 with zipfile.ZipFile(f, 'r') as zipf:
2498 data1 = zipf.read('ones')
2499 data2 = zipf.read('twos')
2500 self.assertEqual(data1, self.data1)
2501 self.assertEqual(data2, self.data2)
2502
2503 def test_write_after_read(self):
2504 for f in get_files(self):
2505 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf:
2506 zipf.writestr('ones', self.data1)
2507 with zipf.open('ones') as zopen1:
2508 zopen1.read(500)
2509 zipf.writestr('twos', self.data2)
2510 with zipfile.ZipFile(f, 'r') as zipf:
2511 data1 = zipf.read('ones')
2512 data2 = zipf.read('twos')
2513 self.assertEqual(data1, self.data1)
2514 self.assertEqual(data2, self.data2)
2515
2516 def test_many_opens(self):
2517 # Verify that read() and open() promptly close the file descriptor,
2518 # and don't rely on the garbage collector to free resources.
2519 self.make_test_archive(TESTFN2)
2520 with zipfile.ZipFile(TESTFN2, mode="r") as zipf:
2521 for x in range(100):
2522 zipf.read('ones')
2523 with zipf.open('ones') as zopen1:
2524 pass
2525 with open(os.devnull) as f:
2526 self.assertLess(f.fileno(), 100)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002527
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002528 def test_write_while_reading(self):
2529 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf:
2530 zipf.writestr('ones', self.data1)
2531 with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf:
2532 with zipf.open('ones', 'r') as r1:
2533 data1 = r1.read(500)
2534 with zipf.open('twos', 'w') as w1:
2535 w1.write(self.data2)
2536 data1 += r1.read()
2537 self.assertEqual(data1, self.data1)
2538 with zipfile.ZipFile(TESTFN2) as zipf:
2539 self.assertEqual(zipf.read('twos'), self.data2)
2540
Guido van Rossumd8faa362007-04-27 19:54:29 +00002541 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002542 unlink(TESTFN2)
2543
Guido van Rossumd8faa362007-04-27 19:54:29 +00002544
Martin v. Löwis59e47792009-01-24 14:10:07 +00002545class TestWithDirectory(unittest.TestCase):
2546 def setUp(self):
2547 os.mkdir(TESTFN2)
2548
Ezio Melottiafd0d112009-07-15 17:17:17 +00002549 def test_extract_dir(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002550 with zipfile.ZipFile(findfile("zipdir.zip")) as zipf:
2551 zipf.extractall(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002552 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2553 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2554 self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
2555
Ezio Melottiafd0d112009-07-15 17:17:17 +00002556 def test_bug_6050(self):
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002557 # Extraction should succeed if directories already exist
2558 os.mkdir(os.path.join(TESTFN2, "a"))
Ezio Melottiafd0d112009-07-15 17:17:17 +00002559 self.test_extract_dir()
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002560
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002561 def test_write_dir(self):
2562 dirpath = os.path.join(TESTFN2, "x")
2563 os.mkdir(dirpath)
2564 mode = os.stat(dirpath).st_mode & 0xFFFF
2565 with zipfile.ZipFile(TESTFN, "w") as zipf:
2566 zipf.write(dirpath)
2567 zinfo = zipf.filelist[0]
2568 self.assertTrue(zinfo.filename.endswith("/x/"))
2569 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2570 zipf.write(dirpath, "y")
2571 zinfo = zipf.filelist[1]
2572 self.assertTrue(zinfo.filename, "y/")
2573 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2574 with zipfile.ZipFile(TESTFN, "r") as zipf:
2575 zinfo = zipf.filelist[0]
2576 self.assertTrue(zinfo.filename.endswith("/x/"))
2577 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2578 zinfo = zipf.filelist[1]
2579 self.assertTrue(zinfo.filename, "y/")
2580 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2581 target = os.path.join(TESTFN2, "target")
2582 os.mkdir(target)
2583 zipf.extractall(target)
2584 self.assertTrue(os.path.isdir(os.path.join(target, "y")))
2585 self.assertEqual(len(os.listdir(target)), 2)
2586
2587 def test_writestr_dir(self):
Martin v. Löwis59e47792009-01-24 14:10:07 +00002588 os.mkdir(os.path.join(TESTFN2, "x"))
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002589 with zipfile.ZipFile(TESTFN, "w") as zipf:
2590 zipf.writestr("x/", b'')
2591 zinfo = zipf.filelist[0]
2592 self.assertEqual(zinfo.filename, "x/")
2593 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2594 with zipfile.ZipFile(TESTFN, "r") as zipf:
2595 zinfo = zipf.filelist[0]
2596 self.assertTrue(zinfo.filename.endswith("x/"))
2597 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2598 target = os.path.join(TESTFN2, "target")
2599 os.mkdir(target)
2600 zipf.extractall(target)
2601 self.assertTrue(os.path.isdir(os.path.join(target, "x")))
2602 self.assertEqual(os.listdir(target), ["x"])
Martin v. Löwis59e47792009-01-24 14:10:07 +00002603
2604 def tearDown(self):
Victor Stinner57004c62014-09-04 00:49:01 +02002605 rmtree(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002606 if os.path.exists(TESTFN):
Ezio Melotti76430242009-07-11 18:28:48 +00002607 unlink(TESTFN)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002608
Guido van Rossumd8faa362007-04-27 19:54:29 +00002609
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002610class ZipInfoTests(unittest.TestCase):
2611 def test_from_file(self):
2612 zi = zipfile.ZipInfo.from_file(__file__)
2613 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2614 self.assertFalse(zi.is_dir())
Serhiy Storchaka8606e952017-03-08 14:37:51 +02002615 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2616
2617 def test_from_file_pathlike(self):
2618 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2619 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2620 self.assertFalse(zi.is_dir())
2621 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2622
2623 def test_from_file_bytes(self):
2624 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2625 self.assertEqual(posixpath.basename(zi.filename), 'test')
2626 self.assertFalse(zi.is_dir())
2627 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2628
2629 def test_from_file_fileno(self):
2630 with open(__file__, 'rb') as f:
2631 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2632 self.assertEqual(posixpath.basename(zi.filename), 'test')
2633 self.assertFalse(zi.is_dir())
2634 self.assertEqual(zi.file_size, os.path.getsize(__file__))
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002635
2636 def test_from_dir(self):
2637 dirpath = os.path.dirname(os.path.abspath(__file__))
2638 zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
2639 self.assertEqual(zi.filename, 'stdlib_tests/')
2640 self.assertTrue(zi.is_dir())
2641 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
2642 self.assertEqual(zi.file_size, 0)
2643
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002644
2645class CommandLineTest(unittest.TestCase):
2646
2647 def zipfilecmd(self, *args, **kwargs):
2648 rc, out, err = script_helper.assert_python_ok('-m', 'zipfile', *args,
2649 **kwargs)
2650 return out.replace(os.linesep.encode(), b'\n')
2651
2652 def zipfilecmd_failure(self, *args):
2653 return script_helper.assert_python_failure('-m', 'zipfile', *args)
2654
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002655 def test_bad_use(self):
2656 rc, out, err = self.zipfilecmd_failure()
2657 self.assertEqual(out, b'')
2658 self.assertIn(b'usage', err.lower())
2659 self.assertIn(b'error', err.lower())
2660 self.assertIn(b'required', err.lower())
2661 rc, out, err = self.zipfilecmd_failure('-l', '')
2662 self.assertEqual(out, b'')
2663 self.assertNotEqual(err.strip(), b'')
2664
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002665 def test_test_command(self):
2666 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002667 for opt in '-t', '--test':
2668 out = self.zipfilecmd(opt, zip_name)
2669 self.assertEqual(out.rstrip(), b'Done testing')
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002670 zip_name = findfile('testtar.tar')
2671 rc, out, err = self.zipfilecmd_failure('-t', zip_name)
2672 self.assertEqual(out, b'')
2673
2674 def test_list_command(self):
2675 zip_name = findfile('zipdir.zip')
2676 t = io.StringIO()
2677 with zipfile.ZipFile(zip_name, 'r') as tf:
2678 tf.printdir(t)
2679 expected = t.getvalue().encode('ascii', 'backslashreplace')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002680 for opt in '-l', '--list':
2681 out = self.zipfilecmd(opt, zip_name,
2682 PYTHONIOENCODING='ascii:backslashreplace')
2683 self.assertEqual(out, expected)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002684
Serhiy Storchakab4293ef2016-10-23 22:32:30 +03002685 @requires_zlib
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002686 def test_create_command(self):
2687 self.addCleanup(unlink, TESTFN)
2688 with open(TESTFN, 'w') as f:
2689 f.write('test 1')
2690 os.mkdir(TESTFNDIR)
2691 self.addCleanup(rmtree, TESTFNDIR)
2692 with open(os.path.join(TESTFNDIR, 'file.txt'), 'w') as f:
2693 f.write('test 2')
2694 files = [TESTFN, TESTFNDIR]
2695 namelist = [TESTFN, TESTFNDIR + '/', TESTFNDIR + '/file.txt']
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002696 for opt in '-c', '--create':
2697 try:
2698 out = self.zipfilecmd(opt, TESTFN2, *files)
2699 self.assertEqual(out, b'')
2700 with zipfile.ZipFile(TESTFN2) as zf:
2701 self.assertEqual(zf.namelist(), namelist)
2702 self.assertEqual(zf.read(namelist[0]), b'test 1')
2703 self.assertEqual(zf.read(namelist[2]), b'test 2')
2704 finally:
2705 unlink(TESTFN2)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002706
2707 def test_extract_command(self):
2708 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002709 for opt in '-e', '--extract':
2710 with temp_dir() as extdir:
2711 out = self.zipfilecmd(opt, zip_name, extdir)
2712 self.assertEqual(out, b'')
2713 with zipfile.ZipFile(zip_name) as zf:
2714 for zi in zf.infolist():
2715 path = os.path.join(extdir,
2716 zi.filename.replace('/', os.sep))
2717 if zi.is_dir():
2718 self.assertTrue(os.path.isdir(path))
2719 else:
2720 self.assertTrue(os.path.isfile(path))
2721 with open(path, 'rb') as f:
2722 self.assertEqual(f.read(), zf.read(zi))
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002723
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002724
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002725class TestExecutablePrependedZip(unittest.TestCase):
2726 """Test our ability to open zip files with an executable prepended."""
2727
2728 def setUp(self):
2729 self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
2730 self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
2731
2732 def _test_zip_works(self, name):
2733 # bpo28494 sanity check: ensure is_zipfile works on these.
2734 self.assertTrue(zipfile.is_zipfile(name),
2735 f'is_zipfile failed on {name}')
2736 # Ensure we can operate on these via ZipFile.
2737 with zipfile.ZipFile(name) as zipfp:
2738 for n in zipfp.namelist():
2739 data = zipfp.read(n)
2740 self.assertIn(b'FAVORITE_NUMBER', data)
2741
2742 def test_read_zip_with_exe_prepended(self):
2743 self._test_zip_works(self.exe_zip)
2744
2745 def test_read_zip64_with_exe_prepended(self):
2746 self._test_zip_works(self.exe_zip64)
2747
2748 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2749 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2750 'Test relies on #!/bin/bash working.')
2751 def test_execute_zip2(self):
2752 output = subprocess.check_output([self.exe_zip, sys.executable])
2753 self.assertIn(b'number in executable: 5', output)
2754
2755 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2756 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2757 'Test relies on #!/bin/bash working.')
2758 def test_execute_zip64(self):
2759 output = subprocess.check_output([self.exe_zip64, sys.executable])
2760 self.assertIn(b'number in executable: 5', output)
2761
2762
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002763# Poor man's technique to consume a (smallish) iterable.
2764consume = tuple
2765
2766
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002767# from jaraco.itertools 5.0
2768class jaraco:
2769 class itertools:
2770 class Counter:
2771 def __init__(self, i):
2772 self.count = 0
2773 self._orig_iter = iter(i)
2774
2775 def __iter__(self):
2776 return self
2777
2778 def __next__(self):
2779 result = next(self._orig_iter)
2780 self.count += 1
2781 return result
2782
2783
shireenraoa4e29912019-08-24 11:26:41 -04002784def add_dirs(zf):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002785 """
shireenraoa4e29912019-08-24 11:26:41 -04002786 Given a writable zip file zf, inject directory entries for
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002787 any directories implied by the presence of children.
2788 """
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002789 for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()):
shireenraoa4e29912019-08-24 11:26:41 -04002790 zf.writestr(name, b"")
2791 return zf
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002792
2793
shireenraoa4e29912019-08-24 11:26:41 -04002794def build_alpharep_fixture():
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002795 """
2796 Create a zip file with this structure:
2797
2798 .
2799 ├── a.txt
shireenraoa4e29912019-08-24 11:26:41 -04002800 ├── b
2801 │ ├── c.txt
2802 │ ├── d
2803 │ │ └── e.txt
2804 │ └── f.txt
2805 └── g
2806 └── h
2807 └── i.txt
2808
2809 This fixture has the following key characteristics:
2810
2811 - a file at the root (a)
2812 - a file two levels deep (b/d/e)
2813 - multiple files in a directory (b/c, b/f)
2814 - a directory containing only a directory (g/h)
2815
2816 "alpha" because it uses alphabet
2817 "rep" because it's a representative example
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002818 """
2819 data = io.BytesIO()
2820 zf = zipfile.ZipFile(data, "w")
2821 zf.writestr("a.txt", b"content of a")
2822 zf.writestr("b/c.txt", b"content of c")
2823 zf.writestr("b/d/e.txt", b"content of e")
shireenraoa4e29912019-08-24 11:26:41 -04002824 zf.writestr("b/f.txt", b"content of f")
2825 zf.writestr("g/h/i.txt", b"content of i")
2826 zf.filename = "alpharep.zip"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002827 return zf
2828
2829
2830class TestPath(unittest.TestCase):
2831 def setUp(self):
2832 self.fixtures = contextlib.ExitStack()
2833 self.addCleanup(self.fixtures.close)
2834
shireenraoa4e29912019-08-24 11:26:41 -04002835 def zipfile_alpharep(self):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002836 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002837 yield build_alpharep_fixture()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002838 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002839 yield add_dirs(build_alpharep_fixture())
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002840
2841 def zipfile_ondisk(self):
2842 tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
shireenraoa4e29912019-08-24 11:26:41 -04002843 for alpharep in self.zipfile_alpharep():
2844 buffer = alpharep.fp
2845 alpharep.close()
2846 path = tmpdir / alpharep.filename
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002847 with path.open("wb") as strm:
2848 strm.write(buffer.getvalue())
2849 yield path
2850
shireenraoa4e29912019-08-24 11:26:41 -04002851 def test_iterdir_and_types(self):
2852 for alpharep in self.zipfile_alpharep():
2853 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002854 assert root.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002855 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002856 assert a.is_file()
2857 assert b.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002858 assert g.is_dir()
2859 c, f, d = b.iterdir()
2860 assert c.is_file() and f.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002861 e, = d.iterdir()
2862 assert e.is_file()
shireenraoa4e29912019-08-24 11:26:41 -04002863 h, = g.iterdir()
2864 i, = h.iterdir()
2865 assert i.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002866
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002867 def test_subdir_is_dir(self):
2868 for alpharep in self.zipfile_alpharep():
2869 root = zipfile.Path(alpharep)
2870 assert (root / 'b').is_dir()
2871 assert (root / 'b/').is_dir()
2872 assert (root / 'g').is_dir()
2873 assert (root / 'g/').is_dir()
2874
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002875 def test_open(self):
shireenraoa4e29912019-08-24 11:26:41 -04002876 for alpharep in self.zipfile_alpharep():
2877 root = zipfile.Path(alpharep)
2878 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002879 with a.open() as strm:
2880 data = strm.read()
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002881 assert data == "content of a"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002882
2883 def test_read(self):
shireenraoa4e29912019-08-24 11:26:41 -04002884 for alpharep in self.zipfile_alpharep():
2885 root = zipfile.Path(alpharep)
2886 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002887 assert a.read_text() == "content of a"
2888 assert a.read_bytes() == b"content of a"
2889
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002890 def test_joinpath(self):
shireenraoa4e29912019-08-24 11:26:41 -04002891 for alpharep in self.zipfile_alpharep():
2892 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002893 a = root.joinpath("a")
2894 assert a.is_file()
2895 e = root.joinpath("b").joinpath("d").joinpath("e.txt")
2896 assert e.read_text() == "content of e"
2897
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002898 def test_traverse_truediv(self):
shireenraoa4e29912019-08-24 11:26:41 -04002899 for alpharep in self.zipfile_alpharep():
2900 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002901 a = root / "a"
2902 assert a.is_file()
2903 e = root / "b" / "d" / "e.txt"
2904 assert e.read_text() == "content of e"
2905
2906 def test_pathlike_construction(self):
2907 """
2908 zipfile.Path should be constructable from a path-like object
2909 """
2910 for zipfile_ondisk in self.zipfile_ondisk():
2911 pathlike = pathlib.Path(str(zipfile_ondisk))
2912 zipfile.Path(pathlike)
2913
2914 def test_traverse_pathlike(self):
shireenraoa4e29912019-08-24 11:26:41 -04002915 for alpharep in self.zipfile_alpharep():
2916 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002917 root / pathlib.Path("a")
2918
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002919 def test_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002920 for alpharep in self.zipfile_alpharep():
2921 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002922 assert (root / 'a').parent.at == ''
2923 assert (root / 'a' / 'b').parent.at == 'a/'
2924
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002925 def test_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002926 for alpharep in self.zipfile_alpharep():
2927 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002928 assert (root / 'b').parent.at == ''
2929 assert (root / 'b/').parent.at == ''
2930
2931 def test_missing_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002932 for alpharep in self.zipfile_alpharep():
2933 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002934 assert (root / 'missing dir/').parent.at == ''
2935
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002936 def test_mutability(self):
2937 """
2938 If the underlying zipfile is changed, the Path object should
2939 reflect that change.
2940 """
2941 for alpharep in self.zipfile_alpharep():
2942 root = zipfile.Path(alpharep)
2943 a, b, g = root.iterdir()
2944 alpharep.writestr('foo.txt', 'foo')
2945 alpharep.writestr('bar/baz.txt', 'baz')
2946 assert any(
2947 child.name == 'foo.txt'
2948 for child in root.iterdir())
2949 assert (root / 'foo.txt').read_text() == 'foo'
2950 baz, = (root / 'bar').iterdir()
2951 assert baz.read_text() == 'baz'
2952
2953 HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
2954
2955 def huge_zipfile(self):
2956 """Create a read-only zipfile with a huge number of entries entries."""
2957 strm = io.BytesIO()
2958 zf = zipfile.ZipFile(strm, "w")
2959 for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
2960 zf.writestr(entry, entry)
2961 zf.mode = 'r'
2962 return zf
2963
2964 def test_joinpath_constant_time(self):
2965 """
2966 Ensure joinpath on items in zipfile is linear time.
2967 """
2968 root = zipfile.Path(self.huge_zipfile())
2969 entries = jaraco.itertools.Counter(root.iterdir())
2970 for entry in entries:
2971 entry.joinpath('suffix')
2972 # Check the file iterated all items
2973 assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
2974
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002975 # @func_timeout.func_set_timeout(3)
2976 def test_implied_dirs_performance(self):
2977 data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
2978 zipfile.CompleteDirs._implied_dirs(data)
2979
shireenraoa4e29912019-08-24 11:26:41 -04002980
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00002981if __name__ == "__main__":
Brett Cannond5b4e1d2013-06-12 19:57:19 -04002982 unittest.main()