00001
00002
00003
00004
00005
00006 import os, sys, zipfile, logging, stat, time
00007 from StringIO import StringIO
00008
00009
00010 import locker
00011
00012
00013 class ZipdirError(RuntimeError):
00014 pass
00015
00016
00017
00018
00019
00020
00021
00022
00023 def _zipChanges(directory, infolist):
00024
00025 infos = {}
00026 for i in infolist:
00027 fn = i.filename
00028 if fn.endswith(".pyc"):
00029 fn = fn[:-1]
00030 infos[fn] = i.date_time
00031
00032
00033 added = []
00034 modified = []
00035 untouched = []
00036 removed = []
00037 all_files = set()
00038
00039 log = logging.getLogger("zipdir")
00040 dirlen = len(directory) + 1
00041 for root, dirs, files in os.walk(directory):
00042 if "lib-dynload" in dirs:
00043
00044 dirs.remove("lib-dynload")
00045 arcdir = root[dirlen:]
00046 for f in files:
00047 ext = os.path.splitext(f)[1]
00048 if ext == ".py":
00049 filename = os.path.join(arcdir, f)
00050 all_files.add(filename)
00051 if filename not in infos:
00052 action = "A"
00053 added.append(filename)
00054 else:
00055 filetime = time.localtime(os.stat(os.path.join(directory,filename))[stat.ST_MTIME])[:6]
00056 if filetime > infos[filename]:
00057 action = "M"
00058 modified.append(filename)
00059 else:
00060 action = "U"
00061 untouched.append(filename)
00062 if action in ['U']:
00063 log.debug(" %s -> %s", action, filename)
00064 else:
00065 log.info(" %s -> %s", action, filename)
00066 elif ext not in [".pyc", ".pyo", ".stamp", ".cmtref"]:
00067 raise ZipdirError("Cannot add '%s' to the zip file, only '.py' are allowed." % os.path.join(arcdir, f))
00068
00069 for filename in infos:
00070 if filename not in all_files:
00071 removed.append(filename)
00072 log.info(" %s -> %s", "R", filename)
00073 return (added, modified, untouched, removed)
00074
00075
00076 def zipdir(directory, no_pyc = False):
00077 log = logging.getLogger("zipdir")
00078 if not os.path.isdir(directory):
00079 raise OSError(20, "Not a directory", directory)
00080 msg = "Zipping directory '%s'"
00081 if no_pyc:
00082 msg += " (without pre-compilation)"
00083 log.info(msg, directory)
00084 filename = os.path.realpath(directory + ".zip")
00085
00086
00087 if os.path.exists(filename):
00088 zipFile = open(filename, "r+b")
00089 else:
00090
00091
00092
00093 zipFile = open(filename, "ab")
00094
00095 locker.lock(zipFile)
00096 try:
00097 if zipfile.is_zipfile(filename):
00098 infolist = zipfile.ZipFile(filename).infolist()
00099 else:
00100 infolist = []
00101 (added, modified, untouched, removed) = _zipChanges(directory, infolist)
00102 if added or modified or removed:
00103 tempBuf = StringIO()
00104 z = zipfile.PyZipFile(tempBuf, "w", zipfile.ZIP_DEFLATED)
00105 for f in added + modified + untouched:
00106 src = os.path.join(directory, f)
00107 if no_pyc:
00108 log.debug("adding '%s'", f)
00109 z.write(src, f)
00110 else:
00111
00112 if os.path.exists(src + 'c'):
00113 log.debug("removing old .pyc for '%s'", f)
00114 os.remove(src + 'c')
00115 log.debug("adding '%s'", f)
00116 z.writepy(src, os.path.dirname(f))
00117 z.close()
00118 zipFile.seek(0)
00119 zipFile.write(tempBuf.getvalue())
00120 zipFile.truncate()
00121 log.info("File '%s' closed", filename)
00122 else:
00123 log.info("Nothing to do on '%s'", filename)
00124 finally:
00125 locker.unlock(zipFile)
00126 zipFile.close()
00127
00128
00129
00130 def main(argv = None):
00131 from optparse import OptionParser
00132 parser = OptionParser(usage = "%prog [options] directory1 [directory2 ...]")
00133 parser.add_option("--no-pyc", action = "store_true",
00134 help = "copy the .py files without pre-compiling them")
00135 parser.add_option("--quiet", action = "store_true",
00136 help = "do not print info messages")
00137 parser.add_option("--debug", action = "store_true",
00138 help = "print debug messages (has priority over --quiet)")
00139
00140 if argv is None:
00141 argv = sys.argv
00142 opts, args = parser.parse_args(argv[1:])
00143
00144 if not args:
00145 parser.error("Specify at least one directory to zip")
00146
00147
00148 level = logging.INFO
00149 if opts.quiet:
00150 level = logging.WARNING
00151 if opts.debug:
00152 level = logging.DEBUG
00153 logging.basicConfig(level = level)
00154
00155 if "GAUDI_BUILD_LOCK" in os.environ:
00156 _scopedLock = locker.LockFile(os.environ["GAUDI_BUILD_LOCK"], temporary = True)
00157
00158 for d in args:
00159 zipdir(d, opts.no_pyc)
00160
00161 if __name__ == '__main__':
00162 main()