2 Read and write ZIP files.
4 import struct
, os
, time
, sys
, shutil
5 import binascii
, cStringIO
, stat
11 import zlib
# We may need its compression method
15 crc32
= binascii
.crc32
17 __all__
= ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
18 "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile" ]
20 class BadZipfile(Exception):
24 class LargeZipFile(Exception):
26 Raised when writing a zipfile, the zipfile requires ZIP64 extensions
27 and those extensions are disabled.
30 error
= BadZipfile
# The exception raised by this module
32 ZIP64_LIMIT
= (1 << 31) - 1
33 ZIP_FILECOUNT_LIMIT
= (1 << 16) - 1
34 ZIP_MAX_COMMENT
= (1 << 16) - 1
36 # constants for Zip file compression methods
39 # Other ZIP compression methods not supported
41 # Below are some formats and associated data for reading/writing headers using
42 # the struct module. The names and structures of headers/records are those used
43 # in the PKWARE description of the ZIP file format:
44 # http://www.pkware.com/documents/casestudies/APPNOTE.TXT
45 # (URL valid as of January 2008)
47 # The "end of central directory" structure, magic number, size, and indices
48 # (section V.I in the format document)
49 structEndArchive
= "<4s4H2LH"
50 stringEndArchive
= "PK\005\006"
51 sizeEndCentDir
= struct
.calcsize(structEndArchive
)
56 _ECD_ENTRIES_THIS_DISK
= 3
57 _ECD_ENTRIES_TOTAL
= 4
61 # These last two indices are not part of the structure as defined in the
62 # spec, but they are used internally by this module as a convenience
66 # The "central directory" structure, magic number, size, and indices
67 # of entries in the structure (section V.F in the format document)
68 structCentralDir
= "<4s4B4HL2L5H2L"
69 stringCentralDir
= "PK\001\002"
70 sizeCentralDir
= struct
.calcsize(structCentralDir
)
72 # indexes of entries in the central directory structure
74 _CD_CREATE_VERSION
= 1
76 _CD_EXTRACT_VERSION
= 3
77 _CD_EXTRACT_SYSTEM
= 4
83 _CD_COMPRESSED_SIZE
= 10
84 _CD_UNCOMPRESSED_SIZE
= 11
85 _CD_FILENAME_LENGTH
= 12
86 _CD_EXTRA_FIELD_LENGTH
= 13
87 _CD_COMMENT_LENGTH
= 14
88 _CD_DISK_NUMBER_START
= 15
89 _CD_INTERNAL_FILE_ATTRIBUTES
= 16
90 _CD_EXTERNAL_FILE_ATTRIBUTES
= 17
91 _CD_LOCAL_HEADER_OFFSET
= 18
93 # The "local file header" structure, magic number, size, and indices
94 # (section V.A in the format document)
95 structFileHeader
= "<4s2B4HL2L2H"
96 stringFileHeader
= "PK\003\004"
97 sizeFileHeader
= struct
.calcsize(structFileHeader
)
100 _FH_EXTRACT_VERSION
= 1
101 _FH_EXTRACT_SYSTEM
= 2
102 _FH_GENERAL_PURPOSE_FLAG_BITS
= 3
103 _FH_COMPRESSION_METHOD
= 4
104 _FH_LAST_MOD_TIME
= 5
105 _FH_LAST_MOD_DATE
= 6
107 _FH_COMPRESSED_SIZE
= 8
108 _FH_UNCOMPRESSED_SIZE
= 9
109 _FH_FILENAME_LENGTH
= 10
110 _FH_EXTRA_FIELD_LENGTH
= 11
112 # The "Zip64 end of central directory locator" structure, magic number, and size
113 structEndArchive64Locator
= "<4sLQL"
114 stringEndArchive64Locator
= "PK\x06\x07"
115 sizeEndCentDir64Locator
= struct
.calcsize(structEndArchive64Locator
)
117 # The "Zip64 end of central directory" record, magic number, size, and indices
118 # (section V.G in the format document)
119 structEndArchive64
= "<4sQ2H2L4Q"
120 stringEndArchive64
= "PK\x06\x06"
121 sizeEndCentDir64
= struct
.calcsize(structEndArchive64
)
124 _CD64_DIRECTORY_RECSIZE
= 1
125 _CD64_CREATE_VERSION
= 2
126 _CD64_EXTRACT_VERSION
= 3
127 _CD64_DISK_NUMBER
= 4
128 _CD64_DISK_NUMBER_START
= 5
129 _CD64_NUMBER_ENTRIES_THIS_DISK
= 6
130 _CD64_NUMBER_ENTRIES_TOTAL
= 7
131 _CD64_DIRECTORY_SIZE
= 8
132 _CD64_OFFSET_START_CENTDIR
= 9
134 def _check_zipfile(fp
):
137 return True # file has correct magic number
142 def is_zipfile(filename
):
143 """Quickly see if a file is a ZIP file by checking the magic number.
145 The filename argument may be a file or file-like object too.
149 if hasattr(filename
, "read"):
150 result
= _check_zipfile(fp
=filename
)
152 with
open(filename
, "rb") as fp
:
153 result
= _check_zipfile(fp
)
158 def _EndRecData64(fpin
, offset
, endrec
):
160 Read the ZIP64 end-of-archive records and use that to update endrec
163 fpin
.seek(offset
- sizeEndCentDir64Locator
, 2)
165 # If the seek fails, the file is not large enough to contain a ZIP64
166 # end-of-archive record, so just return the end record we were given.
169 data
= fpin
.read(sizeEndCentDir64Locator
)
170 if len(data
) != sizeEndCentDir64Locator
:
172 sig
, diskno
, reloff
, disks
= struct
.unpack(structEndArchive64Locator
, data
)
173 if sig
!= stringEndArchive64Locator
:
176 if diskno
!= 0 or disks
!= 1:
177 raise BadZipfile("zipfiles that span multiple disks are not supported")
179 # Assume no 'zip64 extensible data'
180 fpin
.seek(offset
- sizeEndCentDir64Locator
- sizeEndCentDir64
, 2)
181 data
= fpin
.read(sizeEndCentDir64
)
182 if len(data
) != sizeEndCentDir64
:
184 sig
, sz
, create_version
, read_version
, disk_num
, disk_dir
, \
185 dircount
, dircount2
, dirsize
, diroffset
= \
186 struct
.unpack(structEndArchive64
, data
)
187 if sig
!= stringEndArchive64
:
190 # Update the original endrec using data from the ZIP64 record
191 endrec
[_ECD_SIGNATURE
] = sig
192 endrec
[_ECD_DISK_NUMBER
] = disk_num
193 endrec
[_ECD_DISK_START
] = disk_dir
194 endrec
[_ECD_ENTRIES_THIS_DISK
] = dircount
195 endrec
[_ECD_ENTRIES_TOTAL
] = dircount2
196 endrec
[_ECD_SIZE
] = dirsize
197 endrec
[_ECD_OFFSET
] = diroffset
201 def _EndRecData(fpin
):
202 """Return data from the "End of Central Directory" record, or None.
204 The data is a list of the nine items in the ZIP "End of central dir"
205 record followed by a tenth item, the file seek offset of this record."""
207 # Determine file size
209 filesize
= fpin
.tell()
211 # Check to see if this is ZIP file with no archive comment (the
212 # "end of central directory" structure should be the last item in the
213 # file if this is the case).
215 fpin
.seek(-sizeEndCentDir
, 2)
219 if (len(data
) == sizeEndCentDir
and
220 data
[0:4] == stringEndArchive
and
221 data
[-2:] == b
"\000\000"):
222 # the signature is correct and there's no comment, unpack structure
223 endrec
= struct
.unpack(structEndArchive
, data
)
226 # Append a blank comment and record start offset
228 endrec
.append(filesize
- sizeEndCentDir
)
230 # Try to read the "Zip64 end of central directory" structure
231 return _EndRecData64(fpin
, -sizeEndCentDir
, endrec
)
233 # Either this is not a ZIP file, or it is a ZIP file with an archive
234 # comment. Search the end of the file for the "end of central directory"
235 # record signature. The comment is the last item in the ZIP file and may be
236 # up to 64K long. It is assumed that the "end of central directory" magic
237 # number does not appear in the comment.
238 maxCommentStart
= max(filesize
- (1 << 16) - sizeEndCentDir
, 0)
239 fpin
.seek(maxCommentStart
, 0)
241 start
= data
.rfind(stringEndArchive
)
243 # found the magic number; attempt to unpack and interpret
244 recData
= data
[start
:start
+sizeEndCentDir
]
245 if len(recData
) != sizeEndCentDir
:
246 # Zip file is corrupted.
248 endrec
= list(struct
.unpack(structEndArchive
, recData
))
249 commentSize
= endrec
[_ECD_COMMENT_SIZE
] #as claimed by the zip file
250 comment
= data
[start
+sizeEndCentDir
:start
+sizeEndCentDir
+commentSize
]
251 endrec
.append(comment
)
252 endrec
.append(maxCommentStart
+ start
)
254 # Try to read the "Zip64 end of central directory" structure
255 return _EndRecData64(fpin
, maxCommentStart
+ start
- filesize
,
258 # Unable to find a valid end of central directory structure
262 class ZipInfo (object):
263 """Class with attributes describing each file in the ZIP archive."""
287 def __init__(self
, filename
="NoName", date_time
=(1980,1,1,0,0,0)):
288 self
.orig_filename
= filename
# Original file name in archive
290 # Terminate the file name at the first null byte. Null bytes in file
291 # names are used as tricks by viruses in archives.
292 null_byte
= filename
.find(chr(0))
294 filename
= filename
[0:null_byte
]
295 # This is used to ensure paths in generated ZIP files always use
296 # forward slashes as the directory separator, as required by the
297 # ZIP format specification.
298 if os
.sep
!= "/" and os
.sep
in filename
:
299 filename
= filename
.replace(os
.sep
, "/")
301 self
.filename
= filename
# Normalized file name
302 self
.date_time
= date_time
# year, month, day, hour, min, sec
304 if date_time
[0] < 1980:
305 raise ValueError('ZIP does not support timestamps before 1980')
308 self
.compress_type
= ZIP_STORED
# Type of compression for the file
309 self
.comment
= "" # Comment for each file
310 self
.extra
= "" # ZIP extra data
311 if sys
.platform
== 'win32':
312 self
.create_system
= 0 # System which created ZIP archive
314 # Assume everything else is unix-y
315 self
.create_system
= 3 # System which created ZIP archive
316 self
.create_version
= 20 # Version which created ZIP archive
317 self
.extract_version
= 20 # Version needed to extract archive
318 self
.reserved
= 0 # Must be zero
319 self
.flag_bits
= 0 # ZIP flag bits
320 self
.volume
= 0 # Volume number of file header
321 self
.internal_attr
= 0 # Internal attributes
322 self
.external_attr
= 0 # External file attributes
323 # Other attributes are set by class ZipFile:
324 # header_offset Byte offset to the file header
325 # CRC CRC-32 of the uncompressed file
326 # compress_size Size of the compressed file
327 # file_size Size of the uncompressed file
329 def FileHeader(self
, zip64
=None):
330 """Return the per-file header as a string."""
332 dosdate
= (dt
[0] - 1980) << 9 | dt
[1] << 5 | dt
[2]
333 dostime
= dt
[3] << 11 | dt
[4] << 5 |
(dt
[5] // 2)
334 if self
.flag_bits
& 0x08:
335 # Set these to zero because we write them after the file data
336 CRC
= compress_size
= file_size
= 0
339 compress_size
= self
.compress_size
340 file_size
= self
.file_size
345 zip64
= file_size
> ZIP64_LIMIT
or compress_size
> ZIP64_LIMIT
348 extra
= extra
+ struct
.pack(fmt
,
349 1, struct
.calcsize(fmt
)-4, file_size
, compress_size
)
350 if file_size
> ZIP64_LIMIT
or compress_size
> ZIP64_LIMIT
:
352 raise LargeZipFile("Filesize would require ZIP64 extensions")
353 # File is larger than what fits into a 4 byte integer,
354 # fall back to the ZIP64 extension
355 file_size
= 0xffffffff
356 compress_size
= 0xffffffff
357 self
.extract_version
= max(45, self
.extract_version
)
358 self
.create_version
= max(45, self
.extract_version
)
360 filename
, flag_bits
= self
._encodeFilenameFlags
()
361 header
= struct
.pack(structFileHeader
, stringFileHeader
,
362 self
.extract_version
, self
.reserved
, flag_bits
,
363 self
.compress_type
, dostime
, dosdate
, CRC
,
364 compress_size
, file_size
,
365 len(filename
), len(extra
))
366 return header
+ filename
+ extra
368 def _encodeFilenameFlags(self
):
369 if isinstance(self
.filename
, unicode):
371 return self
.filename
.encode('ascii'), self
.flag_bits
372 except UnicodeEncodeError:
373 return self
.filename
.encode('utf-8'), self
.flag_bits |
0x800
375 return self
.filename
, self
.flag_bits
377 def _decodeFilename(self
):
378 if self
.flag_bits
& 0x800:
379 return self
.filename
.decode('utf-8')
383 def _decodeExtra(self
):
384 # Try to decode the extra field.
386 unpack
= struct
.unpack
387 while len(extra
) >= 4:
388 tp
, ln
= unpack('<HH', extra
[:4])
391 counts
= unpack('<QQQ', extra
[4:28])
393 counts
= unpack('<QQ', extra
[4:20])
395 counts
= unpack('<Q', extra
[4:12])
399 raise RuntimeError, "Corrupt extra field %s"%(ln
,)
403 # ZIP64 extension (large files and/or large archives)
404 if self
.file_size
in (0xffffffffffffffffL
, 0xffffffffL
):
405 self
.file_size
= counts
[idx
]
408 if self
.compress_size
== 0xFFFFFFFFL
:
409 self
.compress_size
= counts
[idx
]
412 if self
.header_offset
== 0xffffffffL
:
413 old
= self
.header_offset
414 self
.header_offset
= counts
[idx
]
421 """Class to handle decryption of files stored within a ZIP archive.
423 ZIP supports a password-based form of encryption. Even though known
424 plaintext attacks have been found against it, it is still useful
425 to be able to get data out of such a file.
428 zd = _ZipDecrypter(mypwd)
429 plain_char = zd(cypher_char)
430 plain_text = map(zd, cypher_text)
433 def _GenerateCRCTable():
434 """Generate a CRC-32 table.
436 ZIP encryption uses the CRC32 one-byte primitive for scrambling some
437 internal keys. We noticed that a direct implementation is faster than
438 relying on binascii.crc32().
446 crc
= ((crc
>> 1) & 0x7FFFFFFF) ^ poly
448 crc
= ((crc
>> 1) & 0x7FFFFFFF)
451 crctable
= _GenerateCRCTable()
453 def _crc32(self
, ch
, crc
):
454 """Compute the CRC32 primitive on one byte."""
455 return ((crc
>> 8) & 0xffffff) ^ self
.crctable
[(crc ^
ord(ch
)) & 0xff]
457 def __init__(self
, pwd
):
458 self
.key0
= 305419896
459 self
.key1
= 591751049
460 self
.key2
= 878082192
464 def _UpdateKeys(self
, c
):
465 self
.key0
= self
._crc
32(c
, self
.key0
)
466 self
.key1
= (self
.key1
+ (self
.key0
& 255)) & 4294967295
467 self
.key1
= (self
.key1
* 134775813 + 1) & 4294967295
468 self
.key2
= self
._crc
32(chr((self
.key1
>> 24) & 255), self
.key2
)
470 def __call__(self
, c
):
471 """Decrypt a single character."""
474 c
= c ^
(((k
* (k^
1)) >> 8) & 255)
501 class ZipExtFile(io
.BufferedIOBase
):
502 """File-like object for reading an archive member.
503 Is returned by ZipFile.open().
506 # Max size supported by decompressor.
509 # Read from compressed files in 4k blocks.
512 # Search for universal newlines or line chunks.
513 PATTERN
= re
.compile(r
'^(?P<chunk>[^\r\n]+)|(?P<newline>\n|\r\n?)')
515 def __init__(self
, fileobj
, mode
, zipinfo
, decrypter
=None,
516 close_fileobj
=False):
517 self
._fileobj
= fileobj
518 self
._decrypter
= decrypter
519 self
._close
_fileobj
= close_fileobj
521 self
._compress
_type
= zipinfo
.compress_type
522 self
._compress
_size
= zipinfo
.compress_size
523 self
._compress
_left
= zipinfo
.compress_size
525 if self
._compress
_type
== ZIP_DEFLATED
:
526 self
._decompressor
= zlib
.decompressobj(-15)
527 elif self
._compress
_type
!= ZIP_STORED
:
528 descr
= compressor_names
.get(self
._compress
_type
)
530 raise NotImplementedError("compression type %d (%s)" % (self
._compress
_type
, descr
))
532 raise NotImplementedError("compression type %d" % (self
._compress
_type
,))
533 self
._unconsumed
= ''
535 self
._readbuffer
= ''
538 self
._universal
= 'U' in mode
541 # Adjust read size for encrypted files since the first 12 bytes
542 # are for the encryption/password information.
543 if self
._decrypter
is not None:
544 self
._compress
_left
-= 12
547 self
.name
= zipinfo
.filename
549 if hasattr(zipinfo
, 'CRC'):
550 self
._expected
_crc
= zipinfo
.CRC
551 self
._running
_crc
= crc32(b
'') & 0xffffffff
553 self
._expected
_crc
= None
555 def readline(self
, limit
=-1):
556 """Read and return a line from the stream.
558 If limit is specified, at most limit bytes will be read.
561 if not self
._universal
and limit
< 0:
562 # Shortcut common case - newline found in buffer.
563 i
= self
._readbuffer
.find('\n', self
._offset
) + 1
565 line
= self
._readbuffer
[self
._offset
: i
]
569 if not self
._universal
:
570 return io
.BufferedIOBase
.readline(self
, limit
)
573 while limit
< 0 or len(line
) < limit
:
574 readahead
= self
.peek(2)
579 # Search for universal newlines or line chunks.
581 # The pattern returns either a line chunk or a newline, but not
582 # both. Combined with peek(2), we are assured that the sequence
583 # '\r\n' is always retrieved completely and never split into
584 # separate newlines - '\r', '\n' due to coincidental readaheads.
586 match
= self
.PATTERN
.search(readahead
)
587 newline
= match
.group('newline')
588 if newline
is not None:
589 if self
.newlines
is None:
591 if newline
not in self
.newlines
:
592 self
.newlines
.append(newline
)
593 self
._offset
+= len(newline
)
596 chunk
= match
.group('chunk')
598 chunk
= chunk
[: limit
- len(line
)]
600 self
._offset
+= len(chunk
)
606 """Returns buffered bytes without advancing the position."""
607 if n
> len(self
._readbuffer
) - self
._offset
:
609 if len(chunk
) > self
._offset
:
610 self
._readbuffer
= chunk
+ self
._readbuffer
[self
._offset
:]
613 self
._offset
-= len(chunk
)
615 # Return up to 512 bytes to reduce allocation overhead for tight loops.
616 return self
._readbuffer
[self
._offset
: self
._offset
+ 512]
621 def read(self
, n
=-1):
622 """Read and return up to n bytes.
623 If the argument is omitted, None, or negative, data is read and returned until EOF is reached..
632 data
= self
.read1(n
- len(buf
))
639 def _update_crc(self
, newdata
, eof
):
640 # Update the CRC using the given data.
641 if self
._expected
_crc
is None:
642 # No need to compute the CRC if we don't have a reference value
644 self
._running
_crc
= crc32(newdata
, self
._running
_crc
) & 0xffffffff
645 # Check the CRC if we're at the end of the file
646 if eof
and self
._running
_crc
!= self
._expected
_crc
:
647 raise BadZipfile("Bad CRC-32 for file %r" % self
.name
)
650 """Read up to n bytes with at most one read() system call."""
652 # Simplify algorithm (branching) by transforming negative n to large n.
653 if n
< 0 or n
is None:
656 # Bytes available in read buffer.
657 len_readbuffer
= len(self
._readbuffer
) - self
._offset
660 if self
._compress
_left
> 0 and n
> len_readbuffer
+ len(self
._unconsumed
):
661 nbytes
= n
- len_readbuffer
- len(self
._unconsumed
)
662 nbytes
= max(nbytes
, self
.MIN_READ_SIZE
)
663 nbytes
= min(nbytes
, self
._compress
_left
)
665 data
= self
._fileobj
.read(nbytes
)
666 self
._compress
_left
-= len(data
)
668 if data
and self
._decrypter
is not None:
669 data
= ''.join(map(self
._decrypter
, data
))
671 if self
._compress
_type
== ZIP_STORED
:
672 self
._update
_crc
(data
, eof
=(self
._compress
_left
==0))
673 self
._readbuffer
= self
._readbuffer
[self
._offset
:] + data
676 # Prepare deflated bytes for decompression.
677 self
._unconsumed
+= data
679 # Handle unconsumed data.
680 if (len(self
._unconsumed
) > 0 and n
> len_readbuffer
and
681 self
._compress
_type
== ZIP_DEFLATED
):
682 data
= self
._decompressor
.decompress(
684 max(n
- len_readbuffer
, self
.MIN_READ_SIZE
)
687 self
._unconsumed
= self
._decompressor
.unconsumed_tail
688 eof
= len(self
._unconsumed
) == 0 and self
._compress
_left
== 0
690 data
+= self
._decompressor
.flush()
692 self
._update
_crc
(data
, eof
=eof
)
693 self
._readbuffer
= self
._readbuffer
[self
._offset
:] + data
697 data
= self
._readbuffer
[self
._offset
: self
._offset
+ n
]
698 self
._offset
+= len(data
)
703 if self
._close
_fileobj
:
704 self
._fileobj
.close()
706 super(ZipExtFile
, self
).close()
709 class ZipFile(object):
710 """ Class with methods to open, read, write, close, list zip files.
712 z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
714 file: Either the path to the file, or a file-like object.
715 If it is a path, the file will be opened and closed by ZipFile.
716 mode: The mode can be either read "r", write "w" or append "a".
717 compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
718 allowZip64: if True ZipFile will create files with ZIP64 extensions when
719 needed, otherwise it will raise an exception when this would
724 fp
= None # Set here since __del__ checks it
726 def __init__(self
, file, mode
="r", compression
=ZIP_STORED
, allowZip64
=False):
727 """Open the ZIP file with mode read "r", write "w" or append "a"."""
728 if mode
not in ("r", "w", "a"):
729 raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
731 if compression
== ZIP_STORED
:
733 elif compression
== ZIP_DEFLATED
:
736 "Compression requires the (missing) zlib module"
738 raise RuntimeError, "That compression method is not supported"
740 self
._allowZip
64 = allowZip64
741 self
._didModify
= False
742 self
.debug
= 0 # Level of printing: 0 through 3
743 self
.NameToInfo
= {} # Find file info given name
744 self
.filelist
= [] # List of ZipInfo instances for archive
745 self
.compression
= compression
# Method of compression
746 self
.mode
= key
= mode
.replace('b', '')[0]
750 # Check if we were passed a file-like object
751 if isinstance(file, basestring
):
754 modeDict
= {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
756 self
.fp
= open(file, modeDict
[mode
])
760 self
.fp
= open(file, modeDict
[mode
])
766 self
.filename
= getattr(file, 'name', None)
770 self
._RealGetContents
()
772 # set the modified flag so central directory gets written
773 # even if no files are added to the archive
774 self
._didModify
= True
777 # See if file is a zip file
778 self
._RealGetContents
()
779 # seek to start of directory and overwrite
780 self
.fp
.seek(self
.start_dir
, 0)
782 # file is not a zip file, just append
785 # set the modified flag so central directory gets written
786 # even if no files are added to the archive
787 self
._didModify
= True
789 raise RuntimeError('Mode must be "r", "w" or "a"')
793 if not self
._filePassed
:
800 def __exit__(self
, type, value
, traceback
):
803 def _RealGetContents(self
):
804 """Read in the table of contents for the ZIP file."""
807 endrec
= _EndRecData(fp
)
809 raise BadZipfile("File is not a zip file")
811 raise BadZipfile
, "File is not a zip file"
814 size_cd
= endrec
[_ECD_SIZE
] # bytes in central directory
815 offset_cd
= endrec
[_ECD_OFFSET
] # offset of central directory
816 self
._comment
= endrec
[_ECD_COMMENT
] # archive comment
818 # "concat" is zero, unless zip was concatenated to another file
819 concat
= endrec
[_ECD_LOCATION
] - size_cd
- offset_cd
820 if endrec
[_ECD_SIGNATURE
] == stringEndArchive64
:
821 # If Zip64 extension structures are present, account for them
822 concat
-= (sizeEndCentDir64
+ sizeEndCentDir64Locator
)
825 inferred
= concat
+ offset_cd
826 print "given, inferred, offset", offset_cd
, inferred
, concat
827 # self.start_dir: Position of start of central directory
828 self
.start_dir
= offset_cd
+ concat
829 fp
.seek(self
.start_dir
, 0)
830 data
= fp
.read(size_cd
)
831 fp
= cStringIO
.StringIO(data
)
833 while total
< size_cd
:
834 centdir
= fp
.read(sizeCentralDir
)
835 if len(centdir
) != sizeCentralDir
:
836 raise BadZipfile("Truncated central directory")
837 centdir
= struct
.unpack(structCentralDir
, centdir
)
838 if centdir
[_CD_SIGNATURE
] != stringCentralDir
:
839 raise BadZipfile("Bad magic number for central directory")
842 filename
= fp
.read(centdir
[_CD_FILENAME_LENGTH
])
843 # Create ZipInfo instance to store file information
844 x
= ZipInfo(filename
)
845 x
.extra
= fp
.read(centdir
[_CD_EXTRA_FIELD_LENGTH
])
846 x
.comment
= fp
.read(centdir
[_CD_COMMENT_LENGTH
])
847 x
.header_offset
= centdir
[_CD_LOCAL_HEADER_OFFSET
]
848 (x
.create_version
, x
.create_system
, x
.extract_version
, x
.reserved
,
849 x
.flag_bits
, x
.compress_type
, t
, d
,
850 x
.CRC
, x
.compress_size
, x
.file_size
) = centdir
[1:12]
851 x
.volume
, x
.internal_attr
, x
.external_attr
= centdir
[15:18]
852 # Convert date/time code to (year, month, day, hour, min, sec)
854 x
.date_time
= ( (d
>>9)+1980, (d
>>5)&0xF, d
&0x1F,
855 t
>>11, (t
>>5)&0x3F, (t
&0x1F) * 2 )
858 x
.header_offset
= x
.header_offset
+ concat
859 x
.filename
= x
._decodeFilename
()
860 self
.filelist
.append(x
)
861 self
.NameToInfo
[x
.filename
] = x
863 # update total bytes read from central directory
864 total
= (total
+ sizeCentralDir
+ centdir
[_CD_FILENAME_LENGTH
]
865 + centdir
[_CD_EXTRA_FIELD_LENGTH
]
866 + centdir
[_CD_COMMENT_LENGTH
])
873 """Return a list of file names in the archive."""
875 for data
in self
.filelist
:
876 l
.append(data
.filename
)
880 """Return a list of class ZipInfo instances for files in the
885 """Print a table of contents for the zip file."""
886 print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
887 for zinfo
in self
.filelist
:
888 date
= "%d-%02d-%02d %02d:%02d:%02d" % zinfo
.date_time
[:6]
889 print "%-46s %s %12d" % (zinfo
.filename
, date
, zinfo
.file_size
)
892 """Read all the files and check the CRC."""
894 for zinfo
in self
.filelist
:
896 # Read by chunks, to avoid an OverflowError or a
897 # MemoryError with very large embedded files.
898 with self
.open(zinfo
.filename
, "r") as f
:
899 while f
.read(chunk_size
): # Check CRC-32
902 return zinfo
.filename
904 def getinfo(self
, name
):
905 """Return the instance of ZipInfo given 'name'."""
906 info
= self
.NameToInfo
.get(name
)
909 'There is no item named %r in the archive' % name
)
913 def setpassword(self
, pwd
):
914 """Set default password for encrypted files."""
919 """The comment text associated with the ZIP file."""
923 def comment(self
, comment
):
924 # check for valid comment length
925 if len(comment
) > ZIP_MAX_COMMENT
:
927 warnings
.warn('Archive comment is too long; truncating to %d bytes'
928 % ZIP_MAX_COMMENT
, stacklevel
=2)
929 comment
= comment
[:ZIP_MAX_COMMENT
]
930 self
._comment
= comment
931 self
._didModify
= True
933 def read(self
, name
, pwd
=None):
934 """Return file bytes (as a string) for name."""
935 return self
.open(name
, "r", pwd
).read()
937 def open(self
, name
, mode
="r", pwd
=None):
938 """Return file-like object for 'name'."""
939 if mode
not in ("r", "U", "rU"):
940 raise RuntimeError, 'open() requires mode "r", "U", or "rU"'
942 raise RuntimeError, \
943 "Attempt to read ZIP archive that was already closed"
945 # Only open a new file for instances where we were not
946 # given a file object in the constructor
951 zef_file
= open(self
.filename
, 'rb')
955 # Make sure we have an info object
956 if isinstance(name
, ZipInfo
):
957 # 'name' is already an info object
960 # Get info object for name
961 zinfo
= self
.getinfo(name
)
963 zef_file
.seek(zinfo
.header_offset
, 0)
965 # Skip the file header:
966 fheader
= zef_file
.read(sizeFileHeader
)
967 if len(fheader
) != sizeFileHeader
:
968 raise BadZipfile("Truncated file header")
969 fheader
= struct
.unpack(structFileHeader
, fheader
)
970 if fheader
[_FH_SIGNATURE
] != stringFileHeader
:
971 raise BadZipfile("Bad magic number for file header")
973 fname
= zef_file
.read(fheader
[_FH_FILENAME_LENGTH
])
974 if fheader
[_FH_EXTRA_FIELD_LENGTH
]:
975 zef_file
.read(fheader
[_FH_EXTRA_FIELD_LENGTH
])
977 if fname
!= zinfo
.orig_filename
:
979 'File name in directory "%s" and header "%s" differ.' % (
980 zinfo
.orig_filename
, fname
)
982 # check for encrypted flag & handle password
983 is_encrypted
= zinfo
.flag_bits
& 0x1
989 raise RuntimeError, "File %s is encrypted, " \
990 "password required for extraction" % name
992 zd
= _ZipDecrypter(pwd
)
993 # The first 12 bytes in the cypher stream is an encryption header
994 # used to strengthen the algorithm. The first 11 bytes are
995 # completely random, while the 12th contains the MSB of the CRC,
996 # or the MSB of the file time depending on the header type
997 # and is used to check the correctness of the password.
998 bytes
= zef_file
.read(12)
999 h
= map(zd
, bytes
[0:12])
1000 if zinfo
.flag_bits
& 0x8:
1001 # compare against the file type from extended local headers
1002 check_byte
= (zinfo
._raw
_time
>> 8) & 0xff
1004 # compare against the CRC otherwise
1005 check_byte
= (zinfo
.CRC
>> 24) & 0xff
1006 if ord(h
[11]) != check_byte
:
1007 raise RuntimeError("Bad password for file", name
)
1009 return ZipExtFile(zef_file
, mode
, zinfo
, zd
,
1010 close_fileobj
=should_close
)
1016 def extract(self
, member
, path
=None, pwd
=None):
1017 """Extract a member from the archive to the current working directory,
1018 using its full name. Its file information is extracted as accurately
1019 as possible. `member' may be a filename or a ZipInfo object. You can
1020 specify a different directory using `path'.
1022 if not isinstance(member
, ZipInfo
):
1023 member
= self
.getinfo(member
)
1028 return self
._extract
_member
(member
, path
, pwd
)
1030 def extractall(self
, path
=None, members
=None, pwd
=None):
1031 """Extract all members from the archive to the current working
1032 directory. `path' specifies a different directory to extract to.
1033 `members' is optional and must be a subset of the list returned
1037 members
= self
.namelist()
1039 for zipinfo
in members
:
1040 self
.extract(zipinfo
, path
, pwd
)
1042 def _extract_member(self
, member
, targetpath
, pwd
):
1043 """Extract the ZipInfo object 'member' to a physical
1044 file on the path targetpath.
1046 # build the destination pathname, replacing
1047 # forward slashes to platform specific separators.
1048 arcname
= member
.filename
.replace('/', os
.path
.sep
)
1051 arcname
= arcname
.replace(os
.path
.altsep
, os
.path
.sep
)
1052 # interpret absolute pathname as relative, remove drive letter or
1053 # UNC path, redundant separators, "." and ".." components.
1054 arcname
= os
.path
.splitdrive(arcname
)[1]
1055 arcname
= os
.path
.sep
.join(x
for x
in arcname
.split(os
.path
.sep
)
1056 if x
not in ('', os
.path
.curdir
, os
.path
.pardir
))
1057 if os
.path
.sep
== '\\':
1058 # filter illegal characters on Windows
1060 if isinstance(arcname
, unicode):
1061 table
= {ord(c
): ord('_') for c
in illegal
}
1063 table
= string
.maketrans(illegal
, '_' * len(illegal
))
1064 arcname
= arcname
.translate(table
)
1065 # remove trailing dots
1066 arcname
= (x
.rstrip('.') for x
in arcname
.split(os
.path
.sep
))
1067 arcname
= os
.path
.sep
.join(x
for x
in arcname
if x
)
1069 targetpath
= os
.path
.join(targetpath
, arcname
)
1070 targetpath
= os
.path
.normpath(targetpath
)
1072 # Create all upper directories if necessary.
1073 upperdirs
= os
.path
.dirname(targetpath
)
1074 if upperdirs
and not os
.path
.exists(upperdirs
):
1075 os
.makedirs(upperdirs
)
1077 if member
.filename
[-1] == '/':
1078 if not os
.path
.isdir(targetpath
):
1079 os
.mkdir(targetpath
)
1082 with self
.open(member
, pwd
=pwd
) as source
, \
1083 file(targetpath
, "wb") as target
:
1084 shutil
.copyfileobj(source
, target
)
1088 def _writecheck(self
, zinfo
):
1089 """Check for errors before writing a file to the archive."""
1090 if zinfo
.filename
in self
.NameToInfo
:
1092 warnings
.warn('Duplicate name: %r' % zinfo
.filename
, stacklevel
=3)
1093 if self
.mode
not in ("w", "a"):
1094 raise RuntimeError, 'write() requires mode "w" or "a"'
1096 raise RuntimeError, \
1097 "Attempt to write ZIP archive that was already closed"
1098 if zinfo
.compress_type
== ZIP_DEFLATED
and not zlib
:
1099 raise RuntimeError, \
1100 "Compression requires the (missing) zlib module"
1101 if zinfo
.compress_type
not in (ZIP_STORED
, ZIP_DEFLATED
):
1102 raise RuntimeError, \
1103 "That compression method is not supported"
1104 if not self
._allowZip
64:
1105 requires_zip64
= None
1106 if len(self
.filelist
) >= ZIP_FILECOUNT_LIMIT
:
1107 requires_zip64
= "Files count"
1108 elif zinfo
.file_size
> ZIP64_LIMIT
:
1109 requires_zip64
= "Filesize"
1110 elif zinfo
.header_offset
> ZIP64_LIMIT
:
1111 requires_zip64
= "Zipfile size"
1113 raise LargeZipFile(requires_zip64
+
1114 " would require ZIP64 extensions")
1116 def write(self
, filename
, arcname
=None, compress_type
=None):
1117 """Put the bytes from filename into the archive under the name
1121 "Attempt to write to ZIP archive that was already closed")
1123 st
= os
.stat(filename
)
1124 isdir
= stat
.S_ISDIR(st
.st_mode
)
1125 mtime
= time
.localtime(st
.st_mtime
)
1126 date_time
= mtime
[0:6]
1127 # Create ZipInfo instance to store file information
1130 arcname
= os
.path
.normpath(os
.path
.splitdrive(arcname
)[1])
1131 while arcname
[0] in (os
.sep
, os
.altsep
):
1132 arcname
= arcname
[1:]
1135 zinfo
= ZipInfo(arcname
, date_time
)
1136 zinfo
.external_attr
= (st
[0] & 0xFFFF) << 16L # Unix attributes
1137 if compress_type
is None:
1138 zinfo
.compress_type
= self
.compression
1140 zinfo
.compress_type
= compress_type
1142 zinfo
.file_size
= st
.st_size
1143 zinfo
.flag_bits
= 0x00
1144 zinfo
.header_offset
= self
.fp
.tell() # Start of header bytes
1146 self
._writecheck
(zinfo
)
1147 self
._didModify
= True
1151 zinfo
.compress_size
= 0
1153 zinfo
.external_attr |
= 0x10 # MS-DOS directory flag
1154 self
.filelist
.append(zinfo
)
1155 self
.NameToInfo
[zinfo
.filename
] = zinfo
1156 self
.fp
.write(zinfo
.FileHeader(False))
1159 with
open(filename
, "rb") as fp
:
1160 # Must overwrite CRC and sizes with correct data later
1162 zinfo
.compress_size
= compress_size
= 0
1163 # Compressed size can be larger than uncompressed size
1164 zip64
= self
._allowZip
64 and \
1165 zinfo
.file_size
* 1.05 > ZIP64_LIMIT
1166 self
.fp
.write(zinfo
.FileHeader(zip64
))
1167 if zinfo
.compress_type
== ZIP_DEFLATED
:
1168 cmpr
= zlib
.compressobj(zlib
.Z_DEFAULT_COMPRESSION
,
1174 buf
= fp
.read(1024 * 8)
1177 file_size
= file_size
+ len(buf
)
1178 CRC
= crc32(buf
, CRC
) & 0xffffffff
1180 buf
= cmpr
.compress(buf
)
1181 compress_size
= compress_size
+ len(buf
)
1185 compress_size
= compress_size
+ len(buf
)
1187 zinfo
.compress_size
= compress_size
1189 zinfo
.compress_size
= file_size
1191 zinfo
.file_size
= file_size
1192 if not zip64
and self
._allowZip
64:
1193 if file_size
> ZIP64_LIMIT
:
1194 raise RuntimeError('File size has increased during compressing')
1195 if compress_size
> ZIP64_LIMIT
:
1196 raise RuntimeError('Compressed size larger than uncompressed size')
1197 # Seek backwards and write file header (which will now include
1198 # correct CRC and file sizes)
1199 position
= self
.fp
.tell() # Preserve current position in file
1200 self
.fp
.seek(zinfo
.header_offset
, 0)
1201 self
.fp
.write(zinfo
.FileHeader(zip64
))
1202 self
.fp
.seek(position
, 0)
1203 self
.filelist
.append(zinfo
)
1204 self
.NameToInfo
[zinfo
.filename
] = zinfo
1206 def writestr(self
, zinfo_or_arcname
, bytes
, compress_type
=None):
1207 """Write a file into the archive. The contents is the string
1208 'bytes'. 'zinfo_or_arcname' is either a ZipInfo instance or
1209 the name of the file in the archive."""
1210 if not isinstance(zinfo_or_arcname
, ZipInfo
):
1211 zinfo
= ZipInfo(filename
=zinfo_or_arcname
,
1212 date_time
=time
.localtime(time
.time())[:6])
1214 zinfo
.compress_type
= self
.compression
1215 if zinfo
.filename
[-1] == '/':
1216 zinfo
.external_attr
= 0o40775 << 16 # drwxrwxr-x
1217 zinfo
.external_attr |
= 0x10 # MS-DOS directory flag
1219 zinfo
.external_attr
= 0o600 << 16 # ?rw-------
1221 zinfo
= zinfo_or_arcname
1225 "Attempt to write to ZIP archive that was already closed")
1227 if compress_type
is not None:
1228 zinfo
.compress_type
= compress_type
1230 zinfo
.file_size
= len(bytes
) # Uncompressed size
1231 zinfo
.header_offset
= self
.fp
.tell() # Start of header bytes
1232 self
._writecheck
(zinfo
)
1233 self
._didModify
= True
1234 zinfo
.CRC
= crc32(bytes
) & 0xffffffff # CRC-32 checksum
1235 if zinfo
.compress_type
== ZIP_DEFLATED
:
1236 co
= zlib
.compressobj(zlib
.Z_DEFAULT_COMPRESSION
,
1238 bytes
= co
.compress(bytes
) + co
.flush()
1239 zinfo
.compress_size
= len(bytes
) # Compressed size
1241 zinfo
.compress_size
= zinfo
.file_size
1242 zip64
= zinfo
.file_size
> ZIP64_LIMIT
or \
1243 zinfo
.compress_size
> ZIP64_LIMIT
1244 if zip64
and not self
._allowZip
64:
1245 raise LargeZipFile("Filesize would require ZIP64 extensions")
1246 self
.fp
.write(zinfo
.FileHeader(zip64
))
1247 self
.fp
.write(bytes
)
1248 if zinfo
.flag_bits
& 0x08:
1249 # Write CRC and file sizes after the file data
1250 fmt
= '<LQQ' if zip64
else '<LLL'
1251 self
.fp
.write(struct
.pack(fmt
, zinfo
.CRC
, zinfo
.compress_size
,
1254 self
.filelist
.append(zinfo
)
1255 self
.NameToInfo
[zinfo
.filename
] = zinfo
1258 """Call the "close()" method in case the user forgot."""
1262 """Close the file, and for mode "w" and "a" write the ending
1268 if self
.mode
in ("w", "a") and self
._didModify
: # write ending records
1269 pos1
= self
.fp
.tell()
1270 for zinfo
in self
.filelist
: # write central directory
1271 dt
= zinfo
.date_time
1272 dosdate
= (dt
[0] - 1980) << 9 | dt
[1] << 5 | dt
[2]
1273 dostime
= dt
[3] << 11 | dt
[4] << 5 |
(dt
[5] // 2)
1275 if zinfo
.file_size
> ZIP64_LIMIT \
1276 or zinfo
.compress_size
> ZIP64_LIMIT
:
1277 extra
.append(zinfo
.file_size
)
1278 extra
.append(zinfo
.compress_size
)
1279 file_size
= 0xffffffff
1280 compress_size
= 0xffffffff
1282 file_size
= zinfo
.file_size
1283 compress_size
= zinfo
.compress_size
1285 if zinfo
.header_offset
> ZIP64_LIMIT
:
1286 extra
.append(zinfo
.header_offset
)
1287 header_offset
= 0xffffffffL
1289 header_offset
= zinfo
.header_offset
1291 extra_data
= zinfo
.extra
1293 # Append a ZIP64 field to the extra's
1294 extra_data
= struct
.pack(
1295 '<HH' + 'Q'*len(extra
),
1296 1, 8*len(extra
), *extra
) + extra_data
1298 extract_version
= max(45, zinfo
.extract_version
)
1299 create_version
= max(45, zinfo
.create_version
)
1301 extract_version
= zinfo
.extract_version
1302 create_version
= zinfo
.create_version
1305 filename
, flag_bits
= zinfo
._encodeFilenameFlags
()
1306 centdir
= struct
.pack(structCentralDir
,
1307 stringCentralDir
, create_version
,
1308 zinfo
.create_system
, extract_version
, zinfo
.reserved
,
1309 flag_bits
, zinfo
.compress_type
, dostime
, dosdate
,
1310 zinfo
.CRC
, compress_size
, file_size
,
1311 len(filename
), len(extra_data
), len(zinfo
.comment
),
1312 0, zinfo
.internal_attr
, zinfo
.external_attr
,
1314 except DeprecationWarning:
1315 print >>sys
.stderr
, (structCentralDir
,
1316 stringCentralDir
, create_version
,
1317 zinfo
.create_system
, extract_version
, zinfo
.reserved
,
1318 zinfo
.flag_bits
, zinfo
.compress_type
, dostime
, dosdate
,
1319 zinfo
.CRC
, compress_size
, file_size
,
1320 len(zinfo
.filename
), len(extra_data
), len(zinfo
.comment
),
1321 0, zinfo
.internal_attr
, zinfo
.external_attr
,
1324 self
.fp
.write(centdir
)
1325 self
.fp
.write(filename
)
1326 self
.fp
.write(extra_data
)
1327 self
.fp
.write(zinfo
.comment
)
1329 pos2
= self
.fp
.tell()
1330 # Write end-of-zip-archive record
1331 centDirCount
= len(self
.filelist
)
1332 centDirSize
= pos2
- pos1
1333 centDirOffset
= pos1
1334 requires_zip64
= None
1335 if centDirCount
> ZIP_FILECOUNT_LIMIT
:
1336 requires_zip64
= "Files count"
1337 elif centDirOffset
> ZIP64_LIMIT
:
1338 requires_zip64
= "Central directory offset"
1339 elif centDirSize
> ZIP64_LIMIT
:
1340 requires_zip64
= "Central directory size"
1342 # Need to write the ZIP64 end-of-archive records
1343 if not self
._allowZip
64:
1344 raise LargeZipFile(requires_zip64
+
1345 " would require ZIP64 extensions")
1346 zip64endrec
= struct
.pack(
1347 structEndArchive64
, stringEndArchive64
,
1348 44, 45, 45, 0, 0, centDirCount
, centDirCount
,
1349 centDirSize
, centDirOffset
)
1350 self
.fp
.write(zip64endrec
)
1352 zip64locrec
= struct
.pack(
1353 structEndArchive64Locator
,
1354 stringEndArchive64Locator
, 0, pos2
, 1)
1355 self
.fp
.write(zip64locrec
)
1356 centDirCount
= min(centDirCount
, 0xFFFF)
1357 centDirSize
= min(centDirSize
, 0xFFFFFFFF)
1358 centDirOffset
= min(centDirOffset
, 0xFFFFFFFF)
1360 endrec
= struct
.pack(structEndArchive
, stringEndArchive
,
1361 0, 0, centDirCount
, centDirCount
,
1362 centDirSize
, centDirOffset
, len(self
._comment
))
1363 self
.fp
.write(endrec
)
1364 self
.fp
.write(self
._comment
)
1369 if not self
._filePassed
:
1373 class PyZipFile(ZipFile
):
1374 """Class to create ZIP archives with Python library files and packages."""
1376 def writepy(self
, pathname
, basename
= ""):
1377 """Add all files from "pathname" to the ZIP archive.
1379 If pathname is a package directory, search the directory and
1380 all package subdirectories recursively for all *.py and enter
1381 the modules into the archive. If pathname is a plain
1382 directory, listdir *.py and enter all modules. Else, pathname
1383 must be a Python *.py file and the module will be put into the
1384 archive. Added modules are always module.pyo or module.pyc.
1385 This method will compile the module.py into module.pyc if
1388 dir, name
= os
.path
.split(pathname
)
1389 if os
.path
.isdir(pathname
):
1390 initname
= os
.path
.join(pathname
, "__init__.py")
1391 if os
.path
.isfile(initname
):
1392 # This is a package directory, add it
1394 basename
= "%s/%s" % (basename
, name
)
1398 print "Adding package in", pathname
, "as", basename
1399 fname
, arcname
= self
._get
_codename
(initname
[0:-3], basename
)
1401 print "Adding", arcname
1402 self
.write(fname
, arcname
)
1403 dirlist
= os
.listdir(pathname
)
1404 dirlist
.remove("__init__.py")
1405 # Add all *.py files and package subdirectories
1406 for filename
in dirlist
:
1407 path
= os
.path
.join(pathname
, filename
)
1408 root
, ext
= os
.path
.splitext(filename
)
1409 if os
.path
.isdir(path
):
1410 if os
.path
.isfile(os
.path
.join(path
, "__init__.py")):
1411 # This is a package directory, add it
1412 self
.writepy(path
, basename
) # Recursive call
1414 fname
, arcname
= self
._get
_codename
(path
[0:-3],
1417 print "Adding", arcname
1418 self
.write(fname
, arcname
)
1420 # This is NOT a package directory, add its files at top level
1422 print "Adding files from directory", pathname
1423 for filename
in os
.listdir(pathname
):
1424 path
= os
.path
.join(pathname
, filename
)
1425 root
, ext
= os
.path
.splitext(filename
)
1427 fname
, arcname
= self
._get
_codename
(path
[0:-3],
1430 print "Adding", arcname
1431 self
.write(fname
, arcname
)
1433 if pathname
[-3:] != ".py":
1434 raise RuntimeError, \
1435 'Files added with writepy() must end with ".py"'
1436 fname
, arcname
= self
._get
_codename
(pathname
[0:-3], basename
)
1438 print "Adding file", arcname
1439 self
.write(fname
, arcname
)
1441 def _get_codename(self
, pathname
, basename
):
1442 """Return (filename, archivename) for the path.
1444 Given a module name path, return the correct file path and
1445 archive name, compiling if necessary. For example, given
1446 /python/lib/string, return (/python/lib/string.pyc, string).
1448 file_py
= pathname
+ ".py"
1449 file_pyc
= pathname
+ ".pyc"
1450 file_pyo
= pathname
+ ".pyo"
1451 if os
.path
.isfile(file_pyo
) and \
1452 os
.stat(file_pyo
).st_mtime
>= os
.stat(file_py
).st_mtime
:
1453 fname
= file_pyo
# Use .pyo file
1454 elif not os
.path
.isfile(file_pyc
) or \
1455 os
.stat(file_pyc
).st_mtime
< os
.stat(file_py
).st_mtime
:
1458 print "Compiling", file_py
1460 py_compile
.compile(file_py
, file_pyc
, None, True)
1461 except py_compile
.PyCompileError
,err
:
1466 archivename
= os
.path
.split(fname
)[1]
1468 archivename
= "%s/%s" % (basename
, archivename
)
1469 return (fname
, archivename
)
1472 def main(args
= None):
1474 USAGE
=textwrap
.dedent("""\
1476 zipfile.py -l zipfile.zip # Show listing of a zipfile
1477 zipfile.py -t zipfile.zip # Test if a zipfile is valid
1478 zipfile.py -e zipfile.zip target # Extract zipfile into target dir
1479 zipfile.py -c zipfile.zip src ... # Create zipfile from sources
1484 if not args
or args
[0] not in ('-l', '-c', '-e', '-t'):
1492 with
ZipFile(args
[1], 'r') as zf
:
1495 elif args
[0] == '-t':
1499 with
ZipFile(args
[1], 'r') as zf
:
1500 badfile
= zf
.testzip()
1502 print("The following enclosed file is corrupted: {!r}".format(badfile
))
1503 print "Done testing"
1505 elif args
[0] == '-e':
1510 with
ZipFile(args
[1], 'r') as zf
:
1511 zf
.extractall(args
[2])
1513 elif args
[0] == '-c':
1518 def addToZip(zf
, path
, zippath
):
1519 if os
.path
.isfile(path
):
1520 zf
.write(path
, zippath
, ZIP_DEFLATED
)
1521 elif os
.path
.isdir(path
):
1523 zf
.write(path
, zippath
)
1524 for nm
in os
.listdir(path
):
1526 os
.path
.join(path
, nm
), os
.path
.join(zippath
, nm
))
1529 with
ZipFile(args
[1], 'w', allowZip64
=True) as zf
:
1530 for path
in args
[2:]:
1531 zippath
= os
.path
.basename(path
)
1533 zippath
= os
.path
.basename(os
.path
.dirname(path
))
1534 if zippath
in ('', os
.curdir
, os
.pardir
):
1536 addToZip(zf
, path
, zippath
)
1538 if __name__
== "__main__":