diff options
Diffstat (limited to 'Lib/zipfile.py')
-rw-r--r-- | Lib/zipfile.py | 204 |
1 files changed, 113 insertions, 91 deletions
diff --git a/Lib/zipfile.py b/Lib/zipfile.py index a1b3414..12c1754 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -6,7 +6,7 @@ XXX references to utf-8 need further investigation. import io import os import re -import imp +import importlib.util import sys import time import stat @@ -164,7 +164,7 @@ def _check_zipfile(fp): try: if _EndRecData(fp): return True # file has correct magic number - except IOError: + except OSError: pass return False @@ -180,7 +180,7 @@ def is_zipfile(filename): else: with open(filename, "rb") as fp: result = _check_zipfile(fp) - except IOError: + except OSError: pass return result @@ -190,7 +190,7 @@ def _EndRecData64(fpin, offset, endrec): """ try: fpin.seek(offset - sizeEndCentDir64Locator, 2) - except IOError: + except OSError: # If the seek fails, the file is not large enough to contain a ZIP64 # end-of-archive record, so just return the end record we were given. return endrec @@ -211,8 +211,8 @@ def _EndRecData64(fpin, offset, endrec): if len(data) != sizeEndCentDir64: return endrec sig, sz, create_version, read_version, disk_num, disk_dir, \ - dircount, dircount2, dirsize, diroffset = \ - struct.unpack(structEndArchive64, data) + dircount, dircount2, dirsize, diroffset = \ + struct.unpack(structEndArchive64, data) if sig != stringEndArchive64: return endrec @@ -242,7 +242,7 @@ def _EndRecData(fpin): # file if this is the case). try: fpin.seek(-sizeEndCentDir, 2) - except IOError: + except OSError: return None data = fpin.read() if (len(data) == sizeEndCentDir and @@ -292,26 +292,26 @@ class ZipInfo (object): """Class with attributes describing each file in the ZIP archive.""" __slots__ = ( - 'orig_filename', - 'filename', - 'date_time', - 'compress_type', - 'comment', - 'extra', - 'create_system', - 'create_version', - 'extract_version', - 'reserved', - 'flag_bits', - 'volume', - 'internal_attr', - 'external_attr', - 'header_offset', - 'CRC', - 'compress_size', - 'file_size', - '_raw_time', - ) + 'orig_filename', + 'filename', + 'date_time', + 'compress_type', + 'comment', + 'extra', + 'create_system', + 'create_version', + 'extract_version', + 'reserved', + 'flag_bits', + 'volume', + 'internal_attr', + 'external_attr', + 'header_offset', + 'CRC', + 'compress_size', + 'file_size', + '_raw_time', + ) def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): self.orig_filename = filename # Original file name in archive @@ -376,7 +376,7 @@ class ZipInfo (object): if zip64: fmt = '<HHQQ' extra = extra + struct.pack(fmt, - 1, struct.calcsize(fmt)-4, file_size, compress_size) + 1, struct.calcsize(fmt)-4, file_size, compress_size) if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT: if not zip64: raise LargeZipFile("Filesize would require ZIP64 extensions") @@ -395,10 +395,10 @@ class ZipInfo (object): self.create_version = max(min_version, self.create_version) filename, flag_bits = self._encodeFilenameFlags() header = struct.pack(structFileHeader, stringFileHeader, - self.extract_version, self.reserved, flag_bits, - self.compress_type, dostime, dosdate, CRC, - compress_size, file_size, - len(filename), len(extra)) + self.extract_version, self.reserved, flag_bits, + self.compress_type, dostime, dosdate, CRC, + compress_size, file_size, + len(filename), len(extra)) return header + filename + extra def _encodeFilenameFlags(self): @@ -475,13 +475,15 @@ class _ZipDecrypter: crc = ((crc >> 1) & 0x7FFFFFFF) table[i] = crc return table - crctable = _GenerateCRCTable() + crctable = None def _crc32(self, ch, crc): """Compute the CRC32 primitive on one byte.""" return ((crc >> 8) & 0xffffff) ^ self.crctable[(crc ^ ch) & 0xff] def __init__(self, pwd): + if _ZipDecrypter.crctable is None: + _ZipDecrypter.crctable = _ZipDecrypter._GenerateCRCTable() self.key0 = 305419896 self.key1 = 591751049 self.key2 = 878082192 @@ -511,7 +513,7 @@ class LZMACompressor: def _init(self): props = lzma._encode_filter_properties({'id': lzma.FILTER_LZMA1}) self._comp = lzma.LZMACompressor(lzma.FORMAT_RAW, filters=[ - lzma._decode_filter_properties(lzma.FILTER_LZMA1, props) + lzma._decode_filter_properties(lzma.FILTER_LZMA1, props) ]) return struct.pack('<BBH', 9, 4, len(props)) + props @@ -543,8 +545,8 @@ class LZMADecompressor: return b'' self._decomp = lzma.LZMADecompressor(lzma.FORMAT_RAW, filters=[ - lzma._decode_filter_properties(lzma.FILTER_LZMA1, - self._unconsumed[4:4 + psize]) + lzma._decode_filter_properties(lzma.FILTER_LZMA1, + self._unconsumed[4:4 + psize]) ]) data = self._unconsumed[4 + psize:] del self._unconsumed @@ -580,15 +582,15 @@ def _check_compression(compression): elif compression == ZIP_DEFLATED: if not zlib: raise RuntimeError( - "Compression requires the (missing) zlib module") + "Compression requires the (missing) zlib module") elif compression == ZIP_BZIP2: if not bz2: raise RuntimeError( - "Compression requires the (missing) bz2 module") + "Compression requires the (missing) bz2 module") elif compression == ZIP_LZMA: if not lzma: raise RuntimeError( - "Compression requires the (missing) lzma module") + "Compression requires the (missing) lzma module") else: raise RuntimeError("That compression method is not supported") @@ -596,7 +598,7 @@ def _check_compression(compression): def _get_compressor(compress_type): if compress_type == ZIP_DEFLATED: return zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, - zlib.DEFLATED, -15) + zlib.DEFLATED, -15) elif compress_type == ZIP_BZIP2: return bz2.BZ2Compressor() elif compress_type == ZIP_LZMA: @@ -836,8 +838,8 @@ class ZipExtFile(io.BufferedIOBase): n = max(n, self.MIN_READ_SIZE) data = self._decompressor.decompress(data, n) self._eof = (self._decompressor.eof or - self._compress_left <= 0 and - not self._decompressor.unconsumed_tail) + self._compress_left <= 0 and + not self._decompressor.unconsumed_tail) if self._eof: data += self._decompressor.flush() else: @@ -878,7 +880,7 @@ class ZipExtFile(io.BufferedIOBase): class ZipFile: """ Class with methods to open, read, write, close, list zip files. - z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False) + z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True) file: Either the path to the file, or a file-like object. If it is a path, the file will be opened and closed by ZipFile. @@ -894,7 +896,7 @@ class ZipFile: fp = None # Set here since __del__ checks it _windows_illegal_name_trans_table = None - def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False): + def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True): """Open the ZIP file with mode read "r", write "w" or append "a".""" if mode not in ("r", "w", "a"): raise RuntimeError('ZipFile() requires mode "r", "w", or "a"') @@ -919,7 +921,7 @@ class ZipFile: modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'} try: self.fp = io.open(file, modeDict[mode]) - except IOError: + except OSError: if mode == 'a': mode = key = 'w' self.fp = io.open(file, modeDict[mode]) @@ -970,7 +972,7 @@ class ZipFile: fp = self.fp try: endrec = _EndRecData(fp) - except IOError: + except OSError: raise BadZipFile("File is not a zip file") if not endrec: raise BadZipFile("File is not a zip file") @@ -1018,8 +1020,8 @@ class ZipFile: x.comment = fp.read(centdir[_CD_COMMENT_LENGTH]) x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] (x.create_version, x.create_system, x.extract_version, x.reserved, - x.flag_bits, x.compress_type, t, d, - x.CRC, x.compress_size, x.file_size) = centdir[1:12] + x.flag_bits, x.compress_type, t, d, + x.CRC, x.compress_size, x.file_size) = centdir[1:12] if x.extract_version > MAX_EXTRACT_VERSION: raise NotImplementedError("zip file version %.1f" % (x.extract_version / 10)) @@ -1027,7 +1029,7 @@ class ZipFile: # Convert date/time code to (year, month, day, hour, min, sec) x._raw_time = t x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F, - t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) + t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) x._decodeExtra() x.header_offset = x.header_offset + concat @@ -1105,7 +1107,7 @@ class ZipFile: if len(comment) >= ZIP_MAX_COMMENT: if self.debug: print('Archive comment is too long; truncating to %d bytes' - % ZIP_MAX_COMMENT) + % ZIP_MAX_COMMENT) comment = comment[:ZIP_MAX_COMMENT] self._comment = comment self._didModify = True @@ -1119,11 +1121,15 @@ class ZipFile: """Return file-like object for 'name'.""" if mode not in ("r", "U", "rU"): raise RuntimeError('open() requires mode "r", "U", or "rU"') + if 'U' in mode: + import warnings + warnings.warn("'U' mode is deprecated", + DeprecationWarning, 2) if pwd and not isinstance(pwd, bytes): raise TypeError("pwd: expected bytes, got %s" % type(pwd)) if not self.fp: raise RuntimeError( - "Attempt to read ZIP archive that was already closed") + "Attempt to read ZIP archive that was already closed") # Only open a new file for instances where we were not # given a file object in the constructor @@ -1296,7 +1302,7 @@ class ZipFile: raise RuntimeError('write() requires mode "w" or "a"') if not self.fp: raise RuntimeError( - "Attempt to write ZIP archive that was already closed") + "Attempt to write ZIP archive that was already closed") _check_compression(zinfo.compress_type) if zinfo.file_size > ZIP64_LIMIT: if not self._allowZip64: @@ -1304,14 +1310,14 @@ class ZipFile: if zinfo.header_offset > ZIP64_LIMIT: if not self._allowZip64: raise LargeZipFile( - "Zipfile size would require ZIP64 extensions") + "Zipfile size would require ZIP64 extensions") def write(self, filename, arcname=None, compress_type=None): """Put the bytes from filename into the archive under the name arcname.""" if not self.fp: raise RuntimeError( - "Attempt to write to ZIP archive that was already closed") + "Attempt to write to ZIP archive that was already closed") st = os.stat(filename) isdir = stat.S_ISDIR(st.st_mode) @@ -1358,7 +1364,7 @@ class ZipFile: zinfo.compress_size = compress_size = 0 # Compressed size can be larger than uncompressed size zip64 = self._allowZip64 and \ - zinfo.file_size * 1.05 > ZIP64_LIMIT + zinfo.file_size * 1.05 > ZIP64_LIMIT self.fp.write(zinfo.FileHeader(zip64)) file_size = 0 while 1: @@ -1412,7 +1418,7 @@ class ZipFile: if not self.fp: raise RuntimeError( - "Attempt to write to ZIP archive that was already closed") + "Attempt to write to ZIP archive that was already closed") zinfo.file_size = len(data) # Uncompressed size zinfo.header_offset = self.fp.tell() # Start of header data @@ -1432,7 +1438,7 @@ class ZipFile: else: zinfo.compress_size = zinfo.file_size zip64 = zinfo.file_size > ZIP64_LIMIT or \ - zinfo.compress_size > ZIP64_LIMIT + zinfo.compress_size > ZIP64_LIMIT if zip64 and not self._allowZip64: raise LargeZipFile("Filesize would require ZIP64 extensions") self.fp.write(zinfo.FileHeader(zip64)) @@ -1441,7 +1447,7 @@ class ZipFile: # Write CRC and file sizes after the file data fmt = '<LQQ' if zip64 else '<LLL' self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size, - zinfo.file_size)) + zinfo.file_size)) self.fp.flush() self.filelist.append(zinfo) self.NameToInfo[zinfo.filename] = zinfo @@ -1467,7 +1473,7 @@ class ZipFile: dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) extra = [] if zinfo.file_size > ZIP64_LIMIT \ - or zinfo.compress_size > ZIP64_LIMIT: + or zinfo.compress_size > ZIP64_LIMIT: extra.append(zinfo.file_size) extra.append(zinfo.compress_size) file_size = 0xffffffff @@ -1487,8 +1493,8 @@ class ZipFile: if extra: # Append a ZIP64 field to the extra's extra_data = struct.pack( - '<HH' + 'Q'*len(extra), - 1, 8*len(extra), *extra) + extra_data + '<HH' + 'Q'*len(extra), + 1, 8*len(extra), *extra) + extra_data min_version = ZIP64_VERSION @@ -1502,21 +1508,21 @@ class ZipFile: try: filename, flag_bits = zinfo._encodeFilenameFlags() centdir = struct.pack(structCentralDir, - stringCentralDir, create_version, - zinfo.create_system, extract_version, zinfo.reserved, - flag_bits, zinfo.compress_type, dostime, dosdate, - zinfo.CRC, compress_size, file_size, - len(filename), len(extra_data), len(zinfo.comment), - 0, zinfo.internal_attr, zinfo.external_attr, - header_offset) + stringCentralDir, create_version, + zinfo.create_system, extract_version, zinfo.reserved, + flag_bits, zinfo.compress_type, dostime, dosdate, + zinfo.CRC, compress_size, file_size, + len(filename), len(extra_data), len(zinfo.comment), + 0, zinfo.internal_attr, zinfo.external_attr, + header_offset) except DeprecationWarning: print((structCentralDir, stringCentralDir, create_version, - zinfo.create_system, extract_version, zinfo.reserved, - zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, - zinfo.CRC, compress_size, file_size, - len(zinfo.filename), len(extra_data), len(zinfo.comment), - 0, zinfo.internal_attr, zinfo.external_attr, - header_offset), file=sys.stderr) + zinfo.create_system, extract_version, zinfo.reserved, + zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, + zinfo.CRC, compress_size, file_size, + len(zinfo.filename), len(extra_data), len(zinfo.comment), + 0, zinfo.internal_attr, zinfo.external_attr, + header_offset), file=sys.stderr) raise self.fp.write(centdir) self.fp.write(filename) @@ -1533,22 +1539,22 @@ class ZipFile: centDirSize > ZIP64_LIMIT): # Need to write the ZIP64 end-of-archive records zip64endrec = struct.pack( - structEndArchive64, stringEndArchive64, - 44, 45, 45, 0, 0, centDirCount, centDirCount, - centDirSize, centDirOffset) + structEndArchive64, stringEndArchive64, + 44, 45, 45, 0, 0, centDirCount, centDirCount, + centDirSize, centDirOffset) self.fp.write(zip64endrec) zip64locrec = struct.pack( - structEndArchive64Locator, - stringEndArchive64Locator, 0, pos2, 1) + structEndArchive64Locator, + stringEndArchive64Locator, 0, pos2, 1) self.fp.write(zip64locrec) centDirCount = min(centDirCount, 0xFFFF) centDirSize = min(centDirSize, 0xFFFFFFFF) centDirOffset = min(centDirOffset, 0xFFFFFFFF) endrec = struct.pack(structEndArchive, stringEndArchive, - 0, 0, centDirCount, centDirCount, - centDirSize, centDirOffset, len(self._comment)) + 0, 0, centDirCount, centDirCount, + centDirSize, centDirOffset, len(self._comment)) self.fp.write(endrec) self.fp.write(self._comment) self.fp.flush() @@ -1563,12 +1569,12 @@ class PyZipFile(ZipFile): """Class to create ZIP archives with Python library files and packages.""" def __init__(self, file, mode="r", compression=ZIP_STORED, - allowZip64=False, optimize=-1): + allowZip64=True, optimize=-1): ZipFile.__init__(self, file, mode=mode, compression=compression, allowZip64=allowZip64) self._optimize = optimize - def writepy(self, pathname, basename=""): + def writepy(self, pathname, basename="", filterfunc=None): """Add all files from "pathname" to the ZIP archive. If pathname is a package directory, search the directory and @@ -1579,7 +1585,14 @@ class PyZipFile(ZipFile): archive. Added modules are always module.pyo or module.pyc. This method will compile the module.py into module.pyc if necessary. + If filterfunc(pathname) is given, it is called with every argument. + When it is False, the file or directory is skipped. """ + if filterfunc and not filterfunc(pathname): + if self.debug: + label = 'path' if os.path.isdir(pathname) else 'file' + print('%s "%s" skipped by filterfunc' % (label, pathname)) + return dir, name = os.path.split(pathname) if os.path.isdir(pathname): initname = os.path.join(pathname, "__init__.py") @@ -1604,10 +1617,15 @@ class PyZipFile(ZipFile): if os.path.isdir(path): if os.path.isfile(os.path.join(path, "__init__.py")): # This is a package directory, add it - self.writepy(path, basename) # Recursive call + self.writepy(path, basename, + filterfunc=filterfunc) # Recursive call elif ext == ".py": + if filterfunc and not filterfunc(path): + if self.debug: + print('file "%s" skipped by filterfunc' % path) + continue fname, arcname = self._get_codename(path[0:-3], - basename) + basename) if self.debug: print("Adding", arcname) self.write(fname, arcname) @@ -1619,15 +1637,19 @@ class PyZipFile(ZipFile): path = os.path.join(pathname, filename) root, ext = os.path.splitext(filename) if ext == ".py": + if filterfunc and not filterfunc(path): + if self.debug: + print('file "%s" skipped by filterfunc' % path) + continue fname, arcname = self._get_codename(path[0:-3], - basename) + basename) if self.debug: print("Adding", arcname) self.write(fname, arcname) else: if pathname[-3:] != ".py": raise RuntimeError( - 'Files added with writepy() must end with ".py"') + 'Files added with writepy() must end with ".py"') fname, arcname = self._get_codename(pathname[0:-3], basename) if self.debug: print("Adding file", arcname) @@ -1654,8 +1676,8 @@ class PyZipFile(ZipFile): file_py = pathname + ".py" file_pyc = pathname + ".pyc" file_pyo = pathname + ".pyo" - pycache_pyc = imp.cache_from_source(file_py, True) - pycache_pyo = imp.cache_from_source(file_py, False) + pycache_pyc = importlib.util.cache_from_source(file_py, True) + pycache_pyo = importlib.util.cache_from_source(file_py, False) if self._optimize == -1: # legacy mode: use whatever file is present if (os.path.isfile(file_pyo) and @@ -1766,10 +1788,10 @@ def main(args = None): elif os.path.isdir(path): for nm in os.listdir(path): addToZip(zf, - os.path.join(path, nm), os.path.join(zippath, nm)) + os.path.join(path, nm), os.path.join(zippath, nm)) # else: ignore - with ZipFile(args[1], 'w', allowZip64=True) as zf: + with ZipFile(args[1], 'w') as zf: for src in args[2:]: addToZip(zf, src, os.path.basename(src)) |