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, args in functions:
81  f = getattr(gkl, 'py_bootstrap_%s' % name)
82  f.restype, f.args = restype, args
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.args = c_bool, [IInterface_p]
92  gkl.py_bootstrap_app_run.restype = c_bool
93  gkl.py_bootstrap_app_run.args = [IInterface_p, c_int]
94 
95  gkl.py_helper_printAlgsSequences.restype = None
96  gkl.py_helper_printAlgsSequences.args = [IInterface_p]
97 
98  def createApplicationMgr(self):
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(strm=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, ncpus = None):
244  if not ncpus:
245  # Standard sequential mode
246  result = self.runSerial()
247  else:
248  # Otherwise, run with the specified number of cpus
249  result = self.runParallel(ncpus)
250  return result
251 
252  def basicInit(self):
253  '''
254  Bootstrap the application with minimal use of Python bindings.
255  '''
256  try:
257  from GaudiKernel.Proxy.Configurable import expandvars
258  except ImportError:
259  # pass-through implementation if expandvars is not defined (AthenaCommon)
260  expandvars = lambda data : data
261 
262  from GaudiKernel.Proxy.Configurable import Configurable, getNeededConfigurables
263 
264  global _bootstrap
265  if _bootstrap is None:
266  _bootstrap = BootstrapHelper()
267 
268  self.log.debug('basicInit: instantiate ApplicationMgr')
269  self.ip = self.g = _bootstrap.createApplicationMgr()
270 
271  self.log.debug('basicInit: apply options')
272 
273  # set ApplicationMgr properties
274  comp = 'ApplicationMgr'
275  props = Configurable.allConfigurables.get(comp, {})
276  if props:
277  props = expandvars(props.getValuedProperties())
278  for p, v in props.items() + [('JobOptionsType', 'NONE')]:
279  if not self.g.setProperty(p, str(v)):
280  self.log.error('Cannot set property %s.%s to %s', comp, p, v)
281  sys.exit(10)
282  # issue with missing dictionary with ROOT < 6.2.7
283  if _bootstrap.ROOT_VERSION < (6, 2, 7):
284  # we need to load GaudiPython
285  import GaudiPython
286 
287  self.g.configure()
288 
289  # set MessageSvc properties
290  comp = 'MessageSvc'
291  msp = self.g.getService(comp)
292  if not msp:
293  self.log.error('Cannot get service %s', comp)
294  sys.exit(10)
295  props = Configurable.allConfigurables.get(comp, {})
296  if props:
297  props = expandvars(props.getValuedProperties())
298  for p, v in props.items():
299  if not _bootstrap.setProperty(msp, p, str(v)):
300  self.log.error('Cannot set property %s.%s to %s', comp, p, v)
301  sys.exit(10)
302 
303  # feed JobOptionsSvc
304  comp = 'JobOptionsSvc'
305  jos = self.g.getService(comp)
306  if not jos:
307  self.log.error('Cannot get service %s', comp)
308  sys.exit(10)
309  for n in getNeededConfigurables():
310  c = Configurable.allConfigurables[n]
311  if n in ['ApplicationMgr','MessageSvc']:
312  continue # These are already done
313  for p, v in c.getValuedProperties().items() :
314  v = expandvars(v)
315  # Note: AthenaCommon.Configurable does not have Configurable.PropertyReference
316  if hasattr(Configurable,"PropertyReference") and type(v) == Configurable.PropertyReference:
317  # this is done in "getFullName", but the exception is ignored,
318  # so we do it again to get it
319  v = v.__resolve__()
320  if type(v) == str : v = '"%s"' % v # need double quotes
321  elif type(v) == long: v = '%d' % v # prevent pending 'L'
322  _bootstrap.addPropertyToCatalogue(jos, n, p, str(v))
323  if hasattr(Configurable,"_configurationLocked"):
324  Configurable._configurationLocked = True
325  self.log.debug('basicInit: done')
326 
327  def gaudiPythonInit(self):
328  '''
329  Initialize the application with full Python bindings.
330  '''
331  self.log.debug('gaudiPythonInit: import GaudiPython')
332  import GaudiPython
333  self.log.debug('gaudiPythonInit: instantiate ApplicationMgr')
334  self.g = GaudiPython.AppMgr()
335  self.ip = self.g._ip
336  self.log.debug('gaudiPythonInit: done')
337 
338  def runSerial(self) :
339  #--- Instantiate the ApplicationMgr------------------------------
340  if (self.mainLoop or
341  os.environ.get('GAUDIRUN_USE_GAUDIPYTHON')):
342  self.gaudiPythonInit()
343  else:
344  self.basicInit()
345 
346  self.log.debug('-'*80)
347  self.log.debug('%s: running in serial mode', __name__)
348  self.log.debug('-'*80)
349  sysStart = time()
350 
351  if self.mainLoop:
352  runner = self.mainLoop
353  else:
354  def runner(app, nevt):
355  self.log.debug('initialize')
356  sc = app.initialize()
357  if sc.isSuccess():
358  if self.printsequence:
359  app.printAlgsSequences()
360  self.log.debug('start')
361  sc = app.start()
362  if sc.isSuccess():
363  self.log.debug('run(%d)', nevt)
364  sc = app.run(nevt)
365  self.log.debug('stop')
366  app.stop().ignore()
367  self.log.debug('finalize')
368  app.finalize().ignore()
369  self.log.debug('terminate')
370  sc1 = app.terminate()
371  if sc.isSuccess():
372  sc = sc1
373  else:
374  sc1.ignore()
375  self.log.debug('status code: %s',
376  'SUCCESS' if sc.isSuccess() else 'FAILURE')
377  return sc
378 
379  try:
380  statuscode = runner(self.g, int(self.ip.getProperty('EvtMax').toString()))
381  except SystemError:
382  # It may not be 100% correct, but usually it means a segfault in C++
383  self.ip.setProperty('ReturnCode', str(128 + 11))
384  statuscode = False
385  except Exception, x:
386  print 'Exception:', x
387  # for other exceptions, just set a generic error code
388  self.ip.setProperty('ReturnCode', '1')
389  statuscode = False
390  if hasattr(statuscode, "isSuccess"):
391  success = statuscode.isSuccess()
392  else:
393  success = statuscode
394  #success = self.g.exit().isSuccess() and success
395  if not success and self.ip.getProperty('ReturnCode').toString() == '0':
396  # ensure that the return code is correctly set
397  self.ip.setProperty('ReturnCode', '1')
398  sysTime = time()-sysStart
399  self.log.debug('-'*80)
400  self.log.debug('%s: serial system finished, time taken: %5.4fs', __name__, sysTime)
401  self.log.debug('-'*80)
402  return int(self.ip.getProperty('ReturnCode').toString())
403 
404  def runParallel(self, ncpus) :
405  if self.mainLoop:
406  self.log.fatal("Cannot use custom main loop in multi-process mode, check your options")
407  return 1
408  self.setupParallelLogging( )
409  from Gaudi.Configuration import Configurable
410  import GaudiMP.GMPBase as gpp
411  c = Configurable.allConfigurables
412  self.log.info('-'*80)
413  self.log.info('%s: Parallel Mode : %i '%(__name__, ncpus))
414  from commands import getstatusoutput as gso
415  metadataCommands = [ 'uname -a',
416  'echo $CMTCONFIG',
417  'echo $GAUDIAPPNAME',
418  'echo $GAUDIAPPVERSION']
419  for comm in metadataCommands :
420  s, o = gso( comm )
421  if s :
422  o = "Undetermined"
423  string = '%s: %30s : %s '%(__name__, comm, o)
424  self.log.info( string )
425  try :
426  events = str(c['ApplicationMgr'].EvtMax)
427  except :
428  events = "Undetermined"
429  self.log.info('%s: Events Specified : %s '%(__name__,events))
430  self.log.info('-'*80)
431  # Parall = gpp.Coordinator(ncpus, shared, c, self.log)
432  Parall = gpp.Coord( ncpus, c, self.log )
433  sysStart = time()
434  sc = Parall.Go()
435  self.log.info('MAIN.PY : received %s from Coordinator'%(sc))
436  if sc.isFailure() :
437  return 1
438  sysTime = time()-sysStart
439  self.log.name = 'Gaudi/Main.py Logger'
440  self.log.info('-'*80)
441  self.log.info('%s: parallel system finished, time taken: %5.4fs', __name__, sysTime)
442  self.log.info('-'*80)
443  return 0
int PyHelper() ROOT_VERSION_CODE()
Definition: Bootstrap.cpp:318
def toOpt(value)
Definition: Main.py:117
def initialize()
Definition: AnalysisTest.py:12
def __init__(self, name=Configurable.DefaultName, _enabled=True, kwargs)
std::string toString(const TYPE &obj)
the generic implementation of the type conversion to the string
Definition: ToStream.h:403
const char *PyHelper() getProperty(IInterface *p, char *name)
Definition: Bootstrap.cpp:297
struct GAUDI_API map
Parametrisation class for map-like implementation.
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:30
bool PyHelper() setProperty(IInterface *p, char *name, char *value)
Definition: Bootstrap.cpp:290
The Application Manager class.
Property base class allowing Property* collections to be "homogeneous".
Definition: Property.h:43
IInterface *PyHelper() getService(IInterface *app, char *name)
Definition: Bootstrap.cpp:283
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:133
tuple start
Definition: IOTest.py:88
string type
Definition: gaudirun.py:151
GAUDI_API IAppMgrUI * createApplicationMgr(const std::string &dllname, const std::string &factname)