00001
00002
00003
00004
00005
00006 """
00007 Handle NuWa options file inclusion. Files are located through the
00008 JOBOPTPATH envar and globally executed. If requested, files will
00009 be traced. Note, however, that this option interferes with pdb and
00010 trace.
00011 """
00012
00013 import os, sys, re, fnmatch, string
00014 import __main__
00015
00016 from unixtools import FindFile
00017
00018
00019
00020 __version__ = '1.3.0'
00021 __author__ = 'Wim Lavrijsen (WLavrijsen@lbl.gov)'
00022
00023 __all__ = [ 'include', 'marker', 'lineMarker', 'fidMarker',
00024 'callMarker', 'returnMarker', 'activeMarker', 'silentMarker',
00025 'tracedMarker', 'tracePattern' ]
00026
00027 marker = ' -+-'
00028 __marker__ = ' -+-'
00029 silentMarker = ' '
00030 activeMarker = 'A'
00031 tracedMarker = 'T'
00032 callMarker = 'C'
00033 returnMarker = 'R'
00034 lineMarker = '%3d'
00035 fidMarker = '%2d'
00036
00037
00038 excludeTracePattern = [
00039 '*/GaudiPython/*', '*/GaudiKernel/*',
00040 '*/InstallArea/*/DybPython/*',
00041 '*/python%d.%d/*' % sys.version_info[:2],
00042 '*/InstallArea/python/*/*Conf.py' ]
00043
00044
00045 includeTracePattern = [ ]
00046
00047
00048
00049 import logging
00050 log = logging.getLogger( 'NuWa' )
00051
00052 class IncludeError( RuntimeError ):
00053 pass
00054
00055
00056
00057 try:
00058 optionsPathEnv = os.environ[ 'JOBOPTPATH' ]
00059 except:
00060 optionsPathEnv = os.curdir
00061
00062 optionsPath = re.split( ',|' + os.pathsep, optionsPathEnv )
00063 if '' in optionsPath:
00064 optionsPath[ optionsPath.index( '' ) ] = str(os.curdir)
00065
00066
00067
00068 def basename2( fn ):
00069 return os.path.join( os.path.basename( os.path.dirname( fn ) ), os.path.basename( fn ) )
00070
00071 _filecache = {}
00072 _linecache = {}
00073 _notrcache = {}
00074
00075 class Include:
00076 fid = 0
00077
00078 def __init__( self, show = 1 ):
00079 self._show = show
00080 self._once = []
00081 self._fcurrent = ''
00082
00083 def setShowIncludes( self, show ):
00084 self._show = show
00085
00086 def __call__( self, fn, *args, **kw ):
00087 """Include <fn> in the current scope by executing it globally."""
00088
00089
00090 if fn in self._once:
00091 log.debug( 'file "%s" is blocked; not included', fn )
00092 return
00093
00094
00095 name = FindFile( os.path.expanduser( os.path.expandvars( fn ) ), optionsPath, os.R_OK )
00096 if not name:
00097 name = FindFile( os.path.basename( fn ), optionsPath, os.R_OK )
00098 if name:
00099 log.warning( 'using %s instead of %s', name, fn )
00100 else:
00101 raise IncludeError( 'include file %s can not be found' % fn )
00102
00103 log.debug( 'located %s as %s', fn, name )
00104
00105
00106 show = self._show
00107 if 'show' in kw:
00108 show = kw[ 'show' ]
00109
00110
00111 if show:
00112 log.info( 'including file "%s" with ID %d', fn, self.fid )
00113 else:
00114 log.info( 'including file "%s"', fn )
00115 self._fcurrent = name
00116
00117
00118 if show and self._doTrace( name ):
00119
00120 _filecache[ name ] = open( name, 'r' ).readlines()
00121 _linecache[ name ] = 0, self.fid
00122 self.fid += 1
00123
00124 sys.settrace( self._trace_include )
00125 execfile( name, __main__.__dict__, __main__.__dict__ )
00126 sys.settrace( sys._getframe(0).f_trace )
00127
00128
00129 ncur, fid = _linecache[ name ]
00130 buf = _filecache[ name ]
00131 for i in range( ncur, len(buf) ):
00132 self._oneline( fid, i, silentMarker, buf )
00133
00134 del _filecache[ name ]
00135 del _linecache[ name ]
00136
00137 log.info( 'end of "%s"', fn )
00138
00139 else:
00140
00141 execfile( name, __main__.__dict__, __main__.__dict__ )
00142
00143 def block( self, fn ):
00144 """Disallow the given filename(s) from being included again."""
00145
00146 if type(fn) == list:
00147 self._once += fn
00148 else:
00149 self._once.append( fn )
00150
00151 def unblock( self, fn ):
00152 """Re-allow the given filename from being included."""
00153
00154 self._once.remove( fn )
00155
00156
00157 def _doTrace( self, fn ):
00158
00159
00160
00161
00162 if fn in _notrcache:
00163 return False
00164
00165 doTrace = True
00166 for tracePattern in excludeTracePattern:
00167 if fnmatch.fnmatch( fn, tracePattern ):
00168 doTrace = False
00169 break
00170
00171 if not doTrace:
00172 for tracePattern in includeTracePattern:
00173 if fnmatch.fnmatch( fn, tracePattern ):
00174 doTrace = True
00175 break
00176
00177 if not os.path.exists( fn ):
00178 doTrace = False
00179
00180 if not doTrace:
00181 _notrcache[ fn ] = 1
00182
00183 return doTrace
00184
00185
00186 def _trace_include( self, frame, event, arg ):
00187 fn = frame.f_code.co_filename
00188
00189 if not self._doTrace( fn ):
00190 return self._trace_include
00191
00192 if not _filecache.has_key( fn ):
00193
00194 f = frame.f_back
00195 while f is not None:
00196 try:
00197 if 'import' in _filecache[ f.f_code.co_filename ][ f.f_lineno ]:
00198 return self._trace_include
00199 except:
00200 pass
00201 f = f.f_back
00202 del f
00203
00204
00205 _filecache[ fn ] = open( fn, 'r' ).readlines() or '\n'
00206 _linecache[ fn ] = sys.maxint, self.fid
00207 self.fid += 1
00208
00209 lno = frame.f_lineno
00210 aln = lno - 1 > 0 and lno - 1 or 0
00211
00212 ncur, fid = _linecache[ fn ]
00213 buf = _filecache[ fn ]
00214
00215 if self._fcurrent != fn:
00216 log.info( 'continued trace of "%s"', basename2( fn ) )
00217 self._fcurrent = fn
00218
00219 if event == 'line':
00220 for i in range( ncur, aln ):
00221 self._oneline( fid, i, silentMarker, buf )
00222
00223 if ncur <= aln:
00224 self._oneline( fid, aln, activeMarker, buf )
00225 elif 0 <= aln:
00226 self._oneline( fid, aln, tracedMarker, buf )
00227
00228 if ncur < lno:
00229 _linecache[ fn ] = lno, fid
00230
00231 elif event == 'call':
00232 if lno < ncur:
00233 self._oneline( fid, aln, callMarker, buf )
00234
00235 elif event == 'return':
00236 if lno < ncur:
00237 fln = frame.f_code.co_firstlineno - 1
00238 self._oneline( fid, fln, returnMarker, None )
00239
00240 return self._trace_include
00241
00242
00243 def _oneline( self, fid, lineno, detail, buf ):
00244 print marker, fidMarker % fid, lineMarker % lineno, detail,
00245
00246
00247 if not buf or not buf[ lineno ]:
00248 print
00249 return
00250
00251
00252 line = buf[ lineno ].rstrip()
00253 while line and ( line[-1] == '(' or line[-1] == '\\' ):
00254
00255 try:
00256
00257 print line
00258
00259
00260 lineno += 1
00261 print marker, fidMarker % fid, lineMarker % lineno, detail,
00262 line = buf[ lineno ].rstrip()
00263 except IndexError:
00264
00265
00266 break
00267
00268 print line
00269
00270
00271 include = Include()