The Gaudi Framework  v33r0 (d5ea422b)
Main.py
Go to the documentation of this file.
1 
11 from __future__ import print_function
12 import sys
13 import os
14 from time import time
15 from Gaudi import Configuration
16 import logging
17 
18 import six
19 
20 log = logging.getLogger(__name__)
21 
22 
23 class BootstrapHelper(object):
24  class StatusCode(object):
25  def __init__(self, value):
26  self.value = value
27 
28  def __bool__(self):
29  return self.value
30 
31  __nonzero__ = __bool__
32 
33  def isSuccess(self):
34  return self.value
35 
36  def isFailure(self):
37  return not self.value
38 
39  def ignore(self):
40  pass
41 
42  class Property(object):
43  def __init__(self, value):
44  self.value = value
45 
46  def __str__(self):
47  # TODO why bytes
48  return bytes(self.value).decode('utf-8')
49 
50  toString = __str__
51 
52  class AppMgr(object):
53  def __init__(self, ptr, lib):
54  self.ptr = ptr
55  self.lib = lib
56  self._as_parameter_ = ptr
57 
58  def configure(self):
60  self.lib.py_bootstrap_fsm_configure(self.ptr))
61 
62  def initialize(self):
64  self.lib.py_bootstrap_fsm_initialize(self.ptr))
65 
66  def start(self):
68  self.lib.py_bootstrap_fsm_start(self.ptr))
69 
70  def run(self, nevt):
72  self.lib.py_bootstrap_app_run(self.ptr, nevt))
73 
74  def stop(self):
76  self.lib.py_bootstrap_fsm_stop(self.ptr))
77 
78  def finalize(self):
80  self.lib.py_bootstrap_fsm_finalize(self.ptr))
81 
82  def terminate(self):
84  self.lib.py_bootstrap_fsm_terminate(self.ptr))
85 
86  def getService(self, name):
87  return self.lib.py_bootstrap_getService(self.ptr,
88  name.encode('ascii'))
89 
90  def setProperty(self, name, value):
92  self.lib.py_bootstrap_setProperty(self.ptr,
93  name.encode('ascii'),
94  value.encode('ascii')))
95 
96  def getProperty(self, name):
98  self.lib.py_bootstrap_getProperty(self.ptr,
99  name.encode('ascii')))
100 
102  return self.lib.py_helper_printAlgsSequences(self.ptr)
103 
104  def __init__(self):
105  from ctypes import (PyDLL, util, c_void_p, c_bool, c_char_p, c_int,
106  RTLD_GLOBAL)
107 
108  # Helper class to avoid void* to int conversion
109  # (see http://stackoverflow.com/questions/17840144)
110 
111  class IInterface_p(c_void_p):
112  def __repr__(self):
113  return "IInterface_p(0x%x)" % (0 if self.value is None else
114  self.value)
115 
116  self.log = logging.getLogger('BootstrapHelper')
117  libname = util.find_library('GaudiKernel') or 'libGaudiKernel.so'
118  self.log.debug('loading GaudiKernel (%s)', libname)
119 
120  # FIXME: note that we need PyDLL instead of CDLL if the calls to
121  # Python functions are not protected with the GIL.
122  self.lib = gkl = PyDLL(libname, mode=RTLD_GLOBAL)
123 
124  functions = [
125  ('createApplicationMgr', IInterface_p, []),
126  ('getService', IInterface_p, [IInterface_p, c_char_p]),
127  ('setProperty', c_bool, [IInterface_p, c_char_p, c_char_p]),
128  ('getProperty', c_char_p, [IInterface_p, c_char_p]),
129  ('addPropertyToCatalogue', c_bool,
130  [IInterface_p, c_char_p, c_char_p, c_char_p]),
131  ('ROOT_VERSION_CODE', c_int, []),
132  ]
133 
134  for name, restype, argtypes in functions:
135  f = getattr(gkl, 'py_bootstrap_%s' % name)
136  f.restype, f.argtypes = restype, argtypes
137  # create a delegate method if not already present
138  # (we do not want to use hasattr because it calls "properties")
139  if name not in self.__class__.__dict__:
140  setattr(self, name, f)
141 
142  for name in ('configure', 'initialize', 'start', 'stop', 'finalize',
143  'terminate'):
144  f = getattr(gkl, 'py_bootstrap_fsm_%s' % name)
145  f.restype, f.argtypes = c_bool, [IInterface_p]
146  gkl.py_bootstrap_app_run.restype = c_bool
147  gkl.py_bootstrap_app_run.argtypes = [IInterface_p, c_int]
148 
149  gkl.py_helper_printAlgsSequences.restype = None
150  gkl.py_helper_printAlgsSequences.argtypes = [IInterface_p]
151 
153  ptr = self.lib.py_bootstrap_createApplicationMgr()
154  return self.AppMgr(ptr, self.lib)
155 
156  @property
157  def ROOT_VERSION_CODE(self):
158  return self.lib.py_bootstrap_ROOT_VERSION_CODE()
159 
160  @property
161  def ROOT_VERSION(self):
162  root_version_code = self.ROOT_VERSION_CODE
163  a = root_version_code >> 16 & 0xff
164  b = root_version_code >> 8 & 0xff
165  c = root_version_code & 0xff
166  return (a, b, c)
167 
168 
169 def getAllOpts(explicit_defaults=False):
170  from itertools import chain
171  # old conf
172  from GaudiKernel.Proxy.Configurable import Configurable, getNeededConfigurables
173  old_opts = {}
174 
175  # some algorithms may be generater when we call "getValuedProperties"
176  # so we need a few iterations before we get the full list
177  # (see GaudiConfig.ControlFlow)
178  needed_conf = []
179  count = 0
180  new_count = -1
181  while count != new_count:
182  count = new_count
183  needed_conf = getNeededConfigurables()
184  new_count = len(needed_conf)
185  for n in needed_conf:
186  c = Configurable.allConfigurables[n]
187  if hasattr(c, 'getValuedProperties'):
188  c.getValuedProperties()
189 
190  for n in needed_conf:
191  c = Configurable.allConfigurables[n]
192  items = (chain(c.getDefaultProperties().items(),
193  c.getValuedProperties().items())
194  if explicit_defaults else c.getValuedProperties().items())
195  for p, v in items:
196  # Note: AthenaCommon.Configurable does not have Configurable.PropertyReference
197  if hasattr(Configurable, "PropertyReference") and type(
198  v) == Configurable.PropertyReference:
199  # this is done in "getFullName", but the exception is ignored,
200  # so we do it again to get it
201  v = v.__resolve__()
202  if isinstance(v, str):
203  # properly escape quotes in the string (see gaudi/Gaudi#78)
204  v = '"%s"' % v.replace('"', '\\"')
205  elif sys.version_info < (3, ) and isinstance(v, long):
206  v = '%d' % v # prevent pending 'L'
207  elif hasattr(v, '__opt_value__'):
208  v = v.__opt_value__()
209  old_opts['.'.join((n, p))] = str(v)
210 
211  import GaudiConfig2
212  opts = GaudiConfig2.all_options(explicit_defaults)
213 
214  conflicts = [
215  n for n in set(opts).intersection(old_opts) if opts[n] != old_opts[n]
216  ]
217  if conflicts:
218  conflicts.sort()
219  log.error('Some properties are set in old and new style configuration')
220  log.warning('name: old -> new')
221  for n in conflicts:
222  log.warning('%s: %s -> %s', n, old_opts[n], opts[n])
223  sys.exit(10)
224 
225  opts.update(old_opts)
226  return opts
227 
228 
229 def toOpt(value):
230  '''
231  Helper to convert values to old .opts format.
232 
233  >>> print(toOpt('some "text"'))
234  "some \\"text\\""
235  >>> print(toOpt('first\\nsecond'))
236  "first
237  second"
238  >>> print(toOpt({'a': [1, 2, '3']}))
239  {"a": [1, 2, "3"]}
240  '''
241  if isinstance(value, six.string_types):
242  return '"{0}"'.format(value.replace('"', '\\"'))
243  elif isinstance(value, dict):
244  return '{{{0}}}'.format(', '.join(
245  '{0}: {1}'.format(toOpt(k), toOpt(v)) for k, v in value.items()))
246  elif hasattr(value, '__iter__'):
247  return '[{0}]'.format(', '.join(map(toOpt, value)))
248  else:
249  return repr(value)
250 
251 
252 def parseOpt(s):
253  '''
254  Helper to parse option strings to Python values.
255 
256  Ideally it should just be "eval", but the string parser of Gaudi
257  is different from the Python one, so we get string options that
258  cannot be just evaluated.
259 
260  >>> print(parseOpt('123'))
261  123
262  >>> print(parseOpt('"some\\n\\\\"text\\\\""'))
263  some
264  "text"
265  >>> print(parseOpt(''))
266  <BLANKLINE>
267 
268  (see gaudi/Gaudi#78)
269  '''
270  import re
271  quoted_string = re.compile(r'^"(.*)"$', re.DOTALL)
272  # FIXME: this is needed because we cannot use repr for strings
273  # (see gaudi/Gaudi#78)
274  if not s: # pass through empty strings
275  return s
276  m = quoted_string.match(s)
277  if m:
278  return m.group(1).replace('\\"', '"')
279  return eval(s)
280 
281 
282 class gaudimain(object):
283  def __init__(self):
284  from Configurables import ApplicationMgr
285  appMgr = ApplicationMgr()
286  if "GAUDIAPPNAME" in os.environ:
287  appMgr.AppName = str(os.environ["GAUDIAPPNAME"])
288  if "GAUDIAPPVERSION" in os.environ:
289  appMgr.AppVersion = str(os.environ["GAUDIAPPVERSION"])
290  self.log = logging.getLogger(__name__)
291  self.printsequence = False
292  self.application = 'Gaudi::Application'
293 
295  # ---------------------------------------------------
296  # set up Logging
297  # ----------------
298  # from multiprocessing import enableLogging, getLogger
299  import multiprocessing
300  # preliminaries for handlers/output files, etc.
301  from time import ctime
302  datetime = ctime()
303  datetime = datetime.replace(' ', '_')
304  outfile = open('gaudirun-%s.log' % (datetime), 'w')
305  # two handlers, one for a log file, one for terminal
306  streamhandler = logging.StreamHandler(stream=outfile)
307  console = logging.StreamHandler()
308  # create formatter : the params in parentheses are variable names available via logging
309  formatter = logging.Formatter(
310  "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
311  # add formatter to Handler
312  streamhandler.setFormatter(formatter)
313  console.setFormatter(formatter)
314  # now, configure the logger
315  # enableLogging( level=0 )
316  # self.log = getLogger()
317  self.log = multiprocessing.log_to_stderr()
318  self.log.setLevel(logging.INFO)
319  self.log.name = 'Gaudi/Main.py Logger'
320  self.log.handlers = []
321  # add handlers to logger : one for output to a file, one for console output
322  self.log.addHandler(streamhandler)
323  self.log.addHandler(console)
324  self.log.removeHandler(console)
325  # set level!!
326  self.log.setLevel = logging.INFO
327  # ---------------------------------------------------
328 
329  def generatePyOutput(self, all=False):
330  from pprint import pformat
331  from collections import defaultdict
332  optDict = defaultdict(dict)
333  allOpts = getAllOpts(all)
334  for key in allOpts:
335  c, p = key.rsplit('.', 1)
336  optDict[c][p] = parseOpt(allOpts[key])
337  formatted = pformat(dict(optDict))
338  # Python 2 compatibility
339  if six.PY2:
340  return formatted
341  else:
342  # undo splitting of strings on multiple lines
343  import re
344  return re.sub(r'"\n +"', '', formatted, flags=re.MULTILINE)
345 
346  def generateOptsOutput(self, all=False):
347  opts = getAllOpts(all)
348  keys = sorted(opts)
349  return '\n'.join(
350  '{} = {};'.format(key, toOpt(parseOpt(opts[key]))) for key in keys)
351 
352  def _writepickle(self, filename):
353  # --- Lets take the first file input file as the name of the pickle file
354  import pickle
355  output = open(filename, 'wb')
356  # Dump only the the configurables that make sense to dump (not User ones)
357  from GaudiKernel.Proxy.Configurable import getNeededConfigurables
358  to_dump = {}
359  for n in getNeededConfigurables():
360  to_dump[n] = Configuration.allConfigurables[n]
361  pickle.dump(to_dump, output, -1)
362  output.close()
363 
364  def printconfig(self, old_format=False, all=False):
365  msg = 'Dumping all configurables and properties'
366  if not all:
367  msg += ' (different from default)'
368  log.info(msg)
369  if old_format:
370  print(self.generateOptsOutput(all))
371  else:
372  print(self.generatePyOutput(all))
373  sys.stdout.flush()
374 
375  def writeconfig(self, filename, all=False):
376  write = {".pkl": lambda filename, all: self._writepickle(filename),
377  ".py": lambda filename, all: open(filename, "w").write(self.generatePyOutput(all) + "\n"),
378  ".opts": lambda filename, all: open(filename, "w").write(self.generateOptsOutput(all) + "\n"),
379  }
380  from os.path import splitext
381  ext = splitext(filename)[1]
382  if ext in write:
383  write[ext](filename, all)
384  else:
385  log.error("Unknown file type '%s'. Must be any of %r.", ext,
386  write.keys())
387  sys.exit(1)
388 
389  # Instantiate and run the application.
390  # Depending on the number of CPUs (ncpus) specified, it start
391  def run(self, attach_debugger, ncpus=None):
392  if not ncpus:
393  # Standard sequential mode
394  result = self.runSerial(attach_debugger)
395  else:
396  # Otherwise, run with the specified number of cpus
397  result = self.runParallel(ncpus)
398  return result
399 
400  def hookDebugger(self, debugger='gdb'):
401  import os
402  self.log.info('attaching debugger to PID ' + str(os.getpid()))
403  pid = os.spawnvp(os.P_NOWAIT, debugger,
404  [debugger, '-q', 'python',
405  str(os.getpid())])
406 
407  # give debugger some time to attach to the python process
408  import time
409  time.sleep(5)
410 
411  # verify the process' existence (will raise OSError if failed)
412  os.waitpid(pid, os.WNOHANG)
413  os.kill(pid, 0)
414  return
415 
416  def runSerial(self, attach_debugger):
417  try:
418  from GaudiKernel.Proxy.Configurable import expandvars
419  except ImportError:
420  # pass-through implementation if expandvars is not defined (AthenaCommon)
421  def expandvars(data):
422  return data
423 
424  from GaudiKernel.Proxy.Configurable import Configurable
425 
426  self.log.debug('runSerial: apply options')
427  conf_dict = expandvars(getAllOpts())
428  conf_dict['ApplicationMgr.JobOptionsType'] = '"NONE"'
429 
430  if self.printsequence:
431  conf_dict['ApplicationMgr.PrintAlgsSequence'] = 'true'
432 
433  if hasattr(Configurable, "_configurationLocked"):
434  Configurable._configurationLocked = True
435 
436  if attach_debugger:
437  self.hookDebugger()
438 
439  self.log.debug('-' * 80)
440  self.log.debug('%s: running in serial mode', __name__)
441  self.log.debug('-' * 80)
442  sysStart = time()
443 
444  import Gaudi
445  app = Gaudi.Application.create(self.application, conf_dict)
446  retcode = app.run()
447 
448  sysTime = time() - sysStart
449  self.log.debug('-' * 80)
450  self.log.debug('%s: serial system finished, time taken: %5.4fs',
451  __name__, sysTime)
452  self.log.debug('-' * 80)
453 
454  return retcode
455 
456  def runParallel(self, ncpus):
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 [
464  ('platrofm', ' '.join(os.uname())),
465  ('config', os.environ.get('BINARY_TAG')
466  or 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__, name, value
471  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('%s: parallel system finished, time taken: %5.4fs',
489  __name__, sysTime)
490  self.log.info('-' * 80)
491  return 0
def run(self, attach_debugger, ncpus=None)
Definition: Main.py:391
def setupParallelLogging(self)
Definition: Main.py:294
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
def toOpt(value)
Definition: Main.py:229
def _writepickle(self, filename)
Definition: Main.py:352
def generateOptsOutput(self, all=False)
Definition: Main.py:346
getNeededConfigurables
Definition: Proxy.py:31
def runParallel(self, ncpus)
Definition: Main.py:456
def hookDebugger(self, debugger='gdb')
Definition: Main.py:400
def generatePyOutput(self, all=False)
Definition: Main.py:329
def __init__(self, ptr, lib)
Definition: Main.py:53
void py_helper_printAlgsSequences(IInterface *app)
Helper to call printAlgsSequences from Pyhton ctypes.
struct GAUDI_API map
Parametrisation class for map-like implementation.
def getProperty(self, name)
Definition: Main.py:96
def getService(self, name)
Definition: Main.py:86
def createApplicationMgr(self)
Definition: Main.py:152
def create(cls, appType, opts)
Definition: __init__.py:96
def __init__(self, value)
Definition: Main.py:43
The Application Manager class.
def ROOT_VERSION_CODE(self)
Definition: Main.py:157
def __init__(self)
Definition: Main.py:283
def printconfig(self, old_format=False, all=False)
Definition: Main.py:364
def writeconfig(self, filename, all=False)
Definition: Main.py:375
def ROOT_VERSION(self)
Definition: Main.py:161
def parseOpt(s)
Definition: Main.py:252
def getAllOpts(explicit_defaults=False)
Definition: Main.py:169
def setProperty(self, name, value)
Definition: Main.py:90
def runSerial(self, attach_debugger)
Definition: Main.py:416