xref: /petsc/config/BuildSystem/logger.py (revision 7b5fd022a6ba26727040df7457b27566b4c6742d)
15b6bfdb9SJed Brownfrom __future__ import absolute_import
2179860b2SJed Brownimport args
3179860b2SJed Brownimport sys
4179860b2SJed Brownimport os
59561296dSJacob Faibussowitschimport textwrap
6179860b2SJed Brown
7179860b2SJed Brown# Ugly stuff to have curses called ONLY once, instead of for each
8179860b2SJed Brown# new Configure object created (and flashing the screen)
9179860b2SJed Brownglobal LineWidth
10179860b2SJed Brownglobal RemoveDirectory
11179860b2SJed Brownglobal backupRemoveDirectory
12179860b2SJed BrownLineWidth = -1
13179860b2SJed BrownRemoveDirectory = os.path.join(os.getcwd(),'')
14179860b2SJed BrownbackupRemoveDirectory = ''
15179860b2SJed Brown
167eca831cSJacob Faibussowitsch__global_divider_length = 93
177eca831cSJacob Faibussowitsch
187eca831cSJacob Faibussowitschdef get_global_divider_length():
197eca831cSJacob Faibussowitsch  """
207eca831cSJacob Faibussowitsch  Get the divider length for each banner in the form
217eca831cSJacob Faibussowitsch
227eca831cSJacob Faibussowitsch  ==============================... (or ********************...)
237eca831cSJacob Faibussowitsch     FOO BAR
247eca831cSJacob Faibussowitsch  ==============================...
257eca831cSJacob Faibussowitsch  """
267eca831cSJacob Faibussowitsch  return __global_divider_length
277eca831cSJacob Faibussowitsch
287eca831cSJacob Faibussowitschdef set_global_divider_length(new_len):
297eca831cSJacob Faibussowitsch  """
307eca831cSJacob Faibussowitsch  Set the divider length for each banner in the form
317eca831cSJacob Faibussowitsch
327eca831cSJacob Faibussowitsch  ==============================... (or ********************...)
337eca831cSJacob Faibussowitsch     FOO BAR
347eca831cSJacob Faibussowitsch  ==============================...
357eca831cSJacob Faibussowitsch  """
367eca831cSJacob Faibussowitsch  global __global_divider_length
377eca831cSJacob Faibussowitsch  old_len = __global_divider_length
387eca831cSJacob Faibussowitsch  __global_divider_length = new_len
397eca831cSJacob Faibussowitsch  return old_len
407eca831cSJacob Faibussowitsch
417eca831cSJacob Faibussowitschdef build_multiline_message(sup_title, text, divider_char = None, length = None, prefix = None, **kwargs):
427eca831cSJacob Faibussowitsch  def center_line(line):
437eca831cSJacob Faibussowitsch    return line.center(length).rstrip()
447eca831cSJacob Faibussowitsch
457eca831cSJacob Faibussowitsch  if length is None:
467eca831cSJacob Faibussowitsch    length = get_global_divider_length()
477eca831cSJacob Faibussowitsch  if prefix is None:
487eca831cSJacob Faibussowitsch    prefix = ' '*2
497eca831cSJacob Faibussowitsch
507eca831cSJacob Faibussowitsch  kwargs.setdefault('break_on_hyphens',False)
517eca831cSJacob Faibussowitsch  kwargs.setdefault('break_long_words',False)
527eca831cSJacob Faibussowitsch  kwargs.setdefault('width',length-2)
537eca831cSJacob Faibussowitsch  kwargs.setdefault('initial_indent',prefix)
547eca831cSJacob Faibussowitsch  kwargs.setdefault('subsequent_indent',prefix)
557eca831cSJacob Faibussowitsch
567eca831cSJacob Faibussowitsch  wrapped = [
577eca831cSJacob Faibussowitsch    line for para in text.splitlines() for line in textwrap.wrap(textwrap.dedent(para),**kwargs)
587eca831cSJacob Faibussowitsch  ]
597eca831cSJacob Faibussowitsch  if len(wrapped) == 1:
607eca831cSJacob Faibussowitsch    # center-justify single lines, and remove the bogus prefix
617eca831cSJacob Faibussowitsch    wrapped[0] = center_line(wrapped[0].lstrip())
627eca831cSJacob Faibussowitsch  if divider_char:
637eca831cSJacob Faibussowitsch    # add the divider if we are making a message like
647eca831cSJacob Faibussowitsch    #
657eca831cSJacob Faibussowitsch    # =====================
667eca831cSJacob Faibussowitsch    #   BIG SCARY TITLE
677eca831cSJacob Faibussowitsch    # --------------------- <- divider_char is '-'
687eca831cSJacob Faibussowitsch    #   foo bar
697eca831cSJacob Faibussowitsch    divider_char = str(divider_char)
707eca831cSJacob Faibussowitsch    assert len(divider_char) == 1
717eca831cSJacob Faibussowitsch    wrapped.insert(0, divider_char * length)
727eca831cSJacob Faibussowitsch  if sup_title:
737eca831cSJacob Faibussowitsch    # add the super title if we are making a message like
747eca831cSJacob Faibussowitsch    #
757eca831cSJacob Faibussowitsch    # =====================
767eca831cSJacob Faibussowitsch    #   BIG SCARY TITLE     <- sup_title is 'BIG SCARY TITLE'
777eca831cSJacob Faibussowitsch    # ---------------------
787eca831cSJacob Faibussowitsch    #   foo bar
797eca831cSJacob Faibussowitsch    # add the banner
807eca831cSJacob Faibussowitsch    wrapped.insert(0, center_line(str(sup_title)))
817eca831cSJacob Faibussowitsch  return '\n'.join(wrapped)
827eca831cSJacob Faibussowitsch
837eca831cSJacob Faibussowitschdef build_multiline_error_message(sup_title, text, **kwargs):
847eca831cSJacob Faibussowitsch  kwargs.setdefault('divider_char', '-')
857eca831cSJacob Faibussowitsch  kwargs.setdefault('length', get_global_divider_length())
867eca831cSJacob Faibussowitsch
877eca831cSJacob Faibussowitsch  if not text.endswith('\n'):
887eca831cSJacob Faibussowitsch    text += '\n'
897eca831cSJacob Faibussowitsch
907eca831cSJacob Faibussowitsch  banner_line = kwargs['length']*'*'
917eca831cSJacob Faibussowitsch  return '\n'.join([
927eca831cSJacob Faibussowitsch    banner_line,
937eca831cSJacob Faibussowitsch    build_multiline_message(sup_title, text, **kwargs),
947eca831cSJacob Faibussowitsch    banner_line,
957eca831cSJacob Faibussowitsch    '' # to add an additional newline at the end
967eca831cSJacob Faibussowitsch  ])
977eca831cSJacob Faibussowitsch
98179860b2SJed Brownclass Logger(args.ArgumentProcessor):
99179860b2SJed Brown  '''This class creates a shared log and provides methods for writing to it'''
100179860b2SJed Brown  defaultLog = None
101179860b2SJed Brown  defaultOut = sys.stdout
102179860b2SJed Brown
103179860b2SJed Brown  def __init__(self, clArgs = None, argDB = None, log = None, out = defaultOut, debugLevel = None, debugSections = None, debugIndent = None):
104179860b2SJed Brown    args.ArgumentProcessor.__init__(self, clArgs, argDB)
105179860b2SJed Brown    self.logName       = None
106179860b2SJed Brown    self.log           = log
107179860b2SJed Brown    self.out           = out
108179860b2SJed Brown    self.debugLevel    = debugLevel
109179860b2SJed Brown    self.debugSections = debugSections
110179860b2SJed Brown    self.debugIndent   = debugIndent
111179860b2SJed Brown    self.getRoot()
112179860b2SJed Brown    return
113179860b2SJed Brown
114179860b2SJed Brown  def __getstate__(self):
115179860b2SJed Brown    '''We do not want to pickle the default log stream'''
116179860b2SJed Brown    d = args.ArgumentProcessor.__getstate__(self)
117b59d1e59SMatthew G. Knepley    if 'logBkp' in d:
118b59d1e59SMatthew G. Knepley        del d['logBkp']
119179860b2SJed Brown    if 'log' in d:
120179860b2SJed Brown      if d['log'] is Logger.defaultLog:
121179860b2SJed Brown        del d['log']
122179860b2SJed Brown      else:
123179860b2SJed Brown        d['log'] = None
124179860b2SJed Brown    if 'out' in d:
125179860b2SJed Brown      if d['out'] is Logger.defaultOut:
126179860b2SJed Brown        del d['out']
127179860b2SJed Brown      else:
128179860b2SJed Brown        d['out'] = None
129179860b2SJed Brown    return d
130179860b2SJed Brown
131179860b2SJed Brown  def __setstate__(self, d):
132179860b2SJed Brown    '''We must create the default log stream'''
133179860b2SJed Brown    args.ArgumentProcessor.__setstate__(self, d)
134179860b2SJed Brown    if not 'log' in d:
135179860b2SJed Brown      self.log = self.createLog(None)
136179860b2SJed Brown    if not 'out' in d:
137179860b2SJed Brown      self.out = Logger.defaultOut
138179860b2SJed Brown    self.__dict__.update(d)
139179860b2SJed Brown    return
140179860b2SJed Brown
141179860b2SJed Brown  def setupArguments(self, argDB):
142179860b2SJed Brown    '''Setup types in the argument database'''
143179860b2SJed Brown    import nargs
144179860b2SJed Brown
145179860b2SJed Brown    argDB = args.ArgumentProcessor.setupArguments(self, argDB)
14603e6d329SSatish Balay    argDB.setType('log',           nargs.Arg(None, 'buildsystem.log', 'The filename for the log'))
147179860b2SJed Brown    argDB.setType('logAppend',     nargs.ArgBool(None, 0, 'The flag determining whether we backup or append to the current log', isTemporary = 1))
148179860b2SJed Brown    argDB.setType('debugLevel',    nargs.ArgInt(None, 3, 'Integer 0 to 4, where a higher level means more detail', 0, 5))
149179860b2SJed Brown    argDB.setType('debugSections', nargs.Arg(None, [], 'Message types to print, e.g. [compile,link,hg,install]'))
150179860b2SJed Brown    argDB.setType('debugIndent',   nargs.Arg(None, '  ', 'The string used for log indentation'))
151179860b2SJed Brown    argDB.setType('scrollOutput',  nargs.ArgBool(None, 0, 'Flag to allow output to scroll rather than overwriting a single line'))
152179860b2SJed Brown    argDB.setType('noOutput',      nargs.ArgBool(None, 0, 'Flag to suppress output to the terminal'))
153179860b2SJed Brown    return argDB
154179860b2SJed Brown
155179860b2SJed Brown  def setup(self):
156179860b2SJed Brown    '''Setup the terminal output and filtering flags'''
157179860b2SJed Brown    self.log = self.createLog(self.logName, self.log)
158179860b2SJed Brown    args.ArgumentProcessor.setup(self)
159179860b2SJed Brown
160179860b2SJed Brown    if self.argDB['noOutput']:
161179860b2SJed Brown      self.out           = None
162179860b2SJed Brown    if self.debugLevel is None:
163179860b2SJed Brown      self.debugLevel    = self.argDB['debugLevel']
164179860b2SJed Brown    if self.debugSections is None:
165179860b2SJed Brown      self.debugSections = self.argDB['debugSections']
166179860b2SJed Brown    if self.debugIndent is None:
167179860b2SJed Brown      self.debugIndent   = self.argDB['debugIndent']
168179860b2SJed Brown    return
169179860b2SJed Brown
170179860b2SJed Brown  def checkLog(self, logName):
171179860b2SJed Brown    import nargs
172179860b2SJed Brown    import os
173179860b2SJed Brown
174179860b2SJed Brown    if logName is None:
175179860b2SJed Brown      logName = nargs.Arg.findArgument('log', self.clArgs)
176179860b2SJed Brown    if logName is None:
177179860b2SJed Brown      if not self.argDB is None and 'log' in self.argDB:
178179860b2SJed Brown        logName    = self.argDB['log']
179179860b2SJed Brown      else:
180179860b2SJed Brown        logName    = 'default.log'
181179860b2SJed Brown    self.logName   = logName
182179860b2SJed Brown    self.logExists = os.path.exists(self.logName)
183179860b2SJed Brown    return self.logExists
184179860b2SJed Brown
185179860b2SJed Brown  def createLog(self, logName, initLog = None):
186179860b2SJed Brown    '''Create a default log stream, unless initLog is given'''
187179860b2SJed Brown    import nargs
188179860b2SJed Brown
189179860b2SJed Brown    if not initLog is None:
190179860b2SJed Brown      log = initLog
191179860b2SJed Brown    else:
192179860b2SJed Brown      if Logger.defaultLog is None:
193179860b2SJed Brown        appendArg = nargs.Arg.findArgument('logAppend', self.clArgs)
194179860b2SJed Brown        if self.checkLog(logName):
195179860b2SJed Brown          if not self.argDB is None and ('logAppend' in self.argDB and self.argDB['logAppend']) or (not appendArg is None and bool(appendArg)):
196c6ef1b5bSJed Brown            Logger.defaultLog = open(self.logName, 'a')
197179860b2SJed Brown          else:
198179860b2SJed Brown            try:
199179860b2SJed Brown              import os
200179860b2SJed Brown
201179860b2SJed Brown              os.rename(self.logName, self.logName+'.bkp')
202c6ef1b5bSJed Brown              Logger.defaultLog = open(self.logName, 'w')
203179860b2SJed Brown            except OSError:
20415ac2963SJed Brown              sys.stdout.write('WARNING: Cannot backup log file, appending instead.\n')
205c6ef1b5bSJed Brown              Logger.defaultLog = open(self.logName, 'a')
206179860b2SJed Brown        else:
207c6ef1b5bSJed Brown          Logger.defaultLog = open(self.logName, 'w')
208179860b2SJed Brown      log = Logger.defaultLog
209179860b2SJed Brown    return log
210179860b2SJed Brown
211179860b2SJed Brown  def closeLog(self):
212179860b2SJed Brown    '''Closes the log file'''
213179860b2SJed Brown    self.log.close()
214179860b2SJed Brown
215a75b4e77SMatthew G. Knepley  def saveLog(self):
216dc0f114dSBarry Smith    if self.debugLevel <= 3: return
2172d964b9fSJed Brown    import io
218a75b4e77SMatthew G. Knepley    self.logBkp = self.log
2192d964b9fSJed Brown    self.log = io.StringIO()
220a75b4e77SMatthew G. Knepley
221a75b4e77SMatthew G. Knepley  def restoreLog(self):
222dc0f114dSBarry Smith    if self.debugLevel <= 3: return
223a75b4e77SMatthew G. Knepley    s = self.log.getvalue()
224a75b4e77SMatthew G. Knepley    self.log.close()
225a75b4e77SMatthew G. Knepley    self.log = self.logBkp
226a75b4e77SMatthew G. Knepley    del(self.logBkp)
227a75b4e77SMatthew G. Knepley    return s
228a75b4e77SMatthew G. Knepley
229179860b2SJed Brown  def getLinewidth(self):
230179860b2SJed Brown    global LineWidth
231179860b2SJed Brown    if not hasattr(self, '_linewidth'):
232179860b2SJed Brown      if self.out is None or not self.out.isatty() or self.argDB['scrollOutput']:
233179860b2SJed Brown        self._linewidth = -1
234179860b2SJed Brown      else:
235179860b2SJed Brown        if LineWidth == -1:
236179860b2SJed Brown          try:
237179860b2SJed Brown            import curses
238179860b2SJed Brown
239179860b2SJed Brown            try:
240179860b2SJed Brown              curses.setupterm()
241179860b2SJed Brown              (y, self._linewidth) = curses.initscr().getmaxyx()
242179860b2SJed Brown              curses.endwin()
243179860b2SJed Brown            except curses.error:
244179860b2SJed Brown              self._linewidth = -1
245179860b2SJed Brown          except:
246179860b2SJed Brown            self._linewidth = -1
247179860b2SJed Brown          LineWidth = self._linewidth
248179860b2SJed Brown        else:
249179860b2SJed Brown          self._linewidth = LineWidth
250179860b2SJed Brown    return self._linewidth
251179860b2SJed Brown  def setLinewidth(self, linewidth):
252179860b2SJed Brown    self._linewidth = linewidth
253179860b2SJed Brown    return
254179860b2SJed Brown  linewidth = property(getLinewidth, setLinewidth, doc = 'The maximum number of characters per log line')
255179860b2SJed Brown
256179860b2SJed Brown  def checkWrite(self, f, debugLevel, debugSection, writeAll = 0):
257179860b2SJed Brown    '''Check whether the log line should be written
258179860b2SJed Brown       - If writeAll is true, return true
259179860b2SJed Brown       - If debugLevel >= current level, and debugSection in current section or sections is empty, return true'''
260179860b2SJed Brown    if not isinstance(debugLevel, int):
261179860b2SJed Brown      raise RuntimeError('Debug level must be an integer: '+str(debugLevel))
262179860b2SJed Brown    if f is None:
263179860b2SJed Brown      return False
264179860b2SJed Brown    if writeAll:
265179860b2SJed Brown      return True
266179860b2SJed Brown    if self.debugLevel >= debugLevel and (not len(self.debugSections) or debugSection in self.debugSections):
267179860b2SJed Brown      return True
268179860b2SJed Brown    return False
269179860b2SJed Brown
270ce040abeSJacob Faibussowitsch  def checkANSIEscapeSequences(self, ostream):
271ce040abeSJacob Faibussowitsch    """
272ce040abeSJacob Faibussowitsch    Return True if the stream supports ANSI escape sequences, False otherwise
273ce040abeSJacob Faibussowitsch    """
274ce040abeSJacob Faibussowitsch    try:
275ce040abeSJacob Faibussowitsch      # _io.TextIoWrapper use 'name' attribute to store the file name
276ce040abeSJacob Faibussowitsch      key = ostream.name
277ce040abeSJacob Faibussowitsch    except AttributeError:
278ce040abeSJacob Faibussowitsch      return False
279ce040abeSJacob Faibussowitsch
280ce040abeSJacob Faibussowitsch    try:
281ce040abeSJacob Faibussowitsch      return self._ansi_esc_seq_cache[key]
282ce040abeSJacob Faibussowitsch    except KeyError:
283ce040abeSJacob Faibussowitsch      pass # have not processed this stream before
284ce040abeSJacob Faibussowitsch    except AttributeError:
285ce040abeSJacob Faibussowitsch      # have never done this before
286ce040abeSJacob Faibussowitsch      self._ansi_esc_seq_cache = {}
287ce040abeSJacob Faibussowitsch
288ce040abeSJacob Faibussowitsch    is_a_tty = hasattr(ostream,'isatty') and ostream.isatty()
289ce040abeSJacob Faibussowitsch    return self._ansi_esc_seq_cache.setdefault(key,is_a_tty and (
290ce040abeSJacob Faibussowitsch      sys.platform != 'win32' or os.environ.get('TERM','').startswith(('xterm','ANSI')) or
291ce040abeSJacob Faibussowitsch      # Windows Terminal supports VT codes.
292ce040abeSJacob Faibussowitsch      'WT_SESSION' in os.environ or
293ce040abeSJacob Faibussowitsch      # Microsoft Visual Studio Code's built-in terminal supports colors.
294ce040abeSJacob Faibussowitsch      os.environ.get('TERM_PROGRAM') == 'vscode'
295ce040abeSJacob Faibussowitsch    ))
296ce040abeSJacob Faibussowitsch
297179860b2SJed Brown  def logIndent(self, debugLevel = -1, debugSection = None, comm = None):
298179860b2SJed Brown    '''Write the proper indentation to the log streams'''
299179860b2SJed Brown    import traceback
300179860b2SJed Brown
301179860b2SJed Brown    indentLevel = len(traceback.extract_stack())-5
302179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
303179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
304179860b2SJed Brown        if not comm is None:
305179860b2SJed Brown          f.write('[')
306179860b2SJed Brown          f.write(str(comm.rank()))
307179860b2SJed Brown          f.write(']')
308179860b2SJed Brown        for i in range(indentLevel):
309179860b2SJed Brown          f.write(self.debugIndent)
310179860b2SJed Brown    return
311179860b2SJed Brown
312179860b2SJed Brown  def logBack(self):
313179860b2SJed Brown    '''Backup the current line if we are not scrolling output'''
314d1b3ee28SJacob Faibussowitsch    if self.out is not None and self.linewidth > 0:
315179860b2SJed Brown      self.out.write('\r')
316179860b2SJed Brown    return
317179860b2SJed Brown
318179860b2SJed Brown  def logClear(self):
319179860b2SJed Brown    '''Clear the current line if we are not scrolling output'''
320ce040abeSJacob Faibussowitsch    out,lw = self.out,self.linewidth
321ce040abeSJacob Faibussowitsch    if out is not None and lw > 0:
322ce040abeSJacob Faibussowitsch      out.write('\r\033[K' if self.checkANSIEscapeSequences(out) else ' '*lw)
323ce040abeSJacob Faibussowitsch      try:
324ce040abeSJacob Faibussowitsch        out.flush()
325ce040abeSJacob Faibussowitsch      except AttributeError:
326ce040abeSJacob Faibussowitsch        pass
327179860b2SJed Brown    return
328179860b2SJed Brown
329ce040abeSJacob Faibussowitsch  def logPrintDivider(self, single = False, length = None, **kwargs):
330ce040abeSJacob Faibussowitsch    if length is None:
3317eca831cSJacob Faibussowitsch      length = get_global_divider_length()
332ce040abeSJacob Faibussowitsch    kwargs.setdefault('rmDir',False)
333ce040abeSJacob Faibussowitsch    kwargs.setdefault('indent',False)
334ce040abeSJacob Faibussowitsch    kwargs.setdefault('forceScroll',False)
335ce040abeSJacob Faibussowitsch    kwargs.setdefault('forceNewLine',True)
336ce040abeSJacob Faibussowitsch    divider = ('-' if single else '=')*length
337d1b3ee28SJacob Faibussowitsch    return self.logPrint(divider, **kwargs)
338179860b2SJed Brown
339d1b3ee28SJacob Faibussowitsch  def logPrintWarning(self, msg, title = None, **kwargs):
340d1b3ee28SJacob Faibussowitsch    if title is None:
341d1b3ee28SJacob Faibussowitsch      title = 'WARNING'
342d1b3ee28SJacob Faibussowitsch    return self.logPrintBox(msg,title='***** {} *****'.format(title),**kwargs)
343d1b3ee28SJacob Faibussowitsch
344d1b3ee28SJacob Faibussowitsch  def logPrintBox(self, msg, debugLevel = -1, debugSection = 'screen', indent = 1, comm = None, rmDir = 1, prefix = None, title = None):
345d1b3ee28SJacob Faibussowitsch    if rmDir:
3467eca831cSJacob Faibussowitsch      rmDir = build_multiline_message(title, self.logStripDirectory(msg), prefix=prefix)
3477eca831cSJacob Faibussowitsch    msg = build_multiline_message(title, msg, prefix=prefix)
348179860b2SJed Brown    self.logClear()
349ce040abeSJacob Faibussowitsch    self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection)
350ce040abeSJacob Faibussowitsch    self.logPrint(msg, debugLevel = debugLevel, debugSection = debugSection, rmDir = rmDir, forceNewLine = True, forceScroll = True, indent = 0)
351ce040abeSJacob Faibussowitsch    self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection)
352179860b2SJed Brown    return
353179860b2SJed Brown
354d1b3ee28SJacob Faibussowitsch  def logStripDirectory(self,msg):
355d1b3ee28SJacob Faibussowitsch    return msg.replace(RemoveDirectory,'')
356d1b3ee28SJacob Faibussowitsch
357179860b2SJed Brown  def logClearRemoveDirectory(self):
358179860b2SJed Brown    global RemoveDirectory
359179860b2SJed Brown    global backupRemoveDirectory
360179860b2SJed Brown    backupRemoveDirectory = RemoveDirectory
361179860b2SJed Brown    RemoveDirectory = ''
362179860b2SJed Brown
363179860b2SJed Brown  def logResetRemoveDirectory(self):
364179860b2SJed Brown    global RemoveDirectory
365179860b2SJed Brown    global backupRemoveDirectory
366179860b2SJed Brown    RemoveDirectory = backupRemoveDirectory
367179860b2SJed Brown
368d15db81bSBarry Smith  def logWrite(self, msg, debugLevel = -1, debugSection = None, forceScroll = 0, rmDir = 1):
369179860b2SJed Brown    '''Write the message to the log streams'''
370b5f71184SBarry Smith    '''Generally goes to the file but not the screen'''
371dc0f114dSBarry Smith    if not msg: return
372179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
373179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
374d1b3ee28SJacob Faibussowitsch        if rmDir:
375d1b3ee28SJacob Faibussowitsch          if isinstance(rmDir,str):
376ce040abeSJacob Faibussowitsch            clean_msg = rmDir
377d1b3ee28SJacob Faibussowitsch          else:
378ce040abeSJacob Faibussowitsch            clean_msg = self.logStripDirectory(msg)
379ce040abeSJacob Faibussowitsch        else:
380ce040abeSJacob Faibussowitsch          clean_msg = msg
381ce040abeSJacob Faibussowitsch        if not forceScroll and not writeAll and self.linewidth > 0:
382ce040abeSJacob Faibussowitsch          self.logClear()
383ce040abeSJacob Faibussowitsch          for ms in clean_msg.splitlines():
384d1b3ee28SJacob Faibussowitsch            f.write(ms[:self.linewidth])
385179860b2SJed Brown        else:
386*0e061800SBarry Smith          if writeAll or not msg.startswith('TESTING:') or f.isatty():
387179860b2SJed Brown            if not debugSection is None and not debugSection == 'screen' and len(msg):
388179860b2SJed Brown              f.write(str(debugSection))
389179860b2SJed Brown              f.write(': ')
390ce040abeSJacob Faibussowitsch            f.write(msg if writeAll else clean_msg)
391179860b2SJed Brown        if hasattr(f, 'flush'):
392179860b2SJed Brown          f.flush()
393179860b2SJed Brown    return
394179860b2SJed Brown
395d1b3ee28SJacob Faibussowitsch  def logPrint(self, msg, debugLevel = -1, debugSection = None, indent = 1, comm = None, forceScroll = 0, rmDir = 1, forceNewLine = False):
396179860b2SJed Brown    '''Write the message to the log streams with proper indentation and a newline'''
397b5f71184SBarry Smith    '''Generally goes to the file and the screen'''
398179860b2SJed Brown    if indent:
399179860b2SJed Brown      self.logIndent(debugLevel, debugSection, comm)
400d15db81bSBarry Smith    self.logWrite(msg, debugLevel, debugSection, forceScroll = forceScroll, rmDir = rmDir)
401179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
402179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
403*0e061800SBarry Smith        if forceNewLine or writeAll:
404179860b2SJed Brown          f.write('\n')
405179860b2SJed Brown    return
406179860b2SJed Brown
407179860b2SJed Brown  def getRoot(self):
408179860b2SJed Brown    '''Return the directory containing this module
409179860b2SJed Brown       - This has the problem that when we reload a module of the same name, this gets screwed up
410179860b2SJed Brown         Therefore, we call it in the initializer, and stash it'''
411179860b2SJed Brown    #print '      In getRoot'
412179860b2SJed Brown    #print hasattr(self, '__root')
413179860b2SJed Brown    #print '      done checking'
414179860b2SJed Brown    if not hasattr(self, '__root'):
415179860b2SJed Brown      import os
416179860b2SJed Brown      import sys
417179860b2SJed Brown
418179860b2SJed Brown      # Work around a bug with pdb in 2.3
419179860b2SJed Brown      if hasattr(sys.modules[self.__module__], '__file__') and not os.path.basename(sys.modules[self.__module__].__file__) == 'pdb.py':
420179860b2SJed Brown        self.__root = os.path.abspath(os.path.dirname(sys.modules[self.__module__].__file__))
421179860b2SJed Brown      else:
422179860b2SJed Brown        self.__root = os.getcwd()
423179860b2SJed Brown    #print '      Exiting getRoot'
424179860b2SJed Brown    return self.__root
425179860b2SJed Brown  def setRoot(self, root):
426179860b2SJed Brown    self.__root = root
427179860b2SJed Brown    return
428179860b2SJed Brown  root = property(getRoot, setRoot, doc = 'The directory containing this module')
429