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