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