blob: 643c5b477bab32589f357334495a1d722e8e8a87 [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
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030019from random import randint, random, getrandbits
Tim Petersa19a1682001-03-29 04:36:09 +000020
Serhiy Storchaka61c4c442016-10-23 13:07:59 +030021from test.support import script_helper
Serhiy Storchaka8606e952017-03-08 14:37:51 +020022from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir, temp_cwd,
Serhiy Storchakac5b75db2013-01-29 20:14:08 +020023 requires_zlib, requires_bz2, requires_lzma,
Victor Stinnerd6debb22017-03-27 16:05:26 +020024 captured_stdout)
Guido van Rossum368f04a2000-04-10 13:23:04 +000025
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000026TESTFN2 = TESTFN + "2"
Martin v. Löwis59e47792009-01-24 14:10:07 +000027TESTFNDIR = TESTFN + "d"
Guido van Rossumb5a755e2007-07-18 18:15:48 +000028FIXEDTEST_SIZE = 1000
Georg Brandl5ba11de2011-01-01 10:09:32 +000029DATAFILES_DIR = 'zipfile_datafiles'
Guido van Rossum368f04a2000-04-10 13:23:04 +000030
Christian Heimes790c8232008-01-07 21:14:23 +000031SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
32 ('ziptest2dir/_ziptest2', 'qawsedrftg'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -080033 ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
Christian Heimes790c8232008-01-07 21:14:23 +000034 ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]
35
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +020036def getrandbytes(size):
37 return getrandbits(8 * size).to_bytes(size, 'little')
38
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030039def get_files(test):
40 yield TESTFN2
41 with TemporaryFile() as f:
42 yield f
43 test.assertFalse(f.closed)
44 with io.BytesIO() as f:
45 yield f
46 test.assertFalse(f.closed)
Ezio Melotti76430242009-07-11 18:28:48 +000047
Serhiy Storchakafa6bc292013-07-22 21:00:11 +030048class AbstractTestsWithSourceFile:
49 @classmethod
50 def setUpClass(cls):
51 cls.line_gen = [bytes("Zipfile test line %d. random float: %f\n" %
52 (i, random()), "ascii")
53 for i in range(FIXEDTEST_SIZE)]
54 cls.data = b''.join(cls.line_gen)
55
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000056 def setUp(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000057 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +000058 with open(TESTFN, "wb") as fp:
59 fp.write(self.data)
Tim Peters7d3bad62001-04-04 18:56:49 +000060
Bo Baylesce237c72018-01-29 23:54:07 -060061 def make_test_archive(self, f, compression, compresslevel=None):
62 kwargs = {'compression': compression, 'compresslevel': compresslevel}
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000063 # Create the ZIP archive
Bo Baylesce237c72018-01-29 23:54:07 -060064 with zipfile.ZipFile(f, "w", **kwargs) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000065 zipfp.write(TESTFN, "another.name")
66 zipfp.write(TESTFN, TESTFN)
67 zipfp.writestr("strfile", self.data)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030068 with zipfp.open('written-open-w', mode='w') as f:
69 for line in self.line_gen:
70 f.write(line)
Tim Peters7d3bad62001-04-04 18:56:49 +000071
Bo Baylesce237c72018-01-29 23:54:07 -060072 def zip_test(self, f, compression, compresslevel=None):
73 self.make_test_archive(f, compression, compresslevel)
Guido van Rossumd8faa362007-04-27 19:54:29 +000074
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +000075 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000076 with zipfile.ZipFile(f, "r", compression) as zipfp:
77 self.assertEqual(zipfp.read(TESTFN), self.data)
78 self.assertEqual(zipfp.read("another.name"), self.data)
79 self.assertEqual(zipfp.read("strfile"), self.data)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000080
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000081 # Print the ZIP directory
82 fp = io.StringIO()
83 zipfp.printdir(file=fp)
84 directory = fp.getvalue()
85 lines = directory.splitlines()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +030086 self.assertEqual(len(lines), 5) # Number of files + header
Thomas Wouters0e3f5912006-08-11 14:57:12 +000087
Benjamin Peterson577473f2010-01-19 00:09:57 +000088 self.assertIn('File Name', lines[0])
89 self.assertIn('Modified', lines[0])
90 self.assertIn('Size', lines[0])
Thomas Wouters0e3f5912006-08-11 14:57:12 +000091
Ezio Melotti35386712009-12-31 13:22:41 +000092 fn, date, time_, size = lines[1].split()
93 self.assertEqual(fn, 'another.name')
94 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
95 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
96 self.assertEqual(size, str(len(self.data)))
Thomas Wouters0e3f5912006-08-11 14:57:12 +000097
Ezio Melottifaa6b7f2009-12-30 12:34:59 +000098 # Check the namelist
99 names = zipfp.namelist()
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300100 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000101 self.assertIn(TESTFN, names)
102 self.assertIn("another.name", names)
103 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300104 self.assertIn("written-open-w", names)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000105
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000106 # Check infolist
107 infos = zipfp.infolist()
Ezio Melotti35386712009-12-31 13:22:41 +0000108 names = [i.filename for i in infos]
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300109 self.assertEqual(len(names), 4)
Benjamin Peterson577473f2010-01-19 00:09:57 +0000110 self.assertIn(TESTFN, names)
111 self.assertIn("another.name", names)
112 self.assertIn("strfile", names)
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300113 self.assertIn("written-open-w", names)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000114 for i in infos:
Ezio Melotti35386712009-12-31 13:22:41 +0000115 self.assertEqual(i.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000116
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000117 # check getinfo
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300118 for nm in (TESTFN, "another.name", "strfile", "written-open-w"):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000119 info = zipfp.getinfo(nm)
Ezio Melotti35386712009-12-31 13:22:41 +0000120 self.assertEqual(info.filename, nm)
121 self.assertEqual(info.file_size, len(self.data))
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000122
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000123 # Check that testzip doesn't raise an exception
124 zipfp.testzip()
Tim Peters7d3bad62001-04-04 18:56:49 +0000125
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300126 def test_basic(self):
127 for f in get_files(self):
128 self.zip_test(f, self.compression)
Raymond Hettingerc0fac962003-06-27 22:25:03 +0000129
Ezio Melottiafd0d112009-07-15 17:17:17 +0000130 def zip_open_test(self, f, compression):
131 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000132
133 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000134 with zipfile.ZipFile(f, "r", compression) as zipfp:
135 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000136 with zipfp.open(TESTFN) as zipopen1:
137 while True:
138 read_data = zipopen1.read(256)
139 if not read_data:
140 break
141 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000142
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000143 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000144 with zipfp.open("another.name") as zipopen2:
145 while True:
146 read_data = zipopen2.read(256)
147 if not read_data:
148 break
149 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000150
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000151 self.assertEqual(b''.join(zipdata1), self.data)
152 self.assertEqual(b''.join(zipdata2), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000153
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300154 def test_open(self):
155 for f in get_files(self):
156 self.zip_open_test(f, self.compression)
Georg Brandlb533e262008-05-25 18:19:30 +0000157
Serhiy Storchaka8606e952017-03-08 14:37:51 +0200158 def test_open_with_pathlike(self):
159 path = pathlib.Path(TESTFN2)
160 self.zip_open_test(path, self.compression)
161 with zipfile.ZipFile(path, "r", self.compression) as zipfp:
162 self.assertIsInstance(zipfp.filename, str)
163
Ezio Melottiafd0d112009-07-15 17:17:17 +0000164 def zip_random_open_test(self, f, compression):
165 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000166
167 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000168 with zipfile.ZipFile(f, "r", compression) as zipfp:
169 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +0000170 with zipfp.open(TESTFN) as zipopen1:
171 while True:
172 read_data = zipopen1.read(randint(1, 1024))
173 if not read_data:
174 break
175 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000176
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000177 self.assertEqual(b''.join(zipdata1), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300179 def test_random_open(self):
180 for f in get_files(self):
181 self.zip_random_open_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000182
Serhiy Storchakad2c07a52013-09-27 22:11:57 +0300183 def zip_read1_test(self, f, compression):
184 self.make_test_archive(f, compression)
185
186 # Read the ZIP archive
187 with zipfile.ZipFile(f, "r") as zipfp, \
188 zipfp.open(TESTFN) as zipopen:
189 zipdata = []
190 while True:
191 read_data = zipopen.read1(-1)
192 if not read_data:
193 break
194 zipdata.append(read_data)
195
196 self.assertEqual(b''.join(zipdata), self.data)
197
198 def test_read1(self):
199 for f in get_files(self):
200 self.zip_read1_test(f, self.compression)
201
202 def zip_read1_10_test(self, f, compression):
203 self.make_test_archive(f, compression)
204
205 # Read the ZIP archive
206 with zipfile.ZipFile(f, "r") as zipfp, \
207 zipfp.open(TESTFN) as zipopen:
208 zipdata = []
209 while True:
210 read_data = zipopen.read1(10)
211 self.assertLessEqual(len(read_data), 10)
212 if not read_data:
213 break
214 zipdata.append(read_data)
215
216 self.assertEqual(b''.join(zipdata), self.data)
217
218 def test_read1_10(self):
219 for f in get_files(self):
220 self.zip_read1_10_test(f, self.compression)
221
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000222 def zip_readline_read_test(self, f, compression):
223 self.make_test_archive(f, compression)
224
225 # Read the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300226 with zipfile.ZipFile(f, "r") as zipfp, \
227 zipfp.open(TESTFN) as zipopen:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000228 data = b''
229 while True:
230 read = zipopen.readline()
231 if not read:
232 break
233 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000234
Brian Curtin8fb9b862010-11-18 02:15:28 +0000235 read = zipopen.read(100)
236 if not read:
237 break
238 data += read
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000239
240 self.assertEqual(data, self.data)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300241
242 def test_readline_read(self):
243 # Issue #7610: calls to readline() interleaved with calls to read().
244 for f in get_files(self):
245 self.zip_readline_read_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000246
Ezio Melottiafd0d112009-07-15 17:17:17 +0000247 def zip_readline_test(self, f, compression):
248 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000249
250 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000251 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000252 with zipfp.open(TESTFN) as zipopen:
253 for line in self.line_gen:
254 linedata = zipopen.readline()
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300255 self.assertEqual(linedata, line)
256
257 def test_readline(self):
258 for f in get_files(self):
259 self.zip_readline_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000260
Ezio Melottiafd0d112009-07-15 17:17:17 +0000261 def zip_readlines_test(self, f, compression):
262 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000263
264 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000265 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000266 with zipfp.open(TESTFN) as zipopen:
267 ziplines = zipopen.readlines()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000268 for line, zipline in zip(self.line_gen, ziplines):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300269 self.assertEqual(zipline, line)
270
271 def test_readlines(self):
272 for f in get_files(self):
273 self.zip_readlines_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000274
Ezio Melottiafd0d112009-07-15 17:17:17 +0000275 def zip_iterlines_test(self, f, compression):
276 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000277
278 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000279 with zipfile.ZipFile(f, "r") as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000280 with zipfp.open(TESTFN) as zipopen:
281 for line, zipline in zip(self.line_gen, zipopen):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300282 self.assertEqual(zipline, line)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000283
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300284 def test_iterlines(self):
285 for f in get_files(self):
286 self.zip_iterlines_test(f, self.compression)
Antoine Pitroua32f9a22010-01-27 21:18:57 +0000287
Ezio Melottiafd0d112009-07-15 17:17:17 +0000288 def test_low_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000289 """Check for cases where compressed data is larger than original."""
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000290 # Create the ZIP archive
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300291 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipfp:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000292 zipfp.writestr("strfile", '12')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000293
294 # Get an open object for strfile
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300295 with zipfile.ZipFile(TESTFN2, "r", self.compression) as zipfp:
Brian Curtin8fb9b862010-11-18 02:15:28 +0000296 with zipfp.open("strfile") as openobj:
297 self.assertEqual(openobj.read(1), b'1')
298 self.assertEqual(openobj.read(1), b'2')
Ezio Melotti74c96ec2009-07-08 22:24:06 +0000299
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300300 def test_writestr_compression(self):
301 zipfp = zipfile.ZipFile(TESTFN2, "w")
302 zipfp.writestr("b.txt", "hello world", compress_type=self.compression)
303 info = zipfp.getinfo('b.txt')
304 self.assertEqual(info.compress_type, self.compression)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200305
Bo Baylesce237c72018-01-29 23:54:07 -0600306 def test_writestr_compresslevel(self):
307 zipfp = zipfile.ZipFile(TESTFN2, "w", compresslevel=1)
308 zipfp.writestr("a.txt", "hello world", compress_type=self.compression)
309 zipfp.writestr("b.txt", "hello world", compress_type=self.compression,
310 compresslevel=2)
311
312 # Compression level follows the constructor.
313 a_info = zipfp.getinfo('a.txt')
314 self.assertEqual(a_info.compress_type, self.compression)
315 self.assertEqual(a_info._compresslevel, 1)
316
317 # Compression level is overridden.
318 b_info = zipfp.getinfo('b.txt')
319 self.assertEqual(b_info.compress_type, self.compression)
320 self.assertEqual(b_info._compresslevel, 2)
321
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300322 def test_read_return_size(self):
323 # Issue #9837: ZipExtFile.read() shouldn't return more bytes
324 # than requested.
325 for test_size in (1, 4095, 4096, 4097, 16384):
326 file_size = test_size + 1
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +0200327 junk = getrandbytes(file_size)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300328 with zipfile.ZipFile(io.BytesIO(), "w", self.compression) as zipf:
329 zipf.writestr('foo', junk)
330 with zipf.open('foo', 'r') as fp:
331 buf = fp.read(test_size)
332 self.assertEqual(len(buf), test_size)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200333
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200334 def test_truncated_zipfile(self):
335 fp = io.BytesIO()
336 with zipfile.ZipFile(fp, mode='w') as zipf:
337 zipf.writestr('strfile', self.data, compress_type=self.compression)
338 end_offset = fp.tell()
339 zipfiledata = fp.getvalue()
340
341 fp = io.BytesIO(zipfiledata)
342 with zipfile.ZipFile(fp) as zipf:
343 with zipf.open('strfile') as zipopen:
344 fp.truncate(end_offset - 20)
345 with self.assertRaises(EOFError):
346 zipopen.read()
347
348 fp = io.BytesIO(zipfiledata)
349 with zipfile.ZipFile(fp) as zipf:
350 with zipf.open('strfile') as zipopen:
351 fp.truncate(end_offset - 20)
352 with self.assertRaises(EOFError):
353 while zipopen.read(100):
354 pass
355
356 fp = io.BytesIO(zipfiledata)
357 with zipfile.ZipFile(fp) as zipf:
358 with zipf.open('strfile') as zipopen:
359 fp.truncate(end_offset - 20)
360 with self.assertRaises(EOFError):
361 while zipopen.read1(100):
362 pass
363
Serhiy Storchaka51a43702014-10-29 22:42:06 +0200364 def test_repr(self):
365 fname = 'file.name'
366 for f in get_files(self):
367 with zipfile.ZipFile(f, 'w', self.compression) as zipfp:
368 zipfp.write(TESTFN, fname)
369 r = repr(zipfp)
370 self.assertIn("mode='w'", r)
371
372 with zipfile.ZipFile(f, 'r') as zipfp:
373 r = repr(zipfp)
374 if isinstance(f, str):
375 self.assertIn('filename=%r' % f, r)
376 else:
377 self.assertIn('file=%r' % f, r)
378 self.assertIn("mode='r'", r)
379 r = repr(zipfp.getinfo(fname))
380 self.assertIn('filename=%r' % fname, r)
381 self.assertIn('filemode=', r)
382 self.assertIn('file_size=', r)
383 if self.compression != zipfile.ZIP_STORED:
384 self.assertIn('compress_type=', r)
385 self.assertIn('compress_size=', r)
386 with zipfp.open(fname) as zipopen:
387 r = repr(zipopen)
388 self.assertIn('name=%r' % fname, r)
389 self.assertIn("mode='r'", r)
390 if self.compression != zipfile.ZIP_STORED:
391 self.assertIn('compress_type=', r)
392 self.assertIn('[closed]', repr(zipopen))
393 self.assertIn('[closed]', repr(zipfp))
394
Bo Baylesce237c72018-01-29 23:54:07 -0600395 def test_compresslevel_basic(self):
396 for f in get_files(self):
397 self.zip_test(f, self.compression, compresslevel=9)
398
399 def test_per_file_compresslevel(self):
400 """Check that files within a Zip archive can have different
401 compression levels."""
402 with zipfile.ZipFile(TESTFN2, "w", compresslevel=1) as zipfp:
403 zipfp.write(TESTFN, 'compress_1')
404 zipfp.write(TESTFN, 'compress_9', compresslevel=9)
405 one_info = zipfp.getinfo('compress_1')
406 nine_info = zipfp.getinfo('compress_9')
407 self.assertEqual(one_info._compresslevel, 1)
408 self.assertEqual(nine_info._compresslevel, 9)
409
Serhiy Storchaka2524fde2019-03-30 08:25:19 +0200410 def test_writing_errors(self):
411 class BrokenFile(io.BytesIO):
412 def write(self, data):
413 nonlocal count
414 if count is not None:
415 if count == stop:
416 raise OSError
417 count += 1
418 super().write(data)
419
420 stop = 0
421 while True:
422 testfile = BrokenFile()
423 count = None
424 with zipfile.ZipFile(testfile, 'w', self.compression) as zipfp:
425 with zipfp.open('file1', 'w') as f:
426 f.write(b'data1')
427 count = 0
428 try:
429 with zipfp.open('file2', 'w') as f:
430 f.write(b'data2')
431 except OSError:
432 stop += 1
433 else:
434 break
435 finally:
436 count = None
437 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
438 self.assertEqual(zipfp.namelist(), ['file1'])
439 self.assertEqual(zipfp.read('file1'), b'data1')
440
441 with zipfile.ZipFile(io.BytesIO(testfile.getvalue())) as zipfp:
442 self.assertEqual(zipfp.namelist(), ['file1', 'file2'])
443 self.assertEqual(zipfp.read('file1'), b'data1')
444 self.assertEqual(zipfp.read('file2'), b'data2')
445
446
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300447 def tearDown(self):
448 unlink(TESTFN)
449 unlink(TESTFN2)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200450
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200451
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300452class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
453 unittest.TestCase):
454 compression = zipfile.ZIP_STORED
455 test_low_compression = None
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200456
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300457 def zip_test_writestr_permissions(self, f, compression):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300458 # Make sure that writestr and open(... mode='w') create files with
459 # mode 0600, when they are passed a name rather than a ZipInfo
460 # instance.
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200461
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300462 self.make_test_archive(f, compression)
463 with zipfile.ZipFile(f, "r") as zipfp:
464 zinfo = zipfp.getinfo('strfile')
465 self.assertEqual(zinfo.external_attr, 0o600 << 16)
Martin v. Löwisf6b16a42012-05-01 07:58:44 +0200466
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300467 zinfo2 = zipfp.getinfo('written-open-w')
468 self.assertEqual(zinfo2.external_attr, 0o600 << 16)
469
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300470 def test_writestr_permissions(self):
471 for f in get_files(self):
472 self.zip_test_writestr_permissions(f, zipfile.ZIP_STORED)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +0200473
Ezio Melottiafd0d112009-07-15 17:17:17 +0000474 def test_absolute_arcnames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000475 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
476 zipfp.write(TESTFN, "/absolute")
Georg Brandl8f7c54e2006-02-20 08:40:38 +0000477
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000478 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
479 self.assertEqual(zipfp.namelist(), ["absolute"])
Tim Peters32cbc962006-02-20 21:42:18 +0000480
Ezio Melottiafd0d112009-07-15 17:17:17 +0000481 def test_append_to_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000482 """Test appending to an existing zipfile."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000483 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
484 zipfp.write(TESTFN, TESTFN)
485
486 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
487 zipfp.writestr("strfile", self.data)
488 self.assertEqual(zipfp.namelist(), [TESTFN, "strfile"])
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000489
Ezio Melottiafd0d112009-07-15 17:17:17 +0000490 def test_append_to_non_zip_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000491 """Test appending to an existing file that is not a zipfile."""
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000492 # NOTE: this test fails if len(d) < 22 because of the first
493 # line "fpin.seek(-22, 2)" in _EndRecData
Ezio Melotti35386712009-12-31 13:22:41 +0000494 data = b'I am not a ZipFile!'*10
495 with open(TESTFN2, 'wb') as f:
496 f.write(data)
497
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000498 with zipfile.ZipFile(TESTFN2, "a", zipfile.ZIP_STORED) as zipfp:
499 zipfp.write(TESTFN, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000500
Ezio Melotti35386712009-12-31 13:22:41 +0000501 with open(TESTFN2, 'rb') as f:
502 f.seek(len(data))
503 with zipfile.ZipFile(f, "r") as zipfp:
504 self.assertEqual(zipfp.namelist(), [TESTFN])
Serhiy Storchaka8793b212016-10-07 22:20:50 +0300505 self.assertEqual(zipfp.read(TESTFN), self.data)
506 with open(TESTFN2, 'rb') as f:
507 self.assertEqual(f.read(len(data)), data)
508 zipfiledata = f.read()
509 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
510 self.assertEqual(zipfp.namelist(), [TESTFN])
511 self.assertEqual(zipfp.read(TESTFN), self.data)
512
513 def test_read_concatenated_zip_file(self):
514 with io.BytesIO() as bio:
515 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
516 zipfp.write(TESTFN, TESTFN)
517 zipfiledata = bio.getvalue()
518 data = b'I am not a ZipFile!'*10
519 with open(TESTFN2, 'wb') as f:
520 f.write(data)
521 f.write(zipfiledata)
522
523 with zipfile.ZipFile(TESTFN2) as zipfp:
524 self.assertEqual(zipfp.namelist(), [TESTFN])
525 self.assertEqual(zipfp.read(TESTFN), self.data)
526
527 def test_append_to_concatenated_zip_file(self):
528 with io.BytesIO() as bio:
529 with zipfile.ZipFile(bio, 'w', zipfile.ZIP_STORED) as zipfp:
530 zipfp.write(TESTFN, TESTFN)
531 zipfiledata = bio.getvalue()
532 data = b'I am not a ZipFile!'*1000000
533 with open(TESTFN2, 'wb') as f:
534 f.write(data)
535 f.write(zipfiledata)
536
537 with zipfile.ZipFile(TESTFN2, 'a') as zipfp:
538 self.assertEqual(zipfp.namelist(), [TESTFN])
539 zipfp.writestr('strfile', self.data)
540
541 with open(TESTFN2, 'rb') as f:
542 self.assertEqual(f.read(len(data)), data)
543 zipfiledata = f.read()
544 with io.BytesIO(zipfiledata) as bio, zipfile.ZipFile(bio) as zipfp:
545 self.assertEqual(zipfp.namelist(), [TESTFN, 'strfile'])
546 self.assertEqual(zipfp.read(TESTFN), self.data)
547 self.assertEqual(zipfp.read('strfile'), self.data)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000548
R David Murray4fbb9db2011-06-09 15:50:51 -0400549 def test_ignores_newline_at_end(self):
550 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
551 zipfp.write(TESTFN, TESTFN)
552 with open(TESTFN2, 'a') as f:
553 f.write("\r\n\00\00\00")
554 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
555 self.assertIsInstance(zipfp, zipfile.ZipFile)
556
557 def test_ignores_stuff_appended_past_comments(self):
558 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
559 zipfp.comment = b"this is a comment"
560 zipfp.write(TESTFN, TESTFN)
561 with open(TESTFN2, 'a') as f:
562 f.write("abcdef\r\n")
563 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
564 self.assertIsInstance(zipfp, zipfile.ZipFile)
565 self.assertEqual(zipfp.comment, b"this is a comment")
566
Ezio Melottiafd0d112009-07-15 17:17:17 +0000567 def test_write_default_name(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000568 """Check that calling ZipFile.write without arcname specified
569 produces the expected result."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000570 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
571 zipfp.write(TESTFN)
Brian Curtin8fb9b862010-11-18 02:15:28 +0000572 with open(TESTFN, "rb") as f:
573 self.assertEqual(zipfp.read(TESTFN), f.read())
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000574
Daniel Hillier8d62df62019-11-30 19:30:47 +1100575 def test_io_on_closed_zipextfile(self):
576 fname = "somefile.txt"
577 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
578 zipfp.writestr(fname, "bogus")
579
580 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
581 with zipfp.open(fname) as fid:
582 fid.close()
583 self.assertRaises(ValueError, fid.read)
584 self.assertRaises(ValueError, fid.seek, 0)
585 self.assertRaises(ValueError, fid.tell)
586 self.assertRaises(ValueError, fid.readable)
587 self.assertRaises(ValueError, fid.seekable)
588
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300589 def test_write_to_readonly(self):
590 """Check that trying to call write() on a readonly ZipFile object
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300591 raises a ValueError."""
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300592 with zipfile.ZipFile(TESTFN2, mode="w") as zipfp:
593 zipfp.writestr("somefile.txt", "bogus")
594
595 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300596 self.assertRaises(ValueError, zipfp.write, TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300597
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300598 with zipfile.ZipFile(TESTFN2, mode="r") as zipfp:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +0300599 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +0300600 zipfp.open(TESTFN, mode='w')
601
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300602 def test_add_file_before_1980(self):
603 # Set atime and mtime to 1970-01-01
604 os.utime(TESTFN, (0, 0))
605 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
606 self.assertRaises(ValueError, zipfp.write, TESTFN)
607
Marcel Plch77b112c2018-08-31 16:43:31 +0200608 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
609 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200610 zinfo = zipfp.getinfo(TESTFN)
611 self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0))
612
613 def test_add_file_after_2107(self):
614 # Set atime and mtime to 2108-12-30
Victor Stinnerc232c912020-01-30 15:47:53 +0100615 ts = 4386268800
Marcel Plch7b41dba2018-08-03 17:59:19 +0200616 try:
Victor Stinnerc232c912020-01-30 15:47:53 +0100617 time.localtime(ts)
618 except OverflowError:
619 self.skipTest(f'time.localtime({ts}) raises OverflowError')
620 try:
621 os.utime(TESTFN, (ts, ts))
Marcel Plch7b41dba2018-08-03 17:59:19 +0200622 except OverflowError:
623 self.skipTest('Host fs cannot set timestamp to required value.')
624
Victor Stinner3cb49b62020-01-29 15:23:29 +0100625 mtime_ns = os.stat(TESTFN).st_mtime_ns
626 if mtime_ns != (4386268800 * 10**9):
627 # XFS filesystem is limited to 32-bit timestamp, but the syscall
628 # didn't fail. Moreover, there is a VFS bug which returns
629 # a cached timestamp which is different than the value on disk.
630 #
631 # Test st_mtime_ns rather than st_mtime to avoid rounding issues.
632 #
633 # https://bugzilla.redhat.com/show_bug.cgi?id=1795576
634 # https://bugs.python.org/issue39460#msg360952
635 self.skipTest(f"Linux VFS/XFS kernel bug detected: {mtime_ns=}")
636
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200637 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
638 self.assertRaises(struct.error, zipfp.write, TESTFN)
639
Marcel Plch77b112c2018-08-31 16:43:31 +0200640 with zipfile.ZipFile(TESTFN2, "w", strict_timestamps=False) as zipfp:
641 zipfp.write(TESTFN)
Marcel Plcha2fe1e52018-08-02 15:04:52 +0200642 zinfo = zipfp.getinfo(TESTFN)
643 self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59))
644
Serhiy Storchaka5ce3f102014-01-09 14:50:20 +0200645
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300646@requires_zlib
647class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile,
648 unittest.TestCase):
649 compression = zipfile.ZIP_DEFLATED
650
Ezio Melottiafd0d112009-07-15 17:17:17 +0000651 def test_per_file_compression(self):
Ezio Melotti35386712009-12-31 13:22:41 +0000652 """Check that files within a Zip archive can have different
653 compression options."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000654 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
655 zipfp.write(TESTFN, 'storeme', zipfile.ZIP_STORED)
656 zipfp.write(TESTFN, 'deflateme', zipfile.ZIP_DEFLATED)
657 sinfo = zipfp.getinfo('storeme')
658 dinfo = zipfp.getinfo('deflateme')
659 self.assertEqual(sinfo.compress_type, zipfile.ZIP_STORED)
660 self.assertEqual(dinfo.compress_type, zipfile.ZIP_DEFLATED)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000661
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300662@requires_bz2
663class Bzip2TestsWithSourceFile(AbstractTestsWithSourceFile,
664 unittest.TestCase):
665 compression = zipfile.ZIP_BZIP2
Ezio Melottifaa6b7f2009-12-30 12:34:59 +0000666
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300667@requires_lzma
668class LzmaTestsWithSourceFile(AbstractTestsWithSourceFile,
669 unittest.TestCase):
670 compression = zipfile.ZIP_LZMA
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000671
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300672
673class AbstractTestZip64InSmallFiles:
674 # These tests test the ZIP64 functionality without using large files,
675 # see test_zipfile64 for proper tests.
676
677 @classmethod
678 def setUpClass(cls):
679 line_gen = (bytes("Test of zipfile line %d." % i, "ascii")
680 for i in range(0, FIXEDTEST_SIZE))
681 cls.data = b'\n'.join(line_gen)
682
683 def setUp(self):
684 self._limit = zipfile.ZIP64_LIMIT
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300685 self._filecount_limit = zipfile.ZIP_FILECOUNT_LIMIT
686 zipfile.ZIP64_LIMIT = 1000
687 zipfile.ZIP_FILECOUNT_LIMIT = 9
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300688
689 # Make a source file with some lines
690 with open(TESTFN, "wb") as fp:
691 fp.write(self.data)
692
693 def zip_test(self, f, compression):
694 # Create the ZIP archive
695 with zipfile.ZipFile(f, "w", compression, allowZip64=True) as zipfp:
696 zipfp.write(TESTFN, "another.name")
697 zipfp.write(TESTFN, TESTFN)
698 zipfp.writestr("strfile", self.data)
699
700 # Read the ZIP archive
701 with zipfile.ZipFile(f, "r", compression) as zipfp:
702 self.assertEqual(zipfp.read(TESTFN), self.data)
703 self.assertEqual(zipfp.read("another.name"), self.data)
704 self.assertEqual(zipfp.read("strfile"), self.data)
705
706 # Print the ZIP directory
707 fp = io.StringIO()
708 zipfp.printdir(fp)
709
710 directory = fp.getvalue()
711 lines = directory.splitlines()
712 self.assertEqual(len(lines), 4) # Number of files + header
713
714 self.assertIn('File Name', lines[0])
715 self.assertIn('Modified', lines[0])
716 self.assertIn('Size', lines[0])
717
718 fn, date, time_, size = lines[1].split()
719 self.assertEqual(fn, 'another.name')
720 self.assertTrue(time.strptime(date, '%Y-%m-%d'))
721 self.assertTrue(time.strptime(time_, '%H:%M:%S'))
722 self.assertEqual(size, str(len(self.data)))
723
724 # Check the namelist
725 names = zipfp.namelist()
726 self.assertEqual(len(names), 3)
727 self.assertIn(TESTFN, names)
728 self.assertIn("another.name", names)
729 self.assertIn("strfile", names)
730
731 # Check infolist
732 infos = zipfp.infolist()
733 names = [i.filename for i in infos]
734 self.assertEqual(len(names), 3)
735 self.assertIn(TESTFN, names)
736 self.assertIn("another.name", names)
737 self.assertIn("strfile", names)
738 for i in infos:
739 self.assertEqual(i.file_size, len(self.data))
740
741 # check getinfo
742 for nm in (TESTFN, "another.name", "strfile"):
743 info = zipfp.getinfo(nm)
744 self.assertEqual(info.filename, nm)
745 self.assertEqual(info.file_size, len(self.data))
746
747 # Check that testzip doesn't raise an exception
748 zipfp.testzip()
749
750 def test_basic(self):
751 for f in get_files(self):
752 self.zip_test(f, self.compression)
753
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300754 def test_too_many_files(self):
755 # This test checks that more than 64k files can be added to an archive,
756 # and that the resulting archive can be read properly by ZipFile
757 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
758 allowZip64=True)
759 zipf.debug = 100
760 numfiles = 15
761 for i in range(numfiles):
762 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
763 self.assertEqual(len(zipf.namelist()), numfiles)
764 zipf.close()
765
766 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
767 self.assertEqual(len(zipf2.namelist()), numfiles)
768 for i in range(numfiles):
769 content = zipf2.read("foo%08d" % i).decode('ascii')
770 self.assertEqual(content, "%d" % (i**3 % 57))
771 zipf2.close()
772
773 def test_too_many_files_append(self):
774 zipf = zipfile.ZipFile(TESTFN, "w", self.compression,
775 allowZip64=False)
776 zipf.debug = 100
777 numfiles = 9
778 for i in range(numfiles):
779 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
780 self.assertEqual(len(zipf.namelist()), numfiles)
781 with self.assertRaises(zipfile.LargeZipFile):
782 zipf.writestr("foo%08d" % numfiles, b'')
783 self.assertEqual(len(zipf.namelist()), numfiles)
784 zipf.close()
785
786 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
787 allowZip64=False)
788 zipf.debug = 100
789 self.assertEqual(len(zipf.namelist()), numfiles)
790 with self.assertRaises(zipfile.LargeZipFile):
791 zipf.writestr("foo%08d" % numfiles, b'')
792 self.assertEqual(len(zipf.namelist()), numfiles)
793 zipf.close()
794
795 zipf = zipfile.ZipFile(TESTFN, "a", self.compression,
796 allowZip64=True)
797 zipf.debug = 100
798 self.assertEqual(len(zipf.namelist()), numfiles)
799 numfiles2 = 15
800 for i in range(numfiles, numfiles2):
801 zipf.writestr("foo%08d" % i, "%d" % (i**3 % 57))
802 self.assertEqual(len(zipf.namelist()), numfiles2)
803 zipf.close()
804
805 zipf2 = zipfile.ZipFile(TESTFN, "r", self.compression)
806 self.assertEqual(len(zipf2.namelist()), numfiles2)
807 for i in range(numfiles2):
808 content = zipf2.read("foo%08d" % i).decode('ascii')
809 self.assertEqual(content, "%d" % (i**3 % 57))
810 zipf2.close()
811
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300812 def tearDown(self):
813 zipfile.ZIP64_LIMIT = self._limit
Serhiy Storchaka026a3992014-09-23 22:27:34 +0300814 zipfile.ZIP_FILECOUNT_LIMIT = self._filecount_limit
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300815 unlink(TESTFN)
816 unlink(TESTFN2)
817
818
819class StoredTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
820 unittest.TestCase):
821 compression = zipfile.ZIP_STORED
822
823 def large_file_exception_test(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200824 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300825 self.assertRaises(zipfile.LargeZipFile,
826 zipfp.write, TESTFN, "another.name")
827
828 def large_file_exception_test2(self, f, compression):
Serhiy Storchaka235c5e02013-11-23 15:55:38 +0200829 with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +0300830 self.assertRaises(zipfile.LargeZipFile,
831 zipfp.writestr, "another.name", self.data)
832
833 def test_large_file_exception(self):
834 for f in get_files(self):
835 self.large_file_exception_test(f, zipfile.ZIP_STORED)
836 self.large_file_exception_test2(f, zipfile.ZIP_STORED)
837
838 def test_absolute_arcnames(self):
839 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED,
840 allowZip64=True) as zipfp:
841 zipfp.write(TESTFN, "/absolute")
842
843 with zipfile.ZipFile(TESTFN2, "r", zipfile.ZIP_STORED) as zipfp:
844 self.assertEqual(zipfp.namelist(), ["absolute"])
845
Serhiy Storchaka9bdb7be2018-09-17 15:36:40 +0300846 def test_append(self):
847 # Test that appending to the Zip64 archive doesn't change
848 # extra fields of existing entries.
849 with zipfile.ZipFile(TESTFN2, "w", allowZip64=True) as zipfp:
850 zipfp.writestr("strfile", self.data)
851 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
852 zinfo = zipfp.getinfo("strfile")
853 extra = zinfo.extra
854 with zipfile.ZipFile(TESTFN2, "a", allowZip64=True) as zipfp:
855 zipfp.writestr("strfile2", self.data)
856 with zipfile.ZipFile(TESTFN2, "r", allowZip64=True) as zipfp:
857 zinfo = zipfp.getinfo("strfile")
858 self.assertEqual(zinfo.extra, extra)
859
Daniel Hillierda6ce582019-10-29 18:24:18 +1100860 def make_zip64_file(
861 self, file_size_64_set=False, file_size_extra=False,
862 compress_size_64_set=False, compress_size_extra=False,
863 header_offset_64_set=False, header_offset_extra=False,
864 ):
865 """Generate bytes sequence for a zip with (incomplete) zip64 data.
866
867 The actual values (not the zip 64 0xffffffff values) stored in the file
868 are:
869 file_size: 8
870 compress_size: 8
871 header_offset: 0
872 """
873 actual_size = 8
874 actual_header_offset = 0
875 local_zip64_fields = []
876 central_zip64_fields = []
877
878 file_size = actual_size
879 if file_size_64_set:
880 file_size = 0xffffffff
881 if file_size_extra:
882 local_zip64_fields.append(actual_size)
883 central_zip64_fields.append(actual_size)
884 file_size = struct.pack("<L", file_size)
885
886 compress_size = actual_size
887 if compress_size_64_set:
888 compress_size = 0xffffffff
889 if compress_size_extra:
890 local_zip64_fields.append(actual_size)
891 central_zip64_fields.append(actual_size)
892 compress_size = struct.pack("<L", compress_size)
893
894 header_offset = actual_header_offset
895 if header_offset_64_set:
896 header_offset = 0xffffffff
897 if header_offset_extra:
898 central_zip64_fields.append(actual_header_offset)
899 header_offset = struct.pack("<L", header_offset)
900
901 local_extra = struct.pack(
902 '<HH' + 'Q'*len(local_zip64_fields),
903 0x0001,
904 8*len(local_zip64_fields),
905 *local_zip64_fields
906 )
907
908 central_extra = struct.pack(
909 '<HH' + 'Q'*len(central_zip64_fields),
910 0x0001,
911 8*len(central_zip64_fields),
912 *central_zip64_fields
913 )
914
915 central_dir_size = struct.pack('<Q', 58 + 8 * len(central_zip64_fields))
916 offset_to_central_dir = struct.pack('<Q', 50 + 8 * len(local_zip64_fields))
917
918 local_extra_length = struct.pack("<H", 4 + 8 * len(local_zip64_fields))
919 central_extra_length = struct.pack("<H", 4 + 8 * len(central_zip64_fields))
920
921 filename = b"test.txt"
922 content = b"test1234"
923 filename_length = struct.pack("<H", len(filename))
924 zip64_contents = (
925 # Local file header
926 b"PK\x03\x04\x14\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
927 + compress_size
928 + file_size
929 + filename_length
930 + local_extra_length
931 + filename
932 + local_extra
933 + content
934 # Central directory:
935 + b"PK\x01\x02-\x03-\x00\x00\x00\x00\x00\x00\x00!\x00\x9e%\xf5\xaf"
936 + compress_size
937 + file_size
938 + filename_length
939 + central_extra_length
940 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01"
941 + header_offset
942 + filename
943 + central_extra
944 # Zip64 end of central directory
945 + b"PK\x06\x06,\x00\x00\x00\x00\x00\x00\x00-\x00-"
946 + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00"
947 + b"\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
948 + central_dir_size
949 + offset_to_central_dir
950 # Zip64 end of central directory locator
951 + b"PK\x06\x07\x00\x00\x00\x00l\x00\x00\x00\x00\x00\x00\x00\x01"
952 + b"\x00\x00\x00"
953 # end of central directory
954 + b"PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00:\x00\x00\x002\x00"
955 + b"\x00\x00\x00\x00"
956 )
957 return zip64_contents
958
959 def test_bad_zip64_extra(self):
960 """Missing zip64 extra records raises an exception.
961
962 There are 4 fields that the zip64 format handles (the disk number is
963 not used in this module and so is ignored here). According to the zip
964 spec:
965 The order of the fields in the zip64 extended
966 information record is fixed, but the fields MUST
967 only appear if the corresponding Local or Central
968 directory record field is set to 0xFFFF or 0xFFFFFFFF.
969
970 If the zip64 extra content doesn't contain enough entries for the
971 number of fields marked with 0xFFFF or 0xFFFFFFFF, we raise an error.
972 This test mismatches the length of the zip64 extra field and the number
973 of fields set to indicate the presence of zip64 data.
974 """
975 # zip64 file size present, no fields in extra, expecting one, equals
976 # missing file size.
977 missing_file_size_extra = self.make_zip64_file(
978 file_size_64_set=True,
979 )
980 with self.assertRaises(zipfile.BadZipFile) as e:
981 zipfile.ZipFile(io.BytesIO(missing_file_size_extra))
982 self.assertIn('file size', str(e.exception).lower())
983
984 # zip64 file size present, zip64 compress size present, one field in
985 # extra, expecting two, equals missing compress size.
986 missing_compress_size_extra = self.make_zip64_file(
987 file_size_64_set=True,
988 file_size_extra=True,
989 compress_size_64_set=True,
990 )
991 with self.assertRaises(zipfile.BadZipFile) as e:
992 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
993 self.assertIn('compress size', str(e.exception).lower())
994
995 # zip64 compress size present, no fields in extra, expecting one,
996 # equals missing compress size.
997 missing_compress_size_extra = self.make_zip64_file(
998 compress_size_64_set=True,
999 )
1000 with self.assertRaises(zipfile.BadZipFile) as e:
1001 zipfile.ZipFile(io.BytesIO(missing_compress_size_extra))
1002 self.assertIn('compress size', str(e.exception).lower())
1003
1004 # zip64 file size present, zip64 compress size present, zip64 header
1005 # offset present, two fields in extra, expecting three, equals missing
1006 # header offset
1007 missing_header_offset_extra = self.make_zip64_file(
1008 file_size_64_set=True,
1009 file_size_extra=True,
1010 compress_size_64_set=True,
1011 compress_size_extra=True,
1012 header_offset_64_set=True,
1013 )
1014 with self.assertRaises(zipfile.BadZipFile) as e:
1015 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1016 self.assertIn('header offset', str(e.exception).lower())
1017
1018 # zip64 compress size present, zip64 header offset present, one field
1019 # in extra, expecting two, equals missing header offset
1020 missing_header_offset_extra = self.make_zip64_file(
1021 file_size_64_set=False,
1022 compress_size_64_set=True,
1023 compress_size_extra=True,
1024 header_offset_64_set=True,
1025 )
1026 with self.assertRaises(zipfile.BadZipFile) as e:
1027 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1028 self.assertIn('header offset', str(e.exception).lower())
1029
1030 # zip64 file size present, zip64 header offset present, one field in
1031 # extra, expecting two, equals missing header offset
1032 missing_header_offset_extra = self.make_zip64_file(
1033 file_size_64_set=True,
1034 file_size_extra=True,
1035 compress_size_64_set=False,
1036 header_offset_64_set=True,
1037 )
1038 with self.assertRaises(zipfile.BadZipFile) as e:
1039 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1040 self.assertIn('header offset', str(e.exception).lower())
1041
1042 # zip64 header offset present, no fields in extra, expecting one,
1043 # equals missing header offset
1044 missing_header_offset_extra = self.make_zip64_file(
1045 file_size_64_set=False,
1046 compress_size_64_set=False,
1047 header_offset_64_set=True,
1048 )
1049 with self.assertRaises(zipfile.BadZipFile) as e:
1050 zipfile.ZipFile(io.BytesIO(missing_header_offset_extra))
1051 self.assertIn('header offset', str(e.exception).lower())
1052
1053 def test_generated_valid_zip64_extra(self):
1054 # These values are what is set in the make_zip64_file method.
1055 expected_file_size = 8
1056 expected_compress_size = 8
1057 expected_header_offset = 0
1058 expected_content = b"test1234"
1059
1060 # Loop through the various valid combinations of zip64 masks
1061 # present and extra fields present.
1062 params = (
1063 {"file_size_64_set": True, "file_size_extra": True},
1064 {"compress_size_64_set": True, "compress_size_extra": True},
1065 {"header_offset_64_set": True, "header_offset_extra": True},
1066 )
1067
1068 for r in range(1, len(params) + 1):
1069 for combo in itertools.combinations(params, r):
1070 kwargs = {}
1071 for c in combo:
1072 kwargs.update(c)
1073 with zipfile.ZipFile(io.BytesIO(self.make_zip64_file(**kwargs))) as zf:
1074 zinfo = zf.infolist()[0]
1075 self.assertEqual(zinfo.file_size, expected_file_size)
1076 self.assertEqual(zinfo.compress_size, expected_compress_size)
1077 self.assertEqual(zinfo.header_offset, expected_header_offset)
1078 self.assertEqual(zf.read(zinfo), expected_content)
1079
1080
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001081@requires_zlib
1082class DeflateTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1083 unittest.TestCase):
1084 compression = zipfile.ZIP_DEFLATED
1085
1086@requires_bz2
1087class Bzip2TestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1088 unittest.TestCase):
1089 compression = zipfile.ZIP_BZIP2
1090
1091@requires_lzma
1092class LzmaTestZip64InSmallFiles(AbstractTestZip64InSmallFiles,
1093 unittest.TestCase):
1094 compression = zipfile.ZIP_LZMA
1095
1096
Serhiy Storchaka4c0d9ea2017-04-12 16:03:23 +03001097class AbstractWriterTests:
1098
1099 def tearDown(self):
1100 unlink(TESTFN2)
1101
1102 def test_close_after_close(self):
1103 data = b'content'
1104 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1105 w = zipf.open('test', 'w')
1106 w.write(data)
1107 w.close()
1108 self.assertTrue(w.closed)
1109 w.close()
1110 self.assertTrue(w.closed)
1111 self.assertEqual(zipf.read('test'), data)
1112
1113 def test_write_after_close(self):
1114 data = b'content'
1115 with zipfile.ZipFile(TESTFN2, "w", self.compression) as zipf:
1116 w = zipf.open('test', 'w')
1117 w.write(data)
1118 w.close()
1119 self.assertTrue(w.closed)
1120 self.assertRaises(ValueError, w.write, b'')
1121 self.assertEqual(zipf.read('test'), data)
1122
1123class StoredWriterTests(AbstractWriterTests, unittest.TestCase):
1124 compression = zipfile.ZIP_STORED
1125
1126@requires_zlib
1127class DeflateWriterTests(AbstractWriterTests, unittest.TestCase):
1128 compression = zipfile.ZIP_DEFLATED
1129
1130@requires_bz2
1131class Bzip2WriterTests(AbstractWriterTests, unittest.TestCase):
1132 compression = zipfile.ZIP_BZIP2
1133
1134@requires_lzma
1135class LzmaWriterTests(AbstractWriterTests, unittest.TestCase):
1136 compression = zipfile.ZIP_LZMA
1137
1138
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001139class PyZipFileTests(unittest.TestCase):
1140 def assertCompiledIn(self, name, namelist):
1141 if name + 'o' not in namelist:
1142 self.assertIn(name + 'c', namelist)
1143
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001144 def requiresWriteAccess(self, path):
Berker Peksage1efc072015-02-16 04:36:18 +02001145 # effective_ids unavailable on windows
1146 if not os.access(path, os.W_OK,
1147 effective_ids=os.access in os.supports_effective_ids):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001148 self.skipTest('requires write access to the installed location')
Serhiy Storchakad86a6ef2015-09-19 10:55:20 +03001149 filename = os.path.join(path, 'test_zipfile.try')
1150 try:
1151 fd = os.open(filename, os.O_WRONLY | os.O_CREAT)
1152 os.close(fd)
1153 except Exception:
1154 self.skipTest('requires write access to the installed location')
1155 unlink(filename)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001156
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001157 def test_write_pyfile(self):
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001158 self.requiresWriteAccess(os.path.dirname(__file__))
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001159 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1160 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001161 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001162 path_split = fn.split(os.sep)
1163 if os.altsep is not None:
1164 path_split.extend(fn.split(os.altsep))
1165 if '__pycache__' in path_split:
Serhiy Storchaka9068e4d2013-07-22 21:02:14 +03001166 fn = importlib.util.source_from_cache(fn)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001167 else:
1168 fn = fn[:-1]
1169
1170 zipfp.writepy(fn)
1171
1172 bn = os.path.basename(fn)
1173 self.assertNotIn(bn, zipfp.namelist())
1174 self.assertCompiledIn(bn, zipfp.namelist())
1175
1176 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1177 fn = __file__
Brett Cannonf299abd2015-04-13 14:21:02 -04001178 if fn.endswith('.pyc'):
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001179 fn = fn[:-1]
1180
1181 zipfp.writepy(fn, "testpackage")
1182
1183 bn = "%s/%s" % ("testpackage", os.path.basename(fn))
1184 self.assertNotIn(bn, zipfp.namelist())
1185 self.assertCompiledIn(bn, zipfp.namelist())
1186
1187 def test_write_python_package(self):
1188 import email
1189 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001190 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001191
1192 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1193 zipfp.writepy(packagedir)
1194
1195 # Check for a couple of modules at different levels of the
1196 # hierarchy
1197 names = zipfp.namelist()
1198 self.assertCompiledIn('email/__init__.py', names)
1199 self.assertCompiledIn('email/mime/text.py', names)
1200
Christian Tismer59202e52013-10-21 03:59:23 +02001201 def test_write_filtered_python_package(self):
1202 import test
1203 packagedir = os.path.dirname(test.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001204 self.requiresWriteAccess(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001205
1206 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1207
Christian Tismer59202e52013-10-21 03:59:23 +02001208 # first make sure that the test folder gives error messages
Georg Brandla6065422013-10-21 08:29:29 +02001209 # (on the badsyntax_... files)
1210 with captured_stdout() as reportSIO:
1211 zipfp.writepy(packagedir)
Christian Tismer59202e52013-10-21 03:59:23 +02001212 reportStr = reportSIO.getvalue()
1213 self.assertTrue('SyntaxError' in reportStr)
1214
Christian Tismer410d9312013-10-22 04:09:28 +02001215 # then check that the filter works on the whole package
Georg Brandla6065422013-10-21 08:29:29 +02001216 with captured_stdout() as reportSIO:
1217 zipfp.writepy(packagedir, filterfunc=lambda whatever: False)
Christian Tismer59202e52013-10-21 03:59:23 +02001218 reportStr = reportSIO.getvalue()
1219 self.assertTrue('SyntaxError' not in reportStr)
1220
Christian Tismer410d9312013-10-22 04:09:28 +02001221 # then check that the filter works on individual files
Larry Hastings7e63b362015-05-08 06:54:58 -07001222 def filter(path):
1223 return not os.path.basename(path).startswith("bad")
Serhiy Storchakac46d1fa2014-01-20 21:59:33 +02001224 with captured_stdout() as reportSIO, self.assertWarns(UserWarning):
Larry Hastings7e63b362015-05-08 06:54:58 -07001225 zipfp.writepy(packagedir, filterfunc=filter)
Christian Tismer410d9312013-10-22 04:09:28 +02001226 reportStr = reportSIO.getvalue()
1227 if reportStr:
1228 print(reportStr)
1229 self.assertTrue('SyntaxError' not in reportStr)
1230
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001231 def test_write_with_optimization(self):
1232 import email
1233 packagedir = os.path.dirname(email.__file__)
Serhiy Storchakadb724fe2015-02-14 23:04:35 +02001234 self.requiresWriteAccess(packagedir)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001235 optlevel = 1 if __debug__ else 0
Brett Cannonf299abd2015-04-13 14:21:02 -04001236 ext = '.pyc'
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001237
1238 with TemporaryFile() as t, \
Christian Tismer59202e52013-10-21 03:59:23 +02001239 zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001240 zipfp.writepy(packagedir)
1241
1242 names = zipfp.namelist()
1243 self.assertIn('email/__init__' + ext, names)
1244 self.assertIn('email/mime/text' + ext, names)
1245
1246 def test_write_python_directory(self):
1247 os.mkdir(TESTFN2)
1248 try:
1249 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1250 fp.write("print(42)\n")
1251
1252 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1253 fp.write("print(42 * 42)\n")
1254
1255 with open(os.path.join(TESTFN2, "mod2.txt"), "w") as fp:
1256 fp.write("bla bla bla\n")
1257
1258 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1259 zipfp.writepy(TESTFN2)
1260
1261 names = zipfp.namelist()
1262 self.assertCompiledIn('mod1.py', names)
1263 self.assertCompiledIn('mod2.py', names)
1264 self.assertNotIn('mod2.txt', names)
1265
1266 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001267 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001268
Christian Tismer410d9312013-10-22 04:09:28 +02001269 def test_write_python_directory_filtered(self):
1270 os.mkdir(TESTFN2)
1271 try:
1272 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1273 fp.write("print(42)\n")
1274
1275 with open(os.path.join(TESTFN2, "mod2.py"), "w") as fp:
1276 fp.write("print(42 * 42)\n")
1277
1278 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1279 zipfp.writepy(TESTFN2, filterfunc=lambda fn:
1280 not fn.endswith('mod2.py'))
1281
1282 names = zipfp.namelist()
1283 self.assertCompiledIn('mod1.py', names)
1284 self.assertNotIn('mod2.py', names)
1285
1286 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001287 rmtree(TESTFN2)
Christian Tismer410d9312013-10-22 04:09:28 +02001288
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001289 def test_write_non_pyfile(self):
1290 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1291 with open(TESTFN, 'w') as f:
1292 f.write('most definitely not a python file')
1293 self.assertRaises(RuntimeError, zipfp.writepy, TESTFN)
Victor Stinner88b215e2014-09-04 00:51:09 +02001294 unlink(TESTFN)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001295
1296 def test_write_pyfile_bad_syntax(self):
1297 os.mkdir(TESTFN2)
1298 try:
1299 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1300 fp.write("Bad syntax in python file\n")
1301
1302 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1303 # syntax errors are printed to stdout
1304 with captured_stdout() as s:
1305 zipfp.writepy(os.path.join(TESTFN2, "mod1.py"))
1306
1307 self.assertIn("SyntaxError", s.getvalue())
1308
1309 # as it will not have compiled the python file, it will
Brett Cannonf299abd2015-04-13 14:21:02 -04001310 # include the .py file not .pyc
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001311 names = zipfp.namelist()
1312 self.assertIn('mod1.py', names)
1313 self.assertNotIn('mod1.pyc', names)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001314
1315 finally:
Victor Stinner57004c62014-09-04 00:49:01 +02001316 rmtree(TESTFN2)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001317
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001318 def test_write_pathlike(self):
1319 os.mkdir(TESTFN2)
1320 try:
1321 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
1322 fp.write("print(42)\n")
1323
1324 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
1325 zipfp.writepy(pathlib.Path(TESTFN2) / "mod1.py")
1326 names = zipfp.namelist()
1327 self.assertCompiledIn('mod1.py', names)
1328 finally:
1329 rmtree(TESTFN2)
1330
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001331
1332class ExtractTests(unittest.TestCase):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001333
1334 def make_test_file(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001335 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1336 for fpath, fdata in SMALL_TEST_DATA:
1337 zipfp.writestr(fpath, fdata)
Christian Heimes790c8232008-01-07 21:14:23 +00001338
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001339 def test_extract(self):
1340 with temp_cwd():
1341 self.make_test_file()
1342 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1343 for fpath, fdata in SMALL_TEST_DATA:
1344 writtenfile = zipfp.extract(fpath)
1345
1346 # make sure it was written to the right place
1347 correctfile = os.path.join(os.getcwd(), fpath)
1348 correctfile = os.path.normpath(correctfile)
1349
1350 self.assertEqual(writtenfile, correctfile)
1351
1352 # make sure correct data is in correct file
1353 with open(writtenfile, "rb") as f:
1354 self.assertEqual(fdata.encode(), f.read())
1355
1356 unlink(writtenfile)
1357
1358 def _test_extract_with_target(self, target):
1359 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001360 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1361 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001362 writtenfile = zipfp.extract(fpath, target)
Christian Heimes790c8232008-01-07 21:14:23 +00001363
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001364 # make sure it was written to the right place
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001365 correctfile = os.path.join(target, fpath)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001366 correctfile = os.path.normpath(correctfile)
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001367 self.assertTrue(os.path.samefile(writtenfile, correctfile), (writtenfile, target))
Christian Heimes790c8232008-01-07 21:14:23 +00001368
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001369 # make sure correct data is in correct file
Brian Curtin8fb9b862010-11-18 02:15:28 +00001370 with open(writtenfile, "rb") as f:
1371 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001372
Victor Stinner88b215e2014-09-04 00:51:09 +02001373 unlink(writtenfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001374
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001375 unlink(TESTFN2)
1376
1377 def test_extract_with_target(self):
1378 with temp_dir() as extdir:
1379 self._test_extract_with_target(extdir)
1380
1381 def test_extract_with_target_pathlike(self):
1382 with temp_dir() as extdir:
1383 self._test_extract_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001384
Ezio Melottiafd0d112009-07-15 17:17:17 +00001385 def test_extract_all(self):
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001386 with temp_cwd():
1387 self.make_test_file()
1388 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1389 zipfp.extractall()
1390 for fpath, fdata in SMALL_TEST_DATA:
1391 outfile = os.path.join(os.getcwd(), fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001392
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001393 with open(outfile, "rb") as f:
1394 self.assertEqual(fdata.encode(), f.read())
1395
1396 unlink(outfile)
1397
1398 def _test_extract_all_with_target(self, target):
1399 self.make_test_file()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001400 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001401 zipfp.extractall(target)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001402 for fpath, fdata in SMALL_TEST_DATA:
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001403 outfile = os.path.join(target, fpath)
Christian Heimes790c8232008-01-07 21:14:23 +00001404
Brian Curtin8fb9b862010-11-18 02:15:28 +00001405 with open(outfile, "rb") as f:
1406 self.assertEqual(fdata.encode(), f.read())
Christian Heimes790c8232008-01-07 21:14:23 +00001407
Victor Stinner88b215e2014-09-04 00:51:09 +02001408 unlink(outfile)
Christian Heimes790c8232008-01-07 21:14:23 +00001409
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001410 unlink(TESTFN2)
1411
1412 def test_extract_all_with_target(self):
1413 with temp_dir() as extdir:
1414 self._test_extract_all_with_target(extdir)
1415
1416 def test_extract_all_with_target_pathlike(self):
1417 with temp_dir() as extdir:
1418 self._test_extract_all_with_target(pathlib.Path(extdir))
Christian Heimes790c8232008-01-07 21:14:23 +00001419
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001420 def check_file(self, filename, content):
1421 self.assertTrue(os.path.isfile(filename))
1422 with open(filename, 'rb') as f:
1423 self.assertEqual(f.read(), content)
1424
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001425 def test_sanitize_windows_name(self):
1426 san = zipfile.ZipFile._sanitize_windows_name
1427 # Passing pathsep in allows this test to work regardless of platform.
1428 self.assertEqual(san(r',,?,C:,foo,bar/z', ','), r'_,C_,foo,bar/z')
1429 self.assertEqual(san(r'a\b,c<d>e|f"g?h*i', ','), r'a\b,c_d_e_f_g_h_i')
1430 self.assertEqual(san('../../foo../../ba..r', '/'), r'foo/ba..r')
1431
1432 def test_extract_hackers_arcnames_common_cases(self):
1433 common_hacknames = [
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001434 ('../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'),
1440 ('/foo/../bar', 'foo/bar'),
1441 ('/foo/../../bar', 'foo/bar'),
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001442 ]
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001443 self._test_extract_hackers_arcnames(common_hacknames)
1444
1445 @unittest.skipIf(os.path.sep != '\\', 'Requires \\ as path separator.')
1446 def test_extract_hackers_arcnames_windows_only(self):
1447 """Test combination of path fixing and windows name sanitization."""
1448 windows_hacknames = [
Christian Tismer59202e52013-10-21 03:59:23 +02001449 (r'..\foo\bar', 'foo/bar'),
1450 (r'..\/foo\/bar', 'foo/bar'),
1451 (r'foo/\..\/bar', 'foo/bar'),
1452 (r'foo\/../\bar', 'foo/bar'),
1453 (r'C:foo/bar', 'foo/bar'),
1454 (r'C:/foo/bar', 'foo/bar'),
1455 (r'C://foo/bar', 'foo/bar'),
1456 (r'C:\foo\bar', 'foo/bar'),
1457 (r'//conky/mountpoint/foo/bar', 'foo/bar'),
1458 (r'\\conky\mountpoint\foo\bar', 'foo/bar'),
1459 (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
1460 (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
1461 (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
1462 (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
1463 (r'//?/C:/foo/bar', 'foo/bar'),
1464 (r'\\?\C:\foo\bar', 'foo/bar'),
1465 (r'C:/../C:/foo/bar', 'C_/foo/bar'),
1466 (r'a:b\c<d>e|f"g?h*i', 'b/c_d_e_f_g_h_i'),
1467 ('../../foo../../ba..r', 'foo/ba..r'),
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001468 ]
1469 self._test_extract_hackers_arcnames(windows_hacknames)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001470
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001471 @unittest.skipIf(os.path.sep != '/', r'Requires / as path separator.')
1472 def test_extract_hackers_arcnames_posix_only(self):
1473 posix_hacknames = [
1474 ('//foo/bar', 'foo/bar'),
1475 ('../../foo../../ba..r', 'foo../ba..r'),
1476 (r'foo/..\bar', r'foo/..\bar'),
1477 ]
1478 self._test_extract_hackers_arcnames(posix_hacknames)
1479
1480 def _test_extract_hackers_arcnames(self, hacknames):
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001481 for arcname, fixedname in hacknames:
1482 content = b'foobar' + arcname.encode()
1483 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp:
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001484 zinfo = zipfile.ZipInfo()
1485 # preserve backslashes
1486 zinfo.filename = arcname
1487 zinfo.external_attr = 0o600 << 16
1488 zipfp.writestr(zinfo, content)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001489
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001490 arcname = arcname.replace(os.sep, "/")
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001491 targetpath = os.path.join('target', 'subdir', 'subsub')
1492 correctfile = os.path.join(targetpath, *fixedname.split('/'))
1493
1494 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1495 writtenfile = zipfp.extract(arcname, targetpath)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001496 self.assertEqual(writtenfile, correctfile,
Gregory P. Smith09aa7522013-02-03 00:36:32 -08001497 msg='extract %r: %r != %r' %
1498 (arcname, writtenfile, correctfile))
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001499 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001500 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001501
1502 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1503 zipfp.extractall(targetpath)
1504 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001505 rmtree('target')
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001506
1507 correctfile = os.path.join(os.getcwd(), *fixedname.split('/'))
1508
1509 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1510 writtenfile = zipfp.extract(arcname)
Serhiy Storchakae5e64442013-02-02 19:50:59 +02001511 self.assertEqual(writtenfile, correctfile,
1512 msg="extract %r" % arcname)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001513 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001514 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001515
1516 with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
1517 zipfp.extractall()
1518 self.check_file(correctfile, content)
Victor Stinner57004c62014-09-04 00:49:01 +02001519 rmtree(fixedname.split('/')[0])
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001520
Victor Stinner88b215e2014-09-04 00:51:09 +02001521 unlink(TESTFN2)
Gregory P. Smithb47acbf2013-02-01 11:22:43 -08001522
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001523
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001524class OtherTests(unittest.TestCase):
1525 def test_open_via_zip_info(self):
1526 # Create the ZIP archive
1527 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
1528 zipfp.writestr("name", "foo")
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001529 with self.assertWarns(UserWarning):
1530 zipfp.writestr("name", "bar")
1531 self.assertEqual(zipfp.namelist(), ["name"] * 2)
Ronald Oussorenee5c8852010-02-07 20:24:02 +00001532
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001533 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1534 infos = zipfp.infolist()
1535 data = b""
1536 for info in infos:
1537 with zipfp.open(info) as zipopen:
1538 data += zipopen.read()
1539 self.assertIn(data, {b"foobar", b"barfoo"})
1540 data = b""
1541 for info in infos:
1542 data += zipfp.read(info)
1543 self.assertIn(data, {b"foobar", b"barfoo"})
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02001544
Gregory P. Smithb0d9ca92009-07-07 05:06:04 +00001545 def test_writestr_extended_local_header_issue1202(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001546 with zipfile.ZipFile(TESTFN2, 'w') as orig_zip:
1547 for data in 'abcdefghijklmnop':
1548 zinfo = zipfile.ZipInfo(data)
1549 zinfo.flag_bits |= 0x08 # Include an extended local header.
1550 orig_zip.writestr(zinfo, data)
1551
1552 def test_close(self):
1553 """Check that the zipfile is closed after the 'with' block."""
1554 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1555 for fpath, fdata in SMALL_TEST_DATA:
1556 zipfp.writestr(fpath, fdata)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001557 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1558 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001559
1560 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001561 self.assertIsNotNone(zipfp.fp, 'zipfp is not open')
1562 self.assertIsNone(zipfp.fp, 'zipfp is not closed')
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001563
1564 def test_close_on_exception(self):
1565 """Check that the zipfile is closed if an exception is raised in the
1566 'with' block."""
1567 with zipfile.ZipFile(TESTFN2, "w") as zipfp:
1568 for fpath, fdata in SMALL_TEST_DATA:
1569 zipfp.writestr(fpath, fdata)
1570
1571 try:
1572 with zipfile.ZipFile(TESTFN2, "r") as zipfp2:
Georg Brandl4d540882010-10-28 06:42:33 +00001573 raise zipfile.BadZipFile()
1574 except zipfile.BadZipFile:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001575 self.assertIsNone(zipfp2.fp, 'zipfp is not closed')
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00001576
Martin v. Löwisd099b562012-05-01 14:08:22 +02001577 def test_unsupported_version(self):
1578 # File has an extract_version of 120
1579 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 +02001580 b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00xPK\x01\x02x\x03x\x00\x00\x00\x00'
1581 b'\x00!p\xa1@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
1582 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00xPK\x05\x06'
1583 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 +03001584
Martin v. Löwisd099b562012-05-01 14:08:22 +02001585 self.assertRaises(NotImplementedError, zipfile.ZipFile,
1586 io.BytesIO(data), 'r')
1587
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001588 @requires_zlib
1589 def test_read_unicode_filenames(self):
1590 # bug #10801
1591 fname = findfile('zip_cp437_header.zip')
1592 with zipfile.ZipFile(fname) as zipfp:
1593 for name in zipfp.namelist():
1594 zipfp.open(name).close()
1595
1596 def test_write_unicode_filenames(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001597 with zipfile.ZipFile(TESTFN, "w") as zf:
1598 zf.writestr("foo.txt", "Test for unicode filename")
1599 zf.writestr("\xf6.txt", "Test for unicode filename")
Ezio Melottie9615932010-01-24 19:26:24 +00001600 self.assertIsInstance(zf.infolist()[0].filename, str)
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001601
1602 with zipfile.ZipFile(TESTFN, "r") as zf:
1603 self.assertEqual(zf.filelist[0].filename, "foo.txt")
1604 self.assertEqual(zf.filelist[1].filename, "\xf6.txt")
Martin v. Löwis8570f6a2008-05-05 17:44:38 +00001605
Serhiy Storchaka764fc9b2015-03-25 10:09:41 +02001606 def test_exclusive_create_zip_file(self):
1607 """Test exclusive creating a new zipfile."""
1608 unlink(TESTFN2)
1609 filename = 'testfile.txt'
1610 content = b'hello, world. this is some content.'
1611 with zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED) as zipfp:
1612 zipfp.writestr(filename, content)
1613 with self.assertRaises(FileExistsError):
1614 zipfile.ZipFile(TESTFN2, "x", zipfile.ZIP_STORED)
1615 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
1616 self.assertEqual(zipfp.namelist(), [filename])
1617 self.assertEqual(zipfp.read(filename), content)
1618
Ezio Melottiafd0d112009-07-15 17:17:17 +00001619 def test_create_non_existent_file_for_append(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00001620 if os.path.exists(TESTFN):
1621 os.unlink(TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001622
Thomas Wouterscf297e42007-02-23 15:07:44 +00001623 filename = 'testfile.txt'
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001624 content = b'hello, world. this is some content.'
Guido van Rossumd8faa362007-04-27 19:54:29 +00001625
Thomas Wouterscf297e42007-02-23 15:07:44 +00001626 try:
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001627 with zipfile.ZipFile(TESTFN, 'a') as zf:
1628 zf.writestr(filename, content)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001629 except OSError:
Thomas Wouterscf297e42007-02-23 15:07:44 +00001630 self.fail('Could not append data to a non-existent zip file.')
1631
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001632 self.assertTrue(os.path.exists(TESTFN))
Thomas Wouterscf297e42007-02-23 15:07:44 +00001633
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001634 with zipfile.ZipFile(TESTFN, 'r') as zf:
1635 self.assertEqual(zf.read(filename), content)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001636
Ezio Melottiafd0d112009-07-15 17:17:17 +00001637 def test_close_erroneous_file(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001638 # This test checks that the ZipFile constructor closes the file object
Ezio Melotti35386712009-12-31 13:22:41 +00001639 # it opens if there's an error in the file. If it doesn't, the
1640 # traceback holds a reference to the ZipFile object and, indirectly,
1641 # the file object.
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001642 # On Windows, this causes the os.unlink() call to fail because the
1643 # underlying file is still open. This is SF bug #412214.
1644 #
Ezio Melotti35386712009-12-31 13:22:41 +00001645 with open(TESTFN, "w") as fp:
1646 fp.write("this is not a legal zip file\n")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001647 try:
1648 zf = zipfile.ZipFile(TESTFN)
Georg Brandl4d540882010-10-28 06:42:33 +00001649 except zipfile.BadZipFile:
Guido van Rossumd8faa362007-04-27 19:54:29 +00001650 pass
1651
Ezio Melottiafd0d112009-07-15 17:17:17 +00001652 def test_is_zip_erroneous_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001653 """Check that is_zipfile() correctly identifies non-zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001654 # - passing a filename
1655 with open(TESTFN, "w") as fp:
1656 fp.write("this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001657 self.assertFalse(zipfile.is_zipfile(TESTFN))
Serhiy Storchaka8606e952017-03-08 14:37:51 +02001658 # - passing a path-like object
1659 self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN)))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001660 # - passing a file object
1661 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001662 self.assertFalse(zipfile.is_zipfile(fp))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001663 # - passing a file-like object
1664 fp = io.BytesIO()
1665 fp.write(b"this is not a legal zip file\n")
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001666 self.assertFalse(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001667 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001668 self.assertFalse(zipfile.is_zipfile(fp))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001669
Serhiy Storchakad2b15272013-01-31 15:27:07 +02001670 def test_damaged_zipfile(self):
1671 """Check that zipfiles with missing bytes at the end raise BadZipFile."""
1672 # - Create a valid zip file
1673 fp = io.BytesIO()
1674 with zipfile.ZipFile(fp, mode="w") as zipf:
1675 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1676 zipfiledata = fp.getvalue()
1677
1678 # - Now create copies of it missing the last N bytes and make sure
1679 # a BadZipFile exception is raised when we try to open it
1680 for N in range(len(zipfiledata)):
1681 fp = io.BytesIO(zipfiledata[:N])
1682 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, fp)
1683
Ezio Melottiafd0d112009-07-15 17:17:17 +00001684 def test_is_zip_valid_file(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001685 """Check that is_zipfile() correctly identifies zip files."""
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001686 # - passing a filename
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001687 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1688 zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
1689
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001690 self.assertTrue(zipfile.is_zipfile(TESTFN))
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001691 # - passing a file object
1692 with open(TESTFN, "rb") as fp:
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001693 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001694 fp.seek(0, 0)
Antoine Pitroudb5fe662008-12-27 15:50:40 +00001695 zip_contents = fp.read()
1696 # - passing a file-like object
1697 fp = io.BytesIO()
1698 fp.write(zip_contents)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001699 self.assertTrue(zipfile.is_zipfile(fp))
Ezio Melotti35386712009-12-31 13:22:41 +00001700 fp.seek(0, 0)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03001701 self.assertTrue(zipfile.is_zipfile(fp))
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001702
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001703 def test_non_existent_file_raises_OSError(self):
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001704 # make sure we don't raise an AttributeError when a partially-constructed
1705 # ZipFile instance is finalized; this tests for regression on SF tracker
1706 # bug #403871.
1707
1708 # The bug we're testing for caused an AttributeError to be raised
1709 # when a ZipFile instance was created for a file that did not
1710 # exist; the .fp member was not initialized but was needed by the
1711 # __del__() method. Since the AttributeError is in the __del__(),
1712 # it is ignored, but the user should be sufficiently annoyed by
1713 # the message on the output that regression will be noticed
1714 # quickly.
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001715 self.assertRaises(OSError, zipfile.ZipFile, TESTFN)
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001716
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001717 def test_empty_file_raises_BadZipFile(self):
1718 f = open(TESTFN, 'w')
1719 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001720 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001721
Ezio Melotti35386712009-12-31 13:22:41 +00001722 with open(TESTFN, 'w') as fp:
1723 fp.write("short file")
Georg Brandl4d540882010-10-28 06:42:33 +00001724 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)
Amaury Forgeot d'Arcbc347802009-07-28 22:18:57 +00001725
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001726 def test_closed_zip_raises_ValueError(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001727 """Verify that testzip() doesn't swallow inappropriate exceptions."""
Guido van Rossumd6ca5462007-05-22 01:29:33 +00001728 data = io.BytesIO()
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001729 with zipfile.ZipFile(data, mode="w") as zipf:
1730 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001731
Andrew Svetlov737fb892012-12-18 21:14:22 +02001732 # This is correct; calling .read on a closed ZipFile should raise
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001733 # a ValueError, and so should calling .testzip. An earlier
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001734 # version of .testzip would swallow this exception (and any other)
1735 # and report that the first file in the archive was corrupt.
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001736 self.assertRaises(ValueError, zipf.read, "foo.txt")
1737 self.assertRaises(ValueError, zipf.open, "foo.txt")
1738 self.assertRaises(ValueError, zipf.testzip)
1739 self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus")
Brian Curtin8fb9b862010-11-18 02:15:28 +00001740 with open(TESTFN, 'w') as f:
1741 f.write('zipfile test data')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001742 self.assertRaises(ValueError, zipf.write, TESTFN)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001743
Ezio Melottiafd0d112009-07-15 17:17:17 +00001744 def test_bad_constructor_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001745 """Check that bad modes passed to ZipFile constructor are caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001746 self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001747
Ezio Melottiafd0d112009-07-15 17:17:17 +00001748 def test_bad_open_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001749 """Check that bad modes passed to ZipFile.open are caught."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001750 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1751 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1752
1753 with zipfile.ZipFile(TESTFN, mode="r") as zipf:
Serhiy Storchakae670be22016-06-11 19:32:44 +03001754 # read the data to make sure the file is there
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001755 zipf.read("foo.txt")
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001756 self.assertRaises(ValueError, zipf.open, "foo.txt", "q")
Serhiy Storchakae670be22016-06-11 19:32:44 +03001757 # universal newlines support is removed
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001758 self.assertRaises(ValueError, zipf.open, "foo.txt", "U")
1759 self.assertRaises(ValueError, zipf.open, "foo.txt", "rU")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001760
Ezio Melottiafd0d112009-07-15 17:17:17 +00001761 def test_read0(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001762 """Check that calling read(0) on a ZipExtFile object returns an empty
1763 string and doesn't advance file pointer."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001764 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1765 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1766 # read the data to make sure the file is there
Brian Curtin8fb9b862010-11-18 02:15:28 +00001767 with zipf.open("foo.txt") as f:
1768 for i in range(FIXEDTEST_SIZE):
1769 self.assertEqual(f.read(0), b'')
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001770
Brian Curtin8fb9b862010-11-18 02:15:28 +00001771 self.assertEqual(f.read(), b"O, for a Muse of Fire!")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001772
Ezio Melottiafd0d112009-07-15 17:17:17 +00001773 def test_open_non_existent_item(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001774 """Check that attempting to call open() for an item that doesn't
1775 exist in the archive raises a RuntimeError."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001776 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1777 self.assertRaises(KeyError, zipf.open, "foo.txt", "r")
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001778
Ezio Melottiafd0d112009-07-15 17:17:17 +00001779 def test_bad_compression_mode(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001780 """Check that bad compression methods passed to ZipFile.open are
1781 caught."""
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001782 self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1)
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001783
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001784 def test_unsupported_compression(self):
1785 # data is declared as shrunk, but actually deflated
1786 data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00'
Christian Tismer59202e52013-10-21 03:59:23 +02001787 b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01'
1788 b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00'
1789 b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1790 b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00'
1791 b'/\x00\x00\x00!\x00\x00\x00\x00\x00')
Martin v. Löwisb3260f02012-05-01 08:38:01 +02001792 with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
1793 self.assertRaises(NotImplementedError, zipf.open, 'x')
1794
Ezio Melottiafd0d112009-07-15 17:17:17 +00001795 def test_null_byte_in_filename(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001796 """Check that a filename containing a null byte is properly
1797 terminated."""
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001798 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1799 zipf.writestr("foo.txt\x00qqq", b"O, for a Muse of Fire!")
1800 self.assertEqual(zipf.namelist(), ['foo.txt'])
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00001801
Ezio Melottiafd0d112009-07-15 17:17:17 +00001802 def test_struct_sizes(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001803 """Check that ZIP internal structure sizes are calculated correctly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001804 self.assertEqual(zipfile.sizeEndCentDir, 22)
1805 self.assertEqual(zipfile.sizeCentralDir, 46)
1806 self.assertEqual(zipfile.sizeEndCentDir64, 56)
1807 self.assertEqual(zipfile.sizeEndCentDir64Locator, 20)
1808
Ezio Melottiafd0d112009-07-15 17:17:17 +00001809 def test_comments(self):
Ezio Melotti35386712009-12-31 13:22:41 +00001810 """Check that comments on the archive are handled properly."""
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001811
1812 # check default comment is empty
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001813 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1814 self.assertEqual(zipf.comment, b'')
1815 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1816
1817 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1818 self.assertEqual(zipfr.comment, b'')
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001819
1820 # check a simple short comment
1821 comment = b'Bravely taking to his feet, he beat a very brave retreat.'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001822 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1823 zipf.comment = comment
1824 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1825 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1826 self.assertEqual(zipf.comment, comment)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001827
1828 # check a comment of max length
1829 comment2 = ''.join(['%d' % (i**3 % 10) for i in range((1 << 16)-1)])
1830 comment2 = comment2.encode("ascii")
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001831 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
1832 zipf.comment = comment2
1833 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1834
1835 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1836 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001837
1838 # check a comment that is too long is truncated
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001839 with zipfile.ZipFile(TESTFN, mode="w") as zipf:
Serhiy Storchaka9b7a1a12014-01-20 21:57:40 +02001840 with self.assertWarns(UserWarning):
1841 zipf.comment = comment2 + b'oops'
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00001842 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1843 with zipfile.ZipFile(TESTFN, mode="r") as zipfr:
1844 self.assertEqual(zipfr.comment, comment2)
Martin v. Löwisb09b8442008-07-03 14:13:42 +00001845
Antoine Pitrouc3991852012-06-30 17:31:37 +02001846 # check that comments are correctly modified in append mode
1847 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1848 zipf.comment = b"original comment"
1849 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1850 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1851 zipf.comment = b"an updated comment"
1852 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1853 self.assertEqual(zipf.comment, b"an updated comment")
1854
1855 # check that comments are correctly shortened in append mode
1856 with zipfile.ZipFile(TESTFN,mode="w") as zipf:
1857 zipf.comment = b"original comment that's longer"
1858 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1859 with zipfile.ZipFile(TESTFN,mode="a") as zipf:
1860 zipf.comment = b"shorter comment"
1861 with zipfile.ZipFile(TESTFN,mode="r") as zipf:
1862 self.assertEqual(zipf.comment, b"shorter comment")
1863
R David Murrayf50b38a2012-04-12 18:44:58 -04001864 def test_unicode_comment(self):
1865 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1866 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1867 with self.assertRaises(TypeError):
1868 zipf.comment = "this is an error"
1869
1870 def test_change_comment_in_empty_archive(self):
1871 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1872 self.assertFalse(zipf.filelist)
1873 zipf.comment = b"this is a comment"
1874 with zipfile.ZipFile(TESTFN, "r") as zipf:
1875 self.assertEqual(zipf.comment, b"this is a comment")
1876
1877 def test_change_comment_in_nonempty_archive(self):
1878 with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
1879 zipf.writestr("foo.txt", "O, for a Muse of Fire!")
1880 with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
1881 self.assertTrue(zipf.filelist)
1882 zipf.comment = b"this is a comment"
1883 with zipfile.ZipFile(TESTFN, "r") as zipf:
1884 self.assertEqual(zipf.comment, b"this is a comment")
1885
Georg Brandl268e4d42010-10-14 06:59:45 +00001886 def test_empty_zipfile(self):
1887 # Check that creating a file in 'w' or 'a' mode and closing without
1888 # adding any files to the archives creates a valid empty ZIP file
1889 zipf = zipfile.ZipFile(TESTFN, mode="w")
1890 zipf.close()
1891 try:
1892 zipf = zipfile.ZipFile(TESTFN, mode="r")
1893 except zipfile.BadZipFile:
1894 self.fail("Unable to create empty ZIP file in 'w' mode")
1895
1896 zipf = zipfile.ZipFile(TESTFN, mode="a")
1897 zipf.close()
1898 try:
1899 zipf = zipfile.ZipFile(TESTFN, mode="r")
1900 except:
1901 self.fail("Unable to create empty ZIP file in 'a' mode")
1902
1903 def test_open_empty_file(self):
1904 # Issue 1710703: Check that opening a file with less than 22 bytes
Georg Brandl4d540882010-10-28 06:42:33 +00001905 # raises a BadZipFile exception (rather than the previously unhelpful
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02001906 # OSError)
Georg Brandl268e4d42010-10-14 06:59:45 +00001907 f = open(TESTFN, 'w')
1908 f.close()
Georg Brandl4d540882010-10-28 06:42:33 +00001909 self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN, 'r')
Georg Brandl268e4d42010-10-14 06:59:45 +00001910
Senthil Kumaran29fa9d42011-10-20 01:46:00 +08001911 def test_create_zipinfo_before_1980(self):
1912 self.assertRaises(ValueError,
1913 zipfile.ZipInfo, 'seventies', (1979, 1, 1, 0, 0, 0))
1914
Mickaël Schoentgen992347d2019-09-09 15:08:54 +02001915 def test_create_empty_zipinfo_repr(self):
1916 """Before bpo-26185, repr() on empty ZipInfo object was failing."""
1917 zi = zipfile.ZipInfo(filename="empty")
1918 self.assertEqual(repr(zi), "<ZipInfo filename='empty' file_size=0>")
1919
1920 def test_create_empty_zipinfo_default_attributes(self):
1921 """Ensure all required attributes are set."""
1922 zi = zipfile.ZipInfo()
1923 self.assertEqual(zi.orig_filename, "NoName")
1924 self.assertEqual(zi.filename, "NoName")
1925 self.assertEqual(zi.date_time, (1980, 1, 1, 0, 0, 0))
1926 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
1927 self.assertEqual(zi.comment, b"")
1928 self.assertEqual(zi.extra, b"")
1929 self.assertIn(zi.create_system, (0, 3))
1930 self.assertEqual(zi.create_version, zipfile.DEFAULT_VERSION)
1931 self.assertEqual(zi.extract_version, zipfile.DEFAULT_VERSION)
1932 self.assertEqual(zi.reserved, 0)
1933 self.assertEqual(zi.flag_bits, 0)
1934 self.assertEqual(zi.volume, 0)
1935 self.assertEqual(zi.internal_attr, 0)
1936 self.assertEqual(zi.external_attr, 0)
1937
1938 # Before bpo-26185, both were missing
1939 self.assertEqual(zi.file_size, 0)
1940 self.assertEqual(zi.compress_size, 0)
1941
Gregory P. Smith0af8a862014-05-29 23:42:14 -07001942 def test_zipfile_with_short_extra_field(self):
1943 """If an extra field in the header is less than 4 bytes, skip it."""
1944 zipdata = (
1945 b'PK\x03\x04\x14\x00\x00\x00\x00\x00\x93\x9b\xad@\x8b\x9e'
1946 b'\xd9\xd3\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x03\x00ab'
1947 b'c\x00\x00\x00APK\x01\x02\x14\x03\x14\x00\x00\x00\x00'
1948 b'\x00\x93\x9b\xad@\x8b\x9e\xd9\xd3\x01\x00\x00\x00\x01\x00\x00'
1949 b'\x00\x03\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00'
1950 b'\x00\x00\x00abc\x00\x00PK\x05\x06\x00\x00\x00\x00'
1951 b'\x01\x00\x01\x003\x00\x00\x00%\x00\x00\x00\x00\x00'
1952 )
1953 with zipfile.ZipFile(io.BytesIO(zipdata), 'r') as zipf:
1954 # testzip returns the name of the first corrupt file, or None
1955 self.assertIsNone(zipf.testzip())
1956
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001957 def test_open_conflicting_handles(self):
1958 # It's only possible to open one writable file handle at a time
1959 msg1 = b"It's fun to charter an accountant!"
1960 msg2 = b"And sail the wide accountant sea"
1961 msg3 = b"To find, explore the funds offshore"
1962 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipf:
1963 with zipf.open('foo', mode='w') as w2:
1964 w2.write(msg1)
1965 with zipf.open('bar', mode='w') as w1:
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001966 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001967 zipf.open('handle', mode='w')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001968 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001969 zipf.open('foo', mode='r')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001970 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001971 zipf.writestr('str', 'abcde')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001972 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001973 zipf.write(__file__, 'file')
Serhiy Storchakab0d497c2016-09-10 21:28:07 +03001974 with self.assertRaises(ValueError):
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03001975 zipf.close()
1976 w1.write(msg2)
1977 with zipf.open('baz', mode='w') as w2:
1978 w2.write(msg3)
1979
1980 with zipfile.ZipFile(TESTFN2, 'r') as zipf:
1981 self.assertEqual(zipf.read('foo'), msg1)
1982 self.assertEqual(zipf.read('bar'), msg2)
1983 self.assertEqual(zipf.read('baz'), msg3)
1984 self.assertEqual(zipf.namelist(), ['foo', 'bar', 'baz'])
1985
John Jolly066df4f2018-01-30 01:51:35 -07001986 def test_seek_tell(self):
1987 # Test seek functionality
1988 txt = b"Where's Bruce?"
1989 bloc = txt.find(b"Bruce")
1990 # Check seek on a file
1991 with zipfile.ZipFile(TESTFN, "w") as zipf:
1992 zipf.writestr("foo.txt", txt)
1993 with zipfile.ZipFile(TESTFN, "r") as zipf:
1994 with zipf.open("foo.txt", "r") as fp:
1995 fp.seek(bloc, os.SEEK_SET)
1996 self.assertEqual(fp.tell(), bloc)
1997 fp.seek(-bloc, os.SEEK_CUR)
1998 self.assertEqual(fp.tell(), 0)
1999 fp.seek(bloc, os.SEEK_CUR)
2000 self.assertEqual(fp.tell(), bloc)
2001 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2002 fp.seek(0, os.SEEK_END)
2003 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002004 fp.seek(0, os.SEEK_SET)
2005 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002006 # Check seek on memory file
2007 data = io.BytesIO()
2008 with zipfile.ZipFile(data, mode="w") as zipf:
2009 zipf.writestr("foo.txt", txt)
2010 with zipfile.ZipFile(data, mode="r") as zipf:
2011 with zipf.open("foo.txt", "r") as fp:
2012 fp.seek(bloc, os.SEEK_SET)
2013 self.assertEqual(fp.tell(), bloc)
2014 fp.seek(-bloc, os.SEEK_CUR)
2015 self.assertEqual(fp.tell(), 0)
2016 fp.seek(bloc, os.SEEK_CUR)
2017 self.assertEqual(fp.tell(), bloc)
2018 self.assertEqual(fp.read(5), txt[bloc:bloc+5])
2019 fp.seek(0, os.SEEK_END)
2020 self.assertEqual(fp.tell(), len(txt))
Mickaël Schoentgen3f8c6912018-07-29 20:26:52 +02002021 fp.seek(0, os.SEEK_SET)
2022 self.assertEqual(fp.tell(), 0)
John Jolly066df4f2018-01-30 01:51:35 -07002023
Berker Peksag2f1b8572019-09-12 17:13:44 +03002024 @requires_bz2
2025 def test_decompress_without_3rd_party_library(self):
2026 data = b'PK\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
2027 zip_file = io.BytesIO(data)
2028 with zipfile.ZipFile(zip_file, 'w', compression=zipfile.ZIP_BZIP2) as zf:
2029 zf.writestr('a.txt', b'a')
2030 with mock.patch('zipfile.bz2', None):
2031 with zipfile.ZipFile(zip_file) as zf:
2032 self.assertRaises(RuntimeError, zf.extract, 'a.txt')
2033
Guido van Rossumd8faa362007-04-27 19:54:29 +00002034 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002035 unlink(TESTFN)
2036 unlink(TESTFN2)
2037
Thomas Wouterscf297e42007-02-23 15:07:44 +00002038
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002039class AbstractBadCrcTests:
2040 def test_testzip_with_bad_crc(self):
2041 """Tests that files with bad CRCs return their name from testzip."""
2042 zipdata = self.zip_with_bad_crc
2043
2044 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2045 # testzip returns the name of the first corrupt file, or None
2046 self.assertEqual('afile', zipf.testzip())
2047
2048 def test_read_with_bad_crc(self):
2049 """Tests that files with bad CRCs raise a BadZipFile exception when read."""
2050 zipdata = self.zip_with_bad_crc
2051
2052 # Using ZipFile.read()
2053 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2054 self.assertRaises(zipfile.BadZipFile, zipf.read, 'afile')
2055
2056 # Using ZipExtFile.read()
2057 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2058 with zipf.open('afile', 'r') as corrupt_file:
2059 self.assertRaises(zipfile.BadZipFile, corrupt_file.read)
2060
2061 # Same with small reads (in order to exercise the buffering logic)
2062 with zipfile.ZipFile(io.BytesIO(zipdata), mode="r") as zipf:
2063 with zipf.open('afile', 'r') as corrupt_file:
2064 corrupt_file.MIN_READ_SIZE = 2
2065 with self.assertRaises(zipfile.BadZipFile):
2066 while corrupt_file.read(2):
2067 pass
2068
2069
2070class StoredBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2071 compression = zipfile.ZIP_STORED
2072 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002073 b'PK\003\004\024\0\0\0\0\0 \213\212;:r'
2074 b'\253\377\f\0\0\0\f\0\0\0\005\0\0\000af'
2075 b'ilehello,AworldP'
2076 b'K\001\002\024\003\024\0\0\0\0\0 \213\212;:'
2077 b'r\253\377\f\0\0\0\f\0\0\0\005\0\0\0\0'
2078 b'\0\0\0\0\0\0\0\200\001\0\0\0\000afi'
2079 b'lePK\005\006\0\0\0\0\001\0\001\0003\000'
2080 b'\0\0/\0\0\0\0\0')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002081
2082@requires_zlib
2083class DeflateBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2084 compression = zipfile.ZIP_DEFLATED
2085 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002086 b'PK\x03\x04\x14\x00\x00\x00\x08\x00n}\x0c=FA'
2087 b'KE\x10\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2088 b'ile\xcbH\xcd\xc9\xc9W(\xcf/\xcaI\xc9\xa0'
2089 b'=\x13\x00PK\x01\x02\x14\x03\x14\x00\x00\x00\x08\x00n'
2090 b'}\x0c=FAKE\x10\x00\x00\x00n\x00\x00\x00\x05'
2091 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00'
2092 b'\x00afilePK\x05\x06\x00\x00\x00\x00\x01\x00'
2093 b'\x01\x003\x00\x00\x003\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002094
2095@requires_bz2
2096class Bzip2BadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2097 compression = zipfile.ZIP_BZIP2
2098 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002099 b'PK\x03\x04\x14\x03\x00\x00\x0c\x00nu\x0c=FA'
2100 b'KE8\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2101 b'ileBZh91AY&SY\xd4\xa8\xca'
2102 b'\x7f\x00\x00\x0f\x11\x80@\x00\x06D\x90\x80 \x00 \xa5'
2103 b'P\xd9!\x03\x03\x13\x13\x13\x89\xa9\xa9\xc2u5:\x9f'
2104 b'\x8b\xb9"\x9c(HjTe?\x80PK\x01\x02\x14'
2105 b'\x03\x14\x03\x00\x00\x0c\x00nu\x0c=FAKE8'
2106 b'\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00'
2107 b'\x00 \x80\x80\x81\x00\x00\x00\x00afilePK'
2108 b'\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00\x00[\x00'
2109 b'\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002110
2111@requires_lzma
2112class LzmaBadCrcTests(AbstractBadCrcTests, unittest.TestCase):
2113 compression = zipfile.ZIP_LZMA
2114 zip_with_bad_crc = (
Christian Tismer59202e52013-10-21 03:59:23 +02002115 b'PK\x03\x04\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2116 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00af'
2117 b'ile\t\x04\x05\x00]\x00\x00\x00\x04\x004\x19I'
2118 b'\xee\x8d\xe9\x17\x89:3`\tq!.8\x00PK'
2119 b'\x01\x02\x14\x03\x14\x03\x00\x00\x0e\x00nu\x0c=FA'
2120 b'KE\x1b\x00\x00\x00n\x00\x00\x00\x05\x00\x00\x00\x00\x00'
2121 b'\x00\x00\x00\x00 \x80\x80\x81\x00\x00\x00\x00afil'
2122 b'ePK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x003\x00\x00'
2123 b'\x00>\x00\x00\x00\x00\x00')
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002124
2125
Thomas Wouterscf297e42007-02-23 15:07:44 +00002126class DecryptionTests(unittest.TestCase):
Ezio Melotti35386712009-12-31 13:22:41 +00002127 """Check that ZIP decryption works. Since the library does not
2128 support encryption at the moment, we use a pre-generated encrypted
2129 ZIP file."""
Thomas Wouterscf297e42007-02-23 15:07:44 +00002130
2131 data = (
Christian Tismer59202e52013-10-21 03:59:23 +02002132 b'PK\x03\x04\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00\x1a\x00'
2133 b'\x00\x00\x08\x00\x00\x00test.txt\xfa\x10\xa0gly|\xfa-\xc5\xc0=\xf9y'
2134 b'\x18\xe0\xa8r\xb3Z}Lg\xbc\xae\xf9|\x9b\x19\xe4\x8b\xba\xbb)\x8c\xb0\xdbl'
2135 b'PK\x01\x02\x14\x00\x14\x00\x01\x00\x00\x00n\x92i.#y\xef?&\x00\x00\x00'
2136 b'\x1a\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00 \x00\xb6\x81'
2137 b'\x00\x00\x00\x00test.txtPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x006\x00'
2138 b'\x00\x00L\x00\x00\x00\x00\x00' )
Christian Heimesfdab48e2008-01-20 09:06:41 +00002139 data2 = (
Christian Tismer59202e52013-10-21 03:59:23 +02002140 b'PK\x03\x04\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02'
2141 b'\x00\x00\x04\x00\x15\x00zeroUT\t\x00\x03\xd6\x8b\x92G\xda\x8b\x92GUx\x04'
2142 b'\x00\xe8\x03\xe8\x03\xc7<M\xb5a\xceX\xa3Y&\x8b{oE\xd7\x9d\x8c\x98\x02\xc0'
2143 b'PK\x07\x08xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00PK\x01\x02\x17\x03'
2144 b'\x14\x00\t\x00\x08\x00\xcf}38xu\xaa\xb2\x14\x00\x00\x00\x00\x02\x00\x00'
2145 b'\x04\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00ze'
2146 b'roUT\x05\x00\x03\xd6\x8b\x92GUx\x00\x00PK\x05\x06\x00\x00\x00\x00\x01'
2147 b'\x00\x01\x00?\x00\x00\x00[\x00\x00\x00\x00\x00' )
Thomas Wouterscf297e42007-02-23 15:07:44 +00002148
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002149 plain = b'zipfile.py encryption test'
Christian Heimesfdab48e2008-01-20 09:06:41 +00002150 plain2 = b'\x00'*512
Thomas Wouterscf297e42007-02-23 15:07:44 +00002151
2152 def setUp(self):
Ezio Melotti35386712009-12-31 13:22:41 +00002153 with open(TESTFN, "wb") as fp:
2154 fp.write(self.data)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002155 self.zip = zipfile.ZipFile(TESTFN, "r")
Ezio Melotti35386712009-12-31 13:22:41 +00002156 with open(TESTFN2, "wb") as fp:
2157 fp.write(self.data2)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002158 self.zip2 = zipfile.ZipFile(TESTFN2, "r")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002159
2160 def tearDown(self):
2161 self.zip.close()
2162 os.unlink(TESTFN)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002163 self.zip2.close()
2164 os.unlink(TESTFN2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002165
Ezio Melottiafd0d112009-07-15 17:17:17 +00002166 def test_no_password(self):
Thomas Wouterscf297e42007-02-23 15:07:44 +00002167 # Reading the encrypted file without password
2168 # must generate a RunTime exception
2169 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002170 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002171
Ezio Melottiafd0d112009-07-15 17:17:17 +00002172 def test_bad_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002173 self.zip.setpassword(b"perl")
Thomas Wouterscf297e42007-02-23 15:07:44 +00002174 self.assertRaises(RuntimeError, self.zip.read, "test.txt")
Christian Heimesfdab48e2008-01-20 09:06:41 +00002175 self.zip2.setpassword(b"perl")
2176 self.assertRaises(RuntimeError, self.zip2.read, "zero")
Guido van Rossumd8faa362007-04-27 19:54:29 +00002177
Ezio Melotti975077a2011-05-19 22:03:22 +03002178 @requires_zlib
Ezio Melottiafd0d112009-07-15 17:17:17 +00002179 def test_good_password(self):
Guido van Rossumd6ca5462007-05-22 01:29:33 +00002180 self.zip.setpassword(b"python")
Ezio Melotti35386712009-12-31 13:22:41 +00002181 self.assertEqual(self.zip.read("test.txt"), self.plain)
Christian Heimesfdab48e2008-01-20 09:06:41 +00002182 self.zip2.setpassword(b"12345")
Ezio Melotti35386712009-12-31 13:22:41 +00002183 self.assertEqual(self.zip2.read("zero"), self.plain2)
Thomas Wouterscf297e42007-02-23 15:07:44 +00002184
R. David Murray8d855d82010-12-21 21:53:37 +00002185 def test_unicode_password(self):
2186 self.assertRaises(TypeError, self.zip.setpassword, "unicode")
2187 self.assertRaises(TypeError, self.zip.read, "test.txt", "python")
2188 self.assertRaises(TypeError, self.zip.open, "test.txt", pwd="python")
2189 self.assertRaises(TypeError, self.zip.extract, "test.txt", pwd="python")
2190
Serhiy Storchaka5c32af72019-10-27 10:22:14 +02002191 def test_seek_tell(self):
2192 self.zip.setpassword(b"python")
2193 txt = self.plain
2194 test_word = b'encryption'
2195 bloc = txt.find(test_word)
2196 bloc_len = len(test_word)
2197 with self.zip.open("test.txt", "r") as fp:
2198 fp.seek(bloc, os.SEEK_SET)
2199 self.assertEqual(fp.tell(), bloc)
2200 fp.seek(-bloc, os.SEEK_CUR)
2201 self.assertEqual(fp.tell(), 0)
2202 fp.seek(bloc, os.SEEK_CUR)
2203 self.assertEqual(fp.tell(), bloc)
2204 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2205
2206 # Make sure that the second read after seeking back beyond
2207 # _readbuffer returns the same content (ie. rewind to the start of
2208 # the file to read forward to the required position).
2209 old_read_size = fp.MIN_READ_SIZE
2210 fp.MIN_READ_SIZE = 1
2211 fp._readbuffer = b''
2212 fp._offset = 0
2213 fp.seek(0, os.SEEK_SET)
2214 self.assertEqual(fp.tell(), 0)
2215 fp.seek(bloc, os.SEEK_CUR)
2216 self.assertEqual(fp.read(bloc_len), txt[bloc:bloc+bloc_len])
2217 fp.MIN_READ_SIZE = old_read_size
2218
2219 fp.seek(0, os.SEEK_END)
2220 self.assertEqual(fp.tell(), len(txt))
2221 fp.seek(0, os.SEEK_SET)
2222 self.assertEqual(fp.tell(), 0)
2223
2224 # Read the file completely to definitely call any eof integrity
2225 # checks (crc) and make sure they still pass.
2226 fp.read()
2227
2228
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002229class AbstractTestsWithRandomBinaryFiles:
2230 @classmethod
2231 def setUpClass(cls):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002232 datacount = randint(16, 64)*1024 + randint(1, 1024)
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002233 cls.data = b''.join(struct.pack('<f', random()*randint(-1000, 1000))
2234 for i in range(datacount))
Guido van Rossumd8faa362007-04-27 19:54:29 +00002235
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002236 def setUp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002237 # Make a source file with some lines
Ezio Melotti35386712009-12-31 13:22:41 +00002238 with open(TESTFN, "wb") as fp:
2239 fp.write(self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002240
2241 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002242 unlink(TESTFN)
2243 unlink(TESTFN2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002244
Ezio Melottiafd0d112009-07-15 17:17:17 +00002245 def make_test_archive(self, f, compression):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002246 # Create the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002247 with zipfile.ZipFile(f, "w", compression) as zipfp:
2248 zipfp.write(TESTFN, "another.name")
2249 zipfp.write(TESTFN, TESTFN)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002250
Ezio Melottiafd0d112009-07-15 17:17:17 +00002251 def zip_test(self, f, compression):
2252 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002253
2254 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002255 with zipfile.ZipFile(f, "r", compression) as zipfp:
2256 testdata = zipfp.read(TESTFN)
2257 self.assertEqual(len(testdata), len(self.data))
2258 self.assertEqual(testdata, self.data)
2259 self.assertEqual(zipfp.read("another.name"), self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002260
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002261 def test_read(self):
2262 for f in get_files(self):
2263 self.zip_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002264
Ezio Melottiafd0d112009-07-15 17:17:17 +00002265 def zip_open_test(self, f, compression):
2266 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002267
2268 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002269 with zipfile.ZipFile(f, "r", compression) as zipfp:
2270 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002271 with zipfp.open(TESTFN) as zipopen1:
2272 while True:
2273 read_data = zipopen1.read(256)
2274 if not read_data:
2275 break
2276 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002277
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002278 zipdata2 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002279 with zipfp.open("another.name") as zipopen2:
2280 while True:
2281 read_data = zipopen2.read(256)
2282 if not read_data:
2283 break
2284 zipdata2.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002285
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002286 testdata1 = b''.join(zipdata1)
2287 self.assertEqual(len(testdata1), len(self.data))
2288 self.assertEqual(testdata1, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002289
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002290 testdata2 = b''.join(zipdata2)
Ezio Melotti35386712009-12-31 13:22:41 +00002291 self.assertEqual(len(testdata2), len(self.data))
2292 self.assertEqual(testdata2, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002293
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002294 def test_open(self):
2295 for f in get_files(self):
2296 self.zip_open_test(f, self.compression)
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002297
Ezio Melottiafd0d112009-07-15 17:17:17 +00002298 def zip_random_open_test(self, f, compression):
2299 self.make_test_archive(f, compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002300
2301 # Read the ZIP archive
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002302 with zipfile.ZipFile(f, "r", compression) as zipfp:
2303 zipdata1 = []
Brian Curtin8fb9b862010-11-18 02:15:28 +00002304 with zipfp.open(TESTFN) as zipopen1:
2305 while True:
2306 read_data = zipopen1.read(randint(1, 1024))
2307 if not read_data:
2308 break
2309 zipdata1.append(read_data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002310
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002311 testdata = b''.join(zipdata1)
2312 self.assertEqual(len(testdata), len(self.data))
2313 self.assertEqual(testdata, self.data)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002314
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002315 def test_random_open(self):
2316 for f in get_files(self):
2317 self.zip_random_open_test(f, self.compression)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002318
Antoine Pitrou7c8bcb62010-08-12 15:11:50 +00002319
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002320class StoredTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2321 unittest.TestCase):
2322 compression = zipfile.ZIP_STORED
Martin v. Löwisf6b16a42012-05-01 07:58:44 +02002323
Serhiy Storchakafa6bc292013-07-22 21:00:11 +03002324@requires_zlib
2325class DeflateTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2326 unittest.TestCase):
2327 compression = zipfile.ZIP_DEFLATED
2328
2329@requires_bz2
2330class Bzip2TestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2331 unittest.TestCase):
2332 compression = zipfile.ZIP_BZIP2
2333
2334@requires_lzma
2335class LzmaTestsWithRandomBinaryFiles(AbstractTestsWithRandomBinaryFiles,
2336 unittest.TestCase):
2337 compression = zipfile.ZIP_LZMA
Martin v. Löwis7fb79fc2012-05-13 10:06:36 +02002338
Ezio Melotti76430242009-07-11 18:28:48 +00002339
luzpaza5293b42017-11-05 07:37:50 -06002340# Provide the tell() method but not seek()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002341class Tellable:
2342 def __init__(self, fp):
2343 self.fp = fp
2344 self.offset = 0
2345
2346 def write(self, data):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002347 n = self.fp.write(data)
2348 self.offset += n
2349 return n
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002350
2351 def tell(self):
2352 return self.offset
2353
2354 def flush(self):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002355 self.fp.flush()
2356
2357class Unseekable:
2358 def __init__(self, fp):
2359 self.fp = fp
2360
2361 def write(self, data):
2362 return self.fp.write(data)
2363
2364 def flush(self):
2365 self.fp.flush()
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002366
2367class UnseekableTests(unittest.TestCase):
Serhiy Storchaka77d89972015-03-23 01:09:35 +02002368 def test_writestr(self):
2369 for wrapper in (lambda f: f), Tellable, Unseekable:
2370 with self.subTest(wrapper=wrapper):
2371 f = io.BytesIO()
2372 f.write(b'abc')
2373 bf = io.BufferedWriter(f)
2374 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2375 zipfp.writestr('ones', b'111')
2376 zipfp.writestr('twos', b'222')
2377 self.assertEqual(f.getvalue()[:5], b'abcPK')
2378 with zipfile.ZipFile(f, mode='r') as zipf:
2379 with zipf.open('ones') as zopen:
2380 self.assertEqual(zopen.read(), b'111')
2381 with zipf.open('twos') as zopen:
2382 self.assertEqual(zopen.read(), b'222')
2383
2384 def test_write(self):
2385 for wrapper in (lambda f: f), Tellable, Unseekable:
2386 with self.subTest(wrapper=wrapper):
2387 f = io.BytesIO()
2388 f.write(b'abc')
2389 bf = io.BufferedWriter(f)
2390 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipfp:
2391 self.addCleanup(unlink, TESTFN)
2392 with open(TESTFN, 'wb') as f2:
2393 f2.write(b'111')
2394 zipfp.write(TESTFN, 'ones')
2395 with open(TESTFN, 'wb') as f2:
2396 f2.write(b'222')
2397 zipfp.write(TESTFN, 'twos')
2398 self.assertEqual(f.getvalue()[:5], b'abcPK')
2399 with zipfile.ZipFile(f, mode='r') as zipf:
2400 with zipf.open('ones') as zopen:
2401 self.assertEqual(zopen.read(), b'111')
2402 with zipf.open('twos') as zopen:
2403 self.assertEqual(zopen.read(), b'222')
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002404
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002405 def test_open_write(self):
2406 for wrapper in (lambda f: f), Tellable, Unseekable:
2407 with self.subTest(wrapper=wrapper):
2408 f = io.BytesIO()
2409 f.write(b'abc')
2410 bf = io.BufferedWriter(f)
2411 with zipfile.ZipFile(wrapper(bf), 'w', zipfile.ZIP_STORED) as zipf:
2412 with zipf.open('ones', 'w') as zopen:
2413 zopen.write(b'111')
2414 with zipf.open('twos', 'w') as zopen:
2415 zopen.write(b'222')
2416 self.assertEqual(f.getvalue()[:5], b'abcPK')
2417 with zipfile.ZipFile(f) as zipf:
2418 self.assertEqual(zipf.read('ones'), b'111')
2419 self.assertEqual(zipf.read('twos'), b'222')
2420
Serhiy Storchakaa14f7d22015-01-26 14:01:27 +02002421
Ezio Melotti975077a2011-05-19 22:03:22 +03002422@requires_zlib
Guido van Rossumd8faa362007-04-27 19:54:29 +00002423class TestsWithMultipleOpens(unittest.TestCase):
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002424 @classmethod
2425 def setUpClass(cls):
2426 cls.data1 = b'111' + getrandbytes(10000)
2427 cls.data2 = b'222' + getrandbytes(10000)
2428
2429 def make_test_archive(self, f):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002430 # Create the ZIP archive
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002431 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipfp:
2432 zipfp.writestr('ones', self.data1)
2433 zipfp.writestr('twos', self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002434
Ezio Melottiafd0d112009-07-15 17:17:17 +00002435 def test_same_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002436 # Verify that (when the ZipFile is in control of creating file objects)
2437 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002438 for f in get_files(self):
2439 self.make_test_archive(f)
2440 with zipfile.ZipFile(f, mode="r") as zipf:
2441 with zipf.open('ones') as zopen1, zipf.open('ones') as zopen2:
2442 data1 = zopen1.read(500)
2443 data2 = zopen2.read(500)
2444 data1 += zopen1.read()
2445 data2 += zopen2.read()
2446 self.assertEqual(data1, data2)
2447 self.assertEqual(data1, self.data1)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002448
Ezio Melottiafd0d112009-07-15 17:17:17 +00002449 def test_different_file(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002450 # Verify that (when the ZipFile is in control of creating file objects)
2451 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002452 for f in get_files(self):
2453 self.make_test_archive(f)
2454 with zipfile.ZipFile(f, mode="r") as zipf:
2455 with zipf.open('ones') as zopen1, zipf.open('twos') as zopen2:
2456 data1 = zopen1.read(500)
2457 data2 = zopen2.read(500)
2458 data1 += zopen1.read()
2459 data2 += zopen2.read()
2460 self.assertEqual(data1, self.data1)
2461 self.assertEqual(data2, self.data2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002462
Ezio Melottiafd0d112009-07-15 17:17:17 +00002463 def test_interleaved(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00002464 # Verify that (when the ZipFile is in control of creating file objects)
2465 # multiple open() calls can be made without interfering with each other.
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002466 for f in get_files(self):
2467 self.make_test_archive(f)
2468 with zipfile.ZipFile(f, mode="r") as zipf:
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002469 with zipf.open('ones') as zopen1:
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002470 data1 = zopen1.read(500)
Serhiy Storchakad76c7c22016-05-13 21:18:58 +03002471 with zipf.open('twos') as zopen2:
2472 data2 = zopen2.read(500)
2473 data1 += zopen1.read()
2474 data2 += zopen2.read()
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002475 self.assertEqual(data1, self.data1)
2476 self.assertEqual(data2, self.data2)
2477
2478 def test_read_after_close(self):
2479 for f in get_files(self):
2480 self.make_test_archive(f)
2481 with contextlib.ExitStack() as stack:
2482 with zipfile.ZipFile(f, 'r') as zipf:
2483 zopen1 = stack.enter_context(zipf.open('ones'))
2484 zopen2 = stack.enter_context(zipf.open('twos'))
Brian Curtin8fb9b862010-11-18 02:15:28 +00002485 data1 = zopen1.read(500)
2486 data2 = zopen2.read(500)
Serhiy Storchaka1ad088f2014-12-03 09:11:57 +02002487 data1 += zopen1.read()
2488 data2 += zopen2.read()
2489 self.assertEqual(data1, self.data1)
2490 self.assertEqual(data2, self.data2)
2491
2492 def test_read_after_write(self):
2493 for f in get_files(self):
2494 with zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) as zipf:
2495 zipf.writestr('ones', self.data1)
2496 zipf.writestr('twos', self.data2)
2497 with zipf.open('ones') as zopen1:
2498 data1 = zopen1.read(500)
2499 self.assertEqual(data1, self.data1[:500])
2500 with zipfile.ZipFile(f, 'r') as zipf:
2501 data1 = zipf.read('ones')
2502 data2 = zipf.read('twos')
2503 self.assertEqual(data1, self.data1)
2504 self.assertEqual(data2, self.data2)
2505
2506 def test_write_after_read(self):
2507 for f in get_files(self):
2508 with zipfile.ZipFile(f, "w", zipfile.ZIP_DEFLATED) as zipf:
2509 zipf.writestr('ones', self.data1)
2510 with zipf.open('ones') as zopen1:
2511 zopen1.read(500)
2512 zipf.writestr('twos', self.data2)
2513 with zipfile.ZipFile(f, 'r') as zipf:
2514 data1 = zipf.read('ones')
2515 data2 = zipf.read('twos')
2516 self.assertEqual(data1, self.data1)
2517 self.assertEqual(data2, self.data2)
2518
2519 def test_many_opens(self):
2520 # Verify that read() and open() promptly close the file descriptor,
2521 # and don't rely on the garbage collector to free resources.
2522 self.make_test_archive(TESTFN2)
2523 with zipfile.ZipFile(TESTFN2, mode="r") as zipf:
2524 for x in range(100):
2525 zipf.read('ones')
2526 with zipf.open('ones') as zopen1:
2527 pass
2528 with open(os.devnull) as f:
2529 self.assertLess(f.fileno(), 100)
Guido van Rossumd8faa362007-04-27 19:54:29 +00002530
Serhiy Storchaka18ee29d2016-05-13 13:52:49 +03002531 def test_write_while_reading(self):
2532 with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_DEFLATED) as zipf:
2533 zipf.writestr('ones', self.data1)
2534 with zipfile.ZipFile(TESTFN2, 'a', zipfile.ZIP_DEFLATED) as zipf:
2535 with zipf.open('ones', 'r') as r1:
2536 data1 = r1.read(500)
2537 with zipf.open('twos', 'w') as w1:
2538 w1.write(self.data2)
2539 data1 += r1.read()
2540 self.assertEqual(data1, self.data1)
2541 with zipfile.ZipFile(TESTFN2) as zipf:
2542 self.assertEqual(zipf.read('twos'), self.data2)
2543
Guido van Rossumd8faa362007-04-27 19:54:29 +00002544 def tearDown(self):
Ezio Melotti76430242009-07-11 18:28:48 +00002545 unlink(TESTFN2)
2546
Guido van Rossumd8faa362007-04-27 19:54:29 +00002547
Martin v. Löwis59e47792009-01-24 14:10:07 +00002548class TestWithDirectory(unittest.TestCase):
2549 def setUp(self):
2550 os.mkdir(TESTFN2)
2551
Ezio Melottiafd0d112009-07-15 17:17:17 +00002552 def test_extract_dir(self):
Ezio Melottifaa6b7f2009-12-30 12:34:59 +00002553 with zipfile.ZipFile(findfile("zipdir.zip")) as zipf:
2554 zipf.extractall(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002555 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a")))
2556 self.assertTrue(os.path.isdir(os.path.join(TESTFN2, "a", "b")))
2557 self.assertTrue(os.path.exists(os.path.join(TESTFN2, "a", "b", "c")))
2558
Ezio Melottiafd0d112009-07-15 17:17:17 +00002559 def test_bug_6050(self):
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002560 # Extraction should succeed if directories already exist
2561 os.mkdir(os.path.join(TESTFN2, "a"))
Ezio Melottiafd0d112009-07-15 17:17:17 +00002562 self.test_extract_dir()
Martin v. Löwis70ccd162009-05-24 19:47:22 +00002563
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002564 def test_write_dir(self):
2565 dirpath = os.path.join(TESTFN2, "x")
2566 os.mkdir(dirpath)
2567 mode = os.stat(dirpath).st_mode & 0xFFFF
2568 with zipfile.ZipFile(TESTFN, "w") as zipf:
2569 zipf.write(dirpath)
2570 zinfo = zipf.filelist[0]
2571 self.assertTrue(zinfo.filename.endswith("/x/"))
2572 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2573 zipf.write(dirpath, "y")
2574 zinfo = zipf.filelist[1]
2575 self.assertTrue(zinfo.filename, "y/")
2576 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2577 with zipfile.ZipFile(TESTFN, "r") as zipf:
2578 zinfo = zipf.filelist[0]
2579 self.assertTrue(zinfo.filename.endswith("/x/"))
2580 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2581 zinfo = zipf.filelist[1]
2582 self.assertTrue(zinfo.filename, "y/")
2583 self.assertEqual(zinfo.external_attr, (mode << 16) | 0x10)
2584 target = os.path.join(TESTFN2, "target")
2585 os.mkdir(target)
2586 zipf.extractall(target)
2587 self.assertTrue(os.path.isdir(os.path.join(target, "y")))
2588 self.assertEqual(len(os.listdir(target)), 2)
2589
2590 def test_writestr_dir(self):
Martin v. Löwis59e47792009-01-24 14:10:07 +00002591 os.mkdir(os.path.join(TESTFN2, "x"))
Serhiy Storchaka46a34922014-09-23 22:40:23 +03002592 with zipfile.ZipFile(TESTFN, "w") as zipf:
2593 zipf.writestr("x/", b'')
2594 zinfo = zipf.filelist[0]
2595 self.assertEqual(zinfo.filename, "x/")
2596 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2597 with zipfile.ZipFile(TESTFN, "r") as zipf:
2598 zinfo = zipf.filelist[0]
2599 self.assertTrue(zinfo.filename.endswith("x/"))
2600 self.assertEqual(zinfo.external_attr, (0o40775 << 16) | 0x10)
2601 target = os.path.join(TESTFN2, "target")
2602 os.mkdir(target)
2603 zipf.extractall(target)
2604 self.assertTrue(os.path.isdir(os.path.join(target, "x")))
2605 self.assertEqual(os.listdir(target), ["x"])
Martin v. Löwis59e47792009-01-24 14:10:07 +00002606
2607 def tearDown(self):
Victor Stinner57004c62014-09-04 00:49:01 +02002608 rmtree(TESTFN2)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002609 if os.path.exists(TESTFN):
Ezio Melotti76430242009-07-11 18:28:48 +00002610 unlink(TESTFN)
Martin v. Löwis59e47792009-01-24 14:10:07 +00002611
Guido van Rossumd8faa362007-04-27 19:54:29 +00002612
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002613class ZipInfoTests(unittest.TestCase):
2614 def test_from_file(self):
2615 zi = zipfile.ZipInfo.from_file(__file__)
2616 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2617 self.assertFalse(zi.is_dir())
Serhiy Storchaka8606e952017-03-08 14:37:51 +02002618 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2619
2620 def test_from_file_pathlike(self):
2621 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2622 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2623 self.assertFalse(zi.is_dir())
2624 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2625
2626 def test_from_file_bytes(self):
2627 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2628 self.assertEqual(posixpath.basename(zi.filename), 'test')
2629 self.assertFalse(zi.is_dir())
2630 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2631
2632 def test_from_file_fileno(self):
2633 with open(__file__, 'rb') as f:
2634 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2635 self.assertEqual(posixpath.basename(zi.filename), 'test')
2636 self.assertFalse(zi.is_dir())
2637 self.assertEqual(zi.file_size, os.path.getsize(__file__))
Serhiy Storchaka503f9082016-02-08 00:02:25 +02002638
2639 def test_from_dir(self):
2640 dirpath = os.path.dirname(os.path.abspath(__file__))
2641 zi = zipfile.ZipInfo.from_file(dirpath, 'stdlib_tests')
2642 self.assertEqual(zi.filename, 'stdlib_tests/')
2643 self.assertTrue(zi.is_dir())
2644 self.assertEqual(zi.compress_type, zipfile.ZIP_STORED)
2645 self.assertEqual(zi.file_size, 0)
2646
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002647
2648class CommandLineTest(unittest.TestCase):
2649
2650 def zipfilecmd(self, *args, **kwargs):
2651 rc, out, err = script_helper.assert_python_ok('-m', 'zipfile', *args,
2652 **kwargs)
2653 return out.replace(os.linesep.encode(), b'\n')
2654
2655 def zipfilecmd_failure(self, *args):
2656 return script_helper.assert_python_failure('-m', 'zipfile', *args)
2657
Serhiy Storchaka150cd192017-04-07 18:56:12 +03002658 def test_bad_use(self):
2659 rc, out, err = self.zipfilecmd_failure()
2660 self.assertEqual(out, b'')
2661 self.assertIn(b'usage', err.lower())
2662 self.assertIn(b'error', err.lower())
2663 self.assertIn(b'required', err.lower())
2664 rc, out, err = self.zipfilecmd_failure('-l', '')
2665 self.assertEqual(out, b'')
2666 self.assertNotEqual(err.strip(), b'')
2667
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002668 def test_test_command(self):
2669 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002670 for opt in '-t', '--test':
2671 out = self.zipfilecmd(opt, zip_name)
2672 self.assertEqual(out.rstrip(), b'Done testing')
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002673 zip_name = findfile('testtar.tar')
2674 rc, out, err = self.zipfilecmd_failure('-t', zip_name)
2675 self.assertEqual(out, b'')
2676
2677 def test_list_command(self):
2678 zip_name = findfile('zipdir.zip')
2679 t = io.StringIO()
2680 with zipfile.ZipFile(zip_name, 'r') as tf:
2681 tf.printdir(t)
2682 expected = t.getvalue().encode('ascii', 'backslashreplace')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002683 for opt in '-l', '--list':
2684 out = self.zipfilecmd(opt, zip_name,
2685 PYTHONIOENCODING='ascii:backslashreplace')
2686 self.assertEqual(out, expected)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002687
Serhiy Storchakab4293ef2016-10-23 22:32:30 +03002688 @requires_zlib
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002689 def test_create_command(self):
2690 self.addCleanup(unlink, TESTFN)
2691 with open(TESTFN, 'w') as f:
2692 f.write('test 1')
2693 os.mkdir(TESTFNDIR)
2694 self.addCleanup(rmtree, TESTFNDIR)
2695 with open(os.path.join(TESTFNDIR, 'file.txt'), 'w') as f:
2696 f.write('test 2')
2697 files = [TESTFN, TESTFNDIR]
2698 namelist = [TESTFN, TESTFNDIR + '/', TESTFNDIR + '/file.txt']
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002699 for opt in '-c', '--create':
2700 try:
2701 out = self.zipfilecmd(opt, TESTFN2, *files)
2702 self.assertEqual(out, b'')
2703 with zipfile.ZipFile(TESTFN2) as zf:
2704 self.assertEqual(zf.namelist(), namelist)
2705 self.assertEqual(zf.read(namelist[0]), b'test 1')
2706 self.assertEqual(zf.read(namelist[2]), b'test 2')
2707 finally:
2708 unlink(TESTFN2)
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002709
2710 def test_extract_command(self):
2711 zip_name = findfile('zipdir.zip')
Serhiy Storchaka8c933102016-10-23 13:32:12 +03002712 for opt in '-e', '--extract':
2713 with temp_dir() as extdir:
2714 out = self.zipfilecmd(opt, zip_name, extdir)
2715 self.assertEqual(out, b'')
2716 with zipfile.ZipFile(zip_name) as zf:
2717 for zi in zf.infolist():
2718 path = os.path.join(extdir,
2719 zi.filename.replace('/', os.sep))
2720 if zi.is_dir():
2721 self.assertTrue(os.path.isdir(path))
2722 else:
2723 self.assertTrue(os.path.isfile(path))
2724 with open(path, 'rb') as f:
2725 self.assertEqual(f.read(), zf.read(zi))
Serhiy Storchaka61c4c442016-10-23 13:07:59 +03002726
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002727
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002728class TestExecutablePrependedZip(unittest.TestCase):
2729 """Test our ability to open zip files with an executable prepended."""
2730
2731 def setUp(self):
2732 self.exe_zip = findfile('exe_with_zip', subdir='ziptestdata')
2733 self.exe_zip64 = findfile('exe_with_z64', subdir='ziptestdata')
2734
2735 def _test_zip_works(self, name):
2736 # bpo28494 sanity check: ensure is_zipfile works on these.
2737 self.assertTrue(zipfile.is_zipfile(name),
2738 f'is_zipfile failed on {name}')
2739 # Ensure we can operate on these via ZipFile.
2740 with zipfile.ZipFile(name) as zipfp:
2741 for n in zipfp.namelist():
2742 data = zipfp.read(n)
2743 self.assertIn(b'FAVORITE_NUMBER', data)
2744
2745 def test_read_zip_with_exe_prepended(self):
2746 self._test_zip_works(self.exe_zip)
2747
2748 def test_read_zip64_with_exe_prepended(self):
2749 self._test_zip_works(self.exe_zip64)
2750
2751 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2752 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2753 'Test relies on #!/bin/bash working.')
2754 def test_execute_zip2(self):
2755 output = subprocess.check_output([self.exe_zip, sys.executable])
2756 self.assertIn(b'number in executable: 5', output)
2757
2758 @unittest.skipUnless(sys.executable, 'sys.executable required.')
2759 @unittest.skipUnless(os.access('/bin/bash', os.X_OK),
2760 'Test relies on #!/bin/bash working.')
2761 def test_execute_zip64(self):
2762 output = subprocess.check_output([self.exe_zip64, sys.executable])
2763 self.assertIn(b'number in executable: 5', output)
2764
2765
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002766# Poor man's technique to consume a (smallish) iterable.
2767consume = tuple
2768
2769
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002770# from jaraco.itertools 5.0
2771class jaraco:
2772 class itertools:
2773 class Counter:
2774 def __init__(self, i):
2775 self.count = 0
2776 self._orig_iter = iter(i)
2777
2778 def __iter__(self):
2779 return self
2780
2781 def __next__(self):
2782 result = next(self._orig_iter)
2783 self.count += 1
2784 return result
2785
2786
shireenraoa4e29912019-08-24 11:26:41 -04002787def add_dirs(zf):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002788 """
shireenraoa4e29912019-08-24 11:26:41 -04002789 Given a writable zip file zf, inject directory entries for
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002790 any directories implied by the presence of children.
2791 """
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002792 for name in zipfile.CompleteDirs._implied_dirs(zf.namelist()):
shireenraoa4e29912019-08-24 11:26:41 -04002793 zf.writestr(name, b"")
2794 return zf
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002795
2796
shireenraoa4e29912019-08-24 11:26:41 -04002797def build_alpharep_fixture():
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002798 """
2799 Create a zip file with this structure:
2800
2801 .
2802 ├── a.txt
shireenraoa4e29912019-08-24 11:26:41 -04002803 ├── b
2804 │ ├── c.txt
2805 │ ├── d
2806 │ │ └── e.txt
2807 │ └── f.txt
2808 └── g
2809 └── h
2810 └── i.txt
2811
2812 This fixture has the following key characteristics:
2813
2814 - a file at the root (a)
2815 - a file two levels deep (b/d/e)
2816 - multiple files in a directory (b/c, b/f)
2817 - a directory containing only a directory (g/h)
2818
2819 "alpha" because it uses alphabet
2820 "rep" because it's a representative example
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002821 """
2822 data = io.BytesIO()
2823 zf = zipfile.ZipFile(data, "w")
2824 zf.writestr("a.txt", b"content of a")
2825 zf.writestr("b/c.txt", b"content of c")
2826 zf.writestr("b/d/e.txt", b"content of e")
shireenraoa4e29912019-08-24 11:26:41 -04002827 zf.writestr("b/f.txt", b"content of f")
2828 zf.writestr("g/h/i.txt", b"content of i")
2829 zf.filename = "alpharep.zip"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002830 return zf
2831
2832
2833class TestPath(unittest.TestCase):
2834 def setUp(self):
2835 self.fixtures = contextlib.ExitStack()
2836 self.addCleanup(self.fixtures.close)
2837
shireenraoa4e29912019-08-24 11:26:41 -04002838 def zipfile_alpharep(self):
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002839 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002840 yield build_alpharep_fixture()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002841 with self.subTest():
shireenraoa4e29912019-08-24 11:26:41 -04002842 yield add_dirs(build_alpharep_fixture())
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002843
2844 def zipfile_ondisk(self):
2845 tmpdir = pathlib.Path(self.fixtures.enter_context(temp_dir()))
shireenraoa4e29912019-08-24 11:26:41 -04002846 for alpharep in self.zipfile_alpharep():
2847 buffer = alpharep.fp
2848 alpharep.close()
2849 path = tmpdir / alpharep.filename
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002850 with path.open("wb") as strm:
2851 strm.write(buffer.getvalue())
2852 yield path
2853
shireenraoa4e29912019-08-24 11:26:41 -04002854 def test_iterdir_and_types(self):
2855 for alpharep in self.zipfile_alpharep():
2856 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002857 assert root.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002858 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002859 assert a.is_file()
2860 assert b.is_dir()
shireenraoa4e29912019-08-24 11:26:41 -04002861 assert g.is_dir()
2862 c, f, d = b.iterdir()
2863 assert c.is_file() and f.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002864 e, = d.iterdir()
2865 assert e.is_file()
shireenraoa4e29912019-08-24 11:26:41 -04002866 h, = g.iterdir()
2867 i, = h.iterdir()
2868 assert i.is_file()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002869
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002870 def test_subdir_is_dir(self):
2871 for alpharep in self.zipfile_alpharep():
2872 root = zipfile.Path(alpharep)
2873 assert (root / 'b').is_dir()
2874 assert (root / 'b/').is_dir()
2875 assert (root / 'g').is_dir()
2876 assert (root / 'g/').is_dir()
2877
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002878 def test_open(self):
shireenraoa4e29912019-08-24 11:26:41 -04002879 for alpharep in self.zipfile_alpharep():
2880 root = zipfile.Path(alpharep)
2881 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002882 with a.open() as strm:
2883 data = strm.read()
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002884 assert data == "content of a"
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002885
2886 def test_read(self):
shireenraoa4e29912019-08-24 11:26:41 -04002887 for alpharep in self.zipfile_alpharep():
2888 root = zipfile.Path(alpharep)
2889 a, b, g = root.iterdir()
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002890 assert a.read_text() == "content of a"
2891 assert a.read_bytes() == b"content of a"
2892
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002893 def test_joinpath(self):
shireenraoa4e29912019-08-24 11:26:41 -04002894 for alpharep in self.zipfile_alpharep():
2895 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002896 a = root.joinpath("a")
2897 assert a.is_file()
2898 e = root.joinpath("b").joinpath("d").joinpath("e.txt")
2899 assert e.read_text() == "content of e"
2900
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002901 def test_traverse_truediv(self):
shireenraoa4e29912019-08-24 11:26:41 -04002902 for alpharep in self.zipfile_alpharep():
2903 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002904 a = root / "a"
2905 assert a.is_file()
2906 e = root / "b" / "d" / "e.txt"
2907 assert e.read_text() == "content of e"
2908
2909 def test_pathlike_construction(self):
2910 """
2911 zipfile.Path should be constructable from a path-like object
2912 """
2913 for zipfile_ondisk in self.zipfile_ondisk():
2914 pathlike = pathlib.Path(str(zipfile_ondisk))
2915 zipfile.Path(pathlike)
2916
2917 def test_traverse_pathlike(self):
shireenraoa4e29912019-08-24 11:26:41 -04002918 for alpharep in self.zipfile_alpharep():
2919 root = zipfile.Path(alpharep)
Jason R. Coombsb2758ff2019-05-08 09:45:06 -04002920 root / pathlib.Path("a")
2921
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002922 def test_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002923 for alpharep in self.zipfile_alpharep():
2924 root = zipfile.Path(alpharep)
Jason R. Coombs33e067d2019-05-09 11:34:36 -04002925 assert (root / 'a').parent.at == ''
2926 assert (root / 'a' / 'b').parent.at == 'a/'
2927
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002928 def test_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002929 for alpharep in self.zipfile_alpharep():
2930 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002931 assert (root / 'b').parent.at == ''
2932 assert (root / 'b/').parent.at == ''
2933
2934 def test_missing_dir_parent(self):
shireenraoa4e29912019-08-24 11:26:41 -04002935 for alpharep in self.zipfile_alpharep():
2936 root = zipfile.Path(alpharep)
Jason R. Coombs38f44b42019-07-07 17:37:50 -04002937 assert (root / 'missing dir/').parent.at == ''
2938
Jason R. Coombse5bd7362020-02-11 21:58:47 -05002939 def test_mutability(self):
2940 """
2941 If the underlying zipfile is changed, the Path object should
2942 reflect that change.
2943 """
2944 for alpharep in self.zipfile_alpharep():
2945 root = zipfile.Path(alpharep)
2946 a, b, g = root.iterdir()
2947 alpharep.writestr('foo.txt', 'foo')
2948 alpharep.writestr('bar/baz.txt', 'baz')
2949 assert any(
2950 child.name == 'foo.txt'
2951 for child in root.iterdir())
2952 assert (root / 'foo.txt').read_text() == 'foo'
2953 baz, = (root / 'bar').iterdir()
2954 assert baz.read_text() == 'baz'
2955
2956 HUGE_ZIPFILE_NUM_ENTRIES = 2 ** 13
2957
2958 def huge_zipfile(self):
2959 """Create a read-only zipfile with a huge number of entries entries."""
2960 strm = io.BytesIO()
2961 zf = zipfile.ZipFile(strm, "w")
2962 for entry in map(str, range(self.HUGE_ZIPFILE_NUM_ENTRIES)):
2963 zf.writestr(entry, entry)
2964 zf.mode = 'r'
2965 return zf
2966
2967 def test_joinpath_constant_time(self):
2968 """
2969 Ensure joinpath on items in zipfile is linear time.
2970 """
2971 root = zipfile.Path(self.huge_zipfile())
2972 entries = jaraco.itertools.Counter(root.iterdir())
2973 for entry in entries:
2974 entry.joinpath('suffix')
2975 # Check the file iterated all items
2976 assert entries.count == self.HUGE_ZIPFILE_NUM_ENTRIES
2977
Jason R. Coombs0aeab5c2020-02-29 10:34:11 -06002978 # @func_timeout.func_set_timeout(3)
2979 def test_implied_dirs_performance(self):
2980 data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)]
2981 zipfile.CompleteDirs._implied_dirs(data)
2982
shireenraoa4e29912019-08-24 11:26:41 -04002983
Johannes Gijsbers3caf9c12004-08-19 15:11:50 +00002984if __name__ == "__main__":
Brett Cannond5b4e1d2013-06-12 19:57:19 -04002985 unittest.main()