blob: 687e43df780d65366ef845d66cf263a4fe8aae98 [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
Hai Shideb01622020-07-06 20:29:49 +080022from test.support import (findfile, requires_zlib, requires_bz2,
23 requires_lzma, captured_stdout)
24from test.support.os_helper import TESTFN, unlink, rmtree, temp_dir, temp_cwd
25
Guido van Rossum368f04a2000-04-10 13:23:04 +000026
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000027TESTFN2 = TESTFN + "2"
Martin v. Löwis59e47792009-01-24 14:10:07 +000028TESTFNDIR = TESTFN + "d"
Guido van Rossumb5a755e2007-07-18 18:15:48 +000029FIXEDTEST_SIZE = 1000
Georg Brandl5ba11de2011-01-01 10:09:32 +000030DATAFILES_DIR = 'zipfile_datafiles'
Guido van Rossum368f04a2000-04-10 13:23:04 +000031
Christian Heimes790c8232008-01-07 21:14:23 +000032SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
33 ('ziptest2dir/_ziptest2', 'qawsedrftg'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -080034 ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
Christian Heimes790c8232008-01-07 21:14:23 +000035 ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]
36
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030037def get_files(test):
38 yield TESTFN2
39 with TemporaryFile() as f:
40 yield f
41 test.assertFalse(f.closed)
42 with io.BytesIO() as f:
43 yield f
44 test.assertFalse(f.closed)
Ezio Melotti76430242009-07-11 18:28:48 +000045
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030046class AbstractTestsWithSourceFile:
47 @classmethod
48 def setUpClass(cls):
49 cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" %
50 (i, random()), "ascii")
51 for i in range(FIXEDTEST_SIZE)]
52 cls.data = b''.join(cls.line_gen)
53
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000054 def setUp(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000055 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +000056 with open(TESTFN, "wb") as fp:
57 fp.write(self.data)
Tim Peters7d3bad62001-04-04 18:56:49 +000058
Bo Baylesce237c72018-01-29 23:54:07 -060059 def make_test_archive(self, f, compression, compresslevel=None):
60 kwargs = {'compression': compression, 'compresslevel': compresslevel}
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000061 # Create the ZIP archive
Bo Baylesce237c72018-01-29 23:54:07 -060062 with zipfile.ZipFile(f, "w", **kwargs) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000063 zipfp.write(TESTFN, "another.name")
64 zipfp.write(TESTFN, TESTFN)
65 zipfp.writestr("strfile", self.data)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030066 with zipfp.open('written-open-w', mode='w') as f:
67 for line in self.line_gen:
68 f.write(line)
Tim Peters7d3bad62001-04-04 18:56:49 +000069
Bo Baylesce237c72018-01-29 23:54:07 -060070 def zip_test(self, f, compression, compresslevel=None):
71 self.make_test_archive(f, compression, compresslevel)
Guido van Rossumd8faa362007-04-27 19:54:29 +000072
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000073 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000074 with zipfile.ZipFile(f, "r", compression) as zipfp:
75 self.assertEqual(zipfp.read(TESTFN), self.data)
76 self.assertEqual(zipfp.read("another.name"), self.data)
77 self.assertEqual(zipfp.read("strfile"), self.data)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000078
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000079 # Print the ZIP directory
80 fp = io.StringIO()
81 zipfp.printdir(file=fp)
82 directory = fp.getvalue()
83 lines = directory.splitlines()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030084 self.assertEqual(len(lines), 5) # Number of files + header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000085
Benjamin Peterson577473f2010-01-19 00:09:57 +000086 self.assertIn('File Name', lines[0])
87 self.assertIn('Modified', lines[0])
88 self.assertIn('Size', lines[0])
Thomas Wouters0e3f5912006-08-11 14:57:12 +000089
Ezio Melotti35386712009-12-31 13:22:41 +000090 fn, date, time_, size = lines[1].split()
91 self.assertEqual(fn, 'another.name')
92 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
93 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
94 self.assertEqual(size, str(len(self.data)))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000095
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000096 # Check the namelist
97 names = zipfp.namelist()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030098 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +000099 self.assertIn(TESTFN, names)
100 self.assertIn("another.name", names)
101 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300102 self.assertIn("written-open-w", names)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000103
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000104 # Check infolist
105 infos = zipfp.infolist()
Ezio Melotti35386712009-12-31 13:22:41 +0000106 names = [i.filename for i in infos]
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300107 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000108 self.assertIn(TESTFN, names)
109 self.assertIn("another.name", names)
110 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300111 self.assertIn("written-open-w", names)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000112 for i in infos:
Ezio Melotti35386712009-12-31 13:22:41 +0000113 self.assertEqual(i.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000114
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000115 # check getinfo
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300116 for nm in (TESTFN, "another.name", "strfile", "written-open-w"):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000117 info = zipfp.getinfo(nm)
Ezio Melotti35386712009-12-31 13:22:41 +0000118 self.assertEqual(info.filename, nm)
119 self.assertEqual(info.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000120
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000121 # Check that testzip doesn't raise an exception
122 zipfp.testzip()
Tim Peters7d3bad62001-04-04 18:56:49 +0000123
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300124 def test_basic(self):
125 for f in get_files(self):
126 self.zip_test(f, self.compression)
Raymond Hettingerc0fac962003-06-27 22:25:03 +0000127
Ezio Melottiafd0d112009-07-15 17:17:17 +0000128 def zip_open_test(self, f, compression):
129 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000130
131 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000132 with zipfile.ZipFile(f, "r", compression) as zipfp:
133 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000134 with zipfp.open(TESTFN) as zipopen1:
135 while True:
136 read_data = zipopen1.read(256)
137 if not read_data:
138 break
139 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000140
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000141 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000142 with zipfp.open("another.name") as zipopen2:
143 while True:
144 read_data = zipopen2.read(256)
145 if not read_data:
146 break
147 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000148
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000149 self.assertEqual(b''.join(zipdata1), self.data)
150 self.assertEqual(b''.join(zipdata2), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000151
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300152 def test_open(self):
153 for f in get_files(self):
154 self.zip_open_test(f, self.compression)
Georg Brandlb533e262008-05-25 18:19:30 +0000155
Serhiy Storchaka8606e952017-03-08 14:37:51 +0200156 def test_open_with_pathlike(self):
157 path = pathlib.Path(TESTFN2)
158 self.zip_open_test(path, self.compression)
159 with zipfile.ZipFile(path, "r", self.compression) as zipfp:
160 self.assertIsInstance(zipfp.filename, str)
161
Ezio Melottiafd0d112009-07-15 17:17:17 +0000162 def zip_random_open_test(self, f, compression):
163 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000164
165 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000166 with zipfile.ZipFile(f, "r", compression) as zipfp:
167 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000168 with zipfp.open(TESTFN) as zipopen1:
169 while True:
170 read_data = zipopen1.read(randint(1, 1024))
171 if not read_data:
172 break
173 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000174
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000175 self.assertEqual(b''.join(zipdata1), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000176
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300177 def test_random_open(self):
178 for f in get_files(self):
179 self.zip_random_open_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000180
Serhiy Storchakad2c07a52013-09-27 22:11:57 +0300181 def zip_read1_test(self, f, compression):
182 self.make_test_archive(f, compression)
183
184 # Read the ZIP archive
185 with zipfile.ZipFile(f, "r") as zipfp, \
186 zipfp.open(TESTFN) as zipopen:
187 zipdata = []
188 while True:
189 read_data = zipopen.read1(-1)
190 if not read_data:
191 break
192 zipdata.append(read_data)
193
194 self.assertEqual(b''.join(zipdata), self.data)
195
196 def test_read1(self):
197 for f in get_files(self):
198 self.zip_read1_test(f, self.compression)
199
200 def zip_read1_10_test(self, f, compression):
201 self.make_test_archive(f, compression)
202
203 # Read the ZIP archive
204 with zipfile.ZipFile(f, "r") as zipfp, \
205 zipfp.open(TESTFN) as zipopen:
206 zipdata = []
207 while True:
208 read_data = zipopen.read1(10)
209 self.assertLessEqual(len(read_data), 10)
210 if not read_data:
211 break
212 zipdata.append(read_data)
213
214 self.assertEqual(b''.join(zipdata), self.data)
215
216 def test_read1_10(self):
217 for f in get_files(self):
218 self.zip_read1_10_test(f, self.compression)
219
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000220 def zip_readline_read_test(self, f, compression):
221 self.make_test_archive(f, compression)
222
223 # Read the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300224 with zipfile.ZipFile(f, "r") as zipfp, \
225 zipfp.open(TESTFN) as zipopen:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000226 data = b''
227 while True:
228 read = zipopen.readline()
229 if not read:
230 break
231 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000232
Brian Curtin8fb9b862010-11-18 02:15:28 +0000233 read = zipopen.read(100)
234 if not read:
235 break
236 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000237
238 self.assertEqual(data, self.data)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300239
240 def test_readline_read(self):
241 # Issue #7610: calls to readline() interleaved with calls to read().
242 for f in get_files(self):
243 self.zip_readline_read_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000244
Ezio Melottiafd0d112009-07-15 17:17:17 +0000245 def zip_readline_test(self, f, compression):
246 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000247
248 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000249 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000250 with zipfp.open(TESTFN) as zipopen:
251 for line in self.line_gen:
252 linedata = zipopen.readline()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300253 self.assertEqual(linedata, line)
254
255 def test_readline(self):
256 for f in get_files(self):
257 self.zip_readline_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000258
Ezio Melottiafd0d112009-07-15 17:17:17 +0000259 def zip_readlines_test(self, f, compression):
260 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000261
262 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000263 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000264 with zipfp.open(TESTFN) as zipopen:
265 ziplines = zipopen.readlines()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000266 for line, zipline in zip(self.line_gen, ziplines):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300267 self.assertEqual(zipline, line)
268
269 def test_readlines(self):
270 for f in get_files(self):
271 self.zip_readlines_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000272
Ezio Melottiafd0d112009-07-15 17:17:17 +0000273 def zip_iterlines_test(self, f, compression):
274 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000275
276 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000277 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000278 with zipfp.open(TESTFN) as zipopen:
279 for line, zipline in zip(self.line_gen, zipopen):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300280 self.assertEqual(zipline, line)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000281
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300282 def test_iterlines(self):
283 for f in get_files(self):
284 self.zip_iterlines_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000285
Ezio Melottiafd0d112009-07-15 17:17:17 +0000286 def test_low_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000287 """Check for cases where compressed data is larger than original."""
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000288 # Create the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300289 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000290 zipfp.writestr("strfile", '12')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000291
292 # Get an open object for strfile
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300293 with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000294 with zipfp.open("strfile") as openobj:
295 self.assertEqual(openobj.read(1), b'1')
296 self.assertEqual(openobj.read(1), b'2')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000297
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300298 def test_writestr_compression(self):
299 zipfp = zipfile.ZipFile(TESTFN2, "w")
300 zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
301 info = zipfp.getinfo('b.txt')
302 self.assertEqual(info.compress_type, self.compression)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200303
Bo Baylesce237c72018-01-29 23:54:07 -0600304 def test_writestr_compresslevel(self):
305 zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1)
306 zipfp.writestr("a.txt", "hello world", compress_type=self.compression)
307 zipfp.writestr("b.txt", "hello world", compress_type=self.compression,
308 compresslevel=2)
309
310 # Compression level follows the constructor.
311 a_info = zipfp.getinfo('a.txt')
312 self.assertEqual(a_info.compress_type, self.compression)
313 self.assertEqual(a_info._compresslevel, 1)
314
315 # Compression level is overridden.
316 b_info = zipfp.getinfo('b.txt')
317 self.assertEqual(b_info.compress_type, self.compression)
318 self.assertEqual(b_info._compresslevel, 2)
319
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300320 def test_read_return_size(self):
321 # Issue #9837: ZipExtFile.read() shouldn't return more bytes
322 # than requested.
323 for test_size in (1, 4095, 4096, 4097, 16384):
324 file_size = test_size + 1
Victor Stinner87502dd2020-04-17 22:54:38 +0200325 junk = randbytes(file_size)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300326 with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf:
327 zipf.writestr('foo', junk)
328 with zipf.open('foo', 'r') as fp:
329 buf = fp.read(test_size)
330 self.assertEqual(len(buf), test_size)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200331
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200332 def test_truncated_zipfile(self):
333 fp = io.BytesIO()
334 with zipfile.ZipFile(fp, mode='w') as zipf:
335 zipf.writestr('strfile', self.data, compress_type=self.compression)
336 end_offset = fp.tell()
337 zipfiledata = fp.getvalue()
338
339 fp = io.BytesIO(zipfiledata)
340 with zipfile.ZipFile(fp) as zipf:
341 with zipf.open('strfile') as zipopen:
342 fp.truncate(end_offset - 20)
343 with self.assertRaises(EOFError):
344 zipopen.read()
345
346 fp = io.BytesIO(zipfiledata)
347 with zipfile.ZipFile(fp) as zipf:
348 with zipf.open('strfile') as zipopen:
349 fp.truncate(end_offset - 20)
350 with self.assertRaises(EOFError):
351 while zipopen.read(100):
352 pass
353
354 fp = io.BytesIO(zipfiledata)
355 with zipfile.ZipFile(fp) as zipf:
356 with zipf.open('strfile') as zipopen:
357 fp.truncate(end_offset - 20)
358 with self.assertRaises(EOFError):
359 while zipopen.read1(100):
360 pass
361
Serhiy Storchaka51a43702014-10-29 22:42:06 +0200362 def test_repr(self):
363 fname = 'file.name'
364 for f in get_files(self):
365 with zipfile.ZipFile(f, 'w', self.compression) as zipfp:
366 zipfp.write(TESTFN, fname)
367 r = repr(zipfp)
368 self.assertIn("mode='w'", r)
369
370 with zipfile.ZipFile(f, 'r') as zipfp:
371 r = repr(zipfp)
372 if isinstance(f, str):
373 self.assertIn('filename=%r' % f, r)
374 else:
375 self.assertIn('file=%r' % f, r)
376 self.assertIn("mode='r'", r)
377 r = repr(zipfp.getinfo(fname))
378 self.assertIn('filename=%r' % fname, r)
379 self.assertIn('filemode=', r)
380 self.assertIn('file_size=', r)
381 if self.compression != zipfile.ZIP_STORED:
382 self.assertIn('compress_type=', r)
383 self.assertIn('compress_size=', r)
384 with zipfp.open(fname) as zipopen:
385 r = repr(zipopen)
386 self.assertIn('name=%r' % fname, r)
387 self.assertIn("mode='r'", r)
388 if self.compression != zipfile.ZIP_STORED:
389 self.assertIn('compress_type=', r)
390 self.assertIn('[closed]', repr(zipopen))
391 self.assertIn('[closed]', repr(zipfp))
392
Bo Baylesce237c72018-01-29 23:54:07 -0600393 def test_compresslevel_basic(self):
394 for f in get_files(self):
395 self.zip_test(f, self.compression, compresslevel=9)
396
397 def test_per_file_compresslevel(self):
398 """Check that files within a Zip archive can have different
399 compression levels."""
400 with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp:
401 zipfp.write(TESTFN, 'compress_1')
402 zipfp.write(TESTFN, 'compress_9', compresslevel=9)
403 one_info = zipfp.getinfo('compress_1')
404 nine_info = zipfp.getinfo('compress_9')
405 self.assertEqual(one_info._compresslevel, 1)
406 self.assertEqual(nine_info._compresslevel, 9)
407
Serhiy Storchaka2524fde2019-03-30 08:25:19 +0200408 def test_writing_errors(self):
409 class BrokenFile(io.BytesIO):
410 def write(self, data):
411 nonlocal count
412 if count is not None:
413 if count == stop:
414 raise OSError
415 count += 1
416 super().write(data)
417
418 stop = 0
419 while True:
420 testfile = BrokenFile()
421 count = None
422 with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp:
423 with zipfp.open('file1', 'w') as f:
424 f.write(b'data1')
425 count = 0
426 try:
427 with zipfp.open('file2', 'w') as f:
428 f.write(b'data2')
429 except OSError:
430 stop += 1
431 else:
432 break
433 finally:
434 count = None
435 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
436 self.assertEqual(zipfp.namelist(), ['file1'])
437 self.assertEqual(zipfp.read('file1'), b'data1')
438
439 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
440 self.assertEqual(zipfp.namelist(), ['file1', 'file2'])
441 self.assertEqual(zipfp.read('file1'), b'data1')
442 self.assertEqual(zipfp.read('file2'), b'data2')
443
444
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300445 def tearDown(self):
446 unlink(TESTFN)
447 unlink(TESTFN2)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200448
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200449
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300450class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
451 unittest.TestCase):
452 compression = zipfile.ZIP_STORED
453 test_low_compression = None
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200454
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300455 def zip_test_writestr_permissions(self, f, compression):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300456 # Make sure that writestr and open(... mode='w') create files with
457 # mode 0600, when they are passed a name rather than a ZipInfo
458 # instance.
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200459
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300460 self.make_test_archive(f, compression)
461 with zipfile.ZipFile(f, "r") as zipfp:
462 zinfo = zipfp.getinfo('strfile')
463 self.assertEqual(zinfo.external_attr, 0o600 << 16)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200464
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300465 zinfo2 = zipfp.getinfo('written-open-w')
466 self.assertEqual(zinfo2.external_attr, 0o600 << 16)
467
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300468 def test_writestr_permissions(self):
469 for f in get_files(self):
470 self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +0200471
Ezio Melottiafd0d112009-07-15 17:17:17 +0000472 def test_absolute_arcnames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000473 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
474 zipfp.write(TESTFN, "/absolute")
Georg Brandl8f7c54e2006-02-20 08:40:38 +0000475
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000476 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
477 self.assertEqual(zipfp.namelist(), ["absolute"])
Tim Peters32cbc962006-02-20 21:42:18 +0000478
Ezio Melottiafd0d112009-07-15 17:17:17 +0000479 def test_append_to_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000480 """Test appending to an existing zipfile."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000481 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
482 zipfp.write(TESTFN, TESTFN)
483
484 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
485 zipfp.writestr("strfile", self.data)
486 self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"])
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000487
Ezio Melottiafd0d112009-07-15 17:17:17 +0000488 def test_append_to_non_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000489 """Test appending to an existing file that is not a zipfile."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000490 # NOTE: this test fails if len(d) < 22 because of the first
491 # line "fpin.seek(-22, 2)" in _EndRecData
Ezio Melotti35386712009-12-31 13:22:41 +0000492 data = b'I am not a ZipFile!'*10
493 with open(TESTFN2, 'wb') as f:
494 f.write(data)
495
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000496 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
497 zipfp.write(TESTFN, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000498
Ezio Melotti35386712009-12-31 13:22:41 +0000499 with open(TESTFN2, 'rb') as f:
500 f.seek(len(data))
501 with zipfile.ZipFile(f, "r") as zipfp:
502 self.assertEqual(zipfp.namelist(), [TESTFN])
Serhiy Storchaka8793b212016-10-07 22:20:50 +0300503 self.assertEqual(zipfp.read(TESTFN), self.data)
504 with open(TESTFN2, 'rb') as f:
505 self.assertEqual(f.read(len(data)), data)
506 zipfiledata = f.read()
507 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
508 self.assertEqual(zipfp.namelist(), [TESTFN])
509 self.assertEqual(zipfp.read(TESTFN), self.data)
510
511 def test_read_concatenated_zip_file(self):
512 with io.BytesIO() as bio:
513 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
514 zipfp.write(TESTFN, TESTFN)
515 zipfiledata = bio.getvalue()
516 data = b'I am not a ZipFile!'*10
517 with open(TESTFN2, 'wb') as f:
518 f.write(data)
519 f.write(zipfiledata)
520
521 with zipfile.ZipFile(TESTFN2) as zipfp:
522 self.assertEqual(zipfp.namelist(), [TESTFN])
523 self.assertEqual(zipfp.read(TESTFN), self.data)
524
525 def test_append_to_concatenated_zip_file(self):
526 with io.BytesIO() as bio:
527 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
528 zipfp.write(TESTFN, TESTFN)
529 zipfiledata = bio.getvalue()
530 data = b'I am not a ZipFile!'*1000000
531 with open(TESTFN2, 'wb') as f:
532 f.write(data)
533 f.write(zipfiledata)
534
535 with zipfile.ZipFile(TESTFN2, 'a') as zipfp:
536 self.assertEqual(zipfp.namelist(), [TESTFN])
537 zipfp.writestr('strfile', self.data)
538
539 with open(TESTFN2, 'rb') as f:
540 self.assertEqual(f.read(len(data)), data)
541 zipfiledata = f.read()
542 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
543 self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile'])
544 self.assertEqual(zipfp.read(TESTFN), self.data)
545 self.assertEqual(zipfp.read('strfile'), self.data)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000546
R David Murray4fbb9db2011-06-09 15:50:51 -0400547 def test_ignores_newline_at_end(self):
548 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
549 zipfp.write(TESTFN, TESTFN)
550 with open(TESTFN2, 'a') as f:
551 f.write("\r\n\00\00\00")
552 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
553 self.assertIsInstance(zipfp, zipfile.ZipFile)
554
555 def test_ignores_stuff_appended_past_comments(self):
556 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
557 zipfp.comment = b"this is a comment"
558 zipfp.write(TESTFN, TESTFN)
559 with open(TESTFN2, 'a') as f:
560 f.write("abcdef\r\n")
561 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
562 self.assertIsInstance(zipfp, zipfile.ZipFile)
563 self.assertEqual(zipfp.comment, b"this is a comment")
564
Ezio Melottiafd0d112009-07-15 17:17:17 +0000565 def test_write_default_name(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000566 """Check that calling ZipFile.write without arcname specified
567 produces the expected result."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000568 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
569 zipfp.write(TESTFN)
Brian Curtin8fb9b862010-11-18 02:15:28 +0000570 with open(TESTFN, "rb") as f:
571 self.assertEqual(zipfp.read(TESTFN), f.read())
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000572
Daniel Hillier8d62df62019-11-30 19:30:47 +1100573 def test_io_on_closed_zipextfile(self):
574 fname = "somefile.txt"
575 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
576 zipfp.writestr(fname, "bogus")
577
578 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
579 with zipfp.open(fname) as fid:
580 fid.close()
581 self.assertRaises(ValueError, fid.read)
582 self.assertRaises(ValueError, fid.seek, 0)
583 self.assertRaises(ValueError, fid.tell)
584 self.assertRaises(ValueError, fid.readable)
585 self.assertRaises(ValueError, fid.seekable)
586
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300587 def test_write_to_readonly(self):
588 """Check that trying to call write() on a readonly ZipFile object
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300589 raises a ValueError."""
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300590 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
591 zipfp.writestr("somefile.txt", "bogus")
592
593 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300594 self.assertRaises(ValueError, zipfp.write, TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300595
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300596 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300597 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300598 zipfp.open(TESTFN, mode='w')
599
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300600 def test_add_file_before_1980(self):
601 # Set atime and mtime to 1970-01-01
602 os.utime(TESTFN, (0, 0))
603 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
604 self.assertRaises(ValueError, zipfp.write, TESTFN)
605
Marcel Plch77b112c2018-08-31 16:43:31 +0200606 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
607 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200608 zinfo = zipfp.getinfo(TESTFN)
609 self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0))
610
611 def test_add_file_after_2107(self):
612 # Set atime and mtime to 2108-12-30
Victor Stinnerc232c912020-01-30 15:47:53 +0100613 ts = 4386268800
Marcel Plch7b41dba2018-08-03 17:59:19 +0200614 try:
Victor Stinnerc232c912020-01-30 15:47:53 +0100615 time.localtime(ts)
616 except OverflowError:
617 self.skipTest(f'time.localtime({ts}) raises OverflowError')
618 try:
619 os.utime(TESTFN, (ts, ts))
Marcel Plch7b41dba2018-08-03 17:59:19 +0200620 except OverflowError:
621 self.skipTest('Host fs cannot set timestamp to required value.')
622
Victor Stinner3cb49b62020-01-29 15:23:29 +0100623 mtime_ns = os.stat(TESTFN).st_mtime_ns
624 if mtime_ns != (4386268800 * 10**9):
625 # XFS filesystem is limited to 32-bit timestamp, but the syscall
626 # didn't fail. Moreover, there is a VFS bug which returns
627 # a cached timestamp which is different than the value on disk.
628 #
629 # Test st_mtime_ns rather than st_mtime to avoid rounding issues.
630 #
631 # https://bugzilla.redhat.com/show_bug.cgi?id=1795576
632 # https://bugs.python.org/issue39460#msg360952
633 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}")
634
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200635 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
636 self.assertRaises(struct.error, zipfp.write, TESTFN)
637
Marcel Plch77b112c2018-08-31 16:43:31 +0200638 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
639 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200640 zinfo = zipfp.getinfo(TESTFN)
641 self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59))
642
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200643
Hai Shia3ec3ad2020-05-19 06:02:57 +0800644@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300645class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile,
646 unittest.TestCase):
647 compression = zipfile.ZIP_DEFLATED
648
Ezio Melottiafd0d112009-07-15 17:17:17 +0000649 def test_per_file_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000650 """Check that files within a Zip archive can have different
651 compression options."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000652 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
653 zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED)
654 zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED)
655 sinfo = zipfp.getinfo('storeme')
656 dinfo = zipfp.getinfo('deflateme')
657 self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED)
658 self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000659
Hai Shia3ec3ad2020-05-19 06:02:57 +0800660@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300661class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile,
662 unittest.TestCase):
663 compression = zipfile.ZIP_BZIP2
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000664
Hai Shia3ec3ad2020-05-19 06:02:57 +0800665@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300666class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile,
667 unittest.TestCase):
668 compression = zipfile.ZIP_LZMA
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000669
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300670
671class AbstractTestZip64InSmallFiles:
672 # These tests test the ZIP64 functionality without using large files,
673 # see test_zipfile64 for proper tests.
674
675 @classmethod
676 def setUpClass(cls):
677 line_gen = (bytes("Test of zipfile line %d." % i, "ascii")
678 for i in range(0, FIXEDTEST_SIZE))
679 cls.data = b'\n'.join(line_gen)
680
681 def setUp(self):
682 self._limit = zipfile.ZIP64_LIMIT
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300683 self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT
684 zipfile.ZIP64_LIMIT = 1000
685 zipfile.ZIP_FILECOUNT_LIMIT = 9
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300686
687 # Make a source file with some lines
688 with open(TESTFN, "wb") as fp:
689 fp.write(self.data)
690
691 def zip_test(self, f, compression):
692 # Create the ZIP archive
693 with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp:
694 zipfp.write(TESTFN, "another.name")
695 zipfp.write(TESTFN, TESTFN)
696 zipfp.writestr("strfile", self.data)
697
698 # Read the ZIP archive
699 with zipfile.ZipFile(f, "r", compression) as zipfp:
700 self.assertEqual(zipfp.read(TESTFN), self.data)
701 self.assertEqual(zipfp.read("another.name"), self.data)
702 self.assertEqual(zipfp.read("strfile"), self.data)
703
704 # Print the ZIP directory
705 fp = io.StringIO()
706 zipfp.printdir(fp)
707
708 directory = fp.getvalue()
709 lines = directory.splitlines()
710 self.assertEqual(len(lines), 4) # Number of files + header
711
712 self.assertIn('File Name', lines[0])
713 self.assertIn('Modified', lines[0])
714 self.assertIn('Size', lines[0])
715
716 fn, date, time_, size = lines[1].split()
717 self.assertEqual(fn, 'another.name')
718 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
719 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
720 self.assertEqual(size, str(len(self.data)))
721
722 # Check the namelist
723 names = zipfp.namelist()
724 self.assertEqual(len(names), 3)
725 self.assertIn(TESTFN, names)
726 self.assertIn("another.name", names)
727 self.assertIn("strfile", names)
728
729 # Check infolist
730 infos = zipfp.infolist()
731 names = [i.filename for i in infos]
732 self.assertEqual(len(names), 3)
733 self.assertIn(TESTFN, names)
734 self.assertIn("another.name", names)
735 self.assertIn("strfile", names)
736 for i in infos:
737 self.assertEqual(i.file_size, len(self.data))
738
739 # check getinfo
740 for nm in (TESTFN, "another.name", "strfile"):
741 info = zipfp.getinfo(nm)
742 self.assertEqual(info.filename, nm)
743 self.assertEqual(info.file_size, len(self.data))
744
745 # Check that testzip doesn't raise an exception
746 zipfp.testzip()
747
748 def test_basic(self):
749 for f in get_files(self):
750 self.zip_test(f, self.compression)
751
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300752 def test_too_many_files(self):
753 # This test checks that more than 64k files can be added to an archive,
754 # and that the resulting archive can be read properly by ZipFile
755 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
756 allowZip64=True)
757 zipf.debug = 100
758 numfiles = 15
759 for i in range(numfiles):
760 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
761 self.assertEqual(len(zipf.namelist()), numfiles)
762 zipf.close()
763
764 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
765 self.assertEqual(len(zipf2.namelist()), numfiles)
766 for i in range(numfiles):
767 content = zipf2.read("foo%08d" % i).decode('ascii')
768 self.assertEqual(content, "%d" % (i**3 % 57))
769 zipf2.close()
770
771 def test_too_many_files_append(self):
772 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
773 allowZip64=False)
774 zipf.debug = 100
775 numfiles = 9
776 for i in range(numfiles):
777 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
778 self.assertEqual(len(zipf.namelist()), numfiles)
779 with self.assertRaises(zipfile.LargeZipFile):
780 zipf.writestr("foo%08d" % numfiles, b'')
781 self.assertEqual(len(zipf.namelist()), numfiles)
782 zipf.close()
783
784 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
785 allowZip64=False)
786 zipf.debug = 100
787 self.assertEqual(len(zipf.namelist()), numfiles)
788 with self.assertRaises(zipfile.LargeZipFile):
789 zipf.writestr("foo%08d" % numfiles, b'')
790 self.assertEqual(len(zipf.namelist()), numfiles)
791 zipf.close()
792
793 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
794 allowZip64=True)
795 zipf.debug = 100
796 self.assertEqual(len(zipf.namelist()), numfiles)
797 numfiles2 = 15
798 for i in range(numfiles, numfiles2):
799 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
800 self.assertEqual(len(zipf.namelist()), numfiles2)
801 zipf.close()
802
803 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
804 self.assertEqual(len(zipf2.namelist()), numfiles2)
805 for i in range(numfiles2):
806 content = zipf2.read("foo%08d" % i).decode('ascii')
807 self.assertEqual(content, "%d" % (i**3 % 57))
808 zipf2.close()
809
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300810 def tearDown(self):
811 zipfile.ZIP64_LIMIT = self._limit
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300812 zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300813 unlink(TESTFN)
814 unlink(TESTFN2)
815
816
817class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
818 unittest.TestCase):
819 compression = zipfile.ZIP_STORED
820
821 def large_file_exception_test(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200822 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300823 self.assertRaises(zipfile.LargeZipFile,
824 zipfp.write, TESTFN, "another.name")
825
826 def large_file_exception_test2(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200827 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300828 self.assertRaises(zipfile.LargeZipFile,
829 zipfp.writestr, "another.name", self.data)
830
831 def test_large_file_exception(self):
832 for f in get_files(self):
833 self.large_file_exception_test(f, zipfile.ZIP_STORED)
834 self.large_file_exception_test2(f, zipfile.ZIP_STORED)
835
836 def test_absolute_arcnames(self):
837 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
838 allowZip64=True) as zipfp:
839 zipfp.write(TESTFN, "/absolute")
840
841 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
842 self.assertEqual(zipfp.namelist(), ["absolute"])
843
Serhiy Storchaka9bdb7be2018-09-17 15:36:40 +0300844 def test_append(self):
845 # Test that appending to the Zip64 archive doesn't change
846 # extra fields of existing entries.
847 with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp:
848 zipfp.writestr("strfile", self.data)
849 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
850 zinfo = zipfp.getinfo("strfile")
851 extra = zinfo.extra
852 with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp:
853 zipfp.writestr("strfile2", self.data)
854 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
855 zinfo = zipfp.getinfo("strfile")
856 self.assertEqual(zinfo.extra, extra)
857
Daniel Hillierda6ce582019-10-29 18:24:18 +1100858 def make_zip64_file(
859 self, file_size_64_set=False, file_size_extra=False,
860 compress_size_64_set=False, compress_size_extra=False,
861 header_offset_64_set=False, header_offset_extra=False,
862 ):
863 """Generate bytes sequence for a zip with (incomplete) zip64 data.
864
865 The actual values (not the zip 64 0xffffffff values) stored in the file
866 are:
867 file_size: 8
868 compress_size: 8
869 header_offset: 0
870 """
871 actual_size = 8
872 actual_header_offset = 0
873 local_zip64_fields = []
874 central_zip64_fields = []
875
876 file_size = actual_size
877 if file_size_64_set:
878 file_size = 0xffffffff
879 if file_size_extra:
880 local_zip64_fields.append(actual_size)
881 central_zip64_fields.append(actual_size)
882 file_size = struct.pack("<L", file_size)
883
884 compress_size = actual_size
885 if compress_size_64_set:
886 compress_size = 0xffffffff
887 if compress_size_extra:
888 local_zip64_fields.append(actual_size)
889 central_zip64_fields.append(actual_size)
890 compress_size = struct.pack("<L", compress_size)
891
892 header_offset = actual_header_offset
893 if header_offset_64_set:
894 header_offset = 0xffffffff
895 if header_offset_extra:
896 central_zip64_fields.append(actual_header_offset)
897 header_offset = struct.pack("<L", header_offset)
898
899 local_extra = struct.pack(
900 '<HH' + 'Q'*len(local_zip64_fields),
901 0x0001,
902 8*len(local_zip64_fields),
903 *local_zip64_fields
904 )
905
906 central_extra = struct.pack(
907 '<HH' + 'Q'*len(central_zip64_fields),
908 0x0001,
909 8*len(central_zip64_fields),
910 *central_zip64_fields
911 )
912
913 central_dir_size = struct.pack('<Q', 58 + 8 * len(central_zip64_fields))
914 offset_to_central_dir = struct.pack('<Q', 50 + 8 * len(local_zip64_fields))
915
916 local_extra_length = struct.pack("<H", 4 + 8 * len(local_zip64_fields))
917 central_extra_length = struct.pack("<H", 4 + 8 * len(central_zip64_fields))
918
919 filename = b"test.txt"
920 content = b"test1234"
921 filename_length = struct.pack("<H", len(filename))
922 zip64_contents = (
923 # Local file header
924 b"PK\x03\x04\x14\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
925 + compress_size
926 + file_size
927 + filename_length
928 + local_extra_length
929 + filename
930 + local_extra
931 + content
932 # Central directory:
933 + b"PK\x01\x02-\x03-\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
934 + compress_size
935 + file_size
936 + filename_length
937 + central_extra_length
938 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01"
939 + header_offset
940 + filename
941 + central_extra
942 # Zip64 end of central directory
943 + b"PK\x06\x06,\x00\x00\x00\x00\x00\x00\x00-\x00-"
944 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
945 + b"\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
946 + central_dir_size
947 + offset_to_central_dir
948 # Zip64 end of central directory locator
949 + b"PK\x06\x07\x00\x00\x00\x00l\x00\x00\x00\x00\x00\x00\x00\x01"
950 + b"\x00\x00\x00"
951 # end of central directory
952 + b"PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00:\x00\x00\x002\x00"
953 + b"\x00\x00\x00\x00"
954 )
955 return zip64_contents
956
957 def test_bad_zip64_extra(self):
958 """Missing zip64 extra records raises an exception.
959
960 There are 4 fields that the zip64 format handles (the disk number is
961 not used in this module and so is ignored here). According to the zip
962 spec:
963 The order of the fields in the zip64 extended
964 information record is fixed, but the fields MUST
965 only appear if the corresponding Local or Central
966 directory record field is set to 0xFFFF or 0xFFFFFFFF.
967
968 If the zip64 extra content doesn't contain enough entries for the
969 number of fields marked with 0xFFFF or 0xFFFFFFFF, we raise an error.
970 This test mismatches the length of the zip64 extra field and the number
971 of fields set to indicate the presence of zip64 data.
972 """
973 # zip64 file size present, no fields in extra, expecting one, equals
974 # missing file size.
975 missing_file_size_extra = self.make_zip64_file(
976 file_size_64_set=True,
977 )
978 with self.assertRaises(zipfile.BadZipFile) as e:
979 zipfile.ZipFile(io.BytesIO(missing_file_size_extra))
980 self.assertIn('file size', str(e.exception).lower())
981
982 # zip64 file size present, zip64 compress size present, one field in
983 # extra, expecting two, equals missing compress size.
984 missing_compress_size_extra = self.make_zip64_file(
985 file_size_64_set=True,
986 file_size_extra=True,
987 compress_size_64_set=True,
988 )
989 with self.assertRaises(zipfile.BadZipFile) as e:
990 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
991 self.assertIn('compress size', str(e.exception).lower())
992
993 # zip64 compress size present, no fields in extra, expecting one,
994 # equals missing compress size.
995 missing_compress_size_extra = self.make_zip64_file(
996 compress_size_64_set=True,
997 )
998 with self.assertRaises(zipfile.BadZipFile) as e:
999 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
1000 self.assertIn('compress size', str(e.exception).lower())
1001
1002 # zip64 file size present, zip64 compress size present, zip64 header
1003 # offset present, two fields in extra, expecting three, equals missing
1004 # header offset
1005 missing_header_offset_extra = self.make_zip64_file(
1006 file_size_64_set=True,
1007 file_size_extra=True,
1008 compress_size_64_set=True,
1009 compress_size_extra=True,
1010 header_offset_64_set=True,
1011 )
1012 with self.assertRaises(zipfile.BadZipFile) as e:
1013 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1014 self.assertIn('header offset', str(e.exception).lower())
1015
1016 # zip64 compress size present, zip64 header offset present, one field
1017 # in extra, expecting two, equals missing header offset
1018 missing_header_offset_extra = self.make_zip64_file(
1019 file_size_64_set=False,
1020 compress_size_64_set=True,
1021 compress_size_extra=True,
1022 header_offset_64_set=True,
1023 )
1024 with self.assertRaises(zipfile.BadZipFile) as e:
1025 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1026 self.assertIn('header offset', str(e.exception).lower())
1027
1028 # zip64 file size present, zip64 header offset present, one field in
1029 # extra, expecting two, equals missing header offset
1030 missing_header_offset_extra = self.make_zip64_file(
1031 file_size_64_set=True,
1032 file_size_extra=True,
1033 compress_size_64_set=False,
1034 header_offset_64_set=True,
1035 )
1036 with self.assertRaises(zipfile.BadZipFile) as e:
1037 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1038 self.assertIn('header offset', str(e.exception).lower())
1039
1040 # zip64 header offset present, no fields in extra, expecting one,
1041 # equals missing header offset
1042 missing_header_offset_extra = self.make_zip64_file(
1043 file_size_64_set=False,
1044 compress_size_64_set=False,
1045 header_offset_64_set=True,
1046 )
1047 with self.assertRaises(zipfile.BadZipFile) as e:
1048 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1049 self.assertIn('header offset', str(e.exception).lower())
1050
1051 def test_generated_valid_zip64_extra(self):
1052 # These values are what is set in the make_zip64_file method.
1053 expected_file_size = 8
1054 expected_compress_size = 8
1055 expected_header_offset = 0
1056 expected_content = b"test1234"
1057
1058 # Loop through the various valid combinations of zip64 masks
1059 # present and extra fields present.
1060 params = (
1061 {"file_size_64_set": True, "file_size_extra": True},
1062 {"compress_size_64_set": True, "compress_size_extra": True},
1063 {"header_offset_64_set": True, "header_offset_extra": True},
1064 )
1065
1066 for r in range(1, len(params) + 1):
1067 for combo in itertools.combinations(params, r):
1068 kwargs = {}
1069 for c in combo:
1070 kwargs.update(c)
1071 with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf:
1072 zinfo = zf.infolist()[0]
1073 self.assertEqual(zinfo.file_size, expected_file_size)
1074 self.assertEqual(zinfo.compress_size, expected_compress_size)
1075 self.assertEqual(zinfo.header_offset, expected_header_offset)
1076 self.assertEqual(zf.read(zinfo), expected_content)
1077
1078
Hai Shia3ec3ad2020-05-19 06:02:57 +08001079@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001080class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1081 unittest.TestCase):
1082 compression = zipfile.ZIP_DEFLATED
1083
Hai Shia3ec3ad2020-05-19 06:02:57 +08001084@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001085class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1086 unittest.TestCase):
1087 compression = zipfile.ZIP_BZIP2
1088
Hai Shia3ec3ad2020-05-19 06:02:57 +08001089@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001090class LzmaTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1091 unittest.TestCase):
1092 compression = zipfile.ZIP_LZMA
1093
1094
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001095class AbstractWriterTests:
1096
1097 def tearDown(self):
1098 unlink(TESTFN2)
1099
1100 def test_close_after_close(self):
1101 data = b'content'
1102 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1103 w = zipf.open('test', 'w')
1104 w.write(data)
1105 w.close()
1106 self.assertTrue(w.closed)
1107 w.close()
1108 self.assertTrue(w.closed)
1109 self.assertEqual(zipf.read('test'), data)
1110
1111 def test_write_after_close(self):
1112 data = b'content'
1113 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1114 w = zipf.open('test', 'w')
1115 w.write(data)
1116 w.close()
1117 self.assertTrue(w.closed)
1118 self.assertRaises(ValueError, w.write, b'')
1119 self.assertEqual(zipf.read('test'), data)
1120
1121class StoredWriterTests(AbstractWriterTests, unittest.TestCase):
1122 compression = zipfile.ZIP_STORED
1123
Hai Shia3ec3ad2020-05-19 06:02:57 +08001124@requires_zlib()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001125class DeflateWriterTests(AbstractWriterTests, unittest.TestCase):
1126 compression = zipfile.ZIP_DEFLATED
1127
Hai Shia3ec3ad2020-05-19 06:02:57 +08001128@requires_bz2()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001129class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase):
1130 compression = zipfile.ZIP_BZIP2
1131
Hai Shia3ec3ad2020-05-19 06:02:57 +08001132@requires_lzma()
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001133class LzmaWriterTests(AbstractWriterTests, unittest.TestCase):
1134 compression = zipfile.ZIP_LZMA
1135
1136
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001137class PyZipFileTests(unittest.TestCase):
1138 def assertCompiledIn(self, name, namelist):
1139 if name + 'o' not in namelist:
1140 self.assertIn(name + 'c', namelist)
1141
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001142 def requiresWriteAccess(self, path):
Berker Peksage1efc072015-02-16 04:36:18 +02001143 # effective_ids unavailable on windows
1144 if not os.access(path, os.W_OK,
1145 effective_ids=os.access in os.supports_effective_ids):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001146 self.skipTest('requires write access to the installed location')
Serhiy Storchakad86a6ef2015-09-19 10:55:20 +03001147 filename = os.path.join(path, 'test_zipfile.try')
1148 try:
1149 fd = os.open(filename, os.O_WRONLY | os.O_CREAT)
1150 os.close(fd)
1151 except Exception:
1152 self.skipTest('requires write access to the installed location')
1153 unlink(filename)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001154
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001155 def test_write_pyfile(self):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001156 self.requiresWriteAccess(os.path.dirname(__file__))
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001157 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1158 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001159 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001160 path_split = fn.split(os.sep)
1161 if os.altsep is not None:
1162 path_split.extend(fn.split(os.altsep))
1163 if '__pycache__' in path_split:
Serhiy Storchaka9068e4d2013-07-22 21:02:14 +03001164 fn = importlib.util.source_from_cache(fn)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001165 else:
1166 fn = fn[:-1]
1167
1168 zipfp.writepy(fn)
1169
1170 bn = os.path.basename(fn)
1171 self.assertNotIn(bn, zipfp.namelist())
1172 self.assertCompiledIn(bn, zipfp.namelist())
1173
1174 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1175 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001176 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001177 fn = fn[:-1]
1178
1179 zipfp.writepy(fn, "testpackage")
1180
1181 bn = "%s/%s" % ("testpackage", os.path.basename(fn))
1182 self.assertNotIn(bn, zipfp.namelist())
1183 self.assertCompiledIn(bn, zipfp.namelist())
1184
1185 def test_write_python_package(self):
1186 import email
1187 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001188 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001189
1190 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1191 zipfp.writepy(packagedir)
1192
1193 # Check for a couple of modules at different levels of the
1194 # hierarchy
1195 names = zipfp.namelist()
1196 self.assertCompiledIn('email/__init__.py', names)
1197 self.assertCompiledIn('email/mime/text.py', names)
1198
Christian Tismer59202e52013-10-21 03:59:23 +02001199 def test_write_filtered_python_package(self):
1200 import test
1201 packagedir = os.path.dirname(test.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001202 self.requiresWriteAccess(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001203
1204 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1205
Christian Tismer59202e52013-10-21 03:59:23 +02001206 # first make sure that the test folder gives error messages
Georg Brandla6065422013-10-21 08:29:29 +02001207 # (on the badsyntax_... files)
1208 with captured_stdout() as reportSIO:
1209 zipfp.writepy(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001210 reportStr = reportSIO.getvalue()
1211 self.assertTrue('SyntaxError' in reportStr)
1212
Christian Tismer410d9312013-10-22 04:09:28 +02001213 # then check that the filter works on the whole package
Georg Brandla6065422013-10-21 08:29:29 +02001214 with captured_stdout() as reportSIO:
1215 zipfp.writepy(packagedir, filterfunc=lambda whatever: False)
Christian Tismer59202e52013-10-21 03:59:23 +02001216 reportStr = reportSIO.getvalue()
1217 self.assertTrue('SyntaxError' not in reportStr)
1218
Christian Tismer410d9312013-10-22 04:09:28 +02001219 # then check that the filter works on individual files
Larry Hastings7e63b362015-05-08 06:54:58 -07001220 def filter(path):
1221 return not os.path.basename(path).startswith("bad")
Serhiy Storchakac46d1fa2014-01-20 21:59:33 +02001222 with captured_stdout() as reportSIO, self.assertWarns(UserWarning):
Larry Hastings7e63b362015-05-08 06:54:58 -07001223 zipfp.writepy(packagedir, filterfunc=filter)
Christian Tismer410d9312013-10-22 04:09:28 +02001224 reportStr = reportSIO.getvalue()
1225 if reportStr:
1226 print(reportStr)
1227 self.assertTrue('SyntaxError' not in reportStr)
1228
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001229 def test_write_with_optimization(self):
1230 import email
1231 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001232 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001233 optlevel = 1 if __debug__ else 0
Brett Cannonf299abd2015-04-13 14:21:02 -04001234 ext = '.pyc'
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001235
1236 with TemporaryFile() as t, \
Christian Tismer59202e52013-10-21 03:59:23 +02001237 zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001238 zipfp.writepy(packagedir)
1239
1240 names = zipfp.namelist()
1241 self.assertIn('email/__init__' + ext, names)
1242 self.assertIn('email/mime/text' + ext, names)
1243
1244 def test_write_python_directory(self):
1245 os.mkdir(TESTFN2)
1246 try:
1247 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1248 fp.write("print(42)\n")
1249
1250 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1251 fp.write("print(42 * 42)\n")
1252
1253 with open(os.path.join(TESTFN2, "mod2.txt"), "w") as fp:
1254 fp.write("bla bla bla\n")
1255
1256 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1257 zipfp.writepy(TESTFN2)
1258
1259 names = zipfp.namelist()
1260 self.assertCompiledIn('mod1.py', names)
1261 self.assertCompiledIn('mod2.py', names)
1262 self.assertNotIn('mod2.txt', names)
1263
1264 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001265 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001266
Christian Tismer410d9312013-10-22 04:09:28 +02001267 def test_write_python_directory_filtered(self):
1268 os.mkdir(TESTFN2)
1269 try:
1270 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1271 fp.write("print(42)\n")
1272
1273 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1274 fp.write("print(42 * 42)\n")
1275
1276 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1277 zipfp.writepy(TESTFN2, filterfunc=lambda fn:
1278 not fn.endswith('mod2.py'))
1279
1280 names = zipfp.namelist()
1281 self.assertCompiledIn('mod1.py', names)
1282 self.assertNotIn('mod2.py', names)
1283
1284 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001285 rmtree(TESTFN2)
Christian Tismer410d9312013-10-22 04:09:28 +02001286
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001287 def test_write_non_pyfile(self):
1288 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1289 with open(TESTFN, 'w') as f:
1290 f.write('most definitely not a python file')
1291 self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
Victor Stinner88b215e2014-09-04 00:51:09 +02001292 unlink(TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001293
1294 def test_write_pyfile_bad_syntax(self):
1295 os.mkdir(TESTFN2)
1296 try:
1297 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1298 fp.write("Bad syntax in python file\n")
1299
1300 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1301 # syntax errors are printed to stdout
1302 with captured_stdout() as s:
1303 zipfp.writepy(os.path.join(TESTFN2, "mod1.py"))
1304
1305 self.assertIn("SyntaxError", s.getvalue())
1306
1307 # as it will not have compiled the python file, it will
Brett Cannonf299abd2015-04-13 14:21:02 -04001308 # include the .py file not .pyc
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001309 names = zipfp.namelist()
1310 self.assertIn('mod1.py', names)
1311 self.assertNotIn('mod1.pyc', names)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001312
1313 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001314 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001315
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001316 def test_write_pathlike(self):
1317 os.mkdir(TESTFN2)
1318 try:
1319 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1320 fp.write("print(42)\n")
1321
1322 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1323 zipfp.writepy(pathlib.Path(TESTFN2) / "mod1.py")
1324 names = zipfp.namelist()
1325 self.assertCompiledIn('mod1.py', names)
1326 finally:
1327 rmtree(TESTFN2)
1328
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001329
1330class ExtractTests(unittest.TestCase):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001331
1332 def make_test_file(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001333 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1334 for fpath, fdata in SMALL_TEST_DATA:
1335 zipfp.writestr(fpath, fdata)
Christian Heimes790c8232008-01-07 21:14:23 +00001336
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001337 def test_extract(self):
1338 with temp_cwd():
1339 self.make_test_file()
1340 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1341 for fpath, fdata in SMALL_TEST_DATA:
1342 writtenfile = zipfp.extract(fpath)
1343
1344 # make sure it was written to the right place
1345 correctfile = os.path.join(os.getcwd(), fpath)
1346 correctfile = os.path.normpath(correctfile)
1347
1348 self.assertEqual(writtenfile, correctfile)
1349
1350 # make sure correct data is in correct file
1351 with open(writtenfile, "rb") as f:
1352 self.assertEqual(fdata.encode(), f.read())
1353
1354 unlink(writtenfile)
1355
1356 def _test_extract_with_target(self, target):
1357 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001358 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1359 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001360 writtenfile = zipfp.extract(fpath, target)
Christian Heimes790c8232008-01-07 21:14:23 +00001361
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001362 # make sure it was written to the right place
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001363 correctfile = os.path.join(target, fpath)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001364 correctfile = os.path.normpath(correctfile)
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001365 self.assertTrue(os.path.samefile(writtenfile, correctfile), (writtenfile, target))
Christian Heimes790c8232008-01-07 21:14:23 +00001366
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001367 # make sure correct data is in correct file
Brian Curtin8fb9b862010-11-18 02:15:28 +00001368 with open(writtenfile, "rb") as f:
1369 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001370
Victor Stinner88b215e2014-09-04 00:51:09 +02001371 unlink(writtenfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001372
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001373 unlink(TESTFN2)
1374
1375 def test_extract_with_target(self):
1376 with temp_dir() as extdir:
1377 self._test_extract_with_target(extdir)
1378
1379 def test_extract_with_target_pathlike(self):
1380 with temp_dir() as extdir:
1381 self._test_extract_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001382
Ezio Melottiafd0d112009-07-15 17:17:17 +00001383 def test_extract_all(self):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001384 with temp_cwd():
1385 self.make_test_file()
1386 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1387 zipfp.extractall()
1388 for fpath, fdata in SMALL_TEST_DATA:
1389 outfile = os.path.join(os.getcwd(), fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001390
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001391 with open(outfile, "rb") as f:
1392 self.assertEqual(fdata.encode(), f.read())
1393
1394 unlink(outfile)
1395
1396 def _test_extract_all_with_target(self, target):
1397 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001398 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001399 zipfp.extractall(target)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001400 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001401 outfile = os.path.join(target, fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001402
Brian Curtin8fb9b862010-11-18 02:15:28 +00001403 with open(outfile, "rb") as f:
1404 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001405
Victor Stinner88b215e2014-09-04 00:51:09 +02001406 unlink(outfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001407
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001408 unlink(TESTFN2)
1409
1410 def test_extract_all_with_target(self):
1411 with temp_dir() as extdir:
1412 self._test_extract_all_with_target(extdir)
1413
1414 def test_extract_all_with_target_pathlike(self):
1415 with temp_dir() as extdir:
1416 self._test_extract_all_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001417
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001418 def check_file(self, filename, content):
1419 self.assertTrue(os.path.isfile(filename))
1420 with open(filename, 'rb') as f:
1421 self.assertEqual(f.read(), content)
1422
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001423 def test_sanitize_windows_name(self):
1424 san = zipfile.ZipFile._sanitize_windows_name
1425 # Passing pathsep in allows this test to work regardless of platform.
1426 self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z')
1427 self.assertEqual(san(r'a\b,c<d>e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i')
1428 self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r')
1429
1430 def test_extract_hackers_arcnames_common_cases(self):
1431 common_hacknames = [
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001432 ('../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'),
1439 ('/foo/../../bar', 'foo/bar'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001440 ]
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001441 self._test_extract_hackers_arcnames(common_hacknames)
1442
1443 @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.')
1444 def test_extract_hackers_arcnames_windows_only(self):
1445 """Test combination of path fixing and windows name sanitization."""
1446 windows_hacknames = [
Christian Tismer59202e52013-10-21 03:59:23 +02001447 (r'..\foo\bar', 'foo/bar'),
1448 (r'..\/foo\/bar', 'foo/bar'),
1449 (r'foo/\..\/bar', 'foo/bar'),
1450 (r'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'C:\foo\bar', 'foo/bar'),
1455 (r'//conky/mountpoint/foo/bar', 'foo/bar'),
1456 (r'\\conky\mountpoint\foo\bar', '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'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
1461 (r'//?/C:/foo/bar', 'foo/bar'),
1462 (r'\\?\C:\foo\bar', 'foo/bar'),
1463 (r'C:/../C:/foo/bar', 'C_/foo/bar'),
1464 (r'a:b\c<d>e|f"g?h*i', 'b/c_d_e_f_g_h_i'),
1465 ('../../foo../../ba..r', 'foo/ba..r'),
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001466 ]
1467 self._test_extract_hackers_arcnames(windows_hacknames)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001468
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001469 @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.')
1470 def test_extract_hackers_arcnames_posix_only(self):
1471 posix_hacknames = [
1472 ('//foo/bar', 'foo/bar'),
1473 ('../../foo../../ba..r', 'foo../ba..r'),
1474 (r'foo/..\bar', r'foo/..\bar'),
1475 ]
1476 self._test_extract_hackers_arcnames(posix_hacknames)
1477
1478 def _test_extract_hackers_arcnames(self, hacknames):
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001479 for arcname, fixedname in hacknames:
1480 content = b'foobar' + arcname.encode()
1481 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp:
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001482 zinfo = zipfile.ZipInfo()
1483 # preserve backslashes
1484 zinfo.filename = arcname
1485 zinfo.external_attr = 0o600 << 16
1486 zipfp.writestr(zinfo, content)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001487
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001488 arcname = arcname.replace(os.sep, "/")
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001489 targetpath = os.path.join('target', 'subdir', 'subsub')
1490 correctfile = os.path.join(targetpath, *fixedname.split('/'))
1491
1492 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1493 writtenfile = zipfp.extract(arcname, targetpath)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001494 self.assertEqual(writtenfile, correctfile,
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001495 msg='extract %r: %r != %r' %
1496 (arcname, writtenfile, correctfile))
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001497 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001498 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001499
1500 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1501 zipfp.extractall(targetpath)
1502 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001503 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001504
1505 correctfile = os.path.join(os.getcwd(), *fixedname.split('/'))
1506
1507 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1508 writtenfile = zipfp.extract(arcname)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001509 self.assertEqual(writtenfile, correctfile,
1510 msg="extract %r" % arcname)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001511 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001512 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001513
1514 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1515 zipfp.extractall()
1516 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001517 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001518
Victor Stinner88b215e2014-09-04 00:51:09 +02001519 unlink(TESTFN2)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001520
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001521
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001522class OtherTests(unittest.TestCase):
1523 def test_open_via_zip_info(self):
1524 # Create the ZIP archive
1525 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1526 zipfp.writestr("name", "foo")
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001527 with self.assertWarns(UserWarning):
1528 zipfp.writestr("name", "bar")
1529 self.assertEqual(zipfp.namelist(), ["name"] * 2)
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001530
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001531 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1532 infos = zipfp.infolist()
1533 data = b""
1534 for info in infos:
1535 with zipfp.open(info) as zipopen:
1536 data += zipopen.read()
1537 self.assertIn(data, {b"foobar", b"barfoo"})
1538 data = b""
1539 for info in infos:
1540 data += zipfp.read(info)
1541 self.assertIn(data, {b"foobar", b"barfoo"})
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02001542
Gregory P. Smithb0d9ca92009-07-07 05:06:04 +00001543 def test_writestr_extended_local_header_issue1202(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001544 with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
1545 for data in 'abcdefghijklmnop':
1546 zinfo = zipfile.ZipInfo(data)
1547 zinfo.flag_bits |= 0x08 # Include an extended local header.
1548 orig_zip.writestr(zinfo, data)
1549
1550 def test_close(self):
1551 """Check that the zipfile is closed after the 'with' block."""
1552 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1553 for fpath, fdata in SMALL_TEST_DATA:
1554 zipfp.writestr(fpath, fdata)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001555 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1556 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001557
1558 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001559 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1560 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001561
1562 def test_close_on_exception(self):
1563 """Check that the zipfile is closed if an exception is raised in the
1564 'with' block."""
1565 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1566 for fpath, fdata in SMALL_TEST_DATA:
1567 zipfp.writestr(fpath, fdata)
1568
1569 try:
1570 with zipfile.ZipFile(TESTFN2, "r") as zipfp2:
Georg Brandl4d540882010-10-28 06:42:33 +00001571 raise zipfile.BadZipFile()
1572 except zipfile.BadZipFile:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001573 self.assertIsNone(zipfp2.fp, 'zipfp is not closed')
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00001574
Martin v. Löwisd099b562012-05-01 14:08:22 +02001575 def test_unsupported_version(self):
1576 # File has an extract_version of 120
1577 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 +02001578 b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00'
1579 b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
1580 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06'
1581 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 +03001582
Martin v. Löwisd099b562012-05-01 14:08:22 +02001583 self.assertRaises(NotImplementedError, zipfile.ZipFile,
1584 io.BytesIO(data), 'r')
1585
Hai Shia3ec3ad2020-05-19 06:02:57 +08001586 @requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001587 def test_read_unicode_filenames(self):
1588 # bug #10801
1589 fname = findfile('zip_cp437_header.zip')
1590 with zipfile.ZipFile(fname) as zipfp:
1591 for name in zipfp.namelist():
1592 zipfp.open(name).close()
1593
1594 def test_write_unicode_filenames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001595 with zipfile.ZipFile(TESTFN, "w") as zf:
1596 zf.writestr("foo.txt", "Test for unicode filename")
1597 zf.writestr("\xf6.txt", "Test for unicode filename")
Ezio Melottie9615932010-01-24 19:26:24 +00001598 self.assertIsInstance(zf.infolist()[0].filename, str)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001599
1600 with zipfile.ZipFile(TESTFN, "r") as zf:
1601 self.assertEqual(zf.filelist[0].filename, "foo.txt")
1602 self.assertEqual(zf.filelist[1].filename, "\xf6.txt")
Martin v. Löwis8570f6a2008-05-05 17:44:38 +00001603
Serhiy Storchaka36ff5132020-06-22 11:24:11 +03001604 def test_read_after_write_unicode_filenames(self):
1605 with zipfile.ZipFile(TESTFN2, 'w') as zipfp:
1606 zipfp.writestr('приклад', b'sample')
1607 self.assertEqual(zipfp.read('приклад'), b'sample')
1608
Serhiy Storchaka764fc9b2015-03-25 10:09:41 +02001609 def test_exclusive_create_zip_file(self):
1610 """Test exclusive creating a new zipfile."""
1611 unlink(TESTFN2)
1612 filename = 'testfile.txt'
1613 content = b'hello, world. this is some content.'
1614 with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp:
1615 zipfp.writestr(filename, content)
1616 with self.assertRaises(FileExistsError):
1617 zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED)
1618 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1619 self.assertEqual(zipfp.namelist(), [filename])
1620 self.assertEqual(zipfp.read(filename), content)
1621
Ezio Melottiafd0d112009-07-15 17:17:17 +00001622 def test_create_non_existent_file_for_append(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00001623 if os.path.exists(TESTFN):
1624 os.unlink(TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001625
Thomas Wouterscf297e42007-02-23 15:07:44 +00001626 filename = 'testfile.txt'
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001627 content = b'hello, world. this is some content.'
Guido van Rossumd8faa362007-04-27 19:54:29 +00001628
Thomas Wouterscf297e42007-02-23 15:07:44 +00001629 try:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001630 with zipfile.ZipFile(TESTFN, 'a') as zf:
1631 zf.writestr(filename, content)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001632 except OSError:
Thomas Wouterscf297e42007-02-23 15:07:44 +00001633 self.fail('Could not append data to a non-existent zip file.')
1634
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001635 self.assertTrue(os.path.exists(TESTFN))
Thomas Wouterscf297e42007-02-23 15:07:44 +00001636
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001637 with zipfile.ZipFile(TESTFN, 'r') as zf:
1638 self.assertEqual(zf.read(filename), content)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001639
Ezio Melottiafd0d112009-07-15 17:17:17 +00001640 def test_close_erroneous_file(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001641 # This test checks that the ZipFile constructor closes the file object
Ezio Melotti35386712009-12-31 13:22:41 +00001642 # it opens if there's an error in the file. If it doesn't, the
1643 # traceback holds a reference to the ZipFile object and, indirectly,
1644 # the file object.
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001645 # On Windows, this causes the os.unlink() call to fail because the
1646 # underlying file is still open. This is SF bug #412214.
1647 #
Ezio Melotti35386712009-12-31 13:22:41 +00001648 with open(TESTFN, "w") as fp:
1649 fp.write("this is not a legal zip file\n")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001650 try:
1651 zf = zipfile.ZipFile(TESTFN)
Georg Brandl4d540882010-10-28 06:42:33 +00001652 except zipfile.BadZipFile:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001653 pass
1654
Ezio Melottiafd0d112009-07-15 17:17:17 +00001655 def test_is_zip_erroneous_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001656 """Check that is_zipfile() correctly identifies non-zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001657 # - passing a filename
1658 with open(TESTFN, "w") as fp:
1659 fp.write("this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001660 self.assertFalse(zipfile.is_zipfile(TESTFN))
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001661 # - passing a path-like object
1662 self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN)))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001663 # - passing a file object
1664 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001665 self.assertFalse(zipfile.is_zipfile(fp))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001666 # - passing a file-like object
1667 fp = io.BytesIO()
1668 fp.write(b"this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001669 self.assertFalse(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001670 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001671 self.assertFalse(zipfile.is_zipfile(fp))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001672
Serhiy Storchakad2b15272013-01-31 15:27:07 +02001673 def test_damaged_zipfile(self):
1674 """Check that zipfiles with missing bytes at the end raise BadZipFile."""
1675 # - Create a valid zip file
1676 fp = io.BytesIO()
1677 with zipfile.ZipFile(fp, mode="w") as zipf:
1678 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1679 zipfiledata = fp.getvalue()
1680
1681 # - Now create copies of it missing the last N bytes and make sure
1682 # a BadZipFile exception is raised when we try to open it
1683 for N in range(len(zipfiledata)):
1684 fp = io.BytesIO(zipfiledata[:N])
1685 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp)
1686
Ezio Melottiafd0d112009-07-15 17:17:17 +00001687 def test_is_zip_valid_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001688 """Check that is_zipfile() correctly identifies zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001689 # - passing a filename
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001690 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1691 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1692
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001693 self.assertTrue(zipfile.is_zipfile(TESTFN))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001694 # - passing a file object
1695 with open(TESTFN, "rb") as fp:
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)
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001698 zip_contents = fp.read()
1699 # - passing a file-like object
1700 fp = io.BytesIO()
1701 fp.write(zip_contents)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001702 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001703 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001704 self.assertTrue(zipfile.is_zipfile(fp))
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001705
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001706 def test_non_existent_file_raises_OSError(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001707 # make sure we don't raise an AttributeError when a partially-constructed
1708 # ZipFile instance is finalized; this tests for regression on SF tracker
1709 # bug #403871.
1710
1711 # The bug we're testing for caused an AttributeError to be raised
1712 # when a ZipFile instance was created for a file that did not
1713 # exist; the .fp member was not initialized but was needed by the
1714 # __del__() method. Since the AttributeError is in the __del__(),
1715 # it is ignored, but the user should be sufficiently annoyed by
1716 # the message on the output that regression will be noticed
1717 # quickly.
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001718 self.assertRaises(OSError, zipfile.ZipFile, TESTFN)
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001719
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001720 def test_empty_file_raises_BadZipFile(self):
1721 f = open(TESTFN, 'w')
1722 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001723 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001724
Ezio Melotti35386712009-12-31 13:22:41 +00001725 with open(TESTFN, 'w') as fp:
1726 fp.write("short file")
Georg Brandl4d540882010-10-28 06:42:33 +00001727 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001728
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001729 def test_closed_zip_raises_ValueError(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001730 """Verify that testzip() doesn't swallow inappropriate exceptions."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001731 data = io.BytesIO()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001732 with zipfile.ZipFile(data, mode="w") as zipf:
1733 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001734
Andrew Svetlov737fb892012-12-18 21:14:22 +02001735 # This is correct; calling .read on a closed ZipFile should raise
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001736 # a ValueError, and so should calling .testzip. An earlier
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001737 # version of .testzip would swallow this exception (and any other)
1738 # and report that the first file in the archive was corrupt.
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001739 self.assertRaises(ValueError, zipf.read, "foo.txt")
1740 self.assertRaises(ValueError, zipf.open, "foo.txt")
1741 self.assertRaises(ValueError, zipf.testzip)
1742 self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus")
Brian Curtin8fb9b862010-11-18 02:15:28 +00001743 with open(TESTFN, 'w') as f:
1744 f.write('zipfile test data')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001745 self.assertRaises(ValueError, zipf.write, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001746
Ezio Melottiafd0d112009-07-15 17:17:17 +00001747 def test_bad_constructor_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001748 """Check that bad modes passed to ZipFile constructor are caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001749 self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001750
Ezio Melottiafd0d112009-07-15 17:17:17 +00001751 def test_bad_open_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001752 """Check that bad modes passed to ZipFile.open are caught."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001753 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1754 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1755
1756 with zipfile.ZipFile(TESTFN, mode="r") as zipf:
Serhiy Storchakae670be22016-06-11 19:32:44 +03001757 # read the data to make sure the file is there
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001758 zipf.read("foo.txt")
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001759 self.assertRaises(ValueError, zipf.open, "foo.txt", "q")
Serhiy Storchakae670be22016-06-11 19:32:44 +03001760 # universal newlines support is removed
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001761 self.assertRaises(ValueError, zipf.open, "foo.txt", "U")
1762 self.assertRaises(ValueError, zipf.open, "foo.txt", "rU")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001763
Ezio Melottiafd0d112009-07-15 17:17:17 +00001764 def test_read0(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001765 """Check that calling read(0) on a ZipExtFile object returns an empty
1766 string and doesn't advance file pointer."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001767 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1768 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1769 # read the data to make sure the file is there
Brian Curtin8fb9b862010-11-18 02:15:28 +00001770 with zipf.open("foo.txt") as f:
1771 for i in range(FIXEDTEST_SIZE):
1772 self.assertEqual(f.read(0), b'')
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001773
Brian Curtin8fb9b862010-11-18 02:15:28 +00001774 self.assertEqual(f.read(), b"O, for a Muse of Fire!")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001775
Ezio Melottiafd0d112009-07-15 17:17:17 +00001776 def test_open_non_existent_item(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001777 """Check that attempting to call open() for an item that doesn't
1778 exist in the archive raises a RuntimeError."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001779 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1780 self.assertRaises(KeyError, zipf.open, "foo.txt", "r")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001781
Ezio Melottiafd0d112009-07-15 17:17:17 +00001782 def test_bad_compression_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001783 """Check that bad compression methods passed to ZipFile.open are
1784 caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001785 self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001786
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001787 def test_unsupported_compression(self):
1788 # data is declared as shrunk, but actually deflated
1789 data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00'
Christian Tismer59202e52013-10-21 03:59:23 +02001790 b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01'
1791 b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00'
1792 b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1793 b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00'
1794 b'/\x00\x00\x00!\x00\x00\x00\x00\x00')
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001795 with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
1796 self.assertRaises(NotImplementedError, zipf.open, 'x')
1797
Ezio Melottiafd0d112009-07-15 17:17:17 +00001798 def test_null_byte_in_filename(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001799 """Check that a filename containing a null byte is properly
1800 terminated."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001801 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1802 zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!")
1803 self.assertEqual(zipf.namelist(), ['foo.txt'])
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001804
Ezio Melottiafd0d112009-07-15 17:17:17 +00001805 def test_struct_sizes(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001806 """Check that ZIP internal structure sizes are calculated correctly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001807 self.assertEqual(zipfile.sizeEndCentDir, 22)
1808 self.assertEqual(zipfile.sizeCentralDir, 46)
1809 self.assertEqual(zipfile.sizeEndCentDir64, 56)
1810 self.assertEqual(zipfile.sizeEndCentDir64Locator, 20)
1811
Ezio Melottiafd0d112009-07-15 17:17:17 +00001812 def test_comments(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001813 """Check that comments on the archive are handled properly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001814
1815 # check default comment is empty
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001816 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1817 self.assertEqual(zipf.comment, b'')
1818 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1819
1820 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1821 self.assertEqual(zipfr.comment, b'')
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001822
1823 # check a simple short comment
1824 comment = b'Bravely taking to his feet, he beat a very brave retreat.'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001825 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1826 zipf.comment = comment
1827 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1828 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1829 self.assertEqual(zipf.comment, comment)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001830
1831 # check a comment of max length
1832 comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)])
1833 comment2 = comment2.encode("ascii")
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001834 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1835 zipf.comment = comment2
1836 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1837
1838 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1839 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001840
1841 # check a comment that is too long is truncated
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001842 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001843 with self.assertWarns(UserWarning):
1844 zipf.comment = comment2 + b'oops'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001845 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1846 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1847 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001848
Antoine Pitrouc3991852012-06-30 17:31:37 +02001849 # check that comments are correctly modified in append mode
1850 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1851 zipf.comment = b"original comment"
1852 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1853 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1854 zipf.comment = b"an updated comment"
1855 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1856 self.assertEqual(zipf.comment, b"an updated comment")
1857
1858 # check that comments are correctly shortened in append mode
Jan Mazurff9147d2020-09-28 20:53:33 +02001859 # and the file is indeed truncated
Antoine Pitrouc3991852012-06-30 17:31:37 +02001860 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1861 zipf.comment = b"original comment that's longer"
1862 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
Jan Mazurff9147d2020-09-28 20:53:33 +02001863 original_zip_size = os.path.getsize(TESTFN)
Antoine Pitrouc3991852012-06-30 17:31:37 +02001864 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1865 zipf.comment = b"shorter comment"
Jan Mazurff9147d2020-09-28 20:53:33 +02001866 self.assertTrue(original_zip_size > os.path.getsize(TESTFN))
Antoine Pitrouc3991852012-06-30 17:31:37 +02001867 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1868 self.assertEqual(zipf.comment, b"shorter comment")
1869
R David Murrayf50b38a2012-04-12 18:44:58 -04001870 def test_unicode_comment(self):
1871 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1872 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1873 with self.assertRaises(TypeError):
1874 zipf.comment = "this is an error"
1875
1876 def test_change_comment_in_empty_archive(self):
1877 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1878 self.assertFalse(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
1883 def test_change_comment_in_nonempty_archive(self):
1884 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1885 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1886 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1887 self.assertTrue(zipf.filelist)
1888 zipf.comment = b"this is a comment"
1889 with zipfile.ZipFile(TESTFN, "r") as zipf:
1890 self.assertEqual(zipf.comment, b"this is a comment")
1891
Georg Brandl268e4d42010-10-14 06:59:45 +00001892 def test_empty_zipfile(self):
1893 # Check that creating a file in 'w' or 'a' mode and closing without
1894 # adding any files to the archives creates a valid empty ZIP file
1895 zipf = zipfile.ZipFile(TESTFN, mode="w")
1896 zipf.close()
1897 try:
1898 zipf = zipfile.ZipFile(TESTFN, mode="r")
1899 except zipfile.BadZipFile:
1900 self.fail("Unable to create empty ZIP file in 'w' mode")
1901
1902 zipf = zipfile.ZipFile(TESTFN, mode="a")
1903 zipf.close()
1904 try:
1905 zipf = zipfile.ZipFile(TESTFN, mode="r")
1906 except:
1907 self.fail("Unable to create empty ZIP file in 'a' mode")
1908
1909 def test_open_empty_file(self):
1910 # Issue 1710703: Check that opening a file with less than 22 bytes
Georg Brandl4d540882010-10-28 06:42:33 +00001911 # raises a BadZipFile exception (rather than the previously unhelpful
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001912 # OSError)
Georg Brandl268e4d42010-10-14 06:59:45 +00001913 f = open(TESTFN, 'w')
1914 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001915 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r')
Georg Brandl268e4d42010-10-14 06:59:45 +00001916
Senthil Kumaran29fa9d42011-10-20 01:46:00 +08001917 def test_create_zipinfo_before_1980(self):
1918 self.assertRaises(ValueError,
1919 zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0))
1920
Mickaël Schoentgen992347d2019-09-09 15:08:54 +02001921 def test_create_empty_zipinfo_repr(self):
1922 """Before bpo-26185, repr() on empty ZipInfo object was failing."""
1923 zi = zipfile.ZipInfo(filename="empty")
1924 self.assertEqual(repr(zi), "<ZipInfo filename='empty' file_size=0>")
1925
1926 def test_create_empty_zipinfo_default_attributes(self):
1927 """Ensure all required attributes are set."""
1928 zi = zipfile.ZipInfo()
1929 self.assertEqual(zi.orig_filename, "NoName")
1930 self.assertEqual(zi.filename, "NoName")
1931 self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0))
1932 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
1933 self.assertEqual(zi.comment, b"")
1934 self.assertEqual(zi.extra, b"")
1935 self.assertIn(zi.create_system, (0, 3))
1936 self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION)
1937 self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION)
1938 self.assertEqual(zi.reserved, 0)
1939 self.assertEqual(zi.flag_bits, 0)
1940 self.assertEqual(zi.volume, 0)
1941 self.assertEqual(zi.internal_attr, 0)
1942 self.assertEqual(zi.external_attr, 0)
1943
1944 # Before bpo-26185, both were missing
1945 self.assertEqual(zi.file_size, 0)
1946 self.assertEqual(zi.compress_size, 0)
1947
Gregory P. Smith0af8a862014-05-29 23:42:14 -07001948 def test_zipfile_with_short_extra_field(self):
1949 """If an extra field in the header is less than 4 bytes, skip it."""
1950 zipdata = (
1951 b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e'
1952 b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab'
1953 b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00'
1954 b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00'
1955 b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00'
1956 b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00'
1957 b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00'
1958 )
1959 with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf:
1960 # testzip returns the name of the first corrupt file, or None
1961 self.assertIsNone(zipf.testzip())
1962
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001963 def test_open_conflicting_handles(self):
1964 # It's only possible to open one writable file handle at a time
1965 msg1 = b"It's fun to charter an accountant!"
1966 msg2 = b"And sail the wide accountant sea"
1967 msg3 = b"To find, explore the funds offshore"
1968 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf:
1969 with zipf.open('foo', mode='w') as w2:
1970 w2.write(msg1)
1971 with zipf.open('bar', mode='w') as w1:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001972 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001973 zipf.open('handle', mode='w')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001974 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001975 zipf.open('foo', mode='r')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001976 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001977 zipf.writestr('str', 'abcde')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001978 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001979 zipf.write(__file__, 'file')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001980 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001981 zipf.close()
1982 w1.write(msg2)
1983 with zipf.open('baz', mode='w') as w2:
1984 w2.write(msg3)
1985
1986 with zipfile.ZipFile(TESTFN2, 'r') as zipf:
1987 self.assertEqual(zipf.read('foo'), msg1)
1988 self.assertEqual(zipf.read('bar'), msg2)
1989 self.assertEqual(zipf.read('baz'), msg3)
1990 self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz'])
1991
John Jolly066df4f2018-01-30 01:51:35 -07001992 def test_seek_tell(self):
1993 # Test seek functionality
1994 txt = b"Where's Bruce?"
1995 bloc = txt.find(b"Bruce")
1996 # Check seek on a file
1997 with zipfile.ZipFile(TESTFN, "w") as zipf:
1998 zipf.writestr("foo.txt", txt)
1999 with zipfile.ZipFile(TESTFN, "r") as zipf:
2000 with zipf.open("foo.txt", "r") as fp:
2001 fp.seek(bloc, os.SEEK_SET)
2002 self.assertEqual(fp.tell(), bloc)
2003 fp.seek(-bloc, os.SEEK_CUR)
2004 self.assertEqual(fp.tell(), 0)
2005 fp.seek(bloc, os.SEEK_CUR)
2006 self.assertEqual(fp.tell(), bloc)
2007 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2008 fp.seek(0, os.SEEK_END)
2009 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002010 fp.seek(0, os.SEEK_SET)
2011 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002012 # Check seek on memory file
2013 data = io.BytesIO()
2014 with zipfile.ZipFile(data, mode="w") as zipf:
2015 zipf.writestr("foo.txt", txt)
2016 with zipfile.ZipFile(data, mode="r") as zipf:
2017 with zipf.open("foo.txt", "r") as fp:
2018 fp.seek(bloc, os.SEEK_SET)
2019 self.assertEqual(fp.tell(), bloc)
2020 fp.seek(-bloc, os.SEEK_CUR)
2021 self.assertEqual(fp.tell(), 0)
2022 fp.seek(bloc, os.SEEK_CUR)
2023 self.assertEqual(fp.tell(), bloc)
2024 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2025 fp.seek(0, os.SEEK_END)
2026 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002027 fp.seek(0, os.SEEK_SET)
2028 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002029
Hai Shia3ec3ad2020-05-19 06:02:57 +08002030 @requires_bz2()
Berker Peksag2f1b8572019-09-12 17:13:44 +03002031 def test_decompress_without_3rd_party_library(self):
2032 data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
2033 zip_file = io.BytesIO(data)
2034 with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf:
2035 zf.writestr('a.txt', b'a')
2036 with mock.patch('zipfile.bz2', None):
2037 with zipfile.ZipFile(zip_file) as zf:
2038 self.assertRaises(RuntimeError, zf.extract, 'a.txt')
2039
Guido van Rossumd8faa362007-04-27 19:54:29 +00002040 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002041 unlink(TESTFN)
2042 unlink(TESTFN2)
2043
Thomas Wouterscf297e42007-02-23 15:07:44 +00002044
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002045class AbstractBadCrcTests:
2046 def test_testzip_with_bad_crc(self):
2047 """Tests that files with bad CRCs return their name from testzip."""
2048 zipdata = self.zip_with_bad_crc
2049
2050 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2051 # testzip returns the name of the first corrupt file, or None
2052 self.assertEqual('afile', zipf.testzip())
2053
2054 def test_read_with_bad_crc(self):
2055 """Tests that files with bad CRCs raise a BadZipFile exception when read."""
2056 zipdata = self.zip_with_bad_crc
2057
2058 # Using ZipFile.read()
2059 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2060 self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
2061
2062 # Using ZipExtFile.read()
2063 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2064 with zipf.open('afile', 'r') as corrupt_file:
2065 self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
2066
2067 # Same with small reads (in order to exercise the buffering logic)
2068 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2069 with zipf.open('afile', 'r') as corrupt_file:
2070 corrupt_file.MIN_READ_SIZE = 2
2071 with self.assertRaises(zipfile.BadZipFile):
2072 while corrupt_file.read(2):
2073 pass
2074
2075
2076class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2077 compression = zipfile.ZIP_STORED
2078 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002079 b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
2080 b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
2081 b'ilehello,AworldP'
2082 b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
2083 b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
2084 b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
2085 b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
2086 b'\0\0/\0\0\0\0\0')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002087
Hai Shia3ec3ad2020-05-19 06:02:57 +08002088@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002089class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2090 compression = zipfile.ZIP_DEFLATED
2091 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002092 b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
2093 b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2094 b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
2095 b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
2096 b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
2097 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
2098 b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
2099 b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002100
Hai Shia3ec3ad2020-05-19 06:02:57 +08002101@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002102class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2103 compression = zipfile.ZIP_BZIP2
2104 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002105 b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
2106 b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2107 b'ileBZh91AY&SY\xd4\xa8\xca'
2108 b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
2109 b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
2110 b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
2111 b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
2112 b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
2113 b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
2114 b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
2115 b'\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002116
Hai Shia3ec3ad2020-05-19 06:02:57 +08002117@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002118class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2119 compression = zipfile.ZIP_LZMA
2120 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002121 b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2122 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2123 b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
2124 b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
2125 b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2126 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
2127 b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
2128 b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
2129 b'\x00>\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002130
2131
Thomas Wouterscf297e42007-02-23 15:07:44 +00002132class DecryptionTests(unittest.TestCase):
Ezio Melotti35386712009-12-31 13:22:41 +00002133 """Check that ZIP decryption works. Since the library does not
2134 support encryption at the moment, we use a pre-generated encrypted
2135 ZIP file."""
Thomas Wouterscf297e42007-02-23 15:07:44 +00002136
2137 data = (
Christian Tismer59202e52013-10-21 03:59:23 +02002138 b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
2139 b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
2140 b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
2141 b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
2142 b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
2143 b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
2144 b'\x00\x00L\x00\x00\x00\x00\x00' )
Christian Heimesfdab48e2008-01-20 09:06:41 +00002145 data2 = (
Christian Tismer59202e52013-10-21 03:59:23 +02002146 b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
2147 b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
2148 b'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
2149 b'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
2150 b'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
2151 b'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
2152 b'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
2153 b'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
Thomas Wouterscf297e42007-02-23 15:07:44 +00002154
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002155 plain = b'zipfile.py encryption test'
Christian Heimesfdab48e2008-01-20 09:06:41 +00002156 plain2 = b'\x00'*512
Thomas Wouterscf297e42007-02-23 15:07:44 +00002157
2158 def setUp(self):
Ezio Melotti35386712009-12-31 13:22:41 +00002159 with open(TESTFN, "wb") as fp:
2160 fp.write(self.data)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002161 self.zip = zipfile.ZipFile(TESTFN, "r")
Ezio Melotti35386712009-12-31 13:22:41 +00002162 with open(TESTFN2, "wb") as fp:
2163 fp.write(self.data2)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002164 self.zip2 = zipfile.ZipFile(TESTFN2, "r")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002165
2166 def tearDown(self):
2167 self.zip.close()
2168 os.unlink(TESTFN)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002169 self.zip2.close()
2170 os.unlink(TESTFN2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002171
Ezio Melottiafd0d112009-07-15 17:17:17 +00002172 def test_no_password(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00002173 # Reading the encrypted file without password
2174 # must generate a RunTime exception
2175 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002176 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002177
Ezio Melottiafd0d112009-07-15 17:17:17 +00002178 def test_bad_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002179 self.zip.setpassword(b"perl")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002180 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002181 self.zip2.setpassword(b"perl")
2182 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Guido van Rossumd8faa362007-04-27 19:54:29 +00002183
Hai Shia3ec3ad2020-05-19 06:02:57 +08002184 @requires_zlib()
Ezio Melottiafd0d112009-07-15 17:17:17 +00002185 def test_good_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002186 self.zip.setpassword(b"python")
Ezio Melotti35386712009-12-31 13:22:41 +00002187 self.assertEqual(self.zip.read("test.txt"), self.plain)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002188 self.zip2.setpassword(b"12345")
Ezio Melotti35386712009-12-31 13:22:41 +00002189 self.assertEqual(self.zip2.read("zero"), self.plain2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002190
R. David Murray8d855d82010-12-21 21:53:37 +00002191 def test_unicode_password(self):
2192 self.assertRaises(TypeError, self.zip.setpassword, "unicode")
2193 self.assertRaises(TypeError, self.zip.read, "test.txt", "python")
2194 self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
2195 self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")
2196
Serhiy Storchaka5c32af72019-10-27 10:22:14 +02002197 def test_seek_tell(self):
2198 self.zip.setpassword(b"python")
2199 txt = self.plain
2200 test_word = b'encryption'
2201 bloc = txt.find(test_word)
2202 bloc_len = len(test_word)
2203 with self.zip.open("test.txt", "r") as fp:
2204 fp.seek(bloc, os.SEEK_SET)
2205 self.assertEqual(fp.tell(), bloc)
2206 fp.seek(-bloc, os.SEEK_CUR)
2207 self.assertEqual(fp.tell(), 0)
2208 fp.seek(bloc, os.SEEK_CUR)
2209 self.assertEqual(fp.tell(), bloc)
2210 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2211
2212 # Make sure that the second read after seeking back beyond
2213 # _readbuffer returns the same content (ie. rewind to the start of
2214 # the file to read forward to the required position).
2215 old_read_size = fp.MIN_READ_SIZE
2216 fp.MIN_READ_SIZE = 1
2217 fp._readbuffer = b''
2218 fp._offset = 0
2219 fp.seek(0, os.SEEK_SET)
2220 self.assertEqual(fp.tell(), 0)
2221 fp.seek(bloc, os.SEEK_CUR)
2222 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2223 fp.MIN_READ_SIZE = old_read_size
2224
2225 fp.seek(0, os.SEEK_END)
2226 self.assertEqual(fp.tell(), len(txt))
2227 fp.seek(0, os.SEEK_SET)
2228 self.assertEqual(fp.tell(), 0)
2229
2230 # Read the file completely to definitely call any eof integrity
2231 # checks (crc) and make sure they still pass.
2232 fp.read()
2233
2234
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002235class AbstractTestsWithRandomBinaryFiles:
2236 @classmethod
2237 def setUpClass(cls):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002238 datacount = randint(16, 64)*1024 + randint(1, 1024)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002239 cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
2240 for i in range(datacount))
Guido van Rossumd8faa362007-04-27 19:54:29 +00002241
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002242 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002243 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +00002244 with open(TESTFN, "wb") as fp:
2245 fp.write(self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002246
2247 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002248 unlink(TESTFN)
2249 unlink(TESTFN2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002250
Ezio Melottiafd0d112009-07-15 17:17:17 +00002251 def make_test_archive(self, f, compression):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002252 # Create the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002253 with zipfile.ZipFile(f, "w", compression) as zipfp:
2254 zipfp.write(TESTFN, "another.name")
2255 zipfp.write(TESTFN, TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002256
Ezio Melottiafd0d112009-07-15 17:17:17 +00002257 def zip_test(self, f, compression):
2258 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002259
2260 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002261 with zipfile.ZipFile(f, "r", compression) as zipfp:
2262 testdata = zipfp.read(TESTFN)
2263 self.assertEqual(len(testdata), len(self.data))
2264 self.assertEqual(testdata, self.data)
2265 self.assertEqual(zipfp.read("another.name"), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002266
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002267 def test_read(self):
2268 for f in get_files(self):
2269 self.zip_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002270
Ezio Melottiafd0d112009-07-15 17:17:17 +00002271 def zip_open_test(self, f, compression):
2272 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002273
2274 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002275 with zipfile.ZipFile(f, "r", compression) as zipfp:
2276 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002277 with zipfp.open(TESTFN) as zipopen1:
2278 while True:
2279 read_data = zipopen1.read(256)
2280 if not read_data:
2281 break
2282 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002283
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002284 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002285 with zipfp.open("another.name") as zipopen2:
2286 while True:
2287 read_data = zipopen2.read(256)
2288 if not read_data:
2289 break
2290 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002291
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002292 testdata1 = b''.join(zipdata1)
2293 self.assertEqual(len(testdata1), len(self.data))
2294 self.assertEqual(testdata1, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002295
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002296 testdata2 = b''.join(zipdata2)
Ezio Melotti35386712009-12-31 13:22:41 +00002297 self.assertEqual(len(testdata2), len(self.data))
2298 self.assertEqual(testdata2, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002299
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002300 def test_open(self):
2301 for f in get_files(self):
2302 self.zip_open_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002303
Ezio Melottiafd0d112009-07-15 17:17:17 +00002304 def zip_random_open_test(self, f, compression):
2305 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002306
2307 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002308 with zipfile.ZipFile(f, "r", compression) as zipfp:
2309 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002310 with zipfp.open(TESTFN) as zipopen1:
2311 while True:
2312 read_data = zipopen1.read(randint(1, 1024))
2313 if not read_data:
2314 break
2315 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002316
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002317 testdata = b''.join(zipdata1)
2318 self.assertEqual(len(testdata), len(self.data))
2319 self.assertEqual(testdata, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002320
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002321 def test_random_open(self):
2322 for f in get_files(self):
2323 self.zip_random_open_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002324
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00002325
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002326class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2327 unittest.TestCase):
2328 compression = zipfile.ZIP_STORED
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02002329
Hai Shia3ec3ad2020-05-19 06:02:57 +08002330@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002331class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2332 unittest.TestCase):
2333 compression = zipfile.ZIP_DEFLATED
2334
Hai Shia3ec3ad2020-05-19 06:02:57 +08002335@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002336class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2337 unittest.TestCase):
2338 compression = zipfile.ZIP_BZIP2
2339
Hai Shia3ec3ad2020-05-19 06:02:57 +08002340@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002341class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2342 unittest.TestCase):
2343 compression = zipfile.ZIP_LZMA
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002344
Ezio Melotti76430242009-07-11 18:28:48 +00002345
luzpaza5293b42017-11-05 07:37:50 -06002346# Provide the tell() method but not seek()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002347class Tellable:
2348 def __init__(self, fp):
2349 self.fp = fp
2350 self.offset = 0
2351
2352 def write(self, data):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002353 n = self.fp.write(data)
2354 self.offset += n
2355 return n
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002356
2357 def tell(self):
2358 return self.offset
2359
2360 def flush(self):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002361 self.fp.flush()
2362
2363class Unseekable:
2364 def __init__(self, fp):
2365 self.fp = fp
2366
2367 def write(self, data):
2368 return self.fp.write(data)
2369
2370 def flush(self):
2371 self.fp.flush()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002372
2373class UnseekableTests(unittest.TestCase):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002374 def test_writestr(self):
2375 for wrapper in (lambda f: f), Tellable, Unseekable:
2376 with self.subTest(wrapper=wrapper):
2377 f = io.BytesIO()
2378 f.write(b'abc')
2379 bf = io.BufferedWriter(f)
2380 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2381 zipfp.writestr('ones', b'111')
2382 zipfp.writestr('twos', b'222')
2383 self.assertEqual(f.getvalue()[:5], b'abcPK')
2384 with zipfile.ZipFile(f, mode='r') as zipf:
2385 with zipf.open('ones') as zopen:
2386 self.assertEqual(zopen.read(), b'111')
2387 with zipf.open('twos') as zopen:
2388 self.assertEqual(zopen.read(), b'222')
2389
2390 def test_write(self):
2391 for wrapper in (lambda f: f), Tellable, Unseekable:
2392 with self.subTest(wrapper=wrapper):
2393 f = io.BytesIO()
2394 f.write(b'abc')
2395 bf = io.BufferedWriter(f)
2396 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2397 self.addCleanup(unlink, TESTFN)
2398 with open(TESTFN, 'wb') as f2:
2399 f2.write(b'111')
2400 zipfp.write(TESTFN, 'ones')
2401 with open(TESTFN, 'wb') as f2:
2402 f2.write(b'222')
2403 zipfp.write(TESTFN, 'twos')
2404 self.assertEqual(f.getvalue()[:5], b'abcPK')
2405 with zipfile.ZipFile(f, mode='r') as zipf:
2406 with zipf.open('ones') as zopen:
2407 self.assertEqual(zopen.read(), b'111')
2408 with zipf.open('twos') as zopen:
2409 self.assertEqual(zopen.read(), b'222')
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002410
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002411 def test_open_write(self):
2412 for wrapper in (lambda f: f), Tellable, Unseekable:
2413 with self.subTest(wrapper=wrapper):
2414 f = io.BytesIO()
2415 f.write(b'abc')
2416 bf = io.BufferedWriter(f)
2417 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf:
2418 with zipf.open('ones', 'w') as zopen:
2419 zopen.write(b'111')
2420 with zipf.open('twos', 'w') as zopen:
2421 zopen.write(b'222')
2422 self.assertEqual(f.getvalue()[:5], b'abcPK')
2423 with zipfile.ZipFile(f) as zipf:
2424 self.assertEqual(zipf.read('ones'), b'111')
2425 self.assertEqual(zipf.read('twos'), b'222')
2426
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002427
Hai Shia3ec3ad2020-05-19 06:02:57 +08002428@requires_zlib()
Guido van Rossumd8faa362007-04-27 19:54:29 +00002429class TestsWithMultipleOpens(unittest.TestCase):
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002430 @classmethod
2431 def setUpClass(cls):
Victor Stinner87502dd2020-04-17 22:54:38 +02002432 cls.data1 = b'111' + randbytes(10000)
2433 cls.data2 = b'222' + randbytes(10000)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002434
2435 def make_test_archive(self, f):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002436 # Create the ZIP archive
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002437 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp:
2438 zipfp.writestr('ones', self.data1)
2439 zipfp.writestr('twos', self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002440
Ezio Melottiafd0d112009-07-15 17:17:17 +00002441 def test_same_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002442 # Verify that (when the ZipFile is in control of creating file objects)
2443 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002444 for f in get_files(self):
2445 self.make_test_archive(f)
2446 with zipfile.ZipFile(f, mode="r") as zipf:
2447 with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2:
2448 data1 = zopen1.read(500)
2449 data2 = zopen2.read(500)
2450 data1 += zopen1.read()
2451 data2 += zopen2.read()
2452 self.assertEqual(data1, data2)
2453 self.assertEqual(data1, self.data1)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002454
Ezio Melottiafd0d112009-07-15 17:17:17 +00002455 def test_different_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002456 # Verify that (when the ZipFile is in control of creating file objects)
2457 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002458 for f in get_files(self):
2459 self.make_test_archive(f)
2460 with zipfile.ZipFile(f, mode="r") as zipf:
2461 with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2:
2462 data1 = zopen1.read(500)
2463 data2 = zopen2.read(500)
2464 data1 += zopen1.read()
2465 data2 += zopen2.read()
2466 self.assertEqual(data1, self.data1)
2467 self.assertEqual(data2, self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002468
Ezio Melottiafd0d112009-07-15 17:17:17 +00002469 def test_interleaved(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002470 # Verify that (when the ZipFile is in control of creating file objects)
2471 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002472 for f in get_files(self):
2473 self.make_test_archive(f)
2474 with zipfile.ZipFile(f, mode="r") as zipf:
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002475 with zipf.open('ones') as zopen1:
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002476 data1 = zopen1.read(500)
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002477 with zipf.open('twos') as zopen2:
2478 data2 = zopen2.read(500)
2479 data1 += zopen1.read()
2480 data2 += zopen2.read()
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002481 self.assertEqual(data1, self.data1)
2482 self.assertEqual(data2, self.data2)
2483
2484 def test_read_after_close(self):
2485 for f in get_files(self):
2486 self.make_test_archive(f)
2487 with contextlib.ExitStack() as stack:
2488 with zipfile.ZipFile(f, 'r') as zipf:
2489 zopen1 = stack.enter_context(zipf.open('ones'))
2490 zopen2 = stack.enter_context(zipf.open('twos'))
Brian Curtin8fb9b862010-11-18 02:15:28 +00002491 data1 = zopen1.read(500)
2492 data2 = zopen2.read(500)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002493 data1 += zopen1.read()
2494 data2 += zopen2.read()
2495 self.assertEqual(data1, self.data1)
2496 self.assertEqual(data2, self.data2)
2497
2498 def test_read_after_write(self):
2499 for f in get_files(self):
2500 with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf:
2501 zipf.writestr('ones', self.data1)
2502 zipf.writestr('twos', self.data2)
2503 with zipf.open('ones') as zopen1:
2504 data1 = zopen1.read(500)
2505 self.assertEqual(data1, self.data1[:500])
2506 with zipfile.ZipFile(f, 'r') as zipf:
2507 data1 = zipf.read('ones')
2508 data2 = zipf.read('twos')
2509 self.assertEqual(data1, self.data1)
2510 self.assertEqual(data2, self.data2)
2511
2512 def test_write_after_read(self):
2513 for f in get_files(self):
2514 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf:
2515 zipf.writestr('ones', self.data1)
2516 with zipf.open('ones') as zopen1:
2517 zopen1.read(500)
2518 zipf.writestr('twos', self.data2)
2519 with zipfile.ZipFile(f, 'r') as zipf:
2520 data1 = zipf.read('ones')
2521 data2 = zipf.read('twos')
2522 self.assertEqual(data1, self.data1)
2523 self.assertEqual(data2, self.data2)
2524
2525 def test_many_opens(self):
2526 # Verify that read() and open() promptly close the file descriptor,
2527 # and don't rely on the garbage collector to free resources.
2528 self.make_test_archive(TESTFN2)
2529 with zipfile.ZipFile(TESTFN2, mode="r") as zipf:
2530 for x in range(100):
2531 zipf.read('ones')
2532 with zipf.open('ones') as zopen1:
2533 pass
2534 with open(os.devnull) as f:
2535 self.assertLess(f.fileno(), 100)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002536
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002537 def test_write_while_reading(self):
2538 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf:
2539 zipf.writestr('ones', self.data1)
2540 with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf:
2541 with zipf.open('ones', 'r') as r1:
2542 data1 = r1.read(500)
2543 with zipf.open('twos', 'w') as w1:
2544 w1.write(self.data2)
2545 data1 += r1.read()
2546 self.assertEqual(data1, self.data1)
2547 with zipfile.ZipFile(TESTFN2) as zipf:
2548 self.assertEqual(zipf.read('twos'), self.data2)
2549
Guido van Rossumd8faa362007-04-27 19:54:29 +00002550 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002551 unlink(TESTFN2)
2552
Guido van Rossumd8faa362007-04-27 19:54:29 +00002553
Martin v. Löwis59e47792009-01-24 14:10:07 +00002554class TestWithDirectory(unittest.TestCase):
2555 def setUp(self):
2556 os.mkdir(TESTFN2)
2557
Ezio Melottiafd0d112009-07-15 17:17:17 +00002558 def test_extract_dir(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002559 with zipfile.ZipFile(findfile("zipdir.zip")) as zipf:
2560 zipf.extractall(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002561 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2562 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2563 self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
2564
Ezio Melottiafd0d112009-07-15 17:17:17 +00002565 def test_bug_6050(self):
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002566 # Extraction should succeed if directories already exist
2567 os.mkdir(os.path.join(TESTFN2, "a"))
Ezio Melottiafd0d112009-07-15 17:17:17 +00002568 self.test_extract_dir()
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002569
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002570 def test_write_dir(self):
2571 dirpath = os.path.join(TESTFN2, "x")
2572 os.mkdir(dirpath)
2573 mode = os.stat(dirpath).st_mode & 0xFFFF
2574 with zipfile.ZipFile(TESTFN, "w") as zipf:
2575 zipf.write(dirpath)
2576 zinfo = zipf.filelist[0]
2577 self.assertTrue(zinfo.filename.endswith("/x/"))
2578 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2579 zipf.write(dirpath, "y")
2580 zinfo = zipf.filelist[1]
2581 self.assertTrue(zinfo.filename, "y/")
2582 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2583 with zipfile.ZipFile(TESTFN, "r") as zipf:
2584 zinfo = zipf.filelist[0]
2585 self.assertTrue(zinfo.filename.endswith("/x/"))
2586 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2587 zinfo = zipf.filelist[1]
2588 self.assertTrue(zinfo.filename, "y/")
2589 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2590 target = os.path.join(TESTFN2, "target")
2591 os.mkdir(target)
2592 zipf.extractall(target)
2593 self.assertTrue(os.path.isdir(os.path.join(target, "y")))
2594 self.assertEqual(len(os.listdir(target)), 2)
2595
2596 def test_writestr_dir(self):
Martin v. Löwis59e47792009-01-24 14:10:07 +00002597 os.mkdir(os.path.join(TESTFN2, "x"))
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002598 with zipfile.ZipFile(TESTFN, "w") as zipf:
2599 zipf.writestr("x/", b'')
2600 zinfo = zipf.filelist[0]
2601 self.assertEqual(zinfo.filename, "x/")
2602 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2603 with zipfile.ZipFile(TESTFN, "r") as zipf:
2604 zinfo = zipf.filelist[0]
2605 self.assertTrue(zinfo.filename.endswith("x/"))
2606 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2607 target = os.path.join(TESTFN2, "target")
2608 os.mkdir(target)
2609 zipf.extractall(target)
2610 self.assertTrue(os.path.isdir(os.path.join(target, "x")))
2611 self.assertEqual(os.listdir(target), ["x"])
Martin v. Löwis59e47792009-01-24 14:10:07 +00002612
2613 def tearDown(self):
Victor Stinner57004c62014-09-04 00:49:01 +02002614 rmtree(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002615 if os.path.exists(TESTFN):
Ezio Melotti76430242009-07-11 18:28:48 +00002616 unlink(TESTFN)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002617
Guido van Rossumd8faa362007-04-27 19:54:29 +00002618
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002619class ZipInfoTests(unittest.TestCase):
2620 def test_from_file(self):
2621 zi = zipfile.ZipInfo.from_file(__file__)
2622 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2623 self.assertFalse(zi.is_dir())
Serhiy Storchaka8606e952017-03-08 14:37:51 +02002624 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2625
2626 def test_from_file_pathlike(self):
2627 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2628 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2629 self.assertFalse(zi.is_dir())
2630 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2631
2632 def test_from_file_bytes(self):
2633 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2634 self.assertEqual(posixpath.basename(zi.filename), 'test')
2635 self.assertFalse(zi.is_dir())
2636 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2637
2638 def test_from_file_fileno(self):
2639 with open(__file__, 'rb') as f:
2640 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2641 self.assertEqual(posixpath.basename(zi.filename), 'test')
2642 self.assertFalse(zi.is_dir())
2643 self.assertEqual(zi.file_size, os.path.getsize(__file__))
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002644
2645 def test_from_dir(self):
2646 dirpath = os.path.dirname(os.path.abspath(__file__))
2647 zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
2648 self.assertEqual(zi.filename, 'stdlib_tests/')
2649 self.assertTrue(zi.is_dir())
2650 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
2651 self.assertEqual(zi.file_size, 0)
2652
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002653
2654class CommandLineTest(unittest.TestCase):
2655
2656 def zipfilecmd(self, *args, **kwargs):
2657 rc, out, err = script_helper.assert_python_ok('-m', 'zipfile', *args,
2658 **kwargs)
2659 return out.replace(os.linesep.encode(), b'\n')
2660
2661 def zipfilecmd_failure(self, *args):
2662 return script_helper.assert_python_failure('-m', 'zipfile', *args)
2663
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002664 def test_bad_use(self):
2665 rc, out, err = self.zipfilecmd_failure()
2666 self.assertEqual(out, b'')
2667 self.assertIn(b'usage', err.lower())
2668 self.assertIn(b'error', err.lower())
2669 self.assertIn(b'required', err.lower())
2670 rc, out, err = self.zipfilecmd_failure('-l', '')
2671 self.assertEqual(out, b'')
2672 self.assertNotEqual(err.strip(), b'')
2673
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002674 def test_test_command(self):
2675 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002676 for opt in '-t', '--test':
2677 out = self.zipfilecmd(opt, zip_name)
2678 self.assertEqual(out.rstrip(), b'Done testing')
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002679 zip_name = findfile('testtar.tar')
2680 rc, out, err = self.zipfilecmd_failure('-t', zip_name)
2681 self.assertEqual(out, b'')
2682
2683 def test_list_command(self):
2684 zip_name = findfile('zipdir.zip')
2685 t = io.StringIO()
2686 with zipfile.ZipFile(zip_name, 'r') as tf:
2687 tf.printdir(t)
2688 expected = t.getvalue().encode('ascii', 'backslashreplace')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002689 for opt in '-l', '--list':
2690 out = self.zipfilecmd(opt, zip_name,
2691 PYTHONIOENCODING='ascii:backslashreplace')
2692 self.assertEqual(out, expected)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002693
Hai Shia3ec3ad2020-05-19 06:02:57 +08002694 @requires_zlib()
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002695 def test_create_command(self):
2696 self.addCleanup(unlink, TESTFN)
2697 with open(TESTFN, 'w') as f:
2698 f.write('test 1')
2699 os.mkdir(TESTFNDIR)
2700 self.addCleanup(rmtree, TESTFNDIR)
2701 with open(os.path.join(TESTFNDIR, 'file.txt'), 'w') as f:
2702 f.write('test 2')
2703 files = [TESTFN, TESTFNDIR]
2704 namelist = [TESTFN, TESTFNDIR + '/', TESTFNDIR + '/file.txt']
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002705 for opt in '-c', '--create':
2706 try:
2707 out = self.zipfilecmd(opt, TESTFN2, *files)
2708 self.assertEqual(out, b'')
2709 with zipfile.ZipFile(TESTFN2) as zf:
2710 self.assertEqual(zf.namelist(), namelist)
2711 self.assertEqual(zf.read(namelist[0]), b'test 1')
2712 self.assertEqual(zf.read(namelist[2]), b'test 2')
2713 finally:
2714 unlink(TESTFN2)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002715
2716 def test_extract_command(self):
2717 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002718 for opt in '-e', '--extract':
2719 with temp_dir() as extdir:
2720 out = self.zipfilecmd(opt, zip_name, extdir)
2721 self.assertEqual(out, b'')
2722 with zipfile.ZipFile(zip_name) as zf:
2723 for zi in zf.infolist():
2724 path = os.path.join(extdir,
2725 zi.filename.replace('/', os.sep))
2726 if zi.is_dir():
2727 self.assertTrue(os.path.isdir(path))
2728 else:
2729 self.assertTrue(os.path.isfile(path))
2730 with open(path, 'rb') as f:
2731 self.assertEqual(f.read(), zf.read(zi))
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002732
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002733
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002734class TestExecutablePrependedZip(unittest.TestCase):
2735 """Test our ability to open zip files with an executable prepended."""
2736
2737 def setUp(self):
2738 self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
2739 self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
2740
2741 def _test_zip_works(self, name):
2742 # bpo28494 sanity check: ensure is_zipfile works on these.
2743 self.assertTrue(zipfile.is_zipfile(name),
2744 f'is_zipfile failed on {name}')
2745 # Ensure we can operate on these via ZipFile.
2746 with zipfile.ZipFile(name) as zipfp:
2747 for n in zipfp.namelist():
2748 data = zipfp.read(n)
2749 self.assertIn(b'FAVORITE_NUMBER', data)
2750
2751 def test_read_zip_with_exe_prepended(self):
2752 self._test_zip_works(self.exe_zip)
2753
2754 def test_read_zip64_with_exe_prepended(self):
2755 self._test_zip_works(self.exe_zip64)
2756
2757 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2758 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2759 'Test relies on #!/bin/bash working.')
2760 def test_execute_zip2(self):
2761 output = subprocess.check_output([self.exe_zip, sys.executable])
2762 self.assertIn(b'number in executable: 5', output)
2763
2764 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2765 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2766 'Test relies on #!/bin/bash working.')
2767 def test_execute_zip64(self):
2768 output = subprocess.check_output([self.exe_zip64, sys.executable])
2769 self.assertIn(b'number in executable: 5', output)
2770
2771
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002772# Poor man's technique to consume a (smallish) iterable.
2773consume = tuple
2774
2775
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002776# from jaraco.itertools 5.0
2777class jaraco:
2778 class itertools:
2779 class Counter:
2780 def __init__(self, i):
2781 self.count = 0
2782 self._orig_iter = iter(i)
2783
2784 def __iter__(self):
2785 return self
2786
2787 def __next__(self):
2788 result = next(self._orig_iter)
2789 self.count += 1
2790 return result
2791
2792
shireenraoa4e29912019-08-24 11:26:41 -04002793def add_dirs(zf):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002794 """
shireenraoa4e29912019-08-24 11:26:41 -04002795 Given a writable zip file zf, inject directory entries for
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002796 any directories implied by the presence of children.
2797 """
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002798 for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()):
shireenraoa4e29912019-08-24 11:26:41 -04002799 zf.writestr(name, b"")
2800 return zf
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002801
2802
shireenraoa4e29912019-08-24 11:26:41 -04002803def build_alpharep_fixture():
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002804 """
2805 Create a zip file with this structure:
2806
2807 .
2808 ├── a.txt
shireenraoa4e29912019-08-24 11:26:41 -04002809 ├── b
2810 │ ├── c.txt
2811 │ ├── d
2812 │ │ └── e.txt
2813 │ └── f.txt
2814 └── g
2815 └── h
2816 └── i.txt
2817
2818 This fixture has the following key characteristics:
2819
2820 - a file at the root (a)
2821 - a file two levels deep (b/d/e)
2822 - multiple files in a directory (b/c, b/f)
2823 - a directory containing only a directory (g/h)
2824
2825 "alpha" because it uses alphabet
2826 "rep" because it's a representative example
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002827 """
2828 data = io.BytesIO()
2829 zf = zipfile.ZipFile(data, "w")
2830 zf.writestr("a.txt", b"content of a")
2831 zf.writestr("b/c.txt", b"content of c")
2832 zf.writestr("b/d/e.txt", b"content of e")
shireenraoa4e29912019-08-24 11:26:41 -04002833 zf.writestr("b/f.txt", b"content of f")
2834 zf.writestr("g/h/i.txt", b"content of i")
2835 zf.filename = "alpharep.zip"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002836 return zf
2837
2838
2839class TestPath(unittest.TestCase):
2840 def setUp(self):
2841 self.fixtures = contextlib.ExitStack()
2842 self.addCleanup(self.fixtures.close)
2843
shireenraoa4e29912019-08-24 11:26:41 -04002844 def zipfile_alpharep(self):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002845 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002846 yield build_alpharep_fixture()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002847 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002848 yield add_dirs(build_alpharep_fixture())
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002849
2850 def zipfile_ondisk(self):
2851 tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
shireenraoa4e29912019-08-24 11:26:41 -04002852 for alpharep in self.zipfile_alpharep():
2853 buffer = alpharep.fp
2854 alpharep.close()
2855 path = tmpdir / alpharep.filename
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002856 with path.open("wb") as strm:
2857 strm.write(buffer.getvalue())
2858 yield path
2859
shireenraoa4e29912019-08-24 11:26:41 -04002860 def test_iterdir_and_types(self):
2861 for alpharep in self.zipfile_alpharep():
2862 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002863 assert root.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002864 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002865 assert a.is_file()
2866 assert b.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002867 assert g.is_dir()
2868 c, f, d = b.iterdir()
2869 assert c.is_file() and f.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002870 e, = d.iterdir()
2871 assert e.is_file()
shireenraoa4e29912019-08-24 11:26:41 -04002872 h, = g.iterdir()
2873 i, = h.iterdir()
2874 assert i.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002875
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002876 def test_subdir_is_dir(self):
2877 for alpharep in self.zipfile_alpharep():
2878 root = zipfile.Path(alpharep)
2879 assert (root / 'b').is_dir()
2880 assert (root / 'b/').is_dir()
2881 assert (root / 'g').is_dir()
2882 assert (root / 'g/').is_dir()
2883
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002884 def test_open(self):
shireenraoa4e29912019-08-24 11:26:41 -04002885 for alpharep in self.zipfile_alpharep():
2886 root = zipfile.Path(alpharep)
2887 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002888 with a.open() as strm:
2889 data = strm.read()
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002890 assert data == "content of a"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002891
2892 def test_read(self):
shireenraoa4e29912019-08-24 11:26:41 -04002893 for alpharep in self.zipfile_alpharep():
2894 root = zipfile.Path(alpharep)
2895 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002896 assert a.read_text() == "content of a"
2897 assert a.read_bytes() == b"content of a"
2898
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002899 def test_joinpath(self):
shireenraoa4e29912019-08-24 11:26:41 -04002900 for alpharep in self.zipfile_alpharep():
2901 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002902 a = root.joinpath("a")
2903 assert a.is_file()
2904 e = root.joinpath("b").joinpath("d").joinpath("e.txt")
2905 assert e.read_text() == "content of e"
2906
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002907 def test_traverse_truediv(self):
shireenraoa4e29912019-08-24 11:26:41 -04002908 for alpharep in self.zipfile_alpharep():
2909 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002910 a = root / "a"
2911 assert a.is_file()
2912 e = root / "b" / "d" / "e.txt"
2913 assert e.read_text() == "content of e"
2914
2915 def test_pathlike_construction(self):
2916 """
2917 zipfile.Path should be constructable from a path-like object
2918 """
2919 for zipfile_ondisk in self.zipfile_ondisk():
2920 pathlike = pathlib.Path(str(zipfile_ondisk))
2921 zipfile.Path(pathlike)
2922
2923 def test_traverse_pathlike(self):
shireenraoa4e29912019-08-24 11:26:41 -04002924 for alpharep in self.zipfile_alpharep():
2925 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002926 root / pathlib.Path("a")
2927
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002928 def test_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002929 for alpharep in self.zipfile_alpharep():
2930 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002931 assert (root / 'a').parent.at == ''
2932 assert (root / 'a' / 'b').parent.at == 'a/'
2933
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002934 def test_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002935 for alpharep in self.zipfile_alpharep():
2936 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002937 assert (root / 'b').parent.at == ''
2938 assert (root / 'b/').parent.at == ''
2939
2940 def test_missing_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002941 for alpharep in self.zipfile_alpharep():
2942 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002943 assert (root / 'missing dir/').parent.at == ''
2944
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002945 def test_mutability(self):
2946 """
2947 If the underlying zipfile is changed, the Path object should
2948 reflect that change.
2949 """
2950 for alpharep in self.zipfile_alpharep():
2951 root = zipfile.Path(alpharep)
2952 a, b, g = root.iterdir()
2953 alpharep.writestr('foo.txt', 'foo')
2954 alpharep.writestr('bar/baz.txt', 'baz')
2955 assert any(
2956 child.name == 'foo.txt'
2957 for child in root.iterdir())
2958 assert (root / 'foo.txt').read_text() == 'foo'
2959 baz, = (root / 'bar').iterdir()
2960 assert baz.read_text() == 'baz'
2961
2962 HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
2963
2964 def huge_zipfile(self):
2965 """Create a read-only zipfile with a huge number of entries entries."""
2966 strm = io.BytesIO()
2967 zf = zipfile.ZipFile(strm, "w")
2968 for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
2969 zf.writestr(entry, entry)
2970 zf.mode = 'r'
2971 return zf
2972
2973 def test_joinpath_constant_time(self):
2974 """
2975 Ensure joinpath on items in zipfile is linear time.
2976 """
2977 root = zipfile.Path(self.huge_zipfile())
2978 entries = jaraco.itertools.Counter(root.iterdir())
2979 for entry in entries:
2980 entry.joinpath('suffix')
2981 # Check the file iterated all items
2982 assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
2983
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002984 # @func_timeout.func_set_timeout(3)
2985 def test_implied_dirs_performance(self):
2986 data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
2987 zipfile.CompleteDirs._implied_dirs(data)
2988
shireenraoa4e29912019-08-24 11:26:41 -04002989
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00002990if __name__ == "__main__":
Brett Cannond5b4e1d2013-06-12 19:57:19 -04002991 unittest.main()