1""" 2Read and write ZIP files. 3 4XXX references to utf-8 need further investigation. 5""" 6import io 7import os 8import re 9import importlib.util 10import sys 11import time 12import stat 13import shutil 14import struct 15import binascii 16 17try: 18 import threading 19except ImportError: 20 import dummy_threading as threading 21 22try: 23 import zlib # We may need its compression method 24 crc32 = zlib.crc32 25except ImportError: 26 zlib = None 27 crc32 = binascii.crc32 28 29try: 30 import bz2 # We may need its compression method 31except ImportError: 32 bz2 = None 33 34try: 35 import lzma # We may need its compression method 36except ImportError: 37 lzma = None 38 39__all__ = ["BadZipFile", "BadZipfile", "error", 40 "ZIP_STORED", "ZIP_DEFLATED", "ZIP_BZIP2", "ZIP_LZMA", 41 "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile"] 42 43class BadZipFile(Exception): 44 pass 45 46 47class LargeZipFile(Exception): 48 """ 49 Raised when writing a zipfile, the zipfile requires ZIP64 extensions 50 and those extensions are disabled. 51 """ 52 53error = BadZipfile = BadZipFile # Pre-3.2 compatibility names 54 55 56ZIP64_LIMIT = (1 << 31) - 1 57ZIP_FILECOUNT_LIMIT = (1 << 16) - 1 58ZIP_MAX_COMMENT = (1 << 16) - 1 59 60# constants for Zip file compression methods 61ZIP_STORED = 0 62ZIP_DEFLATED = 8 63ZIP_BZIP2 = 12 64ZIP_LZMA = 14 65# Other ZIP compression methods not supported 66 67DEFAULT_VERSION = 20 68ZIP64_VERSION = 45 69BZIP2_VERSION = 46 70LZMA_VERSION = 63 71# we recognize (but not necessarily support) all features up to that version 72MAX_EXTRACT_VERSION = 63 73 74# Below are some formats and associated data for reading/writing headers using 75# the struct module. The names and structures of headers/records are those used 76# in the PKWARE description of the ZIP file format: 77# http://www.pkware.com/documents/casestudies/APPNOTE.TXT 78# (URL valid as of January 2008) 79 80# The "end of central directory" structure, magic number, size, and indices 81# (section V.I in the format document) 82structEndArchive = b"<4s4H2LH" 83stringEndArchive = b"PK\005\006" 84sizeEndCentDir = struct.calcsize(structEndArchive) 85 86_ECD_SIGNATURE = 0 87_ECD_DISK_NUMBER = 1 88_ECD_DISK_START = 2 89_ECD_ENTRIES_THIS_DISK = 3 90_ECD_ENTRIES_TOTAL = 4 91_ECD_SIZE = 5 92_ECD_OFFSET = 6 93_ECD_COMMENT_SIZE = 7 94# These last two indices are not part of the structure as defined in the 95# spec, but they are used internally by this module as a convenience 96_ECD_COMMENT = 8 97_ECD_LOCATION = 9 98 99# The "central directory" structure, magic number, size, and indices 100# of entries in the structure (section V.F in the format document) 101structCentralDir = "<4s4B4HL2L5H2L" 102stringCentralDir = b"PK\001\002" 103sizeCentralDir = struct.calcsize(structCentralDir) 104 105# indexes of entries in the central directory structure 106_CD_SIGNATURE = 0 107_CD_CREATE_VERSION = 1 108_CD_CREATE_SYSTEM = 2 109_CD_EXTRACT_VERSION = 3 110_CD_EXTRACT_SYSTEM = 4 111_CD_FLAG_BITS = 5 112_CD_COMPRESS_TYPE = 6 113_CD_TIME = 7 114_CD_DATE = 8 115_CD_CRC = 9 116_CD_COMPRESSED_SIZE = 10 117_CD_UNCOMPRESSED_SIZE = 11 118_CD_FILENAME_LENGTH = 12 119_CD_EXTRA_FIELD_LENGTH = 13 120_CD_COMMENT_LENGTH = 14 121_CD_DISK_NUMBER_START = 15 122_CD_INTERNAL_FILE_ATTRIBUTES = 16 123_CD_EXTERNAL_FILE_ATTRIBUTES = 17 124_CD_LOCAL_HEADER_OFFSET = 18 125 126# The "local file header" structure, magic number, size, and indices 127# (section V.A in the format document) 128structFileHeader = "<4s2B4HL2L2H" 129stringFileHeader = b"PK\003\004" 130sizeFileHeader = struct.calcsize(structFileHeader) 131 132_FH_SIGNATURE = 0 133_FH_EXTRACT_VERSION = 1 134_FH_EXTRACT_SYSTEM = 2 135_FH_GENERAL_PURPOSE_FLAG_BITS = 3 136_FH_COMPRESSION_METHOD = 4 137_FH_LAST_MOD_TIME = 5 138_FH_LAST_MOD_DATE = 6 139_FH_CRC = 7 140_FH_COMPRESSED_SIZE = 8 141_FH_UNCOMPRESSED_SIZE = 9 142_FH_FILENAME_LENGTH = 10 143_FH_EXTRA_FIELD_LENGTH = 11 144 145# The "Zip64 end of central directory locator" structure, magic number, and size 146structEndArchive64Locator = "<4sLQL" 147stringEndArchive64Locator = b"PK\x06\x07" 148sizeEndCentDir64Locator = struct.calcsize(structEndArchive64Locator) 149 150# The "Zip64 end of central directory" record, magic number, size, and indices 151# (section V.G in the format document) 152structEndArchive64 = "<4sQ2H2L4Q" 153stringEndArchive64 = b"PK\x06\x06" 154sizeEndCentDir64 = struct.calcsize(structEndArchive64) 155 156_CD64_SIGNATURE = 0 157_CD64_DIRECTORY_RECSIZE = 1 158_CD64_CREATE_VERSION = 2 159_CD64_EXTRACT_VERSION = 3 160_CD64_DISK_NUMBER = 4 161_CD64_DISK_NUMBER_START = 5 162_CD64_NUMBER_ENTRIES_THIS_DISK = 6 163_CD64_NUMBER_ENTRIES_TOTAL = 7 164_CD64_DIRECTORY_SIZE = 8 165_CD64_OFFSET_START_CENTDIR = 9 166 167def _check_zipfile(fp): 168 try: 169 if _EndRecData(fp): 170 return True # file has correct magic number 171 except OSError: 172 pass 173 return False 174 175def is_zipfile(filename): 176 """Quickly see if a file is a ZIP file by checking the magic number. 177 178 The filename argument may be a file or file-like object too. 179 """ 180 result = False 181 try: 182 if hasattr(filename, "read"): 183 result = _check_zipfile(fp=filename) 184 else: 185 with open(filename, "rb") as fp: 186 result = _check_zipfile(fp) 187 except OSError: 188 pass 189 return result 190 191def _EndRecData64(fpin, offset, endrec): 192 """ 193 Read the ZIP64 end-of-archive records and use that to update endrec 194 """ 195 try: 196 fpin.seek(offset - sizeEndCentDir64Locator, 2) 197 except OSError: 198 # If the seek fails, the file is not large enough to contain a ZIP64 199 # end-of-archive record, so just return the end record we were given. 200 return endrec 201 202 data = fpin.read(sizeEndCentDir64Locator) 203 if len(data) != sizeEndCentDir64Locator: 204 return endrec 205 sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data) 206 if sig != stringEndArchive64Locator: 207 return endrec 208 209 if diskno != 0 or disks != 1: 210 raise BadZipFile("zipfiles that span multiple disks are not supported") 211 212 # Assume no 'zip64 extensible data' 213 fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2) 214 data = fpin.read(sizeEndCentDir64) 215 if len(data) != sizeEndCentDir64: 216 return endrec 217 sig, sz, create_version, read_version, disk_num, disk_dir, \ 218 dircount, dircount2, dirsize, diroffset = \ 219 struct.unpack(structEndArchive64, data) 220 if sig != stringEndArchive64: 221 return endrec 222 223 # Update the original endrec using data from the ZIP64 record 224 endrec[_ECD_SIGNATURE] = sig 225 endrec[_ECD_DISK_NUMBER] = disk_num 226 endrec[_ECD_DISK_START] = disk_dir 227 endrec[_ECD_ENTRIES_THIS_DISK] = dircount 228 endrec[_ECD_ENTRIES_TOTAL] = dircount2 229 endrec[_ECD_SIZE] = dirsize 230 endrec[_ECD_OFFSET] = diroffset 231 return endrec 232 233 234def _EndRecData(fpin): 235 """Return data from the "End of Central Directory" record, or None. 236 237 The data is a list of the nine items in the ZIP "End of central dir" 238 record followed by a tenth item, the file seek offset of this record.""" 239 240 # Determine file size 241 fpin.seek(0, 2) 242 filesize = fpin.tell() 243 244 # Check to see if this is ZIP file with no archive comment (the 245 # "end of central directory" structure should be the last item in the 246 # file if this is the case). 247 try: 248 fpin.seek(-sizeEndCentDir, 2) 249 except OSError: 250 return None 251 data = fpin.read() 252 if (len(data) == sizeEndCentDir and 253 data[0:4] == stringEndArchive and 254 data[-2:] == b"\000\000"): 255 # the signature is correct and there's no comment, unpack structure 256 endrec = struct.unpack(structEndArchive, data) 257 endrec=list(endrec) 258 259 # Append a blank comment and record start offset 260 endrec.append(b"") 261 endrec.append(filesize - sizeEndCentDir) 262 263 # Try to read the "Zip64 end of central directory" structure 264 return _EndRecData64(fpin, -sizeEndCentDir, endrec) 265 266 # Either this is not a ZIP file, or it is a ZIP file with an archive 267 # comment. Search the end of the file for the "end of central directory" 268 # record signature. The comment is the last item in the ZIP file and may be 269 # up to 64K long. It is assumed that the "end of central directory" magic 270 # number does not appear in the comment. 271 maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0) 272 fpin.seek(maxCommentStart, 0) 273 data = fpin.read() 274 start = data.rfind(stringEndArchive) 275 if start >= 0: 276 # found the magic number; attempt to unpack and interpret 277 recData = data[start:start+sizeEndCentDir] 278 if len(recData) != sizeEndCentDir: 279 # Zip file is corrupted. 280 return None 281 endrec = list(struct.unpack(structEndArchive, recData)) 282 commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file 283 comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize] 284 endrec.append(comment) 285 endrec.append(maxCommentStart + start) 286 287 # Try to read the "Zip64 end of central directory" structure 288 return _EndRecData64(fpin, maxCommentStart + start - filesize, 289 endrec) 290 291 # Unable to find a valid end of central directory structure 292 return None 293 294 295class ZipInfo (object): 296 """Class with attributes describing each file in the ZIP archive.""" 297 298 __slots__ = ( 299 'orig_filename', 300 'filename', 301 'date_time', 302 'compress_type', 303 'comment', 304 'extra', 305 'create_system', 306 'create_version', 307 'extract_version', 308 'reserved', 309 'flag_bits', 310 'volume', 311 'internal_attr', 312 'external_attr', 313 'header_offset', 314 'CRC', 315 'compress_size', 316 'file_size', 317 '_raw_time', 318 ) 319 320 def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): 321 self.orig_filename = filename # Original file name in archive 322 323 # Terminate the file name at the first null byte. Null bytes in file 324 # names are used as tricks by viruses in archives. 325 null_byte = filename.find(chr(0)) 326 if null_byte >= 0: 327 filename = filename[0:null_byte] 328 # This is used to ensure paths in generated ZIP files always use 329 # forward slashes as the directory separator, as required by the 330 # ZIP format specification. 331 if os.sep != "/" and os.sep in filename: 332 filename = filename.replace(os.sep, "/") 333 334 self.filename = filename # Normalized file name 335 self.date_time = date_time # year, month, day, hour, min, sec 336 337 if date_time[0] < 1980: 338 raise ValueError('ZIP does not support timestamps before 1980') 339 340 # Standard values: 341 self.compress_type = ZIP_STORED # Type of compression for the file 342 self.comment = b"" # Comment for each file 343 self.extra = b"" # ZIP extra data 344 if sys.platform == 'win32': 345 self.create_system = 0 # System which created ZIP archive 346 else: 347 # Assume everything else is unix-y 348 self.create_system = 3 # System which created ZIP archive 349 self.create_version = DEFAULT_VERSION # Version which created ZIP archive 350 self.extract_version = DEFAULT_VERSION # Version needed to extract archive 351 self.reserved = 0 # Must be zero 352 self.flag_bits = 0 # ZIP flag bits 353 self.volume = 0 # Volume number of file header 354 self.internal_attr = 0 # Internal attributes 355 self.external_attr = 0 # External file attributes 356 # Other attributes are set by class ZipFile: 357 # header_offset Byte offset to the file header 358 # CRC CRC-32 of the uncompressed file 359 # compress_size Size of the compressed file 360 # file_size Size of the uncompressed file 361 362 def __repr__(self): 363 result = ['<%s filename=%r' % (self.__class__.__name__, self.filename)] 364 if self.compress_type != ZIP_STORED: 365 result.append(' compress_type=%s' % 366 compressor_names.get(self.compress_type, 367 self.compress_type)) 368 hi = self.external_attr >> 16 369 lo = self.external_attr & 0xFFFF 370 if hi: 371 result.append(' filemode=%r' % stat.filemode(hi)) 372 if lo: 373 result.append(' external_attr=%#x' % lo) 374 isdir = self.is_dir() 375 if not isdir or self.file_size: 376 result.append(' file_size=%r' % self.file_size) 377 if ((not isdir or self.compress_size) and 378 (self.compress_type != ZIP_STORED or 379 self.file_size != self.compress_size)): 380 result.append(' compress_size=%r' % self.compress_size) 381 result.append('>') 382 return ''.join(result) 383 384 def FileHeader(self, zip64=None): 385 """Return the per-file header as a string.""" 386 dt = self.date_time 387 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 388 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 389 if self.flag_bits & 0x08: 390 # Set these to zero because we write them after the file data 391 CRC = compress_size = file_size = 0 392 else: 393 CRC = self.CRC 394 compress_size = self.compress_size 395 file_size = self.file_size 396 397 extra = self.extra 398 399 min_version = 0 400 if zip64 is None: 401 zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT 402 if zip64: 403 fmt = '<HHQQ' 404 extra = extra + struct.pack(fmt, 405 1, struct.calcsize(fmt)-4, file_size, compress_size) 406 if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT: 407 if not zip64: 408 raise LargeZipFile("Filesize would require ZIP64 extensions") 409 # File is larger than what fits into a 4 byte integer, 410 # fall back to the ZIP64 extension 411 file_size = 0xffffffff 412 compress_size = 0xffffffff 413 min_version = ZIP64_VERSION 414 415 if self.compress_type == ZIP_BZIP2: 416 min_version = max(BZIP2_VERSION, min_version) 417 elif self.compress_type == ZIP_LZMA: 418 min_version = max(LZMA_VERSION, min_version) 419 420 self.extract_version = max(min_version, self.extract_version) 421 self.create_version = max(min_version, self.create_version) 422 filename, flag_bits = self._encodeFilenameFlags() 423 header = struct.pack(structFileHeader, stringFileHeader, 424 self.extract_version, self.reserved, flag_bits, 425 self.compress_type, dostime, dosdate, CRC, 426 compress_size, file_size, 427 len(filename), len(extra)) 428 return header + filename + extra 429 430 def _encodeFilenameFlags(self): 431 try: 432 return self.filename.encode('ascii'), self.flag_bits 433 except UnicodeEncodeError: 434 return self.filename.encode('utf-8'), self.flag_bits | 0x800 435 436 def _decodeExtra(self): 437 # Try to decode the extra field. 438 extra = self.extra 439 unpack = struct.unpack 440 while len(extra) >= 4: 441 tp, ln = unpack('<HH', extra[:4]) 442 if tp == 1: 443 if ln >= 24: 444 counts = unpack('<QQQ', extra[4:28]) 445 elif ln == 16: 446 counts = unpack('<QQ', extra[4:20]) 447 elif ln == 8: 448 counts = unpack('<Q', extra[4:12]) 449 elif ln == 0: 450 counts = () 451 else: 452 raise BadZipFile("Corrupt extra field %04x (size=%d)" % (tp, ln)) 453 454 idx = 0 455 456 # ZIP64 extension (large files and/or large archives) 457 if self.file_size in (0xffffffffffffffff, 0xffffffff): 458 self.file_size = counts[idx] 459 idx += 1 460 461 if self.compress_size == 0xFFFFFFFF: 462 self.compress_size = counts[idx] 463 idx += 1 464 465 if self.header_offset == 0xffffffff: 466 old = self.header_offset 467 self.header_offset = counts[idx] 468 idx+=1 469 470 extra = extra[ln+4:] 471 472 @classmethod 473 def from_file(cls, filename, arcname=None): 474 """Construct an appropriate ZipInfo for a file on the filesystem. 475 476 filename should be the path to a file or directory on the filesystem. 477 478 arcname is the name which it will have within the archive (by default, 479 this will be the same as filename, but without a drive letter and with 480 leading path separators removed). 481 """ 482 st = os.stat(filename) 483 isdir = stat.S_ISDIR(st.st_mode) 484 mtime = time.localtime(st.st_mtime) 485 date_time = mtime[0:6] 486 # Create ZipInfo instance to store file information 487 if arcname is None: 488 arcname = filename 489 arcname = os.path.normpath(os.path.splitdrive(arcname)[1]) 490 while arcname[0] in (os.sep, os.altsep): 491 arcname = arcname[1:] 492 if isdir: 493 arcname += '/' 494 zinfo = cls(arcname, date_time) 495 zinfo.external_attr = (st.st_mode & 0xFFFF) << 16 # Unix attributes 496 if isdir: 497 zinfo.file_size = 0 498 zinfo.external_attr |= 0x10 # MS-DOS directory flag 499 else: 500 zinfo.file_size = st.st_size 501 502 return zinfo 503 504 def is_dir(self): 505 """Return True if this archive member is a directory.""" 506 return self.filename[-1] == '/' 507 508 509class _ZipDecrypter: 510 """Class to handle decryption of files stored within a ZIP archive. 511 512 ZIP supports a password-based form of encryption. Even though known 513 plaintext attacks have been found against it, it is still useful 514 to be able to get data out of such a file. 515 516 Usage: 517 zd = _ZipDecrypter(mypwd) 518 plain_char = zd(cypher_char) 519 plain_text = map(zd, cypher_text) 520 """ 521 522 def _GenerateCRCTable(): 523 """Generate a CRC-32 table. 524 525 ZIP encryption uses the CRC32 one-byte primitive for scrambling some 526 internal keys. We noticed that a direct implementation is faster than 527 relying on binascii.crc32(). 528 """ 529 poly = 0xedb88320 530 table = [0] * 256 531 for i in range(256): 532 crc = i 533 for j in range(8): 534 if crc & 1: 535 crc = ((crc >> 1) & 0x7FFFFFFF) ^ poly 536 else: 537 crc = ((crc >> 1) & 0x7FFFFFFF) 538 table[i] = crc 539 return table 540 crctable = None 541 542 def _crc32(self, ch, crc): 543 """Compute the CRC32 primitive on one byte.""" 544 return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ch) & 0xff] 545 546 def __init__(self, pwd): 547 if _ZipDecrypter.crctable is None: 548 _ZipDecrypter.crctable = _ZipDecrypter._GenerateCRCTable() 549 self.key0 = 305419896 550 self.key1 = 591751049 551 self.key2 = 878082192 552 for p in pwd: 553 self._UpdateKeys(p) 554 555 def _UpdateKeys(self, c): 556 self.key0 = self._crc32(c, self.key0) 557 self.key1 = (self.key1 + (self.key0 & 255)) & 4294967295 558 self.key1 = (self.key1 * 134775813 + 1) & 4294967295 559 self.key2 = self._crc32((self.key1 >> 24) & 255, self.key2) 560 561 def __call__(self, c): 562 """Decrypt a single character.""" 563 assert isinstance(c, int) 564 k = self.key2 | 2 565 c = c ^ (((k * (k^1)) >> 8) & 255) 566 self._UpdateKeys(c) 567 return c 568 569 570class LZMACompressor: 571 572 def __init__(self): 573 self._comp = None 574 575 def _init(self): 576 props = lzma._encode_filter_properties({'id': lzma.FILTER_LZMA1}) 577 self._comp = lzma.LZMACompressor(lzma.FORMAT_RAW, filters=[ 578 lzma._decode_filter_properties(lzma.FILTER_LZMA1, props) 579 ]) 580 return struct.pack('<BBH', 9, 4, len(props)) + props 581 582 def compress(self, data): 583 if self._comp is None: 584 return self._init() + self._comp.compress(data) 585 return self._comp.compress(data) 586 587 def flush(self): 588 if self._comp is None: 589 return self._init() + self._comp.flush() 590 return self._comp.flush() 591 592 593class LZMADecompressor: 594 595 def __init__(self): 596 self._decomp = None 597 self._unconsumed = b'' 598 self.eof = False 599 600 def decompress(self, data): 601 if self._decomp is None: 602 self._unconsumed += data 603 if len(self._unconsumed) <= 4: 604 return b'' 605 psize, = struct.unpack('<H', self._unconsumed[2:4]) 606 if len(self._unconsumed) <= 4 + psize: 607 return b'' 608 609 self._decomp = lzma.LZMADecompressor(lzma.FORMAT_RAW, filters=[ 610 lzma._decode_filter_properties(lzma.FILTER_LZMA1, 611 self._unconsumed[4:4 + psize]) 612 ]) 613 data = self._unconsumed[4 + psize:] 614 del self._unconsumed 615 616 result = self._decomp.decompress(data) 617 self.eof = self._decomp.eof 618 return result 619 620 621compressor_names = { 622 0: 'store', 623 1: 'shrink', 624 2: 'reduce', 625 3: 'reduce', 626 4: 'reduce', 627 5: 'reduce', 628 6: 'implode', 629 7: 'tokenize', 630 8: 'deflate', 631 9: 'deflate64', 632 10: 'implode', 633 12: 'bzip2', 634 14: 'lzma', 635 18: 'terse', 636 19: 'lz77', 637 97: 'wavpack', 638 98: 'ppmd', 639} 640 641def _check_compression(compression): 642 if compression == ZIP_STORED: 643 pass 644 elif compression == ZIP_DEFLATED: 645 if not zlib: 646 raise RuntimeError( 647 "Compression requires the (missing) zlib module") 648 elif compression == ZIP_BZIP2: 649 if not bz2: 650 raise RuntimeError( 651 "Compression requires the (missing) bz2 module") 652 elif compression == ZIP_LZMA: 653 if not lzma: 654 raise RuntimeError( 655 "Compression requires the (missing) lzma module") 656 else: 657 raise NotImplementedError("That compression method is not supported") 658 659 660def _get_compressor(compress_type): 661 if compress_type == ZIP_DEFLATED: 662 return zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, 663 zlib.DEFLATED, -15) 664 elif compress_type == ZIP_BZIP2: 665 return bz2.BZ2Compressor() 666 elif compress_type == ZIP_LZMA: 667 return LZMACompressor() 668 else: 669 return None 670 671 672def _get_decompressor(compress_type): 673 if compress_type == ZIP_STORED: 674 return None 675 elif compress_type == ZIP_DEFLATED: 676 return zlib.decompressobj(-15) 677 elif compress_type == ZIP_BZIP2: 678 return bz2.BZ2Decompressor() 679 elif compress_type == ZIP_LZMA: 680 return LZMADecompressor() 681 else: 682 descr = compressor_names.get(compress_type) 683 if descr: 684 raise NotImplementedError("compression type %d (%s)" % (compress_type, descr)) 685 else: 686 raise NotImplementedError("compression type %d" % (compress_type,)) 687 688 689class _SharedFile: 690 def __init__(self, file, pos, close, lock, writing): 691 self._file = file 692 self._pos = pos 693 self._close = close 694 self._lock = lock 695 self._writing = writing 696 697 def read(self, n=-1): 698 with self._lock: 699 if self._writing(): 700 raise ValueError("Can't read from the ZIP file while there " 701 "is an open writing handle on it. " 702 "Close the writing handle before trying to read.") 703 self._file.seek(self._pos) 704 data = self._file.read(n) 705 self._pos = self._file.tell() 706 return data 707 708 def close(self): 709 if self._file is not None: 710 fileobj = self._file 711 self._file = None 712 self._close(fileobj) 713 714# Provide the tell method for unseekable stream 715class _Tellable: 716 def __init__(self, fp): 717 self.fp = fp 718 self.offset = 0 719 720 def write(self, data): 721 n = self.fp.write(data) 722 self.offset += n 723 return n 724 725 def tell(self): 726 return self.offset 727 728 def flush(self): 729 self.fp.flush() 730 731 def close(self): 732 self.fp.close() 733 734 735class ZipExtFile(io.BufferedIOBase): 736 """File-like object for reading an archive member. 737 Is returned by ZipFile.open(). 738 """ 739 740 # Max size supported by decompressor. 741 MAX_N = 1 << 31 - 1 742 743 # Read from compressed files in 4k blocks. 744 MIN_READ_SIZE = 4096 745 746 def __init__(self, fileobj, mode, zipinfo, decrypter=None, 747 close_fileobj=False): 748 self._fileobj = fileobj 749 self._decrypter = decrypter 750 self._close_fileobj = close_fileobj 751 752 self._compress_type = zipinfo.compress_type 753 self._compress_left = zipinfo.compress_size 754 self._left = zipinfo.file_size 755 756 self._decompressor = _get_decompressor(self._compress_type) 757 758 self._eof = False 759 self._readbuffer = b'' 760 self._offset = 0 761 762 self.newlines = None 763 764 # Adjust read size for encrypted files since the first 12 bytes 765 # are for the encryption/password information. 766 if self._decrypter is not None: 767 self._compress_left -= 12 768 769 self.mode = mode 770 self.name = zipinfo.filename 771 772 if hasattr(zipinfo, 'CRC'): 773 self._expected_crc = zipinfo.CRC 774 self._running_crc = crc32(b'') 775 else: 776 self._expected_crc = None 777 778 def __repr__(self): 779 result = ['<%s.%s' % (self.__class__.__module__, 780 self.__class__.__qualname__)] 781 if not self.closed: 782 result.append(' name=%r mode=%r' % (self.name, self.mode)) 783 if self._compress_type != ZIP_STORED: 784 result.append(' compress_type=%s' % 785 compressor_names.get(self._compress_type, 786 self._compress_type)) 787 else: 788 result.append(' [closed]') 789 result.append('>') 790 return ''.join(result) 791 792 def readline(self, limit=-1): 793 """Read and return a line from the stream. 794 795 If limit is specified, at most limit bytes will be read. 796 """ 797 798 if limit < 0: 799 # Shortcut common case - newline found in buffer. 800 i = self._readbuffer.find(b'\n', self._offset) + 1 801 if i > 0: 802 line = self._readbuffer[self._offset: i] 803 self._offset = i 804 return line 805 806 return io.BufferedIOBase.readline(self, limit) 807 808 def peek(self, n=1): 809 """Returns buffered bytes without advancing the position.""" 810 if n > len(self._readbuffer) - self._offset: 811 chunk = self.read(n) 812 if len(chunk) > self._offset: 813 self._readbuffer = chunk + self._readbuffer[self._offset:] 814 self._offset = 0 815 else: 816 self._offset -= len(chunk) 817 818 # Return up to 512 bytes to reduce allocation overhead for tight loops. 819 return self._readbuffer[self._offset: self._offset + 512] 820 821 def readable(self): 822 return True 823 824 def read(self, n=-1): 825 """Read and return up to n bytes. 826 If the argument is omitted, None, or negative, data is read and returned until EOF is reached.. 827 """ 828 if n is None or n < 0: 829 buf = self._readbuffer[self._offset:] 830 self._readbuffer = b'' 831 self._offset = 0 832 while not self._eof: 833 buf += self._read1(self.MAX_N) 834 return buf 835 836 end = n + self._offset 837 if end < len(self._readbuffer): 838 buf = self._readbuffer[self._offset:end] 839 self._offset = end 840 return buf 841 842 n = end - len(self._readbuffer) 843 buf = self._readbuffer[self._offset:] 844 self._readbuffer = b'' 845 self._offset = 0 846 while n > 0 and not self._eof: 847 data = self._read1(n) 848 if n < len(data): 849 self._readbuffer = data 850 self._offset = n 851 buf += data[:n] 852 break 853 buf += data 854 n -= len(data) 855 return buf 856 857 def _update_crc(self, newdata): 858 # Update the CRC using the given data. 859 if self._expected_crc is None: 860 # No need to compute the CRC if we don't have a reference value 861 return 862 self._running_crc = crc32(newdata, self._running_crc) 863 # Check the CRC if we're at the end of the file 864 if self._eof and self._running_crc != self._expected_crc: 865 raise BadZipFile("Bad CRC-32 for file %r" % self.name) 866 867 def read1(self, n): 868 """Read up to n bytes with at most one read() system call.""" 869 870 if n is None or n < 0: 871 buf = self._readbuffer[self._offset:] 872 self._readbuffer = b'' 873 self._offset = 0 874 while not self._eof: 875 data = self._read1(self.MAX_N) 876 if data: 877 buf += data 878 break 879 return buf 880 881 end = n + self._offset 882 if end < len(self._readbuffer): 883 buf = self._readbuffer[self._offset:end] 884 self._offset = end 885 return buf 886 887 n = end - len(self._readbuffer) 888 buf = self._readbuffer[self._offset:] 889 self._readbuffer = b'' 890 self._offset = 0 891 if n > 0: 892 while not self._eof: 893 data = self._read1(n) 894 if n < len(data): 895 self._readbuffer = data 896 self._offset = n 897 buf += data[:n] 898 break 899 if data: 900 buf += data 901 break 902 return buf 903 904 def _read1(self, n): 905 # Read up to n compressed bytes with at most one read() system call, 906 # decrypt and decompress them. 907 if self._eof or n <= 0: 908 return b'' 909 910 # Read from file. 911 if self._compress_type == ZIP_DEFLATED: 912 ## Handle unconsumed data. 913 data = self._decompressor.unconsumed_tail 914 if n > len(data): 915 data += self._read2(n - len(data)) 916 else: 917 data = self._read2(n) 918 919 if self._compress_type == ZIP_STORED: 920 self._eof = self._compress_left <= 0 921 elif self._compress_type == ZIP_DEFLATED: 922 n = max(n, self.MIN_READ_SIZE) 923 data = self._decompressor.decompress(data, n) 924 self._eof = (self._decompressor.eof or 925 self._compress_left <= 0 and 926 not self._decompressor.unconsumed_tail) 927 if self._eof: 928 data += self._decompressor.flush() 929 else: 930 data = self._decompressor.decompress(data) 931 self._eof = self._decompressor.eof or self._compress_left <= 0 932 933 data = data[:self._left] 934 self._left -= len(data) 935 if self._left <= 0: 936 self._eof = True 937 self._update_crc(data) 938 return data 939 940 def _read2(self, n): 941 if self._compress_left <= 0: 942 return b'' 943 944 n = max(n, self.MIN_READ_SIZE) 945 n = min(n, self._compress_left) 946 947 data = self._fileobj.read(n) 948 self._compress_left -= len(data) 949 if not data: 950 raise EOFError 951 952 if self._decrypter is not None: 953 data = bytes(map(self._decrypter, data)) 954 return data 955 956 def close(self): 957 try: 958 if self._close_fileobj: 959 self._fileobj.close() 960 finally: 961 super().close() 962 963 964class _ZipWriteFile(io.BufferedIOBase): 965 def __init__(self, zf, zinfo, zip64): 966 self._zinfo = zinfo 967 self._zip64 = zip64 968 self._zipfile = zf 969 self._compressor = _get_compressor(zinfo.compress_type) 970 self._file_size = 0 971 self._compress_size = 0 972 self._crc = 0 973 974 @property 975 def _fileobj(self): 976 return self._zipfile.fp 977 978 def writable(self): 979 return True 980 981 def write(self, data): 982 nbytes = len(data) 983 self._file_size += nbytes 984 self._crc = crc32(data, self._crc) 985 if self._compressor: 986 data = self._compressor.compress(data) 987 self._compress_size += len(data) 988 self._fileobj.write(data) 989 return nbytes 990 991 def close(self): 992 super().close() 993 # Flush any data from the compressor, and update header info 994 if self._compressor: 995 buf = self._compressor.flush() 996 self._compress_size += len(buf) 997 self._fileobj.write(buf) 998 self._zinfo.compress_size = self._compress_size 999 else: 1000 self._zinfo.compress_size = self._file_size 1001 self._zinfo.CRC = self._crc 1002 self._zinfo.file_size = self._file_size 1003 1004 # Write updated header info 1005 if self._zinfo.flag_bits & 0x08: 1006 # Write CRC and file sizes after the file data 1007 fmt = '<LQQ' if self._zip64 else '<LLL' 1008 self._fileobj.write(struct.pack(fmt, self._zinfo.CRC, 1009 self._zinfo.compress_size, self._zinfo.file_size)) 1010 self._zipfile.start_dir = self._fileobj.tell() 1011 else: 1012 if not self._zip64: 1013 if self._file_size > ZIP64_LIMIT: 1014 raise RuntimeError('File size unexpectedly exceeded ZIP64 ' 1015 'limit') 1016 if self._compress_size > ZIP64_LIMIT: 1017 raise RuntimeError('Compressed size unexpectedly exceeded ' 1018 'ZIP64 limit') 1019 # Seek backwards and write file header (which will now include 1020 # correct CRC and file sizes) 1021 1022 # Preserve current position in file 1023 self._zipfile.start_dir = self._fileobj.tell() 1024 self._fileobj.seek(self._zinfo.header_offset) 1025 self._fileobj.write(self._zinfo.FileHeader(self._zip64)) 1026 self._fileobj.seek(self._zipfile.start_dir) 1027 1028 self._zipfile._writing = False 1029 1030 # Successfully written: Add file to our caches 1031 self._zipfile.filelist.append(self._zinfo) 1032 self._zipfile.NameToInfo[self._zinfo.filename] = self._zinfo 1033 1034class ZipFile: 1035 """ Class with methods to open, read, write, close, list zip files. 1036 1037 z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True) 1038 1039 file: Either the path to the file, or a file-like object. 1040 If it is a path, the file will be opened and closed by ZipFile. 1041 mode: The mode can be either read 'r', write 'w', exclusive create 'x', 1042 or append 'a'. 1043 compression: ZIP_STORED (no compression), ZIP_DEFLATED (requires zlib), 1044 ZIP_BZIP2 (requires bz2) or ZIP_LZMA (requires lzma). 1045 allowZip64: if True ZipFile will create files with ZIP64 extensions when 1046 needed, otherwise it will raise an exception when this would 1047 be necessary. 1048 1049 """ 1050 1051 fp = None # Set here since __del__ checks it 1052 _windows_illegal_name_trans_table = None 1053 1054 def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True): 1055 """Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', 1056 or append 'a'.""" 1057 if mode not in ('r', 'w', 'x', 'a'): 1058 raise ValueError("ZipFile requires mode 'r', 'w', 'x', or 'a'") 1059 1060 _check_compression(compression) 1061 1062 self._allowZip64 = allowZip64 1063 self._didModify = False 1064 self.debug = 0 # Level of printing: 0 through 3 1065 self.NameToInfo = {} # Find file info given name 1066 self.filelist = [] # List of ZipInfo instances for archive 1067 self.compression = compression # Method of compression 1068 self.mode = mode 1069 self.pwd = None 1070 self._comment = b'' 1071 1072 # Check if we were passed a file-like object 1073 if isinstance(file, str): 1074 # No, it's a filename 1075 self._filePassed = 0 1076 self.filename = file 1077 modeDict = {'r' : 'rb', 'w': 'w+b', 'x': 'x+b', 'a' : 'r+b', 1078 'r+b': 'w+b', 'w+b': 'wb', 'x+b': 'xb'} 1079 filemode = modeDict[mode] 1080 while True: 1081 try: 1082 self.fp = io.open(file, filemode) 1083 except OSError: 1084 if filemode in modeDict: 1085 filemode = modeDict[filemode] 1086 continue 1087 raise 1088 break 1089 else: 1090 self._filePassed = 1 1091 self.fp = file 1092 self.filename = getattr(file, 'name', None) 1093 self._fileRefCnt = 1 1094 self._lock = threading.RLock() 1095 self._seekable = True 1096 self._writing = False 1097 1098 try: 1099 if mode == 'r': 1100 self._RealGetContents() 1101 elif mode in ('w', 'x'): 1102 # set the modified flag so central directory gets written 1103 # even if no files are added to the archive 1104 self._didModify = True 1105 self._start_disk = 0 1106 try: 1107 self.start_dir = self.fp.tell() 1108 except (AttributeError, OSError): 1109 self.fp = _Tellable(self.fp) 1110 self.start_dir = 0 1111 self._seekable = False 1112 else: 1113 # Some file-like objects can provide tell() but not seek() 1114 try: 1115 self.fp.seek(self.start_dir) 1116 except (AttributeError, OSError): 1117 self._seekable = False 1118 elif mode == 'a': 1119 try: 1120 # See if file is a zip file 1121 self._RealGetContents() 1122 # seek to start of directory and overwrite 1123 self.fp.seek(self.start_dir) 1124 except BadZipFile: 1125 # file is not a zip file, just append 1126 self.fp.seek(0, 2) 1127 1128 # set the modified flag so central directory gets written 1129 # even if no files are added to the archive 1130 self._didModify = True 1131 self.start_dir = self._start_disk = self.fp.tell() 1132 else: 1133 raise ValueError("Mode must be 'r', 'w', 'x', or 'a'") 1134 except: 1135 fp = self.fp 1136 self.fp = None 1137 self._fpclose(fp) 1138 raise 1139 1140 def __enter__(self): 1141 return self 1142 1143 def __exit__(self, type, value, traceback): 1144 self.close() 1145 1146 def __repr__(self): 1147 result = ['<%s.%s' % (self.__class__.__module__, 1148 self.__class__.__qualname__)] 1149 if self.fp is not None: 1150 if self._filePassed: 1151 result.append(' file=%r' % self.fp) 1152 elif self.filename is not None: 1153 result.append(' filename=%r' % self.filename) 1154 result.append(' mode=%r' % self.mode) 1155 else: 1156 result.append(' [closed]') 1157 result.append('>') 1158 return ''.join(result) 1159 1160 def _RealGetContents(self): 1161 """Read in the table of contents for the ZIP file.""" 1162 fp = self.fp 1163 try: 1164 endrec = _EndRecData(fp) 1165 except OSError: 1166 raise BadZipFile("File is not a zip file") 1167 if not endrec: 1168 raise BadZipFile("File is not a zip file") 1169 if self.debug > 1: 1170 print(endrec) 1171 size_cd = endrec[_ECD_SIZE] # bytes in central directory 1172 offset_cd = endrec[_ECD_OFFSET] # offset of central directory 1173 self._comment = endrec[_ECD_COMMENT] # archive comment 1174 1175 # self._start_disk: Position of the start of ZIP archive 1176 # It is zero, unless ZIP was concatenated to another file 1177 self._start_disk = endrec[_ECD_LOCATION] - size_cd - offset_cd 1178 if endrec[_ECD_SIGNATURE] == stringEndArchive64: 1179 # If Zip64 extension structures are present, account for them 1180 self._start_disk -= (sizeEndCentDir64 + sizeEndCentDir64Locator) 1181 1182 if self.debug > 2: 1183 inferred = self._start_disk + offset_cd 1184 print("given, inferred, offset", offset_cd, inferred, self._start_disk) 1185 # self.start_dir: Position of start of central directory 1186 self.start_dir = offset_cd + self._start_disk 1187 fp.seek(self.start_dir, 0) 1188 data = fp.read(size_cd) 1189 fp = io.BytesIO(data) 1190 total = 0 1191 while total < size_cd: 1192 centdir = fp.read(sizeCentralDir) 1193 if len(centdir) != sizeCentralDir: 1194 raise BadZipFile("Truncated central directory") 1195 centdir = struct.unpack(structCentralDir, centdir) 1196 if centdir[_CD_SIGNATURE] != stringCentralDir: 1197 raise BadZipFile("Bad magic number for central directory") 1198 if self.debug > 2: 1199 print(centdir) 1200 filename = fp.read(centdir[_CD_FILENAME_LENGTH]) 1201 flags = centdir[5] 1202 if flags & 0x800: 1203 # UTF-8 file names extension 1204 filename = filename.decode('utf-8') 1205 else: 1206 # Historical ZIP filename encoding 1207 filename = filename.decode('cp437') 1208 # Create ZipInfo instance to store file information 1209 x = ZipInfo(filename) 1210 x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH]) 1211 x.comment = fp.read(centdir[_CD_COMMENT_LENGTH]) 1212 x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] 1213 (x.create_version, x.create_system, x.extract_version, x.reserved, 1214 x.flag_bits, x.compress_type, t, d, 1215 x.CRC, x.compress_size, x.file_size) = centdir[1:12] 1216 if x.extract_version > MAX_EXTRACT_VERSION: 1217 raise NotImplementedError("zip file version %.1f" % 1218 (x.extract_version / 10)) 1219 x.volume, x.internal_attr, x.external_attr = centdir[15:18] 1220 # Convert date/time code to (year, month, day, hour, min, sec) 1221 x._raw_time = t 1222 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F, 1223 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) 1224 1225 x._decodeExtra() 1226 x.header_offset = x.header_offset + self._start_disk 1227 self.filelist.append(x) 1228 self.NameToInfo[x.filename] = x 1229 1230 # update total bytes read from central directory 1231 total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH] 1232 + centdir[_CD_EXTRA_FIELD_LENGTH] 1233 + centdir[_CD_COMMENT_LENGTH]) 1234 1235 if self.debug > 2: 1236 print("total", total) 1237 1238 1239 def namelist(self): 1240 """Return a list of file names in the archive.""" 1241 return [data.filename for data in self.filelist] 1242 1243 def infolist(self): 1244 """Return a list of class ZipInfo instances for files in the 1245 archive.""" 1246 return self.filelist 1247 1248 def printdir(self, file=None): 1249 """Print a table of contents for the zip file.""" 1250 print("%-46s %19s %12s" % ("File Name", "Modified ", "Size"), 1251 file=file) 1252 for zinfo in self.filelist: 1253 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] 1254 print("%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size), 1255 file=file) 1256 1257 def testzip(self): 1258 """Read all the files and check the CRC.""" 1259 chunk_size = 2 ** 20 1260 for zinfo in self.filelist: 1261 try: 1262 # Read by chunks, to avoid an OverflowError or a 1263 # MemoryError with very large embedded files. 1264 with self.open(zinfo.filename, "r") as f: 1265 while f.read(chunk_size): # Check CRC-32 1266 pass 1267 except BadZipFile: 1268 return zinfo.filename 1269 1270 def getinfo(self, name): 1271 """Return the instance of ZipInfo given 'name'.""" 1272 info = self.NameToInfo.get(name) 1273 if info is None: 1274 raise KeyError( 1275 'There is no item named %r in the archive' % name) 1276 1277 return info 1278 1279 def setpassword(self, pwd): 1280 """Set default password for encrypted files.""" 1281 if pwd and not isinstance(pwd, bytes): 1282 raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) 1283 if pwd: 1284 self.pwd = pwd 1285 else: 1286 self.pwd = None 1287 1288 @property 1289 def comment(self): 1290 """The comment text associated with the ZIP file.""" 1291 return self._comment 1292 1293 @comment.setter 1294 def comment(self, comment): 1295 if not isinstance(comment, bytes): 1296 raise TypeError("comment: expected bytes, got %s" % type(comment).__name__) 1297 # check for valid comment length 1298 if len(comment) > ZIP_MAX_COMMENT: 1299 import warnings 1300 warnings.warn('Archive comment is too long; truncating to %d bytes' 1301 % ZIP_MAX_COMMENT, stacklevel=2) 1302 comment = comment[:ZIP_MAX_COMMENT] 1303 self._comment = comment 1304 self._didModify = True 1305 1306 def read(self, name, pwd=None): 1307 """Return file bytes (as a string) for name.""" 1308 with self.open(name, "r", pwd) as fp: 1309 return fp.read() 1310 1311 def open(self, name, mode="r", pwd=None, *, force_zip64=False): 1312 """Return file-like object for 'name'. 1313 1314 name is a string for the file name within the ZIP file, or a ZipInfo 1315 object. 1316 1317 mode should be 'r' to read a file already in the ZIP file, or 'w' to 1318 write to a file newly added to the archive. 1319 1320 pwd is the password to decrypt files (only used for reading). 1321 1322 When writing, if the file size is not known in advance but may exceed 1323 2 GiB, pass force_zip64 to use the ZIP64 format, which can handle large 1324 files. If the size is known in advance, it is best to pass a ZipInfo 1325 instance for name, with zinfo.file_size set. 1326 """ 1327 if mode not in {"r", "w"}: 1328 raise ValueError('open() requires mode "r" or "w"') 1329 if pwd and not isinstance(pwd, bytes): 1330 raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) 1331 if pwd and (mode == "w"): 1332 raise ValueError("pwd is only supported for reading files") 1333 if not self.fp: 1334 raise ValueError( 1335 "Attempt to use ZIP archive that was already closed") 1336 1337 # Make sure we have an info object 1338 if isinstance(name, ZipInfo): 1339 # 'name' is already an info object 1340 zinfo = name 1341 elif mode == 'w': 1342 zinfo = ZipInfo(name) 1343 zinfo.compress_type = self.compression 1344 else: 1345 # Get info object for name 1346 zinfo = self.getinfo(name) 1347 1348 if mode == 'w': 1349 return self._open_to_write(zinfo, force_zip64=force_zip64) 1350 1351 if self._writing: 1352 raise ValueError("Can't read from the ZIP file while there " 1353 "is an open writing handle on it. " 1354 "Close the writing handle before trying to read.") 1355 1356 # Open for reading: 1357 self._fileRefCnt += 1 1358 zef_file = _SharedFile(self.fp, zinfo.header_offset, 1359 self._fpclose, self._lock, lambda: self._writing) 1360 try: 1361 # Skip the file header: 1362 fheader = zef_file.read(sizeFileHeader) 1363 if len(fheader) != sizeFileHeader: 1364 raise BadZipFile("Truncated file header") 1365 fheader = struct.unpack(structFileHeader, fheader) 1366 if fheader[_FH_SIGNATURE] != stringFileHeader: 1367 raise BadZipFile("Bad magic number for file header") 1368 1369 fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) 1370 if fheader[_FH_EXTRA_FIELD_LENGTH]: 1371 zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH]) 1372 1373 if zinfo.flag_bits & 0x20: 1374 # Zip 2.7: compressed patched data 1375 raise NotImplementedError("compressed patched data (flag bit 5)") 1376 1377 if zinfo.flag_bits & 0x40: 1378 # strong encryption 1379 raise NotImplementedError("strong encryption (flag bit 6)") 1380 1381 if zinfo.flag_bits & 0x800: 1382 # UTF-8 filename 1383 fname_str = fname.decode("utf-8") 1384 else: 1385 fname_str = fname.decode("cp437") 1386 1387 if fname_str != zinfo.orig_filename: 1388 raise BadZipFile( 1389 'File name in directory %r and header %r differ.' 1390 % (zinfo.orig_filename, fname)) 1391 1392 # check for encrypted flag & handle password 1393 is_encrypted = zinfo.flag_bits & 0x1 1394 zd = None 1395 if is_encrypted: 1396 if not pwd: 1397 pwd = self.pwd 1398 if not pwd: 1399 raise RuntimeError("File %r is encrypted, password " 1400 "required for extraction" % name) 1401 1402 zd = _ZipDecrypter(pwd) 1403 # The first 12 bytes in the cypher stream is an encryption header 1404 # used to strengthen the algorithm. The first 11 bytes are 1405 # completely random, while the 12th contains the MSB of the CRC, 1406 # or the MSB of the file time depending on the header type 1407 # and is used to check the correctness of the password. 1408 header = zef_file.read(12) 1409 h = list(map(zd, header[0:12])) 1410 if zinfo.flag_bits & 0x8: 1411 # compare against the file type from extended local headers 1412 check_byte = (zinfo._raw_time >> 8) & 0xff 1413 else: 1414 # compare against the CRC otherwise 1415 check_byte = (zinfo.CRC >> 24) & 0xff 1416 if h[11] != check_byte: 1417 raise RuntimeError("Bad password for file %r" % name) 1418 1419 return ZipExtFile(zef_file, mode, zinfo, zd, True) 1420 except: 1421 zef_file.close() 1422 raise 1423 1424 def _open_to_write(self, zinfo, force_zip64=False): 1425 if force_zip64 and not self._allowZip64: 1426 raise ValueError( 1427 "force_zip64 is True, but allowZip64 was False when opening " 1428 "the ZIP file." 1429 ) 1430 if self._writing: 1431 raise ValueError("Can't write to the ZIP file while there is " 1432 "another write handle open on it. " 1433 "Close the first handle before opening another.") 1434 1435 # Sizes and CRC are overwritten with correct data after processing the file 1436 if not hasattr(zinfo, 'file_size'): 1437 zinfo.file_size = 0 1438 zinfo.compress_size = 0 1439 zinfo.CRC = 0 1440 1441 zinfo.flag_bits = 0x00 1442 if zinfo.compress_type == ZIP_LZMA: 1443 # Compressed data includes an end-of-stream (EOS) marker 1444 zinfo.flag_bits |= 0x02 1445 if not self._seekable: 1446 zinfo.flag_bits |= 0x08 1447 1448 if not zinfo.external_attr: 1449 zinfo.external_attr = 0o600 << 16 # permissions: ?rw------- 1450 1451 # Compressed size can be larger than uncompressed size 1452 zip64 = self._allowZip64 and \ 1453 (force_zip64 or zinfo.file_size * 1.05 > ZIP64_LIMIT) 1454 1455 if self._seekable: 1456 self.fp.seek(self.start_dir) 1457 zinfo.header_offset = self.fp.tell() 1458 1459 self._writecheck(zinfo) 1460 self._didModify = True 1461 1462 self.fp.write(zinfo.FileHeader(zip64)) 1463 1464 self._writing = True 1465 return _ZipWriteFile(self, zinfo, zip64) 1466 1467 def extract(self, member, path=None, pwd=None): 1468 """Extract a member from the archive to the current working directory, 1469 using its full name. Its file information is extracted as accurately 1470 as possible. `member' may be a filename or a ZipInfo object. You can 1471 specify a different directory using `path'. 1472 """ 1473 if not isinstance(member, ZipInfo): 1474 member = self.getinfo(member) 1475 1476 if path is None: 1477 path = os.getcwd() 1478 1479 return self._extract_member(member, path, pwd) 1480 1481 def extractall(self, path=None, members=None, pwd=None): 1482 """Extract all members from the archive to the current working 1483 directory. `path' specifies a different directory to extract to. 1484 `members' is optional and must be a subset of the list returned 1485 by namelist(). 1486 """ 1487 if members is None: 1488 members = self.namelist() 1489 1490 for zipinfo in members: 1491 self.extract(zipinfo, path, pwd) 1492 1493 @classmethod 1494 def _sanitize_windows_name(cls, arcname, pathsep): 1495 """Replace bad characters and remove trailing dots from parts.""" 1496 table = cls._windows_illegal_name_trans_table 1497 if not table: 1498 illegal = ':<>|"?*' 1499 table = str.maketrans(illegal, '_' * len(illegal)) 1500 cls._windows_illegal_name_trans_table = table 1501 arcname = arcname.translate(table) 1502 # remove trailing dots 1503 arcname = (x.rstrip('.') for x in arcname.split(pathsep)) 1504 # rejoin, removing empty parts. 1505 arcname = pathsep.join(x for x in arcname if x) 1506 return arcname 1507 1508 def _extract_member(self, member, targetpath, pwd): 1509 """Extract the ZipInfo object 'member' to a physical 1510 file on the path targetpath. 1511 """ 1512 # build the destination pathname, replacing 1513 # forward slashes to platform specific separators. 1514 arcname = member.filename.replace('/', os.path.sep) 1515 1516 if os.path.altsep: 1517 arcname = arcname.replace(os.path.altsep, os.path.sep) 1518 # interpret absolute pathname as relative, remove drive letter or 1519 # UNC path, redundant separators, "." and ".." components. 1520 arcname = os.path.splitdrive(arcname)[1] 1521 invalid_path_parts = ('', os.path.curdir, os.path.pardir) 1522 arcname = os.path.sep.join(x for x in arcname.split(os.path.sep) 1523 if x not in invalid_path_parts) 1524 if os.path.sep == '\\': 1525 # filter illegal characters on Windows 1526 arcname = self._sanitize_windows_name(arcname, os.path.sep) 1527 1528 targetpath = os.path.join(targetpath, arcname) 1529 targetpath = os.path.normpath(targetpath) 1530 1531 # Create all upper directories if necessary. 1532 upperdirs = os.path.dirname(targetpath) 1533 if upperdirs and not os.path.exists(upperdirs): 1534 os.makedirs(upperdirs) 1535 1536 if member.is_dir(): 1537 if not os.path.isdir(targetpath): 1538 os.mkdir(targetpath) 1539 return targetpath 1540 1541 with self.open(member, pwd=pwd) as source, \ 1542 open(targetpath, "wb") as target: 1543 shutil.copyfileobj(source, target) 1544 1545 return targetpath 1546 1547 def _writecheck(self, zinfo): 1548 """Check for errors before writing a file to the archive.""" 1549 if zinfo.filename in self.NameToInfo: 1550 import warnings 1551 warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3) 1552 if self.mode not in ('w', 'x', 'a'): 1553 raise ValueError("write() requires mode 'w', 'x', or 'a'") 1554 if not self.fp: 1555 raise ValueError( 1556 "Attempt to write ZIP archive that was already closed") 1557 _check_compression(zinfo.compress_type) 1558 if not self._allowZip64: 1559 requires_zip64 = None 1560 if len(self.filelist) >= ZIP_FILECOUNT_LIMIT: 1561 requires_zip64 = "Files count" 1562 elif zinfo.file_size > ZIP64_LIMIT: 1563 requires_zip64 = "Filesize" 1564 elif zinfo.header_offset > ZIP64_LIMIT: 1565 requires_zip64 = "Zipfile size" 1566 if requires_zip64: 1567 raise LargeZipFile(requires_zip64 + 1568 " would require ZIP64 extensions") 1569 1570 def write(self, filename, arcname=None, compress_type=None): 1571 """Put the bytes from filename into the archive under the name 1572 arcname.""" 1573 if not self.fp: 1574 raise ValueError( 1575 "Attempt to write to ZIP archive that was already closed") 1576 if self._writing: 1577 raise ValueError( 1578 "Can't write to ZIP archive while an open writing handle exists" 1579 ) 1580 1581 zinfo = ZipInfo.from_file(filename, arcname) 1582 1583 if zinfo.is_dir(): 1584 zinfo.compress_size = 0 1585 zinfo.CRC = 0 1586 else: 1587 if compress_type is not None: 1588 zinfo.compress_type = compress_type 1589 else: 1590 zinfo.compress_type = self.compression 1591 1592 if zinfo.is_dir(): 1593 with self._lock: 1594 if self._seekable: 1595 self.fp.seek(self.start_dir) 1596 zinfo.header_offset = self.fp.tell() # Start of header bytes 1597 if zinfo.compress_type == ZIP_LZMA: 1598 # Compressed data includes an end-of-stream (EOS) marker 1599 zinfo.flag_bits |= 0x02 1600 1601 self._writecheck(zinfo) 1602 self._didModify = True 1603 1604 self.filelist.append(zinfo) 1605 self.NameToInfo[zinfo.filename] = zinfo 1606 self.fp.write(zinfo.FileHeader(False)) 1607 self.start_dir = self.fp.tell() 1608 else: 1609 with open(filename, "rb") as src, self.open(zinfo, 'w') as dest: 1610 shutil.copyfileobj(src, dest, 1024*8) 1611 1612 def writestr(self, zinfo_or_arcname, data, compress_type=None): 1613 """Write a file into the archive. The contents is 'data', which 1614 may be either a 'str' or a 'bytes' instance; if it is a 'str', 1615 it is encoded as UTF-8 first. 1616 'zinfo_or_arcname' is either a ZipInfo instance or 1617 the name of the file in the archive.""" 1618 if isinstance(data, str): 1619 data = data.encode("utf-8") 1620 if not isinstance(zinfo_or_arcname, ZipInfo): 1621 zinfo = ZipInfo(filename=zinfo_or_arcname, 1622 date_time=time.localtime(time.time())[:6]) 1623 zinfo.compress_type = self.compression 1624 if zinfo.filename[-1] == '/': 1625 zinfo.external_attr = 0o40775 << 16 # drwxrwxr-x 1626 zinfo.external_attr |= 0x10 # MS-DOS directory flag 1627 else: 1628 zinfo.external_attr = 0o600 << 16 # ?rw------- 1629 else: 1630 zinfo = zinfo_or_arcname 1631 1632 if not self.fp: 1633 raise ValueError( 1634 "Attempt to write to ZIP archive that was already closed") 1635 if self._writing: 1636 raise ValueError( 1637 "Can't write to ZIP archive while an open writing handle exists." 1638 ) 1639 1640 if compress_type is not None: 1641 zinfo.compress_type = compress_type 1642 1643 zinfo.file_size = len(data) # Uncompressed size 1644 with self._lock: 1645 with self.open(zinfo, mode='w') as dest: 1646 dest.write(data) 1647 1648 def __del__(self): 1649 """Call the "close()" method in case the user forgot.""" 1650 self.close() 1651 1652 def close(self): 1653 """Close the file, and for mode 'w', 'x' and 'a' write the ending 1654 records.""" 1655 if self.fp is None: 1656 return 1657 1658 if self._writing: 1659 raise ValueError("Can't close the ZIP file while there is " 1660 "an open writing handle on it. " 1661 "Close the writing handle before closing the zip.") 1662 1663 try: 1664 if self.mode in ('w', 'x', 'a') and self._didModify: # write ending records 1665 with self._lock: 1666 if self._seekable: 1667 self.fp.seek(self.start_dir) 1668 self._write_end_record() 1669 finally: 1670 fp = self.fp 1671 self.fp = None 1672 self._fpclose(fp) 1673 1674 def _write_end_record(self): 1675 for zinfo in self.filelist: # write central directory 1676 dt = zinfo.date_time 1677 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 1678 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 1679 extra = [] 1680 if zinfo.file_size > ZIP64_LIMIT \ 1681 or zinfo.compress_size > ZIP64_LIMIT: 1682 extra.append(zinfo.file_size) 1683 extra.append(zinfo.compress_size) 1684 file_size = 0xffffffff 1685 compress_size = 0xffffffff 1686 else: 1687 file_size = zinfo.file_size 1688 compress_size = zinfo.compress_size 1689 1690 header_offset = zinfo.header_offset - self._start_disk 1691 if header_offset > ZIP64_LIMIT: 1692 extra.append(header_offset) 1693 header_offset = 0xffffffff 1694 1695 extra_data = zinfo.extra 1696 min_version = 0 1697 if extra: 1698 # Append a ZIP64 field to the extra's 1699 extra_data = struct.pack( 1700 '<HH' + 'Q'*len(extra), 1701 1, 8*len(extra), *extra) + extra_data 1702 1703 min_version = ZIP64_VERSION 1704 1705 if zinfo.compress_type == ZIP_BZIP2: 1706 min_version = max(BZIP2_VERSION, min_version) 1707 elif zinfo.compress_type == ZIP_LZMA: 1708 min_version = max(LZMA_VERSION, min_version) 1709 1710 extract_version = max(min_version, zinfo.extract_version) 1711 create_version = max(min_version, zinfo.create_version) 1712 try: 1713 filename, flag_bits = zinfo._encodeFilenameFlags() 1714 centdir = struct.pack(structCentralDir, 1715 stringCentralDir, create_version, 1716 zinfo.create_system, extract_version, zinfo.reserved, 1717 flag_bits, zinfo.compress_type, dostime, dosdate, 1718 zinfo.CRC, compress_size, file_size, 1719 len(filename), len(extra_data), len(zinfo.comment), 1720 0, zinfo.internal_attr, zinfo.external_attr, 1721 header_offset) 1722 except DeprecationWarning: 1723 print((structCentralDir, stringCentralDir, create_version, 1724 zinfo.create_system, extract_version, zinfo.reserved, 1725 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, 1726 zinfo.CRC, compress_size, file_size, 1727 len(zinfo.filename), len(extra_data), len(zinfo.comment), 1728 0, zinfo.internal_attr, zinfo.external_attr, 1729 header_offset), file=sys.stderr) 1730 raise 1731 self.fp.write(centdir) 1732 self.fp.write(filename) 1733 self.fp.write(extra_data) 1734 self.fp.write(zinfo.comment) 1735 1736 pos2 = self.fp.tell() 1737 # Write end-of-zip-archive record 1738 centDirCount = len(self.filelist) 1739 centDirSize = pos2 - self.start_dir 1740 centDirOffset = self.start_dir - self._start_disk 1741 requires_zip64 = None 1742 if centDirCount > ZIP_FILECOUNT_LIMIT: 1743 requires_zip64 = "Files count" 1744 elif centDirOffset > ZIP64_LIMIT: 1745 requires_zip64 = "Central directory offset" 1746 elif centDirSize > ZIP64_LIMIT: 1747 requires_zip64 = "Central directory size" 1748 if requires_zip64: 1749 # Need to write the ZIP64 end-of-archive records 1750 if not self._allowZip64: 1751 raise LargeZipFile(requires_zip64 + 1752 " would require ZIP64 extensions") 1753 zip64endrec = struct.pack( 1754 structEndArchive64, stringEndArchive64, 1755 44, 45, 45, 0, 0, centDirCount, centDirCount, 1756 centDirSize, centDirOffset) 1757 self.fp.write(zip64endrec) 1758 1759 zip64locrec = struct.pack( 1760 structEndArchive64Locator, 1761 stringEndArchive64Locator, 0, pos2, 1) 1762 self.fp.write(zip64locrec) 1763 centDirCount = min(centDirCount, 0xFFFF) 1764 centDirSize = min(centDirSize, 0xFFFFFFFF) 1765 centDirOffset = min(centDirOffset, 0xFFFFFFFF) 1766 1767 endrec = struct.pack(structEndArchive, stringEndArchive, 1768 0, 0, centDirCount, centDirCount, 1769 centDirSize, centDirOffset, len(self._comment)) 1770 self.fp.write(endrec) 1771 self.fp.write(self._comment) 1772 self.fp.flush() 1773 1774 def _fpclose(self, fp): 1775 assert self._fileRefCnt > 0 1776 self._fileRefCnt -= 1 1777 if not self._fileRefCnt and not self._filePassed: 1778 fp.close() 1779 1780 1781class PyZipFile(ZipFile): 1782 """Class to create ZIP archives with Python library files and packages.""" 1783 1784 def __init__(self, file, mode="r", compression=ZIP_STORED, 1785 allowZip64=True, optimize=-1): 1786 ZipFile.__init__(self, file, mode=mode, compression=compression, 1787 allowZip64=allowZip64) 1788 self._optimize = optimize 1789 1790 def writepy(self, pathname, basename="", filterfunc=None): 1791 """Add all files from "pathname" to the ZIP archive. 1792 1793 If pathname is a package directory, search the directory and 1794 all package subdirectories recursively for all *.py and enter 1795 the modules into the archive. If pathname is a plain 1796 directory, listdir *.py and enter all modules. Else, pathname 1797 must be a Python *.py file and the module will be put into the 1798 archive. Added modules are always module.pyc. 1799 This method will compile the module.py into module.pyc if 1800 necessary. 1801 If filterfunc(pathname) is given, it is called with every argument. 1802 When it is False, the file or directory is skipped. 1803 """ 1804 if filterfunc and not filterfunc(pathname): 1805 if self.debug: 1806 label = 'path' if os.path.isdir(pathname) else 'file' 1807 print('%s %r skipped by filterfunc' % (label, pathname)) 1808 return 1809 dir, name = os.path.split(pathname) 1810 if os.path.isdir(pathname): 1811 initname = os.path.join(pathname, "__init__.py") 1812 if os.path.isfile(initname): 1813 # This is a package directory, add it 1814 if basename: 1815 basename = "%s/%s" % (basename, name) 1816 else: 1817 basename = name 1818 if self.debug: 1819 print("Adding package in", pathname, "as", basename) 1820 fname, arcname = self._get_codename(initname[0:-3], basename) 1821 if self.debug: 1822 print("Adding", arcname) 1823 self.write(fname, arcname) 1824 dirlist = os.listdir(pathname) 1825 dirlist.remove("__init__.py") 1826 # Add all *.py files and package subdirectories 1827 for filename in dirlist: 1828 path = os.path.join(pathname, filename) 1829 root, ext = os.path.splitext(filename) 1830 if os.path.isdir(path): 1831 if os.path.isfile(os.path.join(path, "__init__.py")): 1832 # This is a package directory, add it 1833 self.writepy(path, basename, 1834 filterfunc=filterfunc) # Recursive call 1835 elif ext == ".py": 1836 if filterfunc and not filterfunc(path): 1837 if self.debug: 1838 print('file %r skipped by filterfunc' % path) 1839 continue 1840 fname, arcname = self._get_codename(path[0:-3], 1841 basename) 1842 if self.debug: 1843 print("Adding", arcname) 1844 self.write(fname, arcname) 1845 else: 1846 # This is NOT a package directory, add its files at top level 1847 if self.debug: 1848 print("Adding files from directory", pathname) 1849 for filename in os.listdir(pathname): 1850 path = os.path.join(pathname, filename) 1851 root, ext = os.path.splitext(filename) 1852 if ext == ".py": 1853 if filterfunc and not filterfunc(path): 1854 if self.debug: 1855 print('file %r skipped by filterfunc' % path) 1856 continue 1857 fname, arcname = self._get_codename(path[0:-3], 1858 basename) 1859 if self.debug: 1860 print("Adding", arcname) 1861 self.write(fname, arcname) 1862 else: 1863 if pathname[-3:] != ".py": 1864 raise RuntimeError( 1865 'Files added with writepy() must end with ".py"') 1866 fname, arcname = self._get_codename(pathname[0:-3], basename) 1867 if self.debug: 1868 print("Adding file", arcname) 1869 self.write(fname, arcname) 1870 1871 def _get_codename(self, pathname, basename): 1872 """Return (filename, archivename) for the path. 1873 1874 Given a module name path, return the correct file path and 1875 archive name, compiling if necessary. For example, given 1876 /python/lib/string, return (/python/lib/string.pyc, string). 1877 """ 1878 def _compile(file, optimize=-1): 1879 import py_compile 1880 if self.debug: 1881 print("Compiling", file) 1882 try: 1883 py_compile.compile(file, doraise=True, optimize=optimize) 1884 except py_compile.PyCompileError as err: 1885 print(err.msg) 1886 return False 1887 return True 1888 1889 file_py = pathname + ".py" 1890 file_pyc = pathname + ".pyc" 1891 pycache_opt0 = importlib.util.cache_from_source(file_py, optimization='') 1892 pycache_opt1 = importlib.util.cache_from_source(file_py, optimization=1) 1893 pycache_opt2 = importlib.util.cache_from_source(file_py, optimization=2) 1894 if self._optimize == -1: 1895 # legacy mode: use whatever file is present 1896 if (os.path.isfile(file_pyc) and 1897 os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime): 1898 # Use .pyc file. 1899 arcname = fname = file_pyc 1900 elif (os.path.isfile(pycache_opt0) and 1901 os.stat(pycache_opt0).st_mtime >= os.stat(file_py).st_mtime): 1902 # Use the __pycache__/*.pyc file, but write it to the legacy pyc 1903 # file name in the archive. 1904 fname = pycache_opt0 1905 arcname = file_pyc 1906 elif (os.path.isfile(pycache_opt1) and 1907 os.stat(pycache_opt1).st_mtime >= os.stat(file_py).st_mtime): 1908 # Use the __pycache__/*.pyc file, but write it to the legacy pyc 1909 # file name in the archive. 1910 fname = pycache_opt1 1911 arcname = file_pyc 1912 elif (os.path.isfile(pycache_opt2) and 1913 os.stat(pycache_opt2).st_mtime >= os.stat(file_py).st_mtime): 1914 # Use the __pycache__/*.pyc file, but write it to the legacy pyc 1915 # file name in the archive. 1916 fname = pycache_opt2 1917 arcname = file_pyc 1918 else: 1919 # Compile py into PEP 3147 pyc file. 1920 if _compile(file_py): 1921 if sys.flags.optimize == 0: 1922 fname = pycache_opt0 1923 elif sys.flags.optimize == 1: 1924 fname = pycache_opt1 1925 else: 1926 fname = pycache_opt2 1927 arcname = file_pyc 1928 else: 1929 fname = arcname = file_py 1930 else: 1931 # new mode: use given optimization level 1932 if self._optimize == 0: 1933 fname = pycache_opt0 1934 arcname = file_pyc 1935 else: 1936 arcname = file_pyc 1937 if self._optimize == 1: 1938 fname = pycache_opt1 1939 elif self._optimize == 2: 1940 fname = pycache_opt2 1941 else: 1942 msg = "invalid value for 'optimize': {!r}".format(self._optimize) 1943 raise ValueError(msg) 1944 if not (os.path.isfile(fname) and 1945 os.stat(fname).st_mtime >= os.stat(file_py).st_mtime): 1946 if not _compile(file_py, optimize=self._optimize): 1947 fname = arcname = file_py 1948 archivename = os.path.split(arcname)[1] 1949 if basename: 1950 archivename = "%s/%s" % (basename, archivename) 1951 return (fname, archivename) 1952 1953 1954def main(args = None): 1955 import textwrap 1956 USAGE=textwrap.dedent("""\ 1957 Usage: 1958 zipfile.py -l zipfile.zip # Show listing of a zipfile 1959 zipfile.py -t zipfile.zip # Test if a zipfile is valid 1960 zipfile.py -e zipfile.zip target # Extract zipfile into target dir 1961 zipfile.py -c zipfile.zip src ... # Create zipfile from sources 1962 """) 1963 if args is None: 1964 args = sys.argv[1:] 1965 1966 if not args or args[0] not in ('-l', '-c', '-e', '-t'): 1967 print(USAGE) 1968 sys.exit(1) 1969 1970 if args[0] == '-l': 1971 if len(args) != 2: 1972 print(USAGE) 1973 sys.exit(1) 1974 with ZipFile(args[1], 'r') as zf: 1975 zf.printdir() 1976 1977 elif args[0] == '-t': 1978 if len(args) != 2: 1979 print(USAGE) 1980 sys.exit(1) 1981 with ZipFile(args[1], 'r') as zf: 1982 badfile = zf.testzip() 1983 if badfile: 1984 print("The following enclosed file is corrupted: {!r}".format(badfile)) 1985 print("Done testing") 1986 1987 elif args[0] == '-e': 1988 if len(args) != 3: 1989 print(USAGE) 1990 sys.exit(1) 1991 1992 with ZipFile(args[1], 'r') as zf: 1993 zf.extractall(args[2]) 1994 1995 elif args[0] == '-c': 1996 if len(args) < 3: 1997 print(USAGE) 1998 sys.exit(1) 1999 2000 def addToZip(zf, path, zippath): 2001 if os.path.isfile(path): 2002 zf.write(path, zippath, ZIP_DEFLATED) 2003 elif os.path.isdir(path): 2004 if zippath: 2005 zf.write(path, zippath) 2006 for nm in os.listdir(path): 2007 addToZip(zf, 2008 os.path.join(path, nm), os.path.join(zippath, nm)) 2009 # else: ignore 2010 2011 with ZipFile(args[1], 'w') as zf: 2012 for path in args[2:]: 2013 zippath = os.path.basename(path) 2014 if not zippath: 2015 zippath = os.path.basename(os.path.dirname(path)) 2016 if zippath in ('', os.curdir, os.pardir): 2017 zippath = '' 2018 addToZip(zf, path, zippath) 2019 2020if __name__ == "__main__": 2021 main() 2022