The Gaudi Framework  v29r0 (ff2e7097)
Main.py
Go to the documentation of this file.
1 import sys
2 import os
3 from time import time
4 from Gaudi import Configuration
5 import logging
6 
7 log = logging.getLogger(__name__)
8 
9 
10 class BootstrapHelper(object):
11  class StatusCode(object):
12  def __init__(self, value):
13  self.value = value
14 
15  def __bool__(self):
16  return self.value
17  __nonzero__ = __bool__
18 
19  def isSuccess(self):
20  return self.value
21 
22  def isFailure(self):
23  return not self.value
24 
25  def ignore(self):
26  pass
27 
28  class Property(object):
29  def __init__(self, value):
30  self.value = value
31 
32  def __str__(self):
33  return str(self.value)
34  toString = __str__
35 
36  class AppMgr(object):
37  def __init__(self, ptr, lib):
38  self.ptr = ptr
39  self.lib = lib
40  self._as_parameter_ = ptr
41 
42  def configure(self):
43  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_configure(self.ptr))
44 
45  def initialize(self):
46  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_initialize(self.ptr))
47 
48  def start(self):
49  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_start(self.ptr))
50 
51  def run(self, nevt):
52  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_app_run(self.ptr, nevt))
53 
54  def stop(self):
55  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_stop(self.ptr))
56 
57  def finalize(self):
58  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_finalize(self.ptr))
59 
60  def terminate(self):
61  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_fsm_terminate(self.ptr))
62 
63  def getService(self, name):
64  return self.lib.py_bootstrap_getService(self.ptr, name)
65 
66  def setProperty(self, name, value):
67  return BootstrapHelper.StatusCode(self.lib.py_bootstrap_setProperty(self.ptr, name, value))
68 
69  def getProperty(self, name):
70  return BootstrapHelper.Property(self.lib.py_bootstrap_getProperty(self.ptr, name))
71 
72  def printAlgsSequences(self):
73  return self.lib.py_helper_printAlgsSequences(self.ptr)
74 
75  def __init__(self):
76  from ctypes import PyDLL, util, c_void_p, c_bool, c_char_p, c_int
77  # Helper class to avoid void* to int conversion
78  # (see http://stackoverflow.com/questions/17840144)
79 
80  class IInterface_p(c_void_p):
81  def __repr__(self):
82  return "IInterface_p(0x%x)" % (0 if self.value is None
83  else self.value)
84  self.log = logging.getLogger('BootstrapHelper')
85  libname = util.find_library('GaudiKernel') or 'libGaudiKernel.so'
86  self.log.debug('loading GaudiKernel (%s)', libname)
87 
88  # FIXME: note that we need PyDLL instead of CDLL if the calls to
89  # Python functions are not protected with the GIL.
90  self.lib = gkl = PyDLL(libname)
91 
92  functions = [('createApplicationMgr', IInterface_p, []),
93  ('getService', IInterface_p, [IInterface_p, c_char_p]),
94  ('setProperty', c_bool, [
95  IInterface_p, c_char_p, c_char_p]),
96  ('getProperty', c_char_p, [IInterface_p, c_char_p]),
97  ('addPropertyToCatalogue', c_bool, [
98  IInterface_p, c_char_p, c_char_p, c_char_p]),
99  ('ROOT_VERSION_CODE', c_int, []),
100  ]
101 
102  for name, restype, argtypes in functions:
103  f = getattr(gkl, 'py_bootstrap_%s' % name)
104  f.restype, f.argtypes = restype, argtypes
105  # create a delegate method if not already present
106  # (we do not want to use hasattr because it calls "properties")
107  if name not in self.__class__.__dict__:
108  setattr(self, name, f)
109 
110  for name in ('configure', 'initialize', 'start',
111  'stop', 'finalize', 'terminate'):
112  f = getattr(gkl, 'py_bootstrap_fsm_%s' % name)
113  f.restype, f.argtypes = c_bool, [IInterface_p]
114  gkl.py_bootstrap_app_run.restype = c_bool
115  gkl.py_bootstrap_app_run.argtypes = [IInterface_p, c_int]
116 
117  gkl.py_helper_printAlgsSequences.restype = None
118  gkl.py_helper_printAlgsSequences.argtypes = [IInterface_p]
119 
121  ptr = self.lib.py_bootstrap_createApplicationMgr()
122  return self.AppMgr(ptr, self.lib)
123 
124  @property
125  def ROOT_VERSION_CODE(self):
126  return self.lib.py_bootstrap_ROOT_VERSION_CODE()
127 
128  @property
129  def ROOT_VERSION(self):
130  root_version_code = self.ROOT_VERSION_CODE
131  a = root_version_code >> 16 & 0xff
132  b = root_version_code >> 8 & 0xff
133  c = root_version_code & 0xff
134  return (a, b, c)
135 
136 
137 _bootstrap = None
138 
139 
140 def toOpt(value):
141  '''
142  Helper to convert values to old .opts format.
143 
144  >>> print toOpt('some "text"')
145  "some \\"text\\""
146  >>> print toOpt('first\\nsecond')
147  "first
148  second"
149  >>> print toOpt({'a': [1, 2, '3']})
150  {"a": [1, 2, "3"]}
151  '''
152  if isinstance(value, basestring):
153  return '"{0}"'.format(value.replace('"', '\\"'))
154  elif isinstance(value, dict):
155  return '{{{0}}}'.format(', '.join('{0}: {1}'.format(toOpt(k), toOpt(v))
156  for k, v in value.iteritems()))
157  elif hasattr(value, '__iter__'):
158  return '[{0}]'.format(', '.join(map(toOpt, value)))
159  else:
160  return repr(value)
161 
162 
163 class gaudimain(object):
164  # main loop implementation, None stands for the default
165  mainLoop = None
166 
167  def __init__(self):
168  from Configurables import ApplicationMgr
169  appMgr = ApplicationMgr()
170  if "GAUDIAPPNAME" in os.environ:
171  appMgr.AppName = str(os.environ["GAUDIAPPNAME"])
172  if "GAUDIAPPVERSION" in os.environ:
173  appMgr.AppVersion = str(os.environ["GAUDIAPPVERSION"])
174  self.log = logging.getLogger(__name__)
175  self.printsequence = False
176 
178  # ---------------------------------------------------
179  # set up Logging
180  # ----------------
181  # from multiprocessing import enableLogging, getLogger
182  import multiprocessing
183  # preliminaries for handlers/output files, etc.
184  from time import ctime
185  datetime = ctime()
186  datetime = datetime.replace(' ', '_')
187  outfile = open('gaudirun-%s.log' % (datetime), 'w')
188  # two handlers, one for a log file, one for terminal
189  streamhandler = logging.StreamHandler(stream=outfile)
190  console = logging.StreamHandler()
191  # create formatter : the params in parentheses are variable names available via logging
192  formatter = logging.Formatter(
193  "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
194  # add formatter to Handler
195  streamhandler.setFormatter(formatter)
196  console.setFormatter(formatter)
197  # now, configure the logger
198  # enableLogging( level=0 )
199  # self.log = getLogger()
200  self.log = multiprocessing.log_to_stderr()
201  self.log.setLevel(logging.INFO)
202  self.log.name = 'Gaudi/Main.py Logger'
203  self.log.handlers = []
204  # add handlers to logger : one for output to a file, one for console output
205  self.log.addHandler(streamhandler)
206  self.log.addHandler(console)
207  self.log.removeHandler(console)
208  # set level!!
209  self.log.setLevel = logging.INFO
210  # ---------------------------------------------------
211 
212  def generatePyOutput(self, all=False):
213  from pprint import pformat
214  conf_dict = Configuration.configurationDict(all)
215  return pformat(conf_dict)
216 
217  def generateOptsOutput(self, all=False):
218  from pprint import pformat
219  conf_dict = Configuration.configurationDict(all)
220  out = []
221  names = conf_dict.keys()
222  names.sort()
223  for n in names:
224  props = conf_dict[n].keys()
225  props.sort()
226  for p in props:
227  out.append('%s.%s = %s;' % (n, p, toOpt(conf_dict[n][p])))
228  return "\n".join(out)
229 
230  def _writepickle(self, filename):
231  #--- Lets take the first file input file as the name of the pickle file
232  import pickle
233  output = open(filename, 'wb')
234  # Dump only the the configurables that make sense to dump (not User ones)
235  from GaudiKernel.Proxy.Configurable import getNeededConfigurables
236  to_dump = {}
237  for n in getNeededConfigurables():
238  to_dump[n] = Configuration.allConfigurables[n]
239  pickle.dump(to_dump, output, -1)
240  output.close()
241 
242  def printconfig(self, old_format=False, all=False):
243  msg = 'Dumping all configurables and properties'
244  if not all:
245  msg += ' (different from default)'
246  log.info(msg)
247  conf_dict = Configuration.configurationDict(all)
248  if old_format:
249  print self.generateOptsOutput(all)
250  else:
251  print self.generatePyOutput(all)
252 
253  def writeconfig(self, filename, all=False):
254  write = {".pkl": lambda filename, all: self._writepickle(filename),
255  ".py": lambda filename, all: open(filename, "w").write(self.generatePyOutput(all) + "\n"),
256  ".opts": lambda filename, all: open(filename, "w").write(self.generateOptsOutput(all) + "\n"),
257  }
258  from os.path import splitext
259  ext = splitext(filename)[1]
260  if ext in write:
261  write[ext](filename, all)
262  else:
263  log.error("Unknown file type '%s'. Must be any of %r.",
264  ext, write.keys())
265  sys.exit(1)
266 
267  # Instantiate and run the application.
268  # Depending on the number of CPUs (ncpus) specified, it start
269  def run(self, attach_debugger, ncpus=None):
270  if not ncpus:
271  # Standard sequential mode
272  result = self.runSerial(attach_debugger)
273  else:
274  # Otherwise, run with the specified number of cpus
275  result = self.runParallel(ncpus)
276  return result
277 
278  def hookDebugger(self, debugger='gdb'):
279  import os
280  self.log.info('attaching debugger to PID ' + str(os.getpid()))
281  pid = os.spawnvp(os.P_NOWAIT,
282  debugger, [debugger, '-q', 'python', str(os.getpid())])
283 
284  # give debugger some time to attach to the python process
285  import time
286  time.sleep(5)
287 
288  # verify the process' existence (will raise OSError if failed)
289  os.waitpid(pid, os.WNOHANG)
290  os.kill(pid, 0)
291  return
292 
293  def basicInit(self):
294  '''
295  Bootstrap the application with minimal use of Python bindings.
296  '''
297  try:
298  from GaudiKernel.Proxy.Configurable import expandvars
299  except ImportError:
300  # pass-through implementation if expandvars is not defined (AthenaCommon)
301  def expandvars(data): return data
302 
303  from GaudiKernel.Proxy.Configurable import Configurable, getNeededConfigurables
304 
305  global _bootstrap
306  if _bootstrap is None:
307  _bootstrap = BootstrapHelper()
308 
309  self.log.debug('basicInit: instantiate ApplicationMgr')
310  self.ip = self.g = _bootstrap.createApplicationMgr()
311 
312  self.log.debug('basicInit: apply options')
313 
314  # set ApplicationMgr properties
315  comp = 'ApplicationMgr'
316  props = Configurable.allConfigurables.get(comp, {})
317  if props:
318  props = expandvars(props.getValuedProperties())
319  for p, v in props.items() + [('JobOptionsType', 'NONE')]:
320  if not self.g.setProperty(p, str(v)):
321  self.log.error('Cannot set property %s.%s to %s', comp, p, v)
322  sys.exit(10)
323  # issue with missing dictionary with ROOT < 6.2.7
324  if _bootstrap.ROOT_VERSION < (6, 2, 7):
325  # we need to load GaudiPython
326  import GaudiPython
327 
328  self.g.configure()
329 
330  # set MessageSvc properties
331  comp = 'MessageSvc'
332  msp = self.g.getService(comp)
333  if not msp:
334  self.log.error('Cannot get service %s', comp)
335  sys.exit(10)
336  props = Configurable.allConfigurables.get(comp, {})
337  if props:
338  props = expandvars(props.getValuedProperties())
339  for p, v in props.items():
340  if not _bootstrap.setProperty(msp, p, str(v)):
341  self.log.error('Cannot set property %s.%s to %s', comp, p, v)
342  sys.exit(10)
343 
344  # feed JobOptionsSvc
345  comp = 'JobOptionsSvc'
346  jos = self.g.getService(comp)
347  if not jos:
348  self.log.error('Cannot get service %s', comp)
349  sys.exit(10)
350  for n in getNeededConfigurables():
351  c = Configurable.allConfigurables[n]
352  if n in ['ApplicationMgr', 'MessageSvc']:
353  continue # These are already done
354  for p, v in c.getValuedProperties().items():
355  v = expandvars(v)
356  # Note: AthenaCommon.Configurable does not have Configurable.PropertyReference
357  if hasattr(Configurable, "PropertyReference") and type(v) == Configurable.PropertyReference:
358  # this is done in "getFullName", but the exception is ignored,
359  # so we do it again to get it
360  v = v.__resolve__()
361  if type(v) == str:
362  v = '"%s"' % v # need double quotes
363  elif type(v) == long:
364  v = '%d' % v # prevent pending 'L'
365  _bootstrap.addPropertyToCatalogue(jos, n, p, str(v))
366  if hasattr(Configurable, "_configurationLocked"):
367  Configurable._configurationLocked = True
368  self.log.debug('basicInit: done')
369 
370  def gaudiPythonInit(self):
371  '''
372  Initialize the application with full Python bindings.
373  '''
374  self.log.debug('gaudiPythonInit: import GaudiPython')
375  import GaudiPython
376  self.log.debug('gaudiPythonInit: instantiate ApplicationMgr')
377  self.g = GaudiPython.AppMgr()
378  self.ip = self.g._ip
379  self.log.debug('gaudiPythonInit: done')
380 
381  def runSerial(self, attach_debugger):
382  #--- Instantiate the ApplicationMgr------------------------------
383  if (self.mainLoop or
384  os.environ.get('GAUDIRUN_USE_GAUDIPYTHON')):
385  self.gaudiPythonInit()
386  else:
387  self.basicInit()
388 
389  self.log.debug('-' * 80)
390  self.log.debug('%s: running in serial mode', __name__)
391  self.log.debug('-' * 80)
392  sysStart = time()
393 
394  if self.mainLoop:
395  runner = self.mainLoop
396  else:
397  def runner(app, nevt):
398  self.log.debug('initialize')
399  sc = app.initialize()
400  if sc.isSuccess():
401  if self.printsequence:
402  app.printAlgsSequences()
403  self.log.debug('start')
404  sc = app.start()
405  if sc.isSuccess():
406  self.log.debug('run(%d)', nevt)
407  sc = app.run(nevt)
408  self.log.debug('stop')
409  app.stop().ignore()
410  self.log.debug('finalize')
411  app.finalize().ignore()
412  self.log.debug('terminate')
413  sc1 = app.terminate()
414  if sc.isSuccess():
415  sc = sc1
416  else:
417  sc1.ignore()
418  self.log.debug('status code: %s',
419  'SUCCESS' if sc.isSuccess() else 'FAILURE')
420  return sc
421 
422  if (attach_debugger == True):
423  self.hookDebugger()
424 
425  try:
426  statuscode = runner(self.g, int(
427  self.ip.getProperty('EvtMax').toString()))
428  except SystemError:
429  # It may not be 100% correct, but usually it means a segfault in C++
430  self.ip.setProperty('ReturnCode', str(128 + 11))
431  statuscode = False
432  except Exception, x:
433  print 'Exception:', x
434  # for other exceptions, just set a generic error code
435  self.ip.setProperty('ReturnCode', '1')
436  statuscode = False
437  if hasattr(statuscode, "isSuccess"):
438  success = statuscode.isSuccess()
439  else:
440  success = statuscode
441  #success = self.g.exit().isSuccess() and success
442  if not success and self.ip.getProperty('ReturnCode').toString() == '0':
443  # ensure that the return code is correctly set
444  self.ip.setProperty('ReturnCode', '1')
445  sysTime = time() - sysStart
446  self.log.debug('-' * 80)
447  self.log.debug(
448  '%s: serial system finished, time taken: %5.4fs', __name__, sysTime)
449  self.log.debug('-' * 80)
450  return int(self.ip.getProperty('ReturnCode').toString())
451 
452  def runParallel(self, ncpus):
453  if self.mainLoop:
454  self.log.fatal(
455  "Cannot use custom main loop in multi-process mode, check your options")
456  return 1
457  self.setupParallelLogging()
458  from Gaudi.Configuration import Configurable
459  import GaudiMP.GMPBase as gpp
460  c = Configurable.allConfigurables
461  self.log.info('-' * 80)
462  self.log.info('%s: Parallel Mode : %i ', __name__, ncpus)
463  for name, value in [('platrofm', ' '.join(os.uname())),
464  ('config', os.environ.get('BINARY_TAG') or
465  os.environ.get('CMTCONFIG')),
466  ('app. name', os.environ.get('GAUDIAPPNAME')),
467  ('app. version', os.environ.get('GAUDIAPPVERSION')),
468  ]:
469  self.log.info('%s: %30s : %s ', __name__,
470  name, value or 'Undefined')
471  try:
472  events = str(c['ApplicationMgr'].EvtMax)
473  except:
474  events = "Undetermined"
475  self.log.info('%s: Events Specified : %s ', __name__, events)
476  self.log.info('-' * 80)
477  # Parall = gpp.Coordinator(ncpus, shared, c, self.log)
478  Parall = gpp.Coord(ncpus, c, self.log)
479  sysStart = time()
480  sc = Parall.Go()
481  self.log.info('MAIN.PY : received %s from Coordinator' % (sc))
482  if sc.isFailure():
483  return 1
484  sysTime = time() - sysStart
485  self.log.name = 'Gaudi/Main.py Logger'
486  self.log.info('-' * 80)
487  self.log.info(
488  '%s: parallel system finished, time taken: %5.4fs', __name__, sysTime)
489  self.log.info('-' * 80)
490  return 0
def run(self, attach_debugger, ncpus=None)
Definition: Main.py:269
def setupParallelLogging(self)
Definition: Main.py:177
def basicInit(self)
Definition: Main.py:293
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:120
def toOpt(value)
Definition: Main.py:140
def _writepickle(self, filename)
Definition: Main.py:230
def generateOptsOutput(self, all=False)
Definition: Main.py:217
def gaudiPythonInit(self)
Definition: Main.py:370
getNeededConfigurables
Definition: Proxy.py:20
def runParallel(self, ncpus)
Definition: Main.py:452
def hookDebugger(self, debugger='gdb')
Definition: Main.py:278
def generatePyOutput(self, all=False)
Definition: Main.py:212
def __init__(self, ptr, lib)
Definition: Main.py:37
struct GAUDI_API map
Parametrisation class for map-like implementation.
def getProperty(self, name)
Definition: Main.py:69
def getService(self, name)
Definition: Main.py:63
def createApplicationMgr(self)
Definition: Main.py:120
def __init__(self, value)
Definition: Main.py:29
The Application Manager class.
def ROOT_VERSION_CODE(self)
Definition: Main.py:125
def __init__(self)
Definition: Main.py:167
def __init__(self)
Definition: Main.py:75
def printconfig(self, old_format=False, all=False)
Definition: Main.py:242
def writeconfig(self, filename, all=False)
Definition: Main.py:253
def ROOT_VERSION(self)
Definition: Main.py:129
std::string toString(const Type &)
def setProperty(self, name, value)
Definition: Main.py:66
def runSerial(self, attach_debugger)
Definition: Main.py:381