summaryrefslogtreecommitdiffstats
path: root/Lib/zipfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/zipfile.py')
-rw-r--r--Lib/zipfile.py204
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))