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