Gaudi Framework, version v23r6

Home   Generated: Wed Jan 30 2013
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
ZipPythonDir.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 ## file ZipPythonDir.py
4 # Script to generate a zip file that can replace a directory in the python path.
5 
6 import os
7 import sys
8 import zipfile
9 import logging
10 import stat
11 import time
12 import re
13 import codecs
14 from StringIO import StringIO
15 
16 # Add to the path the entry needed to import the locker module.
17 import locker
18 
19 ## Class for generic exception coming from the zipdir() function
20 class ZipdirError(RuntimeError):
21  pass
22 
23 ## Collect the changes to be applied to the zip file.
24 #
25 # @param directory: directory to be packed in the zip file
26 # @param infolist: list of ZipInfo objects already contained in the zip archive
27 #
28 # @return: tuple of (added, modified, untouched, removed) entries in the directory with respect to the zip file
29 #
30 def _zipChanges(directory, infolist):
31  # gets the dates of the files in the zip archive
32  infos = {}
33  for i in infolist:
34  fn = i.filename
35  if fn.endswith(".pyc"):
36  fn = fn[:-1]
37  infos[fn] = i.date_time
38 
39  # gets the changes
40  added = []
41  modified = []
42  untouched = []
43  removed = []
44  all_files = set()
45 
46  log = logging.getLogger("zipdir")
47  dirlen = len(directory) + 1
48  for root, dirs, files in os.walk(directory):
49  if "lib-dynload" in dirs:
50  # exclude the directory containing binary modules
51  dirs.remove("lib-dynload")
52  arcdir = root[dirlen:]
53  for f in files:
54  ext = os.path.splitext(f)[1]
55  if ext == ".py": # extensions that can enter the zip file
56  filename = os.path.join(arcdir, f)
57  all_files.add(filename)
58  if filename not in infos:
59  action = "A"
60  added.append(filename)
61  else:
62  filetime = time.localtime(os.stat(os.path.join(directory,filename))[stat.ST_MTIME])[:6]
63  if filetime > infos[filename]:
64  action = "M"
65  modified.append(filename)
66  else:
67  action = "U"
68  untouched.append(filename)
69  if action in ['U']:
70  log.debug(" %s -> %s", action, filename)
71  else:
72  log.info(" %s -> %s", action, filename)
73  # cases that can be ignored
74  elif ext not in [".pyc", ".pyo", ".stamp", ".cmtref"] and not f.startswith('.__afs'):
75  raise ZipdirError("Cannot add '%s' to the zip file, only '.py' are allowed." % os.path.join(arcdir, f))
76  # check for removed files
77  for filename in infos:
78  if filename not in all_files:
79  removed.append(filename)
80  log.info(" %s -> %s", "R", filename)
81  return (added, modified, untouched, removed)
82 
83 def checkEncoding(path):
84  '''
85  Check that a file honors the declared encoding (default ASCII for Python 2
86  and UTF-8 for Python 3).
87 
88  Raises a UnicodeDecodeError in case of problems.
89 
90  See http://www.python.org/dev/peps/pep-0263/
91  '''
92  # default encoding
93  if sys.version_info[0] <= 2:
94  enc = 'ascii'
95  else:
96  enc = 'utf-8'
97 
98  # find the encoding of the file, if specified
99  enc_exp = re.compile(r"coding[:=]\s*([-\w.]+)")
100  f = open(path)
101  count = 2 # number of lines
102  while count:
103  m = enc_exp.search(f.readline())
104  if m:
105  enc = m.group(1)
106  break
107  count -= 1
108 
109  logging.getLogger('checkEncoding').debug('checking encoding %s on %s', enc, path)
110  # try to read the file with the declared encoding
111  codecs.open(path, encoding=enc).read()
112 
113 
114 ## Make a zip file out of a directory containing python modules
115 def zipdir(directory, no_pyc = False):
116  log = logging.getLogger("zipdir")
117  if not os.path.isdir(directory):
118  raise OSError(20, "Not a directory", directory)
119  msg = "Zipping directory '%s'"
120  if no_pyc:
121  msg += " (without pre-compilation)"
122  log.info(msg, directory)
123  filename = os.path.realpath(directory + ".zip")
124 
125  # Open the file in read an update mode
126  if os.path.exists(filename):
127  zipFile = open(filename, "r+b")
128  else:
129  # If the file does not exist, we need to create it.
130  # "append mode" ensures that, in case of two processes trying to
131  # create the file, they do not truncate each other file
132  zipFile = open(filename, "ab")
133 
134  locker.lock(zipFile)
135  try:
136  if zipfile.is_zipfile(filename):
137  infolist = zipfile.ZipFile(filename).infolist()
138  else:
139  infolist = []
140  (added, modified, untouched, removed) = _zipChanges(directory, infolist)
141  if added or modified or removed:
142  tempBuf = StringIO()
143  z = zipfile.PyZipFile(tempBuf, "w", zipfile.ZIP_DEFLATED)
144  for f in added + modified + untouched:
145  src = os.path.join(directory, f)
146  checkEncoding(src)
147  if no_pyc:
148  log.debug("adding '%s'", f)
149  z.write(src, f)
150  else:
151  # Remove the .pyc file to always force a re-compilation
152  if os.path.exists(src + 'c'):
153  log.debug("removing old .pyc for '%s'", f)
154  os.remove(src + 'c')
155  log.debug("adding '%s'", f)
156  z.writepy(src, os.path.dirname(f))
157  z.close()
158  zipFile.seek(0)
159  zipFile.write(tempBuf.getvalue())
160  zipFile.truncate()
161  log.info("File '%s' closed", filename)
162  else:
163  log.info("Nothing to do on '%s'", filename)
164  except UnicodeDecodeError, x:
165  log.error("Wrong encoding in file '%s':", src)
166  log.error(" %s", x)
167  log.error("Probably you forgot the line '# -*- coding: utf-8 -*-'")
168  sys.exit(1)
169  finally:
170  locker.unlock(zipFile)
171  zipFile.close()
172 
173 ## Main function of the script.
174 # Parse arguments and call zipdir() for each directory passed as argument
175 def main(argv = None):
176  from optparse import OptionParser
177  parser = OptionParser(usage = "%prog [options] directory1 [directory2 ...]")
178  parser.add_option("--no-pyc", action = "store_true",
179  help = "copy the .py files without pre-compiling them")
180  parser.add_option("--quiet", action = "store_true",
181  help = "do not print info messages")
182  parser.add_option("--debug", action = "store_true",
183  help = "print debug messages (has priority over --quiet)")
184 
185  if argv is None:
186  argv = sys.argv
187  opts, args = parser.parse_args(argv[1:])
188 
189  if not args:
190  parser.error("Specify at least one directory to zip")
191 
192  # Initialize the logging module
193  level = logging.INFO
194  if opts.quiet:
195  level = logging.WARNING
196  if opts.debug:
197  level = logging.DEBUG
198  logging.basicConfig(level = level)
199 
200  if "GAUDI_BUILD_LOCK" in os.environ:
201  _scopedLock = locker.LockFile(os.environ["GAUDI_BUILD_LOCK"], temporary = True)
202  # zip all the directories passed as arguments
203  for d in args:
204  zipdir(d, opts.no_pyc)
205 
206 if __name__ == '__main__':
207  main()

Generated at Wed Jan 30 2013 17:13:41 for Gaudi Framework, version v23r6 by Doxygen version 1.8.2 written by Dimitri van Heesch, © 1997-2004