ATLAS Offline Software
Include.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # File: AthenaCommon/python/Include.py
4 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
5 
6 """Handle Athena options file inclusion. Files are located through the
7 JOBOPTSEARCHPATH envar and globally executed. If requested, files will be
8 traced. Note, however, that this option interferes with pdb and trace."""
9 
10 import os, sys, re, fnmatch
11 from AthenaCommon.Utils.unixtools import FindFile
12 
13 
14 __version__ = '1.3.0'
15 __author__ = 'Wim Lavrijsen (WLavrijsen@lbl.gov)'
16 
17 __all__ = [ 'include', 'marker', 'lineMarker', 'fidMarker',
18  'callMarker', 'returnMarker', 'activeMarker', 'silentMarker',
19  'tracedMarker' ]
20 
21 marker = ' -+-'
22 __marker__ = ' -+-'
23 silentMarker = ' '
24 activeMarker = 'A'
25 tracedMarker = 'T'
26 callMarker = 'C'
27 returnMarker = 'R'
28 lineMarker = '%3d'
29 fidMarker = '%2d'
30 
31 # do not trace any files that fnmatch these names:
32 excludeTracePattern = [
33  '*/GaudiPython/*', '*/GaudiKernel/*', # Gaudi framework files
34  '*/AthenaCommon/*', # Athena framework files
35  '*/python%d.%d/*' % sys.version_info[:2], # python system
36  '*/python/*/*Conf.py', # generated conf files
37  '*/PyUtils/decorator.py', # very verbose
38  '*/PyUtils/Decorators.py', # ditto
39  '*/PyUtils/Helper*.py', # ditto
40  '*/lib/ROOT.py', # ROOT import hook gets executed very many times
41  '*importlib._bootstrap*',
42  ]
43 
44 # unless they are explicitly included here:
45 includeTracePattern = [ '*/AthenaCommon/Bootstrap.py' ]
46 
47 
48 
49 from AthenaCommon import Logging
50 log = Logging.logging.getLogger( 'Athena' )
51 
52 class IncludeError( RuntimeError ):
53  pass
54 
55 
56 
57 try:
58  optionsPathEnv = os.environ[ 'JOBOPTSEARCHPATH' ]
59 except Exception:
60  optionsPathEnv = os.curdir
61 
62 optionsPath = re.split( ',|' + os.pathsep, optionsPathEnv )
63 if '' in optionsPath:
64  optionsPath[ optionsPath.index( '' ) ] = str(os.curdir)
65 
66 
67 
68 def basename2( fn ):
69  return os.path.join( os.path.basename( os.path.dirname( fn ) ), os.path.basename( fn ) )
70 
71 _filecache = {}
72 _linecache = {}
73 _notrcache = {}
74 
75 class Include( object ):
76  fid = 0
77 
78  def __init__( self, show = True, collect = 1, clean = False ):
79  self._show = show
80  self._once = []
81  self._fcurrent = ''
82  self.msg = Logging.logging.getLogger( 'Athena' )
83 
84  if clean:
85  self._workspace = {}
86  else:
87  import __main__
88  self._workspace = __main__.__dict__
89 
90  if collect:
91  self._collect = 1
92 
93  def setShowIncludes( self, show ):
94  self._show = show
95 
96  def setCollect( self, collect ):
97  if collect and not hasattr( self, '_collect' ):
98  self._collect = 1
99  elif not collect and hasattr( self, '_collect' ):
100  del self._collect
101 
102  def setClean( self, clean ):
103  import __main__
104  if clean:
105  if self._workspace is __main__.__dict__:
106  self._workspace = {}
107  self._workspace.update( __main__.__dict__ )
108  else:
109  self._workspace = __main__.__dict__
110 
111  def __call__( self, fn, *args, **kw ):
112  """Include <fn> in the current scope by executing it globally."""
113 
114  # some basic sanity check first
115  if isinstance(fn, str) and len(fn) == 0:
116  raise IncludeError("can not 'include' empty filenames")
117 
118  # don't include file if not allowed (has to be exact match in py code)
119  if fn in self._once:
120  self.msg.debug( 'file "%s" is blocked; not included', fn )
121  return
122 
123  # locate the file
124  name = FindFile( os.path.expanduser( os.path.expandvars( fn ) ), optionsPath, os.R_OK )
125  if not name:
126  name = FindFile( os.path.basename( fn ), optionsPath, os.R_OK )
127  if name:
128  self.msg.warning( 'using %s instead of %s', name, fn )
129  else:
130  raise IncludeError( 'include file %s can not be found' % fn )
131 
132  self.msg.debug( 'located %s as %s', fn, name )
133 
134  # print if 'show' is set to non-null
135  show = self._show
136  if 'show' in kw:
137  show = kw[ 'show' ]
138 
139  # notify of start of file inclusion
140  if show:
141  self.msg.info( 'including file "%s" with ID %d', fn, self.fid )
142  else:
143  self.msg.info( 'including file "%s"', fn )
144  self._fcurrent = name
145 
146  # actual inclusion
147  if show and self._doTrace( name ):
148  # traced
149  _filecache[ name ] = open( name, 'r' ).readlines()
150  _linecache[ name ] = 0, self.fid
151  self.fid += 1
152 
153  from past.builtins import execfile
154  sys.settrace( self._trace_include )
155  execfile( name, self._workspace, self._workspace )
156  sys.settrace( sys._getframe(0).f_trace )
157 
158  # finish file printout
159  ncur, fid = _linecache[ name ]
160  buf = _filecache[ name ]
161  for i in range( ncur, len(buf) ):
162  self._oneline( fid, i, silentMarker, buf )
163 
164  del _filecache[ name ]
165  del _linecache[ name ]
166 
167  self.msg.info( 'end of "%s"', fn )
168 
169  else:
170  # non-traced
171  #execfile( name, self._workspace, self._workspace )
172  exec(compile(open(name).read(), name, 'exec'), self._workspace, self._workspace)
173 
174 
175  if hasattr( self, '_collect' ):
176  if not self._collect % 10:
177  import gc
178  gc.collect()
179  else:
180  self._collect += 1
181 
182  def block( self, fn ):
183  """Disallow the given filename(s) from being included again."""
184 
185  if type(fn) is list:
186  self._once += fn
187  else:
188  self._once.append( fn )
189 
190  def unblock( self, fn ):
191  """Re-allow the given filename from being included."""
192 
193  self._once.remove( fn )
194 
195  # match files to trace ------------------------------------------------------
196  def _doTrace( self, fn ):
197  # Tracing into non-included files is controlled with two variables:
198  # excludeTracePattern and includeTracePattern. The former goes first,
199  # the latter can override any excluded results.
200 
201  if fn in _notrcache:
202  return False
203 
204  doTrace = True
205  for tracePattern in excludeTracePattern:
206  if fnmatch.fnmatch( fn, tracePattern ):
207  doTrace = False
208  break
209 
210  if not doTrace:
211  for tracePattern in includeTracePattern:
212  if fnmatch.fnmatch( fn, tracePattern ):
213  doTrace = True
214  break
215 
216  if not doTrace:
217  _notrcache[ fn ] = 1
218 
219  return doTrace
220 
221  # code tracer ---------------------------------------------------------------
222  def _trace_include( self, frame, event, arg ):
223  fn = frame.f_code.co_filename
224  if fn.find ('importlib._bootstrap') >= 0:
225  return self._trace_include
226 
227  if not os.path.exists( fn ):
228  fn = FindFile( basename2( fn ), sys.path, os.R_OK )
229 
230  if not ( fn and self._doTrace( fn ) ):
231  return self._trace_include
232 
233  if fn not in _filecache:
234  # wait until importing of the module is done to minimize pollution
235  f = frame.f_back
236  while f is not None:
237  try:
238  if 'import' in _filecache[ f.f_code.co_filename ][ f.f_lineno ]:
239  return self._trace_include
240  except Exception:
241  pass
242  f = f.f_back
243  del f
244 
245  # import is done, and we're back, accept this file from this point on
246  _filecache[ fn ] = open( fn, 'r' ).readlines() or '\n'
247  _linecache[ fn ] = sys.maxsize, self.fid
248  self.fid += 1
249 
250  lno = frame.f_lineno
251  aln = lno - 1 > 0 and lno - 1 or 0
252 
253  ncur, fid = _linecache[ fn ]
254  buf = _filecache[ fn ]
255 
256  if self._fcurrent != fn:
257  self.msg.info( 'continued trace of "%s"', basename2( fn ) )
258  self._fcurrent = fn
259 
260  if event == 'line':
261  for i in range( ncur, aln ):
262  self._oneline( fid, i, silentMarker, buf )
263 
264  if ncur <= aln:
265  self._oneline( fid, aln, activeMarker, buf )
266  elif 0 <= aln:
267  self._oneline( fid, aln, tracedMarker, buf )
268 
269  if ncur < lno:
270  _linecache[ fn ] = lno, fid
271 
272  elif event == 'call':
273  if lno < ncur:
274  self._oneline( fid, aln, callMarker, buf )
275 
276  elif event == 'return':
277  if lno < ncur:
278  fln = frame.f_code.co_firstlineno - 1
279  self._oneline( fid, fln, returnMarker, None )
280 
281  return self._trace_include
282 
283  # formatted line printer
284  def _oneline( self, fid, lineno, detail, buf ):
285  print (marker, fidMarker % fid, lineMarker % lineno, detail,)
286 
287  try:
288 
289  # simple eol case
290  if not buf or not buf[ lineno ]:
291  print()
292  return
293 
294  # in general, an interpreter "line" may be longer than a file line
295  line = buf[ lineno ].rstrip()
296  while line and ( line[-1] == '(' or line[-1] == '\\' ):
297  # this line appears to have a continuation ...
298  try:
299  # output traced line
300  print (line)
301 
302  # output continued line
303  lineno += 1
304  print (marker, fidMarker % fid, lineMarker % lineno, detail,)
305  line = buf[ lineno ].rstrip()
306  except IndexError:
307  # shouldn't happen; but must mean that the diagnosis above is
308  # wrong and that there is no continuation, keep silent
309  break
310 
311  print (line)
312 
313  except IndexError:
314  log.warning( 'index (%d) out of range while scanning include file %d', lineno, fid )
315 
316 
317 # use this for backward compatibility
318 include = Include(show = False)
grepfile.info
info
Definition: grepfile.py:38
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition: openCoraCool.cxx:569
python.Include.Include.block
def block(self, fn)
Definition: Include.py:182
python.Include.Include._collect
_collect
Definition: Include.py:91
python.Include.Include._show
_show
Definition: Include.py:79
python.Include.Include._workspace
_workspace
Definition: Include.py:85
python.Include.IncludeError
Definition: Include.py:52
python.Include.Include._doTrace
def _doTrace(self, fn)
Definition: Include.py:196
python.Include.Include.setShowIncludes
def setShowIncludes(self, show)
Definition: Include.py:93
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.Include.basename2
def basename2(fn)
files inclusion -------------------------------------------------------—
Definition: Include.py:68
PixelModuleFeMask_create_db.remove
string remove
Definition: PixelModuleFeMask_create_db.py:83
python.Include.Include.__init__
def __init__(self, show=True, collect=1, clean=False)
Definition: Include.py:78
python.Include.Include.msg
msg
Definition: Include.py:82
python.Include.Include
Definition: Include.py:75
python.Utils.unixtools.FindFile
def FindFile(filename, pathlist, access)
helper -------------------------------------------------------------------—
Definition: unixtools.py:20
LArG4FSStartPointFilter.exec
exec
Definition: LArG4FSStartPointFilter.py:103
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
python.Include.Include._fcurrent
_fcurrent
Definition: Include.py:81
debug
const bool debug
Definition: MakeUncertaintyPlots.cxx:53
python.Include.Include._trace_include
def _trace_include(self, frame, event, arg)
Definition: Include.py:222
python.Include.Include.fid
int fid
Definition: Include.py:76
python.Include.Include._once
_once
Definition: Include.py:80
Trk::open
@ open
Definition: BinningType.h:40
python.Include.Include.unblock
def unblock(self, fn)
Definition: Include.py:190
python.Include.Include.__call__
def __call__(self, fn, *args, **kw)
Definition: Include.py:111
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
python.Include.Include._oneline
def _oneline(self, fid, lineno, detail, buf)
Definition: Include.py:284
pickleTool.object
object
Definition: pickleTool.py:30
python.Include.Include.setClean
def setClean(self, clean)
Definition: Include.py:102
str
Definition: BTagTrackIpAccessor.cxx:11
python.Include.Include.setCollect
def setCollect(self, collect)
Definition: Include.py:96
dbg::print
void print(std::FILE *stream, std::format_string< Args... > fmt, Args &&... args)
Definition: SGImplSvc.cxx:70
WriteBchToCool.update
update
Definition: WriteBchToCool.py:67