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