0001 "Read and write ZIP files." 0002 0003 import struct, os, time 0004 import binascii 0005 0006 try: 0007 import zlib # We may need its compression method 0008 except ImportError: 0009 zlib = None 0010 0011 __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile", 0012 "ZipInfo", "ZipFile", "PyZipFile"] 0013 0014 class BadZipfile(Exception): 0015 pass 0016 error = BadZipfile # The exception raised by this module 0017 0018 # constants for Zip file compression methods 0019 ZIP_STORED = 0 0020 ZIP_DEFLATED = 8 0021 # Other ZIP compression methods not supported 0022 0023 # Here are some struct module formats for reading headers 0024 structEndArchive = "<4s4H2lH" # 9 items, end of archive, 22 bytes 0025 stringEndArchive = "PK\005\006" # magic number for end of archive record 0026 structCentralDir = "<4s4B4HlLL5HLl"# 19 items, central directory, 46 bytes 0027 stringCentralDir = "PK\001\002" # magic number for central directory 0028 structFileHeader = "<4s2B4HlLL2H" # 12 items, file header record, 30 bytes 0029 stringFileHeader = "PK\003\004" # magic number for file header 0030 0031 # indexes of entries in the central directory structure 0032 _CD_SIGNATURE = 0 0033 _CD_CREATE_VERSION = 1 0034 _CD_CREATE_SYSTEM = 2 0035 _CD_EXTRACT_VERSION = 3 0036 _CD_EXTRACT_SYSTEM = 4 # is this meaningful? 0037 _CD_FLAG_BITS = 5 0038 _CD_COMPRESS_TYPE = 6 0039 _CD_TIME = 7 0040 _CD_DATE = 8 0041 _CD_CRC = 9 0042 _CD_COMPRESSED_SIZE = 10 0043 _CD_UNCOMPRESSED_SIZE = 11 0044 _CD_FILENAME_LENGTH = 12 0045 _CD_EXTRA_FIELD_LENGTH = 13 0046 _CD_COMMENT_LENGTH = 14 0047 _CD_DISK_NUMBER_START = 15 0048 _CD_INTERNAL_FILE_ATTRIBUTES = 16 0049 _CD_EXTERNAL_FILE_ATTRIBUTES = 17 0050 _CD_LOCAL_HEADER_OFFSET = 18 0051 0052 # indexes of entries in the local file header structure 0053 _FH_SIGNATURE = 0 0054 _FH_EXTRACT_VERSION = 1 0055 _FH_EXTRACT_SYSTEM = 2 # is this meaningful? 0056 _FH_GENERAL_PURPOSE_FLAG_BITS = 3 0057 _FH_COMPRESSION_METHOD = 4 0058 _FH_LAST_MOD_TIME = 5 0059 _FH_LAST_MOD_DATE = 6 0060 _FH_CRC = 7 0061 _FH_COMPRESSED_SIZE = 8 0062 _FH_UNCOMPRESSED_SIZE = 9 0063 _FH_FILENAME_LENGTH = 10 0064 _FH_EXTRA_FIELD_LENGTH = 11 0065 0066 def is_zipfile(filename): 0067 """Quickly see if file is a ZIP file by checking the magic number.""" 0068 try: 0069 fpin = open(filename, "rb") 0070 endrec = _EndRecData(fpin) 0071 fpin.close() 0072 if endrec: 0073 return True # file has correct magic number 0074 except IOError: 0075 pass 0076 return False 0077 0078 def _EndRecData(fpin): 0079 """Return data from the "End of Central Directory" record, or None. 0080 0081 The data is a list of the nine items in the ZIP "End of central dir" 0082 record followed by a tenth item, the file seek offset of this record.""" 0083 fpin.seek(-22, 2) # Assume no archive comment. 0084 filesize = fpin.tell() + 22 # Get file size 0085 data = fpin.read() 0086 if data[0:4] == stringEndArchive and data[-2:] == "\000\000": 0087 endrec = struct.unpack(structEndArchive, data) 0088 endrec = list(endrec) 0089 endrec.append("") # Append the archive comment 0090 endrec.append(filesize - 22) # Append the record start offset 0091 return endrec 0092 # Search the last END_BLOCK bytes of the file for the record signature. 0093 # The comment is appended to the ZIP file and has a 16 bit length. 0094 # So the comment may be up to 64K long. We limit the search for the 0095 # signature to a few Kbytes at the end of the file for efficiency. 0096 # also, the signature must not appear in the comment. 0097 END_BLOCK = min(filesize, 1024 * 4) 0098 fpin.seek(filesize - END_BLOCK, 0) 0099 data = fpin.read() 0100 start = data.rfind(stringEndArchive) 0101 if start >= 0: # Correct signature string was found 0102 endrec = struct.unpack(structEndArchive, data[start:start+22]) 0103 endrec = list(endrec) 0104 comment = data[start+22:] 0105 if endrec[7] == len(comment): # Comment length checks out 0106 # Append the archive comment and start offset 0107 endrec.append(comment) 0108 endrec.append(filesize - END_BLOCK + start) 0109 return endrec 0110 return # Error, return None 0111 0112 0113 class ZipInfo: 0114 """Class with attributes describing each file in the ZIP archive.""" 0115 0116 def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)): 0117 self.orig_filename = filename # Original file name in archive 0118 # Terminate the file name at the first null byte. Null bytes in file 0119 # names are used as tricks by viruses in archives. 0120 null_byte = filename.find(chr(0)) 0121 if null_byte >= 0: 0122 filename = filename[0:null_byte] 0123 # This is used to ensure paths in generated ZIP files always use 0124 # forward slashes as the directory separator, as required by the 0125 # ZIP format specification. 0126 if os.sep != "/": 0127 filename = filename.replace(os.sep, "/") 0128 self.filename = filename # Normalized file name 0129 self.date_time = date_time # year, month, day, hour, min, sec 0130 # Standard values: 0131 self.compress_type = ZIP_STORED # Type of compression for the file 0132 self.comment = "" # Comment for each file 0133 self.extra = "" # ZIP extra data 0134 self.create_system = 0 # System which created ZIP archive 0135 self.create_version = 20 # Version which created ZIP archive 0136 self.extract_version = 20 # Version needed to extract archive 0137 self.reserved = 0 # Must be zero 0138 self.flag_bits = 0 # ZIP flag bits 0139 self.volume = 0 # Volume number of file header 0140 self.internal_attr = 0 # Internal attributes 0141 self.external_attr = 0 # External file attributes 0142 # Other attributes are set by class ZipFile: 0143 # header_offset Byte offset to the file header 0144 # file_offset Byte offset to the start of the file data 0145 # CRC CRC-32 of the uncompressed file 0146 # compress_size Size of the compressed file 0147 # file_size Size of the uncompressed file 0148 0149 def FileHeader(self): 0150 """Return the per-file header as a string.""" 0151 dt = self.date_time 0152 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 0153 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 0154 if self.flag_bits & 0x08: 0155 # Set these to zero because we write them after the file data 0156 CRC = compress_size = file_size = 0 0157 else: 0158 CRC = self.CRC 0159 compress_size = self.compress_size 0160 file_size = self.file_size 0161 header = struct.pack(structFileHeader, stringFileHeader, 0162 self.extract_version, self.reserved, self.flag_bits, 0163 self.compress_type, dostime, dosdate, CRC, 0164 compress_size, file_size, 0165 len(self.filename), len(self.extra)) 0166 return header + self.filename + self.extra 0167 0168 0169 class ZipFile: 0170 """ Class with methods to open, read, write, close, list zip files. 0171 0172 z = ZipFile(file, mode="r", compression=ZIP_STORED) 0173 0174 file: Either the path to the file, or a file-like object. 0175 If it is a path, the file will be opened and closed by ZipFile. 0176 mode: The mode can be either read "r", write "w" or append "a". 0177 compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib). 0178 """ 0179 0180 fp = None # Set here since __del__ checks it 0181 0182 def __init__(self, file, mode="r", compression=ZIP_STORED): 0183 """Open the ZIP file with mode read "r", write "w" or append "a".""" 0184 if compression == ZIP_STORED: 0185 pass 0186 elif compression == ZIP_DEFLATED: 0187 if not zlib: 0188 raise RuntimeError,\ 0189 "Compression requires the (missing) zlib module" 0190 else: 0191 raise RuntimeError, "That compression method is not supported" 0192 self.debug = 0 # Level of printing: 0 through 3 0193 self.NameToInfo = {} # Find file info given name 0194 self.filelist = [] # List of ZipInfo instances for archive 0195 self.compression = compression # Method of compression 0196 self.mode = key = mode[0] 0197 0198 # Check if we were passed a file-like object 0199 if isinstance(file, basestring): 0200 self._filePassed = 0 0201 self.filename = file 0202 modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'} 0203 self.fp = open(file, modeDict[mode]) 0204 else: 0205 self._filePassed = 1 0206 self.fp = file 0207 self.filename = getattr(file, 'name', None) 0208 0209 if key == 'r': 0210 self._GetContents() 0211 elif key == 'w': 0212 pass 0213 elif key == 'a': 0214 try: # See if file is a zip file 0215 self._RealGetContents() 0216 # seek to start of directory and overwrite 0217 self.fp.seek(self.start_dir, 0) 0218 except BadZipfile: # file is not a zip file, just append 0219 self.fp.seek(0, 2) 0220 else: 0221 if not self._filePassed: 0222 self.fp.close() 0223 self.fp = None 0224 raise RuntimeError, 'Mode must be "r", "w" or "a"' 0225 0226 def _GetContents(self): 0227 """Read the directory, making sure we close the file if the format 0228 is bad.""" 0229 try: 0230 self._RealGetContents() 0231 except BadZipfile: 0232 if not self._filePassed: 0233 self.fp.close() 0234 self.fp = None 0235 raise 0236 0237 def _RealGetContents(self): 0238 """Read in the table of contents for the ZIP file.""" 0239 fp = self.fp 0240 endrec = _EndRecData(fp) 0241 if not endrec: 0242 raise BadZipfile, "File is not a zip file" 0243 if self.debug > 1: 0244 print endrec 0245 size_cd = endrec[5] # bytes in central directory 0246 offset_cd = endrec[6] # offset of central directory 0247 self.comment = endrec[8] # archive comment 0248 # endrec[9] is the offset of the "End of Central Dir" record 0249 x = endrec[9] - size_cd 0250 # "concat" is zero, unless zip was concatenated to another file 0251 concat = x - offset_cd 0252 if self.debug > 2: 0253 print "given, inferred, offset", offset_cd, x, concat 0254 # self.start_dir: Position of start of central directory 0255 self.start_dir = offset_cd + concat 0256 fp.seek(self.start_dir, 0) 0257 total = 0 0258 while total < size_cd: 0259 centdir = fp.read(46) 0260 total = total + 46 0261 if centdir[0:4] != stringCentralDir: 0262 raise BadZipfile, "Bad magic number for central directory" 0263 centdir = struct.unpack(structCentralDir, centdir) 0264 if self.debug > 2: 0265 print centdir 0266 filename = fp.read(centdir[_CD_FILENAME_LENGTH]) 0267 # Create ZipInfo instance to store file information 0268 x = ZipInfo(filename) 0269 x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH]) 0270 x.comment = fp.read(centdir[_CD_COMMENT_LENGTH]) 0271 total = (total + centdir[_CD_FILENAME_LENGTH] 0272 + centdir[_CD_EXTRA_FIELD_LENGTH] 0273 + centdir[_CD_COMMENT_LENGTH]) 0274 x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] + concat 0275 # file_offset must be computed below... 0276 (x.create_version, x.create_system, x.extract_version, x.reserved, 0277 x.flag_bits, x.compress_type, t, d, 0278 x.CRC, x.compress_size, x.file_size) = centdir[1:12] 0279 x.volume, x.internal_attr, x.external_attr = centdir[15:18] 0280 # Convert date/time code to (year, month, day, hour, min, sec) 0281 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F, 0282 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 ) 0283 self.filelist.append(x) 0284 self.NameToInfo[x.filename] = x 0285 if self.debug > 2: 0286 print "total", total 0287 for data in self.filelist: 0288 fp.seek(data.header_offset, 0) 0289 fheader = fp.read(30) 0290 if fheader[0:4] != stringFileHeader: 0291 raise BadZipfile, "Bad magic number for file header" 0292 fheader = struct.unpack(structFileHeader, fheader) 0293 # file_offset is computed here, since the extra field for 0294 # the central directory and for the local file header 0295 # refer to different fields, and they can have different 0296 # lengths 0297 data.file_offset = (data.header_offset + 30 0298 + fheader[_FH_FILENAME_LENGTH] 0299 + fheader[_FH_EXTRA_FIELD_LENGTH]) 0300 fname = fp.read(fheader[_FH_FILENAME_LENGTH]) 0301 if fname != data.orig_filename: 0302 raise RuntimeError, \ 0303 'File name in directory "%s" and header "%s" differ.' % ( 0304 data.orig_filename, fname) 0305 0306 def namelist(self): 0307 """Return a list of file names in the archive.""" 0308 l = [] 0309 for data in self.filelist: 0310 l.append(data.filename) 0311 return l 0312 0313 def infolist(self): 0314 """Return a list of class ZipInfo instances for files in the 0315 archive.""" 0316 return self.filelist 0317 0318 def printdir(self): 0319 """Print a table of contents for the zip file.""" 0320 print "%-46s %19s %12s" % ("File Name", "Modified ", "Size") 0321 for zinfo in self.filelist: 0322 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time 0323 print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size) 0324 0325 def testzip(self): 0326 """Read all the files and check the CRC.""" 0327 for zinfo in self.filelist: 0328 try: 0329 self.read(zinfo.filename) # Check CRC-32 0330 except BadZipfile: 0331 return zinfo.filename 0332 0333 def getinfo(self, name): 0334 """Return the instance of ZipInfo given 'name'.""" 0335 return self.NameToInfo[name] 0336 0337 def read(self, name): 0338 """Return file bytes (as a string) for name.""" 0339 if self.mode not in ("r", "a"): 0340 raise RuntimeError, 'read() requires mode "r" or "a"' 0341 if not self.fp: 0342 raise RuntimeError, \ 0343 "Attempt to read ZIP archive that was already closed" 0344 zinfo = self.getinfo(name) 0345 filepos = self.fp.tell() 0346 self.fp.seek(zinfo.file_offset, 0) 0347 bytes = self.fp.read(zinfo.compress_size) 0348 self.fp.seek(filepos, 0) 0349 if zinfo.compress_type == ZIP_STORED: 0350 pass 0351 elif zinfo.compress_type == ZIP_DEFLATED: 0352 if not zlib: 0353 raise RuntimeError, \ 0354 "De-compression requires the (missing) zlib module" 0355 # zlib compress/decompress code by Jeremy Hylton of CNRI 0356 dc = zlib.decompressobj(-15) 0357 bytes = dc.decompress(bytes) 0358 # need to feed in unused pad byte so that zlib won't choke 0359 ex = dc.decompress('Z') + dc.flush() 0360 if ex: 0361 bytes = bytes + ex 0362 else: 0363 raise BadZipfile, \ 0364 "Unsupported compression method %d for file %s" % \ 0365 (zinfo.compress_type, name) 0366 crc = binascii.crc32(bytes) 0367 if crc != zinfo.CRC: 0368 raise BadZipfile, "Bad CRC-32 for file %s" % name 0369 return bytes 0370 0371 def _writecheck(self, zinfo): 0372 """Check for errors before writing a file to the archive.""" 0373 if zinfo.filename in self.NameToInfo: 0374 if self.debug: # Warning for duplicate names 0375 print "Duplicate name:", zinfo.filename 0376 if self.mode not in ("w", "a"): 0377 raise RuntimeError, 'write() requires mode "w" or "a"' 0378 if not self.fp: 0379 raise RuntimeError, \ 0380 "Attempt to write ZIP archive that was already closed" 0381 if zinfo.compress_type == ZIP_DEFLATED and not zlib: 0382 raise RuntimeError, \ 0383 "Compression requires the (missing) zlib module" 0384 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED): 0385 raise RuntimeError, \ 0386 "That compression method is not supported" 0387 0388 def write(self, filename, arcname=None, compress_type=None): 0389 """Put the bytes from filename into the archive under the name 0390 arcname.""" 0391 st = os.stat(filename) 0392 mtime = time.localtime(st.st_mtime) 0393 date_time = mtime[0:6] 0394 # Create ZipInfo instance to store file information 0395 if arcname is None: 0396 zinfo = ZipInfo(filename, date_time) 0397 else: 0398 zinfo = ZipInfo(arcname, date_time) 0399 zinfo.external_attr = (st[0] & 0xFFFF) << 16L # Unix attributes 0400 if compress_type is None: 0401 zinfo.compress_type = self.compression 0402 else: 0403 zinfo.compress_type = compress_type 0404 self._writecheck(zinfo) 0405 fp = open(filename, "rb") 0406 zinfo.flag_bits = 0x00 0407 zinfo.header_offset = self.fp.tell() # Start of header bytes 0408 # Must overwrite CRC and sizes with correct data later 0409 zinfo.CRC = CRC = 0 0410 zinfo.compress_size = compress_size = 0 0411 zinfo.file_size = file_size = 0 0412 self.fp.write(zinfo.FileHeader()) 0413 zinfo.file_offset = self.fp.tell() # Start of file bytes 0414 if zinfo.compress_type == ZIP_DEFLATED: 0415 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, 0416 zlib.DEFLATED, -15) 0417 else: 0418 cmpr = None 0419 while 1: 0420 buf = fp.read(1024 * 8) 0421 if not buf: 0422 break 0423 file_size = file_size + len(buf) 0424 CRC = binascii.crc32(buf, CRC) 0425 if cmpr: 0426 buf = cmpr.compress(buf) 0427 compress_size = compress_size + len(buf) 0428 self.fp.write(buf) 0429 fp.close() 0430 if cmpr: 0431 buf = cmpr.flush() 0432 compress_size = compress_size + len(buf) 0433 self.fp.write(buf) 0434 zinfo.compress_size = compress_size 0435 else: 0436 zinfo.compress_size = file_size 0437 zinfo.CRC = CRC 0438 zinfo.file_size = file_size 0439 # Seek backwards and write CRC and file sizes 0440 position = self.fp.tell() # Preserve current position in file 0441 self.fp.seek(zinfo.header_offset + 14, 0) 0442 self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size, 0443 zinfo.file_size)) 0444 self.fp.seek(position, 0) 0445 self.filelist.append(zinfo) 0446 self.NameToInfo[zinfo.filename] = zinfo 0447 0448 def writestr(self, zinfo_or_arcname, bytes): 0449 """Write a file into the archive. The contents is the string 0450 'bytes'. 'zinfo_or_arcname' is either a ZipInfo instance or 0451 the name of the file in the archive.""" 0452 if not isinstance(zinfo_or_arcname, ZipInfo): 0453 zinfo = ZipInfo(filename=zinfo_or_arcname, 0454 date_time=time.localtime(time.time())) 0455 zinfo.compress_type = self.compression 0456 else: 0457 zinfo = zinfo_or_arcname 0458 self._writecheck(zinfo) 0459 zinfo.file_size = len(bytes) # Uncompressed size 0460 zinfo.CRC = binascii.crc32(bytes) # CRC-32 checksum 0461 if zinfo.compress_type == ZIP_DEFLATED: 0462 co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, 0463 zlib.DEFLATED, -15) 0464 bytes = co.compress(bytes) + co.flush() 0465 zinfo.compress_size = len(bytes) # Compressed size 0466 else: 0467 zinfo.compress_size = zinfo.file_size 0468 zinfo.header_offset = self.fp.tell() # Start of header bytes 0469 self.fp.write(zinfo.FileHeader()) 0470 zinfo.file_offset = self.fp.tell() # Start of file bytes 0471 self.fp.write(bytes) 0472 if zinfo.flag_bits & 0x08: 0473 # Write CRC and file sizes after the file data 0474 self.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size, 0475 zinfo.file_size)) 0476 self.filelist.append(zinfo) 0477 self.NameToInfo[zinfo.filename] = zinfo 0478 0479 def __del__(self): 0480 """Call the "close()" method in case the user forgot.""" 0481 self.close() 0482 0483 def close(self): 0484 """Close the file, and for mode "w" and "a" write the ending 0485 records.""" 0486 if self.fp is None: 0487 return 0488 if self.mode in ("w", "a"): # write ending records 0489 count = 0 0490 pos1 = self.fp.tell() 0491 for zinfo in self.filelist: # write central directory 0492 count = count + 1 0493 dt = zinfo.date_time 0494 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2] 0495 dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2) 0496 centdir = struct.pack(structCentralDir, 0497 stringCentralDir, zinfo.create_version, 0498 zinfo.create_system, zinfo.extract_version, zinfo.reserved, 0499 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate, 0500 zinfo.CRC, zinfo.compress_size, zinfo.file_size, 0501 len(zinfo.filename), len(zinfo.extra), len(zinfo.comment), 0502 0, zinfo.internal_attr, zinfo.external_attr, 0503 zinfo.header_offset) 0504 self.fp.write(centdir) 0505 self.fp.write(zinfo.filename) 0506 self.fp.write(zinfo.extra) 0507 self.fp.write(zinfo.comment) 0508 pos2 = self.fp.tell() 0509 # Write end-of-zip-archive record 0510 endrec = struct.pack(structEndArchive, stringEndArchive, 0511 0, 0, count, count, pos2 - pos1, pos1, 0) 0512 self.fp.write(endrec) 0513 self.fp.flush() 0514 if not self._filePassed: 0515 self.fp.close() 0516 self.fp = None 0517 0518 0519 class PyZipFile(ZipFile): 0520 """Class to create ZIP archives with Python library files and packages.""" 0521 0522 def writepy(self, pathname, basename = ""): 0523 """Add all files from "pathname" to the ZIP archive. 0524 0525 If pathname is a package directory, search the directory and 0526 all package subdirectories recursively for all *.py and enter 0527 the modules into the archive. If pathname is a plain 0528 directory, listdir *.py and enter all modules. Else, pathname 0529 must be a Python *.py file and the module will be put into the 0530 archive. Added modules are always module.pyo or module.pyc. 0531 This method will compile the module.py into module.pyc if 0532 necessary. 0533 """ 0534 dir, name = os.path.split(pathname) 0535 if os.path.isdir(pathname): 0536 initname = os.path.join(pathname, "__init__.py") 0537 if os.path.isfile(initname): 0538 # This is a package directory, add it 0539 if basename: 0540 basename = "%s/%s" % (basename, name) 0541 else: 0542 basename = name 0543 if self.debug: 0544 print "Adding package in", pathname, "as", basename 0545 fname, arcname = self._get_codename(initname[0:-3], basename) 0546 if self.debug: 0547 print "Adding", arcname 0548 self.write(fname, arcname) 0549 dirlist = os.listdir(pathname) 0550 dirlist.remove("__init__.py") 0551 # Add all *.py files and package subdirectories 0552 for filename in dirlist: 0553 path = os.path.join(pathname, filename) 0554 root, ext = os.path.splitext(filename) 0555 if os.path.isdir(path): 0556 if os.path.isfile(os.path.join(path, "__init__.py")): 0557 # This is a package directory, add it 0558 self.writepy(path, basename) # Recursive call 0559 elif ext == ".py": 0560 fname, arcname = self._get_codename(path[0:-3], 0561 basename) 0562 if self.debug: 0563 print "Adding", arcname 0564 self.write(fname, arcname) 0565 else: 0566 # This is NOT a package directory, add its files at top level 0567 if self.debug: 0568 print "Adding files from directory", pathname 0569 for filename in os.listdir(pathname): 0570 path = os.path.join(pathname, filename) 0571 root, ext = os.path.splitext(filename) 0572 if ext == ".py": 0573 fname, arcname = self._get_codename(path[0:-3], 0574 basename) 0575 if self.debug: 0576 print "Adding", arcname 0577 self.write(fname, arcname) 0578 else: 0579 if pathname[-3:] != ".py": 0580 raise RuntimeError, \ 0581 'Files added with writepy() must end with ".py"' 0582 fname, arcname = self._get_codename(pathname[0:-3], basename) 0583 if self.debug: 0584 print "Adding file", arcname 0585 self.write(fname, arcname) 0586 0587 def _get_codename(self, pathname, basename): 0588 """Return (filename, archivename) for the path. 0589 0590 Given a module name path, return the correct file path and 0591 archive name, compiling if necessary. For example, given 0592 /python/lib/string, return (/python/lib/string.pyc, string). 0593 """ 0594 file_py = pathname + ".py" 0595 file_pyc = pathname + ".pyc" 0596 file_pyo = pathname + ".pyo" 0597 if os.path.isfile(file_pyo) and \ 0598 os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime: 0599 fname = file_pyo # Use .pyo file 0600 elif not os.path.isfile(file_pyc) or \ 0601 os.stat(file_pyc).st_mtime < os.stat(file_py).st_mtime: 0602 import py_compile 0603 if self.debug: 0604 print "Compiling", file_py 0605 try: 0606 py_compile.compile(file_py, file_pyc, None, True) 0607 except py_compile.PyCompileError,err: 0608 print err.msg 0609 fname = file_pyc 0610 else: 0611 fname = file_pyc 0612 archivename = os.path.split(fname)[1] 0613 if basename: 0614 archivename = "%s/%s" % (basename, archivename) 0615 return (fname, archivename) 0616
Generated by PyXR 0.9.4