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