| # Gaudi Framework, version v21r9 |

Home |  | Generated: 3 May 2010

# ZipPythonDir.py

Go to the documentation of this file.

    00001 #!/usr/bin/env python
    00002
    00003 ## file ZipPythonDir.py
    00004 #  Script to generate a zip file that can replace a directory in the python path.
    00005
    00006 import os, sys, zipfile, logging, stat, time
    00007 from StringIO import StringIO
    00008
    00009 # Add to the path the entry needed to import the locker module.
    00010 import locker
    00011
    00012 ## Class for generic exception coming from the zipdir() function
    00013 class ZipdirError(RuntimeError):
    00014     pass
    00015
    00016 ## Collect the changes to be applied to the zip file.
    00017 #
    00018 #  @param directory: directory to be packed in the zip file
    00019 #  @param infolist: list of ZipInfo objects already contained in the zip archive
    00020 #
    00021 #  @return: tuple of (added, modified, untouched, removed) entries in the directory with respect to the zip file
    00022 #
    00023 def _zipChanges(directory, infolist):
    00024     # gets the dates of the files in the zip archive
    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     # gets the changes
    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         arcdir = root[dirlen:]
    00043         for f in files:
    00044             ext = os.path.splitext(f)[1]
    00045             if ext == ".py": # extensions that can enter the zip file
    00046                 filename = os.path.join(arcdir, f)
    00047                 all_files.add(filename)
    00048                 if filename not in infos:
    00049                     action = "A"
    00050                     added.append(filename)
    00051                 else:
    00052                     filetime = time.localtime(os.stat(os.path.join(directory,filename))[stat.ST_MTIME])[:6]
    00053                     if filetime > infos[filename]:
    00054                         action = "M"
    00055                         modified.append(filename)
    00056                     else:
    00057                         action = "U"
    00058                         untouched.append(filename)
    00059                 if action in ['U']:
    00060                     log.debug(" %s -> %s", action, filename)
    00061                 else:
    00062                     log.info(" %s -> %s", action, filename)
    00063             elif ext not in [".pyc", ".pyo", ".stamp", ".cmtref"]: # extensions that can be ignored
    00064                 raise ZipdirError("Cannot add '%s' to the zip file, only '.py' are allowed." % os.path.join(arcdir, f))
    00065     # check for removed files
    00066     for filename in infos:
    00067         if filename not in all_files:
    00068             removed.append(filename)
    00069             log.info(" %s -> %s", "R", filename)
    00070     return (added, modified, untouched, removed)
    00071
    00072 ## Make a zip file out of a directory containing python modules
    00073 def zipdir(directory, no_pyc = False):
    00074     log = logging.getLogger("zipdir")
    00075     if not os.path.isdir(directory):
    00076         raise OSError(20, "Not a directory", directory)
    00077     msg = "Zipping directory '%s'"
    00078     if no_pyc:
    00079         msg += " (without pre-compilation)"
    00080     log.info(msg, directory)
    00081     filename = os.path.realpath(directory + ".zip")
    00082
    00083     # Open the file in read an update mode
    00084     if os.path.exists(filename):
    00085         zipFile = open(filename, "r+b")
    00086     else:
    00087         # If the file does not exist, we need to create it.
    00088         # "append mode" ensures that, in case of two processes trying to
    00089         # create the file, they do not truncate each other file
    00090         zipFile = open(filename, "ab")
    00091
    00092     locker.lock(zipFile)
    00093     try:
    00094         if zipfile.is_zipfile(filename):
    00095             infolist = zipfile.ZipFile(filename).infolist()
    00096         else:
    00097             infolist = []
    00098         (added, modified, untouched, removed) = _zipChanges(directory, infolist)
    00099         if added or modified or removed:
    00100             tempBuf = StringIO()
    00101             z = zipfile.PyZipFile(tempBuf, "w", zipfile.ZIP_DEFLATED)
    00102             for f in added + modified + untouched:
    00103                 src = os.path.join(directory, f)
    00104                 if no_pyc:
    00105                     log.debug("adding '%s'", f)
    00106                     z.write(src, f)
    00107                 else:
    00108                     # Remove the .pyc file to always force a re-compilation
    00109                     if os.path.exists(src + 'c'):
    00110                         log.debug("removing old .pyc for '%s'", f)
    00111                         os.remove(src + 'c')
    00112                     log.debug("adding '%s'", f)
    00113                     z.writepy(src, os.path.dirname(f))
    00114             z.close()
    00115             zipFile.seek(0)
    00116             zipFile.write(tempBuf.getvalue())
    00117             zipFile.truncate()
    00118             log.info("File '%s' closed", filename)
    00119         else:
    00120             log.info("Nothing to do on '%s'", filename)
    00121     finally:
    00122         locker.unlock(zipFile)
    00123         zipFile.close()
    00124
    00125 ## Main function of the script.
    00126 #  Parse arguments and call zipdir() for each directory passed as argument
    00127 def main(argv = None):
    00128     from optparse import OptionParser
    00129     parser = OptionParser(usage = "%prog [options] directory1 [directory2 ...]")
    00130     parser.add_option("--no-pyc", action = "store_true",
    00131                       help = "copy the .py files without pre-compiling them")
    00132     parser.add_option("--quiet", action = "store_true",
    00133                       help = "do not print info messages")
    00134     parser.add_option("--debug", action = "store_true",
    00135                       help = "print debug messages (has priority over --quiet)")
    00136
    00137     if argv is None:
    00138         argv = sys.argv
    00139     opts, args = parser.parse_args(argv[1:])
    00140
    00141     if not args:
    00142         parser.error("Specify at least one directory to zip")
    00143
    00144     # Initialize the logging module
    00145     level = logging.INFO
    00146     if opts.quiet:
    00147         level = logging.WARNING
    00148     if opts.debug:
    00149         level = logging.DEBUG
    00150     logging.basicConfig(level = level)
    00151
    00152     if "GAUDI_BUILD_LOCK" in os.environ:
    00153         _scopedLock = locker.LockFile(os.environ["GAUDI_BUILD_LOCK"], temporary =  True)
    00154     # zip all the directories passed as arguments
    00155     for d in args:
    00156         zipdir(d, opts.no_pyc)
    00157
    00158 if __name__ == '__main__':
    00159     main()