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 getLinewidth(self): 141 global LineWidth 142 if not hasattr(self, '_linewidth'): 143 if self.out is None or not self.out.isatty() or self.argDB['scrollOutput']: 144 self._linewidth = -1 145 else: 146 if LineWidth == -1: 147 try: 148 import curses 149 150 try: 151 curses.setupterm() 152 (y, self._linewidth) = curses.initscr().getmaxyx() 153 curses.endwin() 154 except curses.error: 155 self._linewidth = -1 156 except: 157 self._linewidth = -1 158 LineWidth = self._linewidth 159 else: 160 self._linewidth = LineWidth 161 return self._linewidth 162 def setLinewidth(self, linewidth): 163 self._linewidth = linewidth 164 return 165 linewidth = property(getLinewidth, setLinewidth, doc = 'The maximum number of characters per log line') 166 167 def checkWrite(self, f, debugLevel, debugSection, writeAll = 0): 168 '''Check whether the log line should be written 169 - If writeAll is true, return true 170 - If debugLevel >= current level, and debugSection in current section or sections is empty, return true''' 171 if not isinstance(debugLevel, int): 172 raise RuntimeError('Debug level must be an integer: '+str(debugLevel)) 173 if f is None: 174 return False 175 if writeAll: 176 return True 177 if self.debugLevel >= debugLevel and (not len(self.debugSections) or debugSection in self.debugSections): 178 return True 179 return False 180 181 def logIndent(self, debugLevel = -1, debugSection = None, comm = None): 182 '''Write the proper indentation to the log streams''' 183 import traceback 184 185 indentLevel = len(traceback.extract_stack())-5 186 for writeAll, f in enumerate([self.out, self.log]): 187 if self.checkWrite(f, debugLevel, debugSection, writeAll): 188 if not comm is None: 189 f.write('[') 190 f.write(str(comm.rank())) 191 f.write(']') 192 for i in range(indentLevel): 193 f.write(self.debugIndent) 194 return 195 196 def logBack(self): 197 '''Backup the current line if we are not scrolling output''' 198 if not self.out is None and self.linewidth > 0: 199 self.out.write('\r') 200 return 201 202 def logClear(self): 203 '''Clear the current line if we are not scrolling output''' 204 if not self.out is None and self.linewidth > 0: 205 self.out.write('\r') 206 self.out.write(''.join([' '] * self.linewidth)) 207 self.out.write('\r') 208 return 209 210 def logPrintDivider(self, debugLevel = -1, debugSection = None, single = 0): 211 if single: 212 self.logPrint('-------------------------------------------------------------------------------', debugLevel = debugLevel, debugSection = debugSection) 213 else: 214 self.logPrint('===============================================================================', debugLevel = debugLevel, debugSection = debugSection) 215 return 216 217 def logPrintBox(self,msg, debugLevel = -1, debugSection = 'screen', indent = 1, comm = None): 218 self.logClear() 219 self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection) 220 [self.logPrint(' '+line, debugLevel = debugLevel, debugSection = debugSection) for line in msg.split('\n')] 221 self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection) 222 self.logPrint('', debugLevel = debugLevel, debugSection = debugSection) 223 return 224 225 def logClearRemoveDirectory(self): 226 global RemoveDirectory 227 global backupRemoveDirectory 228 backupRemoveDirectory = RemoveDirectory 229 RemoveDirectory = '' 230 231 def logResetRemoveDirectory(self): 232 global RemoveDirectory 233 global backupRemoveDirectory 234 RemoveDirectory = backupRemoveDirectory 235 236 237 def logWrite(self, msg, debugLevel = -1, debugSection = None, forceScroll = 0): 238 '''Write the message to the log streams''' 239 for writeAll, f in enumerate([self.out, self.log]): 240 if self.checkWrite(f, debugLevel, debugSection, writeAll): 241 if not forceScroll and not writeAll and self.linewidth > 0: 242 global RemoveDirectory 243 self.logBack() 244 msg = msg.replace(RemoveDirectory,'') 245 for ms in msg.split('\n'): 246 f.write(ms[0:self.linewidth]) 247 f.write(''.join([' '] * (self.linewidth - len(ms)))) 248 else: 249 if not debugSection is None and not debugSection == 'screen' and len(msg): 250 f.write(str(debugSection)) 251 f.write(': ') 252 f.write(msg) 253 if hasattr(f, 'flush'): 254 f.flush() 255 return 256 257 def logPrint(self, msg, debugLevel = -1, debugSection = None, indent = 1, comm = None, forceScroll = 0): 258 '''Write the message to the log streams with proper indentation and a newline''' 259 if indent: 260 self.logIndent(debugLevel, debugSection, comm) 261 self.logWrite(msg, debugLevel, debugSection, forceScroll = forceScroll) 262 for writeAll, f in enumerate([self.out, self.log]): 263 if self.checkWrite(f, debugLevel, debugSection, writeAll): 264 if writeAll or self.linewidth < 0: 265 f.write('\n') 266 return 267 268 269 def getRoot(self): 270 '''Return the directory containing this module 271 - This has the problem that when we reload a module of the same name, this gets screwed up 272 Therefore, we call it in the initializer, and stash it''' 273 #print ' In getRoot' 274 #print hasattr(self, '__root') 275 #print ' done checking' 276 if not hasattr(self, '__root'): 277 import os 278 import sys 279 280 # Work around a bug with pdb in 2.3 281 if hasattr(sys.modules[self.__module__], '__file__') and not os.path.basename(sys.modules[self.__module__].__file__) == 'pdb.py': 282 self.__root = os.path.abspath(os.path.dirname(sys.modules[self.__module__].__file__)) 283 else: 284 self.__root = os.getcwd() 285 #print ' Exiting getRoot' 286 return self.__root 287 def setRoot(self, root): 288 self.__root = root 289 return 290 root = property(getRoot, setRoot, doc = 'The directory containing this module') 291