This commit is contained in:
Kovid Goyal 2015-11-28 17:15:00 +05:30
parent b3b901ac45
commit 91fbb9d2cd

View File

@ -26,6 +26,7 @@ class BadZipfile(Exception):
class LargeZipFile(Exception): class LargeZipFile(Exception):
""" """
Raised when writing a zipfile, the zipfile requires ZIP64 extensions Raised when writing a zipfile, the zipfile requires ZIP64 extensions
and those extensions are disabled. and those extensions are disabled.
@ -278,6 +279,7 @@ def _EndRecData(fpin):
class ZipInfo (object): class ZipInfo (object):
"""Class with attributes describing each file in the ZIP archive.""" """Class with attributes describing each file in the ZIP archive."""
__slots__ = ( __slots__ = (
@ -405,7 +407,7 @@ class ZipInfo (object):
elif ln == 0: elif ln == 0:
counts = () counts = ()
else: else:
raise RuntimeError, "Corrupt extra field %s"%(ln,) raise RuntimeError("Corrupt extra field %s"%(ln,))
idx = 0 idx = 0
@ -426,6 +428,7 @@ class ZipInfo (object):
class _ZipDecrypter: class _ZipDecrypter:
"""Class to handle decryption of files stored within a ZIP archive. """Class to handle decryption of files stored within a ZIP archive.
ZIP supports a password-based form of encryption. Even though known ZIP supports a password-based form of encryption. Even though known
@ -485,6 +488,7 @@ class _ZipDecrypter:
return c return c
class ZipExtFile(io.BufferedIOBase): class ZipExtFile(io.BufferedIOBase):
"""File-like object for reading an archive member. """File-like object for reading an archive member.
Is returned by ZipFile.open(). Is returned by ZipFile.open().
""" """
@ -688,6 +692,7 @@ class ZipExtFile(io.BufferedIOBase):
class ZipFile: class ZipFile:
""" Class with methods to open, read, write, close, list and update zip files. """ Class with methods to open, read, write, close, list and update zip files.
z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False) z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
@ -713,10 +718,10 @@ class ZipFile:
pass pass
elif compression == ZIP_DEFLATED: elif compression == ZIP_DEFLATED:
if not zlib: if not zlib:
raise RuntimeError,\ raise RuntimeError(
"Compression requires the (missing) zlib module" "Compression requires the (missing) zlib module")
else: else:
raise RuntimeError, "That compression method is not supported" raise RuntimeError("That compression method is not supported")
self._allowZip64 = allowZip64 self._allowZip64 = allowZip64
self._didModify = False self._didModify = False
@ -771,7 +776,7 @@ class ZipFile:
if not self._filePassed: if not self._filePassed:
self.fp.close() self.fp.close()
self.fp = None self.fp = None
raise RuntimeError, 'Mode must be "r", "w" or "a"' raise RuntimeError('Mode must be "r", "w" or "a"')
def __enter__(self): def __enter__(self):
return self return self
@ -823,7 +828,7 @@ class ZipFile:
while total < size_cd: while total < size_cd:
centdir = fp.read(sizeCentralDir) centdir = fp.read(sizeCentralDir)
if centdir[0:4] != stringCentralDir: if centdir[0:4] != stringCentralDir:
raise BadZipfile, "Bad magic number for central directory" raise BadZipfile("Bad magic number for central directory")
centdir = struct.unpack(structCentralDir, centdir) centdir = struct.unpack(structCentralDir, centdir)
if self.debug > 2: if self.debug > 2:
print centdir print centdir
@ -849,9 +854,9 @@ class ZipFile:
self.NameToInfo[x.filename] = x self.NameToInfo[x.filename] = x
# update total bytes read from central directory # update total bytes read from central directory
total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH] total = (total + sizeCentralDir + centdir[_CD_FILENAME_LENGTH] +
+ centdir[_CD_EXTRA_FIELD_LENGTH] centdir[_CD_EXTRA_FIELD_LENGTH] +
+ centdir[_CD_COMMENT_LENGTH]) centdir[_CD_COMMENT_LENGTH])
if self.debug > 2: if self.debug > 2:
print "total", total print "total", total
@ -861,15 +866,15 @@ class ZipFile:
self.fp.seek(zip_info.header_offset, 0) self.fp.seek(zip_info.header_offset, 0)
fheader = self.fp.read(30) fheader = self.fp.read(30)
if fheader[0:4] != stringFileHeader: if fheader[0:4] != stringFileHeader:
raise BadZipfile, "Bad magic number for file header" raise BadZipfile("Bad magic number for file header")
fheader = struct.unpack(structFileHeader, fheader) fheader = struct.unpack(structFileHeader, fheader)
# file_offset is computed here, since the extra field for # file_offset is computed here, since the extra field for
# the central directory and for the local file header # the central directory and for the local file header
# refer to different fields, and they can have different # refer to different fields, and they can have different
# lengths # lengths
file_offset = (zip_info.header_offset + 30 file_offset = (zip_info.header_offset + 30 +
+ fheader[_FH_FILENAME_LENGTH] fheader[_FH_FILENAME_LENGTH] +
+ fheader[_FH_EXTRA_FIELD_LENGTH]) fheader[_FH_EXTRA_FIELD_LENGTH])
fname = self.fp.read(fheader[_FH_FILENAME_LENGTH]) fname = self.fp.read(fheader[_FH_FILENAME_LENGTH])
if fname != zip_info.orig_filename: if fname != zip_info.orig_filename:
raise RuntimeError( raise RuntimeError(
@ -987,10 +992,10 @@ class ZipFile:
def open(self, name, mode="r", pwd=None): def open(self, name, mode="r", pwd=None):
"""Return file-like object for 'name'.""" """Return file-like object for 'name'."""
if mode not in ("r", "U", "rU"): if mode not in ("r", "U", "rU"):
raise RuntimeError, 'open() requires mode "r", "U", or "rU"' raise RuntimeError('open() requires mode "r", "U", or "rU"')
if not self.fp: if not self.fp:
raise RuntimeError, \ 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 # Only open a new file for instances where we were not
# given a file object in the constructor # given a file object in the constructor
@ -1012,7 +1017,7 @@ class ZipFile:
# Skip the file header: # Skip the file header:
fheader = zef_file.read(sizeFileHeader) fheader = zef_file.read(sizeFileHeader)
if fheader[0:4] != stringFileHeader: if fheader[0:4] != stringFileHeader:
raise BadZipfile, "Bad magic number for file header" raise BadZipfile("Bad magic number for file header")
fheader = struct.unpack(structFileHeader, fheader) fheader = struct.unpack(structFileHeader, fheader)
fname = zef_file.read(fheader[_FH_FILENAME_LENGTH]) fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
@ -1034,8 +1039,8 @@ class ZipFile:
if not pwd: if not pwd:
pwd = self.pwd pwd = self.pwd
if not pwd: if not pwd:
raise RuntimeError, "File %s is encrypted, " \ raise RuntimeError(("File %s is encrypted, "
"password required for extraction" % name "password required for extraction") % name)
zd = _ZipDecrypter(pwd) zd = _ZipDecrypter(pwd)
# The first 12 bytes in the cypher stream is an encryption header # The first 12 bytes in the cypher stream is an encryption header
@ -1093,8 +1098,8 @@ class ZipFile:
# build the destination pathname, replacing # build the destination pathname, replacing
# forward slashes to platform specific separators. # forward slashes to platform specific separators.
# Strip trailing path separator, unless it represents the root. # Strip trailing path separator, unless it represents the root.
if (targetpath[-1:] in (os.path.sep, os.path.altsep) if (targetpath[-1:] in (os.path.sep, os.path.altsep) and
and len(os.path.splitdrive(targetpath)[1]) > 1): len(os.path.splitdrive(targetpath)[1]) > 1):
targetpath = targetpath[:-1] targetpath = targetpath[:-1]
base_target = targetpath # Added by Kovid base_target = targetpath # Added by Kovid
@ -1157,16 +1162,16 @@ class ZipFile:
if self.debug: # Warning for duplicate names if self.debug: # Warning for duplicate names
print "Duplicate name:", zinfo.filename print "Duplicate name:", zinfo.filename
if self.mode not in ("w", "a"): if self.mode not in ("w", "a"):
raise RuntimeError, 'write() requires mode "w" or "a"' raise RuntimeError('write() requires mode "w" or "a"')
if not self.fp: if not self.fp:
raise RuntimeError, \ raise RuntimeError(
"Attempt to write ZIP archive that was already closed" "Attempt to write ZIP archive that was already closed")
if zinfo.compress_type == ZIP_DEFLATED and not zlib: if zinfo.compress_type == ZIP_DEFLATED and not zlib:
raise RuntimeError, \ raise RuntimeError(
"Compression requires the (missing) zlib module" "Compression requires the (missing) zlib module")
if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED): if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
raise RuntimeError, \ raise RuntimeError(
"That compression method is not supported" "That compression method is not supported")
if zinfo.file_size > ZIP64_LIMIT: if zinfo.file_size > ZIP64_LIMIT:
if not self._allowZip64: if not self._allowZip64:
raise LargeZipFile("Filesize would require ZIP64 extensions") raise LargeZipFile("Filesize would require ZIP64 extensions")
@ -1326,7 +1331,6 @@ class ZipFile:
finally: finally:
os.chdir(cwd) os.chdir(cwd)
def __del__(self): def __del__(self):
"""Call the "close()" method in case the user forgot.""" """Call the "close()" method in case the user forgot."""
self.close() self.close()
@ -1487,6 +1491,7 @@ def safe_replace(zipstream, name, datastream, extra_replacements={},
zipstream.flush() zipstream.flush()
class PyZipFile(ZipFile): class PyZipFile(ZipFile):
"""Class to create ZIP archives with Python library files and packages.""" """Class to create ZIP archives with Python library files and packages."""
def writepy(self, pathname, basename=""): def writepy(self, pathname, basename=""):
@ -1547,8 +1552,8 @@ class PyZipFile(ZipFile):
self.write(fname, arcname) self.write(fname, arcname)
else: else:
if pathname[-3:] != ".py": if pathname[-3:] != ".py":
raise RuntimeError, \ 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) fname, arcname = self._get_codename(pathname[0:-3], basename)
if self.debug: if self.debug:
print "Adding file", arcname print "Adding file", arcname