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