blob: 2851051425bf12f942d8640788970add98dfe80a [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. Smithb0d9ca922009-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
1859 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1860 zipf.comment = b"original comment that's longer"
1861 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1862 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1863 zipf.comment = b"shorter comment"
1864 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1865 self.assertEqual(zipf.comment, b"shorter comment")
1866
R David Murrayf50b38a2012-04-12 18:44:58 -04001867 def test_unicode_comment(self):
1868 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1869 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1870 with self.assertRaises(TypeError):
1871 zipf.comment = "this is an error"
1872
1873 def test_change_comment_in_empty_archive(self):
1874 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1875 self.assertFalse(zipf.filelist)
1876 zipf.comment = b"this is a comment"
1877 with zipfile.ZipFile(TESTFN, "r") as zipf:
1878 self.assertEqual(zipf.comment, b"this is a comment")
1879
1880 def test_change_comment_in_nonempty_archive(self):
1881 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1882 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1883 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1884 self.assertTrue(zipf.filelist)
1885 zipf.comment = b"this is a comment"
1886 with zipfile.ZipFile(TESTFN, "r") as zipf:
1887 self.assertEqual(zipf.comment, b"this is a comment")
1888
Georg Brandl268e4d42010-10-14 06:59:45 +00001889 def test_empty_zipfile(self):
1890 # Check that creating a file in 'w' or 'a' mode and closing without
1891 # adding any files to the archives creates a valid empty ZIP file
1892 zipf = zipfile.ZipFile(TESTFN, mode="w")
1893 zipf.close()
1894 try:
1895 zipf = zipfile.ZipFile(TESTFN, mode="r")
1896 except zipfile.BadZipFile:
1897 self.fail("Unable to create empty ZIP file in 'w' mode")
1898
1899 zipf = zipfile.ZipFile(TESTFN, mode="a")
1900 zipf.close()
1901 try:
1902 zipf = zipfile.ZipFile(TESTFN, mode="r")
1903 except:
1904 self.fail("Unable to create empty ZIP file in 'a' mode")
1905
1906 def test_open_empty_file(self):
1907 # Issue 1710703: Check that opening a file with less than 22 bytes
Georg Brandl4d540882010-10-28 06:42:33 +00001908 # raises a BadZipFile exception (rather than the previously unhelpful
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001909 # OSError)
Georg Brandl268e4d42010-10-14 06:59:45 +00001910 f = open(TESTFN, 'w')
1911 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001912 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r')
Georg Brandl268e4d42010-10-14 06:59:45 +00001913
Senthil Kumaran29fa9d42011-10-20 01:46:00 +08001914 def test_create_zipinfo_before_1980(self):
1915 self.assertRaises(ValueError,
1916 zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0))
1917
Mickaël Schoentgen992347d2019-09-09 15:08:54 +02001918 def test_create_empty_zipinfo_repr(self):
1919 """Before bpo-26185, repr() on empty ZipInfo object was failing."""
1920 zi = zipfile.ZipInfo(filename="empty")
1921 self.assertEqual(repr(zi), "<ZipInfo filename='empty' file_size=0>")
1922
1923 def test_create_empty_zipinfo_default_attributes(self):
1924 """Ensure all required attributes are set."""
1925 zi = zipfile.ZipInfo()
1926 self.assertEqual(zi.orig_filename, "NoName")
1927 self.assertEqual(zi.filename, "NoName")
1928 self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0))
1929 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
1930 self.assertEqual(zi.comment, b"")
1931 self.assertEqual(zi.extra, b"")
1932 self.assertIn(zi.create_system, (0, 3))
1933 self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION)
1934 self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION)
1935 self.assertEqual(zi.reserved, 0)
1936 self.assertEqual(zi.flag_bits, 0)
1937 self.assertEqual(zi.volume, 0)
1938 self.assertEqual(zi.internal_attr, 0)
1939 self.assertEqual(zi.external_attr, 0)
1940
1941 # Before bpo-26185, both were missing
1942 self.assertEqual(zi.file_size, 0)
1943 self.assertEqual(zi.compress_size, 0)
1944
Gregory P. Smith0af8a862014-05-29 23:42:14 -07001945 def test_zipfile_with_short_extra_field(self):
1946 """If an extra field in the header is less than 4 bytes, skip it."""
1947 zipdata = (
1948 b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e'
1949 b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab'
1950 b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00'
1951 b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00'
1952 b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00'
1953 b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00'
1954 b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00'
1955 )
1956 with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf:
1957 # testzip returns the name of the first corrupt file, or None
1958 self.assertIsNone(zipf.testzip())
1959
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001960 def test_open_conflicting_handles(self):
1961 # It's only possible to open one writable file handle at a time
1962 msg1 = b"It's fun to charter an accountant!"
1963 msg2 = b"And sail the wide accountant sea"
1964 msg3 = b"To find, explore the funds offshore"
1965 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf:
1966 with zipf.open('foo', mode='w') as w2:
1967 w2.write(msg1)
1968 with zipf.open('bar', mode='w') as w1:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001969 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001970 zipf.open('handle', mode='w')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001971 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001972 zipf.open('foo', mode='r')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001973 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001974 zipf.writestr('str', 'abcde')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001975 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001976 zipf.write(__file__, 'file')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001977 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001978 zipf.close()
1979 w1.write(msg2)
1980 with zipf.open('baz', mode='w') as w2:
1981 w2.write(msg3)
1982
1983 with zipfile.ZipFile(TESTFN2, 'r') as zipf:
1984 self.assertEqual(zipf.read('foo'), msg1)
1985 self.assertEqual(zipf.read('bar'), msg2)
1986 self.assertEqual(zipf.read('baz'), msg3)
1987 self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz'])
1988
John Jolly066df4f2018-01-30 01:51:35 -07001989 def test_seek_tell(self):
1990 # Test seek functionality
1991 txt = b"Where's Bruce?"
1992 bloc = txt.find(b"Bruce")
1993 # Check seek on a file
1994 with zipfile.ZipFile(TESTFN, "w") as zipf:
1995 zipf.writestr("foo.txt", txt)
1996 with zipfile.ZipFile(TESTFN, "r") as zipf:
1997 with zipf.open("foo.txt", "r") as fp:
1998 fp.seek(bloc, os.SEEK_SET)
1999 self.assertEqual(fp.tell(), bloc)
2000 fp.seek(-bloc, os.SEEK_CUR)
2001 self.assertEqual(fp.tell(), 0)
2002 fp.seek(bloc, os.SEEK_CUR)
2003 self.assertEqual(fp.tell(), bloc)
2004 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2005 fp.seek(0, os.SEEK_END)
2006 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002007 fp.seek(0, os.SEEK_SET)
2008 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002009 # Check seek on memory file
2010 data = io.BytesIO()
2011 with zipfile.ZipFile(data, mode="w") as zipf:
2012 zipf.writestr("foo.txt", txt)
2013 with zipfile.ZipFile(data, mode="r") as zipf:
2014 with zipf.open("foo.txt", "r") as fp:
2015 fp.seek(bloc, os.SEEK_SET)
2016 self.assertEqual(fp.tell(), bloc)
2017 fp.seek(-bloc, os.SEEK_CUR)
2018 self.assertEqual(fp.tell(), 0)
2019 fp.seek(bloc, os.SEEK_CUR)
2020 self.assertEqual(fp.tell(), bloc)
2021 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2022 fp.seek(0, os.SEEK_END)
2023 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002024 fp.seek(0, os.SEEK_SET)
2025 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002026
Hai Shia3ec3ad2020-05-19 06:02:57 +08002027 @requires_bz2()
Berker Peksag2f1b8572019-09-12 17:13:44 +03002028 def test_decompress_without_3rd_party_library(self):
2029 data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
2030 zip_file = io.BytesIO(data)
2031 with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf:
2032 zf.writestr('a.txt', b'a')
2033 with mock.patch('zipfile.bz2', None):
2034 with zipfile.ZipFile(zip_file) as zf:
2035 self.assertRaises(RuntimeError, zf.extract, 'a.txt')
2036
Guido van Rossumd8faa362007-04-27 19:54:29 +00002037 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002038 unlink(TESTFN)
2039 unlink(TESTFN2)
2040
Thomas Wouterscf297e42007-02-23 15:07:44 +00002041
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002042class AbstractBadCrcTests:
2043 def test_testzip_with_bad_crc(self):
2044 """Tests that files with bad CRCs return their name from testzip."""
2045 zipdata = self.zip_with_bad_crc
2046
2047 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2048 # testzip returns the name of the first corrupt file, or None
2049 self.assertEqual('afile', zipf.testzip())
2050
2051 def test_read_with_bad_crc(self):
2052 """Tests that files with bad CRCs raise a BadZipFile exception when read."""
2053 zipdata = self.zip_with_bad_crc
2054
2055 # Using ZipFile.read()
2056 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2057 self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
2058
2059 # Using ZipExtFile.read()
2060 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2061 with zipf.open('afile', 'r') as corrupt_file:
2062 self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
2063
2064 # Same with small reads (in order to exercise the buffering logic)
2065 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2066 with zipf.open('afile', 'r') as corrupt_file:
2067 corrupt_file.MIN_READ_SIZE = 2
2068 with self.assertRaises(zipfile.BadZipFile):
2069 while corrupt_file.read(2):
2070 pass
2071
2072
2073class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2074 compression = zipfile.ZIP_STORED
2075 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002076 b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
2077 b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
2078 b'ilehello,AworldP'
2079 b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
2080 b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
2081 b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
2082 b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
2083 b'\0\0/\0\0\0\0\0')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002084
Hai Shia3ec3ad2020-05-19 06:02:57 +08002085@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002086class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2087 compression = zipfile.ZIP_DEFLATED
2088 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002089 b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
2090 b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2091 b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
2092 b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
2093 b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
2094 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
2095 b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
2096 b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002097
Hai Shia3ec3ad2020-05-19 06:02:57 +08002098@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002099class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2100 compression = zipfile.ZIP_BZIP2
2101 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002102 b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
2103 b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2104 b'ileBZh91AY&SY\xd4\xa8\xca'
2105 b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
2106 b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
2107 b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
2108 b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
2109 b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
2110 b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
2111 b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
2112 b'\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002113
Hai Shia3ec3ad2020-05-19 06:02:57 +08002114@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002115class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2116 compression = zipfile.ZIP_LZMA
2117 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002118 b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2119 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2120 b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
2121 b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
2122 b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2123 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
2124 b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
2125 b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
2126 b'\x00>\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002127
2128
Thomas Wouterscf297e42007-02-23 15:07:44 +00002129class DecryptionTests(unittest.TestCase):
Ezio Melotti35386712009-12-31 13:22:41 +00002130 """Check that ZIP decryption works. Since the library does not
2131 support encryption at the moment, we use a pre-generated encrypted
2132 ZIP file."""
Thomas Wouterscf297e42007-02-23 15:07:44 +00002133
2134 data = (
Christian Tismer59202e52013-10-21 03:59:23 +02002135 b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
2136 b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
2137 b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
2138 b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
2139 b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
2140 b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
2141 b'\x00\x00L\x00\x00\x00\x00\x00' )
Christian Heimesfdab48e2008-01-20 09:06:41 +00002142 data2 = (
Christian Tismer59202e52013-10-21 03:59:23 +02002143 b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
2144 b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
2145 b'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
2146 b'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
2147 b'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
2148 b'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
2149 b'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
2150 b'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
Thomas Wouterscf297e42007-02-23 15:07:44 +00002151
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002152 plain = b'zipfile.py encryption test'
Christian Heimesfdab48e2008-01-20 09:06:41 +00002153 plain2 = b'\x00'*512
Thomas Wouterscf297e42007-02-23 15:07:44 +00002154
2155 def setUp(self):
Ezio Melotti35386712009-12-31 13:22:41 +00002156 with open(TESTFN, "wb") as fp:
2157 fp.write(self.data)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002158 self.zip = zipfile.ZipFile(TESTFN, "r")
Ezio Melotti35386712009-12-31 13:22:41 +00002159 with open(TESTFN2, "wb") as fp:
2160 fp.write(self.data2)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002161 self.zip2 = zipfile.ZipFile(TESTFN2, "r")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002162
2163 def tearDown(self):
2164 self.zip.close()
2165 os.unlink(TESTFN)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002166 self.zip2.close()
2167 os.unlink(TESTFN2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002168
Ezio Melottiafd0d112009-07-15 17:17:17 +00002169 def test_no_password(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00002170 # Reading the encrypted file without password
2171 # must generate a RunTime exception
2172 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002173 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002174
Ezio Melottiafd0d112009-07-15 17:17:17 +00002175 def test_bad_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002176 self.zip.setpassword(b"perl")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002177 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002178 self.zip2.setpassword(b"perl")
2179 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Guido van Rossumd8faa362007-04-27 19:54:29 +00002180
Hai Shia3ec3ad2020-05-19 06:02:57 +08002181 @requires_zlib()
Ezio Melottiafd0d112009-07-15 17:17:17 +00002182 def test_good_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002183 self.zip.setpassword(b"python")
Ezio Melotti35386712009-12-31 13:22:41 +00002184 self.assertEqual(self.zip.read("test.txt"), self.plain)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002185 self.zip2.setpassword(b"12345")
Ezio Melotti35386712009-12-31 13:22:41 +00002186 self.assertEqual(self.zip2.read("zero"), self.plain2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002187
R. David Murray8d855d82010-12-21 21:53:37 +00002188 def test_unicode_password(self):
2189 self.assertRaises(TypeError, self.zip.setpassword, "unicode")
2190 self.assertRaises(TypeError, self.zip.read, "test.txt", "python")
2191 self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
2192 self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")
2193
Serhiy Storchaka5c32af72019-10-27 10:22:14 +02002194 def test_seek_tell(self):
2195 self.zip.setpassword(b"python")
2196 txt = self.plain
2197 test_word = b'encryption'
2198 bloc = txt.find(test_word)
2199 bloc_len = len(test_word)
2200 with self.zip.open("test.txt", "r") as fp:
2201 fp.seek(bloc, os.SEEK_SET)
2202 self.assertEqual(fp.tell(), bloc)
2203 fp.seek(-bloc, os.SEEK_CUR)
2204 self.assertEqual(fp.tell(), 0)
2205 fp.seek(bloc, os.SEEK_CUR)
2206 self.assertEqual(fp.tell(), bloc)
2207 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2208
2209 # Make sure that the second read after seeking back beyond
2210 # _readbuffer returns the same content (ie. rewind to the start of
2211 # the file to read forward to the required position).
2212 old_read_size = fp.MIN_READ_SIZE
2213 fp.MIN_READ_SIZE = 1
2214 fp._readbuffer = b''
2215 fp._offset = 0
2216 fp.seek(0, os.SEEK_SET)
2217 self.assertEqual(fp.tell(), 0)
2218 fp.seek(bloc, os.SEEK_CUR)
2219 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2220 fp.MIN_READ_SIZE = old_read_size
2221
2222 fp.seek(0, os.SEEK_END)
2223 self.assertEqual(fp.tell(), len(txt))
2224 fp.seek(0, os.SEEK_SET)
2225 self.assertEqual(fp.tell(), 0)
2226
2227 # Read the file completely to definitely call any eof integrity
2228 # checks (crc) and make sure they still pass.
2229 fp.read()
2230
2231
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002232class AbstractTestsWithRandomBinaryFiles:
2233 @classmethod
2234 def setUpClass(cls):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002235 datacount = randint(16, 64)*1024 + randint(1, 1024)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002236 cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
2237 for i in range(datacount))
Guido van Rossumd8faa362007-04-27 19:54:29 +00002238
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002239 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002240 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +00002241 with open(TESTFN, "wb") as fp:
2242 fp.write(self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002243
2244 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002245 unlink(TESTFN)
2246 unlink(TESTFN2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002247
Ezio Melottiafd0d112009-07-15 17:17:17 +00002248 def make_test_archive(self, f, compression):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002249 # Create the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002250 with zipfile.ZipFile(f, "w", compression) as zipfp:
2251 zipfp.write(TESTFN, "another.name")
2252 zipfp.write(TESTFN, TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002253
Ezio Melottiafd0d112009-07-15 17:17:17 +00002254 def zip_test(self, f, compression):
2255 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002256
2257 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002258 with zipfile.ZipFile(f, "r", compression) as zipfp:
2259 testdata = zipfp.read(TESTFN)
2260 self.assertEqual(len(testdata), len(self.data))
2261 self.assertEqual(testdata, self.data)
2262 self.assertEqual(zipfp.read("another.name"), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002263
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002264 def test_read(self):
2265 for f in get_files(self):
2266 self.zip_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002267
Ezio Melottiafd0d112009-07-15 17:17:17 +00002268 def zip_open_test(self, f, compression):
2269 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002270
2271 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002272 with zipfile.ZipFile(f, "r", compression) as zipfp:
2273 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002274 with zipfp.open(TESTFN) as zipopen1:
2275 while True:
2276 read_data = zipopen1.read(256)
2277 if not read_data:
2278 break
2279 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002280
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002281 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002282 with zipfp.open("another.name") as zipopen2:
2283 while True:
2284 read_data = zipopen2.read(256)
2285 if not read_data:
2286 break
2287 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002288
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002289 testdata1 = b''.join(zipdata1)
2290 self.assertEqual(len(testdata1), len(self.data))
2291 self.assertEqual(testdata1, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002292
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002293 testdata2 = b''.join(zipdata2)
Ezio Melotti35386712009-12-31 13:22:41 +00002294 self.assertEqual(len(testdata2), len(self.data))
2295 self.assertEqual(testdata2, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002296
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002297 def test_open(self):
2298 for f in get_files(self):
2299 self.zip_open_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002300
Ezio Melottiafd0d112009-07-15 17:17:17 +00002301 def zip_random_open_test(self, f, compression):
2302 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002303
2304 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002305 with zipfile.ZipFile(f, "r", compression) as zipfp:
2306 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002307 with zipfp.open(TESTFN) as zipopen1:
2308 while True:
2309 read_data = zipopen1.read(randint(1, 1024))
2310 if not read_data:
2311 break
2312 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002313
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002314 testdata = b''.join(zipdata1)
2315 self.assertEqual(len(testdata), len(self.data))
2316 self.assertEqual(testdata, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002317
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002318 def test_random_open(self):
2319 for f in get_files(self):
2320 self.zip_random_open_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002321
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00002322
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002323class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2324 unittest.TestCase):
2325 compression = zipfile.ZIP_STORED
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02002326
Hai Shia3ec3ad2020-05-19 06:02:57 +08002327@requires_zlib()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002328class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2329 unittest.TestCase):
2330 compression = zipfile.ZIP_DEFLATED
2331
Hai Shia3ec3ad2020-05-19 06:02:57 +08002332@requires_bz2()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002333class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2334 unittest.TestCase):
2335 compression = zipfile.ZIP_BZIP2
2336
Hai Shia3ec3ad2020-05-19 06:02:57 +08002337@requires_lzma()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002338class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2339 unittest.TestCase):
2340 compression = zipfile.ZIP_LZMA
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002341
Ezio Melotti76430242009-07-11 18:28:48 +00002342
luzpaza5293b42017-11-05 07:37:50 -06002343# Provide the tell() method but not seek()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002344class Tellable:
2345 def __init__(self, fp):
2346 self.fp = fp
2347 self.offset = 0
2348
2349 def write(self, data):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002350 n = self.fp.write(data)
2351 self.offset += n
2352 return n
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002353
2354 def tell(self):
2355 return self.offset
2356
2357 def flush(self):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002358 self.fp.flush()
2359
2360class Unseekable:
2361 def __init__(self, fp):
2362 self.fp = fp
2363
2364 def write(self, data):
2365 return self.fp.write(data)
2366
2367 def flush(self):
2368 self.fp.flush()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002369
2370class UnseekableTests(unittest.TestCase):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002371 def test_writestr(self):
2372 for wrapper in (lambda f: f), Tellable, Unseekable:
2373 with self.subTest(wrapper=wrapper):
2374 f = io.BytesIO()
2375 f.write(b'abc')
2376 bf = io.BufferedWriter(f)
2377 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2378 zipfp.writestr('ones', b'111')
2379 zipfp.writestr('twos', b'222')
2380 self.assertEqual(f.getvalue()[:5], b'abcPK')
2381 with zipfile.ZipFile(f, mode='r') as zipf:
2382 with zipf.open('ones') as zopen:
2383 self.assertEqual(zopen.read(), b'111')
2384 with zipf.open('twos') as zopen:
2385 self.assertEqual(zopen.read(), b'222')
2386
2387 def test_write(self):
2388 for wrapper in (lambda f: f), Tellable, Unseekable:
2389 with self.subTest(wrapper=wrapper):
2390 f = io.BytesIO()
2391 f.write(b'abc')
2392 bf = io.BufferedWriter(f)
2393 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2394 self.addCleanup(unlink, TESTFN)
2395 with open(TESTFN, 'wb') as f2:
2396 f2.write(b'111')
2397 zipfp.write(TESTFN, 'ones')
2398 with open(TESTFN, 'wb') as f2:
2399 f2.write(b'222')
2400 zipfp.write(TESTFN, 'twos')
2401 self.assertEqual(f.getvalue()[:5], b'abcPK')
2402 with zipfile.ZipFile(f, mode='r') as zipf:
2403 with zipf.open('ones') as zopen:
2404 self.assertEqual(zopen.read(), b'111')
2405 with zipf.open('twos') as zopen:
2406 self.assertEqual(zopen.read(), b'222')
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002407
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002408 def test_open_write(self):
2409 for wrapper in (lambda f: f), Tellable, Unseekable:
2410 with self.subTest(wrapper=wrapper):
2411 f = io.BytesIO()
2412 f.write(b'abc')
2413 bf = io.BufferedWriter(f)
2414 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf:
2415 with zipf.open('ones', 'w') as zopen:
2416 zopen.write(b'111')
2417 with zipf.open('twos', 'w') as zopen:
2418 zopen.write(b'222')
2419 self.assertEqual(f.getvalue()[:5], b'abcPK')
2420 with zipfile.ZipFile(f) as zipf:
2421 self.assertEqual(zipf.read('ones'), b'111')
2422 self.assertEqual(zipf.read('twos'), b'222')
2423
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002424
Hai Shia3ec3ad2020-05-19 06:02:57 +08002425@requires_zlib()
Guido van Rossumd8faa362007-04-27 19:54:29 +00002426class TestsWithMultipleOpens(unittest.TestCase):
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002427 @classmethod
2428 def setUpClass(cls):
Victor Stinner87502dd2020-04-17 22:54:38 +02002429 cls.data1 = b'111' + randbytes(10000)
2430 cls.data2 = b'222' + randbytes(10000)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002431
2432 def make_test_archive(self, f):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002433 # Create the ZIP archive
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002434 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp:
2435 zipfp.writestr('ones', self.data1)
2436 zipfp.writestr('twos', self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002437
Ezio Melottiafd0d112009-07-15 17:17:17 +00002438 def test_same_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002439 # Verify that (when the ZipFile is in control of creating file objects)
2440 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002441 for f in get_files(self):
2442 self.make_test_archive(f)
2443 with zipfile.ZipFile(f, mode="r") as zipf:
2444 with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2:
2445 data1 = zopen1.read(500)
2446 data2 = zopen2.read(500)
2447 data1 += zopen1.read()
2448 data2 += zopen2.read()
2449 self.assertEqual(data1, data2)
2450 self.assertEqual(data1, self.data1)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002451
Ezio Melottiafd0d112009-07-15 17:17:17 +00002452 def test_different_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002453 # Verify that (when the ZipFile is in control of creating file objects)
2454 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002455 for f in get_files(self):
2456 self.make_test_archive(f)
2457 with zipfile.ZipFile(f, mode="r") as zipf:
2458 with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2:
2459 data1 = zopen1.read(500)
2460 data2 = zopen2.read(500)
2461 data1 += zopen1.read()
2462 data2 += zopen2.read()
2463 self.assertEqual(data1, self.data1)
2464 self.assertEqual(data2, self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002465
Ezio Melottiafd0d112009-07-15 17:17:17 +00002466 def test_interleaved(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002467 # Verify that (when the ZipFile is in control of creating file objects)
2468 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002469 for f in get_files(self):
2470 self.make_test_archive(f)
2471 with zipfile.ZipFile(f, mode="r") as zipf:
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002472 with zipf.open('ones') as zopen1:
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002473 data1 = zopen1.read(500)
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002474 with zipf.open('twos') as zopen2:
2475 data2 = zopen2.read(500)
2476 data1 += zopen1.read()
2477 data2 += zopen2.read()
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002478 self.assertEqual(data1, self.data1)
2479 self.assertEqual(data2, self.data2)
2480
2481 def test_read_after_close(self):
2482 for f in get_files(self):
2483 self.make_test_archive(f)
2484 with contextlib.ExitStack() as stack:
2485 with zipfile.ZipFile(f, 'r') as zipf:
2486 zopen1 = stack.enter_context(zipf.open('ones'))
2487 zopen2 = stack.enter_context(zipf.open('twos'))
Brian Curtin8fb9b862010-11-18 02:15:28 +00002488 data1 = zopen1.read(500)
2489 data2 = zopen2.read(500)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002490 data1 += zopen1.read()
2491 data2 += zopen2.read()
2492 self.assertEqual(data1, self.data1)
2493 self.assertEqual(data2, self.data2)
2494
2495 def test_read_after_write(self):
2496 for f in get_files(self):
2497 with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf:
2498 zipf.writestr('ones', self.data1)
2499 zipf.writestr('twos', self.data2)
2500 with zipf.open('ones') as zopen1:
2501 data1 = zopen1.read(500)
2502 self.assertEqual(data1, self.data1[:500])
2503 with zipfile.ZipFile(f, 'r') as zipf:
2504 data1 = zipf.read('ones')
2505 data2 = zipf.read('twos')
2506 self.assertEqual(data1, self.data1)
2507 self.assertEqual(data2, self.data2)
2508
2509 def test_write_after_read(self):
2510 for f in get_files(self):
2511 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf:
2512 zipf.writestr('ones', self.data1)
2513 with zipf.open('ones') as zopen1:
2514 zopen1.read(500)
2515 zipf.writestr('twos', self.data2)
2516 with zipfile.ZipFile(f, 'r') as zipf:
2517 data1 = zipf.read('ones')
2518 data2 = zipf.read('twos')
2519 self.assertEqual(data1, self.data1)
2520 self.assertEqual(data2, self.data2)
2521
2522 def test_many_opens(self):
2523 # Verify that read() and open() promptly close the file descriptor,
2524 # and don't rely on the garbage collector to free resources.
2525 self.make_test_archive(TESTFN2)
2526 with zipfile.ZipFile(TESTFN2, mode="r") as zipf:
2527 for x in range(100):
2528 zipf.read('ones')
2529 with zipf.open('ones') as zopen1:
2530 pass
2531 with open(os.devnull) as f:
2532 self.assertLess(f.fileno(), 100)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002533
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002534 def test_write_while_reading(self):
2535 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf:
2536 zipf.writestr('ones', self.data1)
2537 with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf:
2538 with zipf.open('ones', 'r') as r1:
2539 data1 = r1.read(500)
2540 with zipf.open('twos', 'w') as w1:
2541 w1.write(self.data2)
2542 data1 += r1.read()
2543 self.assertEqual(data1, self.data1)
2544 with zipfile.ZipFile(TESTFN2) as zipf:
2545 self.assertEqual(zipf.read('twos'), self.data2)
2546
Guido van Rossumd8faa362007-04-27 19:54:29 +00002547 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002548 unlink(TESTFN2)
2549
Guido van Rossumd8faa362007-04-27 19:54:29 +00002550
Martin v. Löwis59e47792009-01-24 14:10:07 +00002551class TestWithDirectory(unittest.TestCase):
2552 def setUp(self):
2553 os.mkdir(TESTFN2)
2554
Ezio Melottiafd0d112009-07-15 17:17:17 +00002555 def test_extract_dir(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002556 with zipfile.ZipFile(findfile("zipdir.zip")) as zipf:
2557 zipf.extractall(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002558 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2559 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2560 self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
2561
Ezio Melottiafd0d112009-07-15 17:17:17 +00002562 def test_bug_6050(self):
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002563 # Extraction should succeed if directories already exist
2564 os.mkdir(os.path.join(TESTFN2, "a"))
Ezio Melottiafd0d112009-07-15 17:17:17 +00002565 self.test_extract_dir()
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002566
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002567 def test_write_dir(self):
2568 dirpath = os.path.join(TESTFN2, "x")
2569 os.mkdir(dirpath)
2570 mode = os.stat(dirpath).st_mode & 0xFFFF
2571 with zipfile.ZipFile(TESTFN, "w") as zipf:
2572 zipf.write(dirpath)
2573 zinfo = zipf.filelist[0]
2574 self.assertTrue(zinfo.filename.endswith("/x/"))
2575 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2576 zipf.write(dirpath, "y")
2577 zinfo = zipf.filelist[1]
2578 self.assertTrue(zinfo.filename, "y/")
2579 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2580 with zipfile.ZipFile(TESTFN, "r") as zipf:
2581 zinfo = zipf.filelist[0]
2582 self.assertTrue(zinfo.filename.endswith("/x/"))
2583 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2584 zinfo = zipf.filelist[1]
2585 self.assertTrue(zinfo.filename, "y/")
2586 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2587 target = os.path.join(TESTFN2, "target")
2588 os.mkdir(target)
2589 zipf.extractall(target)
2590 self.assertTrue(os.path.isdir(os.path.join(target, "y")))
2591 self.assertEqual(len(os.listdir(target)), 2)
2592
2593 def test_writestr_dir(self):
Martin v. Löwis59e47792009-01-24 14:10:07 +00002594 os.mkdir(os.path.join(TESTFN2, "x"))
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002595 with zipfile.ZipFile(TESTFN, "w") as zipf:
2596 zipf.writestr("x/", b'')
2597 zinfo = zipf.filelist[0]
2598 self.assertEqual(zinfo.filename, "x/")
2599 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2600 with zipfile.ZipFile(TESTFN, "r") as zipf:
2601 zinfo = zipf.filelist[0]
2602 self.assertTrue(zinfo.filename.endswith("x/"))
2603 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2604 target = os.path.join(TESTFN2, "target")
2605 os.mkdir(target)
2606 zipf.extractall(target)
2607 self.assertTrue(os.path.isdir(os.path.join(target, "x")))
2608 self.assertEqual(os.listdir(target), ["x"])
Martin v. Löwis59e47792009-01-24 14:10:07 +00002609
2610 def tearDown(self):
Victor Stinner57004c62014-09-04 00:49:01 +02002611 rmtree(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002612 if os.path.exists(TESTFN):
Ezio Melotti76430242009-07-11 18:28:48 +00002613 unlink(TESTFN)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002614
Guido van Rossumd8faa362007-04-27 19:54:29 +00002615
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002616class ZipInfoTests(unittest.TestCase):
2617 def test_from_file(self):
2618 zi = zipfile.ZipInfo.from_file(__file__)
2619 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2620 self.assertFalse(zi.is_dir())
Serhiy Storchaka8606e952017-03-08 14:37:51 +02002621 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2622
2623 def test_from_file_pathlike(self):
2624 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2625 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2626 self.assertFalse(zi.is_dir())
2627 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2628
2629 def test_from_file_bytes(self):
2630 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2631 self.assertEqual(posixpath.basename(zi.filename), 'test')
2632 self.assertFalse(zi.is_dir())
2633 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2634
2635 def test_from_file_fileno(self):
2636 with open(__file__, 'rb') as f:
2637 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2638 self.assertEqual(posixpath.basename(zi.filename), 'test')
2639 self.assertFalse(zi.is_dir())
2640 self.assertEqual(zi.file_size, os.path.getsize(__file__))
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002641
2642 def test_from_dir(self):
2643 dirpath = os.path.dirname(os.path.abspath(__file__))
2644 zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
2645 self.assertEqual(zi.filename, 'stdlib_tests/')
2646 self.assertTrue(zi.is_dir())
2647 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
2648 self.assertEqual(zi.file_size, 0)
2649
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002650
2651class CommandLineTest(unittest.TestCase):
2652
2653 def zipfilecmd(self, *args, **kwargs):
2654 rc, out, err = script_helper.assert_python_ok('-m', 'zipfile', *args,
2655 **kwargs)
2656 return out.replace(os.linesep.encode(), b'\n')
2657
2658 def zipfilecmd_failure(self, *args):
2659 return script_helper.assert_python_failure('-m', 'zipfile', *args)
2660
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002661 def test_bad_use(self):
2662 rc, out, err = self.zipfilecmd_failure()
2663 self.assertEqual(out, b'')
2664 self.assertIn(b'usage', err.lower())
2665 self.assertIn(b'error', err.lower())
2666 self.assertIn(b'required', err.lower())
2667 rc, out, err = self.zipfilecmd_failure('-l', '')
2668 self.assertEqual(out, b'')
2669 self.assertNotEqual(err.strip(), b'')
2670
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002671 def test_test_command(self):
2672 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002673 for opt in '-t', '--test':
2674 out = self.zipfilecmd(opt, zip_name)
2675 self.assertEqual(out.rstrip(), b'Done testing')
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002676 zip_name = findfile('testtar.tar')
2677 rc, out, err = self.zipfilecmd_failure('-t', zip_name)
2678 self.assertEqual(out, b'')
2679
2680 def test_list_command(self):
2681 zip_name = findfile('zipdir.zip')
2682 t = io.StringIO()
2683 with zipfile.ZipFile(zip_name, 'r') as tf:
2684 tf.printdir(t)
2685 expected = t.getvalue().encode('ascii', 'backslashreplace')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002686 for opt in '-l', '--list':
2687 out = self.zipfilecmd(opt, zip_name,
2688 PYTHONIOENCODING='ascii:backslashreplace')
2689 self.assertEqual(out, expected)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002690
Hai Shia3ec3ad2020-05-19 06:02:57 +08002691 @requires_zlib()
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002692 def test_create_command(self):
2693 self.addCleanup(unlink, TESTFN)
2694 with open(TESTFN, 'w') as f:
2695 f.write('test 1')
2696 os.mkdir(TESTFNDIR)
2697 self.addCleanup(rmtree, TESTFNDIR)
2698 with open(os.path.join(TESTFNDIR, 'file.txt'), 'w') as f:
2699 f.write('test 2')
2700 files = [TESTFN, TESTFNDIR]
2701 namelist = [TESTFN, TESTFNDIR + '/', TESTFNDIR + '/file.txt']
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002702 for opt in '-c', '--create':
2703 try:
2704 out = self.zipfilecmd(opt, TESTFN2, *files)
2705 self.assertEqual(out, b'')
2706 with zipfile.ZipFile(TESTFN2) as zf:
2707 self.assertEqual(zf.namelist(), namelist)
2708 self.assertEqual(zf.read(namelist[0]), b'test 1')
2709 self.assertEqual(zf.read(namelist[2]), b'test 2')
2710 finally:
2711 unlink(TESTFN2)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002712
2713 def test_extract_command(self):
2714 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002715 for opt in '-e', '--extract':
2716 with temp_dir() as extdir:
2717 out = self.zipfilecmd(opt, zip_name, extdir)
2718 self.assertEqual(out, b'')
2719 with zipfile.ZipFile(zip_name) as zf:
2720 for zi in zf.infolist():
2721 path = os.path.join(extdir,
2722 zi.filename.replace('/', os.sep))
2723 if zi.is_dir():
2724 self.assertTrue(os.path.isdir(path))
2725 else:
2726 self.assertTrue(os.path.isfile(path))
2727 with open(path, 'rb') as f:
2728 self.assertEqual(f.read(), zf.read(zi))
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002729
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002730
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002731class TestExecutablePrependedZip(unittest.TestCase):
2732 """Test our ability to open zip files with an executable prepended."""
2733
2734 def setUp(self):
2735 self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
2736 self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
2737
2738 def _test_zip_works(self, name):
2739 # bpo28494 sanity check: ensure is_zipfile works on these.
2740 self.assertTrue(zipfile.is_zipfile(name),
2741 f'is_zipfile failed on {name}')
2742 # Ensure we can operate on these via ZipFile.
2743 with zipfile.ZipFile(name) as zipfp:
2744 for n in zipfp.namelist():
2745 data = zipfp.read(n)
2746 self.assertIn(b'FAVORITE_NUMBER', data)
2747
2748 def test_read_zip_with_exe_prepended(self):
2749 self._test_zip_works(self.exe_zip)
2750
2751 def test_read_zip64_with_exe_prepended(self):
2752 self._test_zip_works(self.exe_zip64)
2753
2754 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2755 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2756 'Test relies on #!/bin/bash working.')
2757 def test_execute_zip2(self):
2758 output = subprocess.check_output([self.exe_zip, sys.executable])
2759 self.assertIn(b'number in executable: 5', output)
2760
2761 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2762 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2763 'Test relies on #!/bin/bash working.')
2764 def test_execute_zip64(self):
2765 output = subprocess.check_output([self.exe_zip64, sys.executable])
2766 self.assertIn(b'number in executable: 5', output)
2767
2768
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002769# Poor man's technique to consume a (smallish) iterable.
2770consume = tuple
2771
2772
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002773# from jaraco.itertools 5.0
2774class jaraco:
2775 class itertools:
2776 class Counter:
2777 def __init__(self, i):
2778 self.count = 0
2779 self._orig_iter = iter(i)
2780
2781 def __iter__(self):
2782 return self
2783
2784 def __next__(self):
2785 result = next(self._orig_iter)
2786 self.count += 1
2787 return result
2788
2789
shireenraoa4e29912019-08-24 11:26:41 -04002790def add_dirs(zf):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002791 """
shireenraoa4e29912019-08-24 11:26:41 -04002792 Given a writable zip file zf, inject directory entries for
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002793 any directories implied by the presence of children.
2794 """
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002795 for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()):
shireenraoa4e29912019-08-24 11:26:41 -04002796 zf.writestr(name, b"")
2797 return zf
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002798
2799
shireenraoa4e29912019-08-24 11:26:41 -04002800def build_alpharep_fixture():
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002801 """
2802 Create a zip file with this structure:
2803
2804 .
2805 ├── a.txt
shireenraoa4e29912019-08-24 11:26:41 -04002806 ├── b
2807 │ ├── c.txt
2808 │ ├── d
2809 │ │ └── e.txt
2810 │ └── f.txt
2811 └── g
2812 └── h
2813 └── i.txt
2814
2815 This fixture has the following key characteristics:
2816
2817 - a file at the root (a)
2818 - a file two levels deep (b/d/e)
2819 - multiple files in a directory (b/c, b/f)
2820 - a directory containing only a directory (g/h)
2821
2822 "alpha" because it uses alphabet
2823 "rep" because it's a representative example
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002824 """
2825 data = io.BytesIO()
2826 zf = zipfile.ZipFile(data, "w")
2827 zf.writestr("a.txt", b"content of a")
2828 zf.writestr("b/c.txt", b"content of c")
2829 zf.writestr("b/d/e.txt", b"content of e")
shireenraoa4e29912019-08-24 11:26:41 -04002830 zf.writestr("b/f.txt", b"content of f")
2831 zf.writestr("g/h/i.txt", b"content of i")
2832 zf.filename = "alpharep.zip"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002833 return zf
2834
2835
2836class TestPath(unittest.TestCase):
2837 def setUp(self):
2838 self.fixtures = contextlib.ExitStack()
2839 self.addCleanup(self.fixtures.close)
2840
shireenraoa4e29912019-08-24 11:26:41 -04002841 def zipfile_alpharep(self):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002842 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002843 yield build_alpharep_fixture()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002844 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002845 yield add_dirs(build_alpharep_fixture())
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002846
2847 def zipfile_ondisk(self):
2848 tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
shireenraoa4e29912019-08-24 11:26:41 -04002849 for alpharep in self.zipfile_alpharep():
2850 buffer = alpharep.fp
2851 alpharep.close()
2852 path = tmpdir / alpharep.filename
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002853 with path.open("wb") as strm:
2854 strm.write(buffer.getvalue())
2855 yield path
2856
shireenraoa4e29912019-08-24 11:26:41 -04002857 def test_iterdir_and_types(self):
2858 for alpharep in self.zipfile_alpharep():
2859 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002860 assert root.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002861 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002862 assert a.is_file()
2863 assert b.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002864 assert g.is_dir()
2865 c, f, d = b.iterdir()
2866 assert c.is_file() and f.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002867 e, = d.iterdir()
2868 assert e.is_file()
shireenraoa4e29912019-08-24 11:26:41 -04002869 h, = g.iterdir()
2870 i, = h.iterdir()
2871 assert i.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002872
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002873 def test_subdir_is_dir(self):
2874 for alpharep in self.zipfile_alpharep():
2875 root = zipfile.Path(alpharep)
2876 assert (root / 'b').is_dir()
2877 assert (root / 'b/').is_dir()
2878 assert (root / 'g').is_dir()
2879 assert (root / 'g/').is_dir()
2880
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002881 def test_open(self):
shireenraoa4e29912019-08-24 11:26:41 -04002882 for alpharep in self.zipfile_alpharep():
2883 root = zipfile.Path(alpharep)
2884 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002885 with a.open() as strm:
2886 data = strm.read()
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002887 assert data == "content of a"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002888
2889 def test_read(self):
shireenraoa4e29912019-08-24 11:26:41 -04002890 for alpharep in self.zipfile_alpharep():
2891 root = zipfile.Path(alpharep)
2892 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002893 assert a.read_text() == "content of a"
2894 assert a.read_bytes() == b"content of a"
2895
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002896 def test_joinpath(self):
shireenraoa4e29912019-08-24 11:26:41 -04002897 for alpharep in self.zipfile_alpharep():
2898 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002899 a = root.joinpath("a")
2900 assert a.is_file()
2901 e = root.joinpath("b").joinpath("d").joinpath("e.txt")
2902 assert e.read_text() == "content of e"
2903
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002904 def test_traverse_truediv(self):
shireenraoa4e29912019-08-24 11:26:41 -04002905 for alpharep in self.zipfile_alpharep():
2906 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002907 a = root / "a"
2908 assert a.is_file()
2909 e = root / "b" / "d" / "e.txt"
2910 assert e.read_text() == "content of e"
2911
2912 def test_pathlike_construction(self):
2913 """
2914 zipfile.Path should be constructable from a path-like object
2915 """
2916 for zipfile_ondisk in self.zipfile_ondisk():
2917 pathlike = pathlib.Path(str(zipfile_ondisk))
2918 zipfile.Path(pathlike)
2919
2920 def test_traverse_pathlike(self):
shireenraoa4e29912019-08-24 11:26:41 -04002921 for alpharep in self.zipfile_alpharep():
2922 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002923 root / pathlib.Path("a")
2924
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002925 def test_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002926 for alpharep in self.zipfile_alpharep():
2927 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002928 assert (root / 'a').parent.at == ''
2929 assert (root / 'a' / 'b').parent.at == 'a/'
2930
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002931 def test_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002932 for alpharep in self.zipfile_alpharep():
2933 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002934 assert (root / 'b').parent.at == ''
2935 assert (root / 'b/').parent.at == ''
2936
2937 def test_missing_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002938 for alpharep in self.zipfile_alpharep():
2939 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002940 assert (root / 'missing dir/').parent.at == ''
2941
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002942 def test_mutability(self):
2943 """
2944 If the underlying zipfile is changed, the Path object should
2945 reflect that change.
2946 """
2947 for alpharep in self.zipfile_alpharep():
2948 root = zipfile.Path(alpharep)
2949 a, b, g = root.iterdir()
2950 alpharep.writestr('foo.txt', 'foo')
2951 alpharep.writestr('bar/baz.txt', 'baz')
2952 assert any(
2953 child.name == 'foo.txt'
2954 for child in root.iterdir())
2955 assert (root / 'foo.txt').read_text() == 'foo'
2956 baz, = (root / 'bar').iterdir()
2957 assert baz.read_text() == 'baz'
2958
2959 HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
2960
2961 def huge_zipfile(self):
2962 """Create a read-only zipfile with a huge number of entries entries."""
2963 strm = io.BytesIO()
2964 zf = zipfile.ZipFile(strm, "w")
2965 for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
2966 zf.writestr(entry, entry)
2967 zf.mode = 'r'
2968 return zf
2969
2970 def test_joinpath_constant_time(self):
2971 """
2972 Ensure joinpath on items in zipfile is linear time.
2973 """
2974 root = zipfile.Path(self.huge_zipfile())
2975 entries = jaraco.itertools.Counter(root.iterdir())
2976 for entry in entries:
2977 entry.joinpath('suffix')
2978 # Check the file iterated all items
2979 assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
2980
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002981 # @func_timeout.func_set_timeout(3)
2982 def test_implied_dirs_performance(self):
2983 data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
2984 zipfile.CompleteDirs._implied_dirs(data)
2985
shireenraoa4e29912019-08-24 11:26:41 -04002986
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00002987if __name__ == "__main__":
Brett Cannond5b4e1d2013-06-12 19:57:19 -04002988 unittest.main()