ATLAS Offline Software
Loading...
Searching...
No Matches
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
7JOBOPTSEARCHPATH envar and globally executed. If requested, files will be
8traced. Note, however, that this option interferes with pdb and trace."""
9
10import os, sys, re, fnmatch
11from 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
21marker = ' -+-'
22__marker__ = ' -+-'
23silentMarker = ' '
24activeMarker = 'A'
25tracedMarker = 'T'
26callMarker = 'C'
27returnMarker = 'R'
28lineMarker = '%3d'
29fidMarker = '%2d'
30
31# do not trace any files that fnmatch these names:
32excludeTracePattern = [
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:
45includeTracePattern = [ '*/AthenaCommon/Bootstrap.py' ]
46
47
48
49from AthenaCommon import Logging
50log = Logging.logging.getLogger( 'Athena' )
51
52class IncludeError( RuntimeError ):
53 pass
54
55
56
57try:
58 optionsPathEnv = os.environ[ 'JOBOPTSEARCHPATH' ]
59except Exception:
60 optionsPathEnv = os.curdir
61
62optionsPath = re.split( ',|' + os.pathsep, optionsPathEnv )
63if '' in optionsPath:
64 optionsPath[ optionsPath.index( '' ) ] = str(os.curdir)
65
66
67
68def 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
75class 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
318include = Include(show = False)
const bool debug
void print(char *figname, TCanvas *c1)
setClean(self, clean)
Definition Include.py:102
__call__(self, fn, *args, **kw)
Definition Include.py:111
__init__(self, show=True, collect=1, clean=False)
Definition Include.py:78
setShowIncludes(self, show)
Definition Include.py:93
setCollect(self, collect)
Definition Include.py:96
_oneline(self, fid, lineno, detail, buf)
Definition Include.py:284
basename2(fn)
files inclusion -------------------------------------------------------—
Definition Include.py:68
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)