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