1import args 2import sys 3import os 4 5# Ugly stuff to have curses called ONLY once, instead of for each 6# new Configure object created (and flashing the screen) 7global LineWidth 8global RemoveDirectory 9global backupRemoveDirectory 10LineWidth = -1 11RemoveDirectory = os.path.join(os.getcwd(),'') 12backupRemoveDirectory = '' 13 14# Compatibility fixes 15try: 16 enumerate([0, 1]) 17except NameError: 18 def enumerate(l): 19 return zip(range(len(l)), l) 20try: 21 True, False 22except NameError: 23 True, False = (0==0, 0!=0) 24 25class Logger(args.ArgumentProcessor): 26 '''This class creates a shared log and provides methods for writing to it''' 27 defaultLog = None 28 defaultOut = sys.stdout 29 30 def __init__(self, clArgs = None, argDB = None, log = None, out = defaultOut, debugLevel = None, debugSections = None, debugIndent = None): 31 args.ArgumentProcessor.__init__(self, clArgs, argDB) 32 self.logName = None 33 self.log = log 34 self.out = out 35 self.debugLevel = debugLevel 36 self.debugSections = debugSections 37 self.debugIndent = debugIndent 38 self.getRoot() 39 return 40 41 def __getstate__(self): 42 '''We do not want to pickle the default log stream''' 43 d = args.ArgumentProcessor.__getstate__(self) 44 if 'logBkp' in d: 45 del d['logBkp'] 46 if 'log' in d: 47 if d['log'] is Logger.defaultLog: 48 del d['log'] 49 else: 50 d['log'] = None 51 if 'out' in d: 52 if d['out'] is Logger.defaultOut: 53 del d['out'] 54 else: 55 d['out'] = None 56 return d 57 58 def __setstate__(self, d): 59 '''We must create the default log stream''' 60 args.ArgumentProcessor.__setstate__(self, d) 61 if not 'log' in d: 62 self.log = self.createLog(None) 63 if not 'out' in d: 64 self.out = Logger.defaultOut 65 self.__dict__.update(d) 66 return 67 68 def setupArguments(self, argDB): 69 '''Setup types in the argument database''' 70 import nargs 71 72 argDB = args.ArgumentProcessor.setupArguments(self, argDB) 73 argDB.setType('log', nargs.Arg(None, 'buildsystem.log', 'The filename for the log')) 74 argDB.setType('logAppend', nargs.ArgBool(None, 0, 'The flag determining whether we backup or append to the current log', isTemporary = 1)) 75 argDB.setType('debugLevel', nargs.ArgInt(None, 3, 'Integer 0 to 4, where a higher level means more detail', 0, 5)) 76 argDB.setType('debugSections', nargs.Arg(None, [], 'Message types to print, e.g. [compile,link,hg,install]')) 77 argDB.setType('debugIndent', nargs.Arg(None, ' ', 'The string used for log indentation')) 78 argDB.setType('scrollOutput', nargs.ArgBool(None, 0, 'Flag to allow output to scroll rather than overwriting a single line')) 79 argDB.setType('noOutput', nargs.ArgBool(None, 0, 'Flag to suppress output to the terminal')) 80 return argDB 81 82 def setup(self): 83 '''Setup the terminal output and filtering flags''' 84 self.log = self.createLog(self.logName, self.log) 85 args.ArgumentProcessor.setup(self) 86 87 if self.argDB['noOutput']: 88 self.out = None 89 if self.debugLevel is None: 90 self.debugLevel = self.argDB['debugLevel'] 91 if self.debugSections is None: 92 self.debugSections = self.argDB['debugSections'] 93 if self.debugIndent is None: 94 self.debugIndent = self.argDB['debugIndent'] 95 return 96 97 def checkLog(self, logName): 98 import nargs 99 import os 100 101 if logName is None: 102 logName = nargs.Arg.findArgument('log', self.clArgs) 103 if logName is None: 104 if not self.argDB is None and 'log' in self.argDB: 105 logName = self.argDB['log'] 106 else: 107 logName = 'default.log' 108 self.logName = logName 109 self.logExists = os.path.exists(self.logName) 110 return self.logExists 111 112 def createLog(self, logName, initLog = None): 113 '''Create a default log stream, unless initLog is given''' 114 import nargs 115 116 if not initLog is None: 117 log = initLog 118 else: 119 if Logger.defaultLog is None: 120 appendArg = nargs.Arg.findArgument('logAppend', self.clArgs) 121 if self.checkLog(logName): 122 if not self.argDB is None and ('logAppend' in self.argDB and self.argDB['logAppend']) or (not appendArg is None and bool(appendArg)): 123 Logger.defaultLog = file(self.logName, 'a') 124 else: 125 try: 126 import os 127 128 os.rename(self.logName, self.logName+'.bkp') 129 Logger.defaultLog = file(self.logName, 'w') 130 except OSError: 131 sys.stdout.write('WARNING: Cannot backup log file, appending instead.\n') 132 Logger.defaultLog = file(self.logName, 'a') 133 else: 134 Logger.defaultLog = file(self.logName, 'w') 135 log = Logger.defaultLog 136 return log 137 138 def closeLog(self): 139 '''Closes the log file''' 140 self.log.close() 141 142 def saveLog(self): 143 import StringIO 144 self.logBkp = self.log 145 self.log = StringIO.StringIO() 146 147 def restoreLog(self): 148 s = self.log.getvalue() 149 self.log.close() 150 self.log = self.logBkp 151 del(self.logBkp) 152 return s 153 154 def getLinewidth(self): 155 global LineWidth 156 if not hasattr(self, '_linewidth'): 157 if self.out is None or not self.out.isatty() or self.argDB['scrollOutput']: 158 self._linewidth = -1 159 else: 160 if LineWidth == -1: 161 try: 162 import curses 163 164 try: 165 curses.setupterm() 166 (y, self._linewidth) = curses.initscr().getmaxyx() 167 curses.endwin() 168 except curses.error: 169 self._linewidth = -1 170 except: 171 self._linewidth = -1 172 LineWidth = self._linewidth 173 else: 174 self._linewidth = LineWidth 175 return self._linewidth 176 def setLinewidth(self, linewidth): 177 self._linewidth = linewidth 178 return 179 linewidth = property(getLinewidth, setLinewidth, doc = 'The maximum number of characters per log line') 180 181 def checkWrite(self, f, debugLevel, debugSection, writeAll = 0): 182 '''Check whether the log line should be written 183 - If writeAll is true, return true 184 - If debugLevel >= current level, and debugSection in current section or sections is empty, return true''' 185 if not isinstance(debugLevel, int): 186 raise RuntimeError('Debug level must be an integer: '+str(debugLevel)) 187 if f is None: 188 return False 189 if writeAll: 190 return True 191 if self.debugLevel >= debugLevel and (not len(self.debugSections) or debugSection in self.debugSections): 192 return True 193 return False 194 195 def logIndent(self, debugLevel = -1, debugSection = None, comm = None): 196 '''Write the proper indentation to the log streams''' 197 import traceback 198 199 indentLevel = len(traceback.extract_stack())-5 200 for writeAll, f in enumerate([self.out, self.log]): 201 if self.checkWrite(f, debugLevel, debugSection, writeAll): 202 if not comm is None: 203 f.write('[') 204 f.write(str(comm.rank())) 205 f.write(']') 206 for i in range(indentLevel): 207 f.write(self.debugIndent) 208 return 209 210 def logBack(self): 211 '''Backup the current line if we are not scrolling output''' 212 if not self.out is None and self.linewidth > 0: 213 self.out.write('\r') 214 return 215 216 def logClear(self): 217 '''Clear the current line if we are not scrolling output''' 218 if not self.out is None and self.linewidth > 0: 219 self.out.write('\r') 220 self.out.write(''.join([' '] * self.linewidth)) 221 self.out.write('\r') 222 return 223 224 def logPrintDivider(self, debugLevel = -1, debugSection = None, single = 0): 225 if single: 226 self.logPrint('-------------------------------------------------------------------------------', debugLevel = debugLevel, debugSection = debugSection) 227 else: 228 self.logPrint('===============================================================================', debugLevel = debugLevel, debugSection = debugSection) 229 return 230 231 def logPrintBox(self,msg, debugLevel = -1, debugSection = 'screen', indent = 1, comm = None): 232 self.logClear() 233 self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection) 234 [self.logPrint(' '+line, debugLevel = debugLevel, debugSection = debugSection) for line in msg.split('\n')] 235 self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection) 236 self.logPrint('', debugLevel = debugLevel, debugSection = debugSection) 237 return 238 239 def logClearRemoveDirectory(self): 240 global RemoveDirectory 241 global backupRemoveDirectory 242 backupRemoveDirectory = RemoveDirectory 243 RemoveDirectory = '' 244 245 def logResetRemoveDirectory(self): 246 global RemoveDirectory 247 global backupRemoveDirectory 248 RemoveDirectory = backupRemoveDirectory 249 250 251 def logWrite(self, msg, debugLevel = -1, debugSection = None, forceScroll = 0): 252 '''Write the message to the log streams''' 253 for writeAll, f in enumerate([self.out, self.log]): 254 if self.checkWrite(f, debugLevel, debugSection, writeAll): 255 if not forceScroll and not writeAll and self.linewidth > 0: 256 global RemoveDirectory 257 self.logBack() 258 msg = msg.replace(RemoveDirectory,'') 259 for ms in msg.split('\n'): 260 f.write(ms[0:self.linewidth]) 261 f.write(''.join([' '] * (self.linewidth - len(ms)))) 262 else: 263 if not debugSection is None and not debugSection == 'screen' and len(msg): 264 f.write(str(debugSection)) 265 f.write(': ') 266 f.write(msg) 267 if hasattr(f, 'flush'): 268 f.flush() 269 return 270 271 def logPrint(self, msg, debugLevel = -1, debugSection = None, indent = 1, comm = None, forceScroll = 0): 272 '''Write the message to the log streams with proper indentation and a newline''' 273 if indent: 274 self.logIndent(debugLevel, debugSection, comm) 275 self.logWrite(msg, debugLevel, debugSection, forceScroll = forceScroll) 276 for writeAll, f in enumerate([self.out, self.log]): 277 if self.checkWrite(f, debugLevel, debugSection, writeAll): 278 if writeAll or self.linewidth < 0: 279 f.write('\n') 280 return 281 282 283 def getRoot(self): 284 '''Return the directory containing this module 285 - This has the problem that when we reload a module of the same name, this gets screwed up 286 Therefore, we call it in the initializer, and stash it''' 287 #print ' In getRoot' 288 #print hasattr(self, '__root') 289 #print ' done checking' 290 if not hasattr(self, '__root'): 291 import os 292 import sys 293 294 # Work around a bug with pdb in 2.3 295 if hasattr(sys.modules[self.__module__], '__file__') and not os.path.basename(sys.modules[self.__module__].__file__) == 'pdb.py': 296 self.__root = os.path.abspath(os.path.dirname(sys.modules[self.__module__].__file__)) 297 else: 298 self.__root = os.getcwd() 299 #print ' Exiting getRoot' 300 return self.__root 301 def setRoot(self, root): 302 self.__root = root 303 return 304 root = property(getRoot, setRoot, doc = 'The directory containing this module') 305