Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Configurable.py
Go to the documentation of this file.
1 # File: AthenaCommon/python/Configurable.py
2 # Author: Wim Lavrijsen (WLavrijsen@lbl.gov)
3 # Author: Martin Woudstra (Martin.Woudstra@cern.ch)
4 
5 import copy
6 import string
7 import types
8 import os
9 import sys
10 from inspect import isclass
11 import GaudiKernel.ConfigurableMeta as ConfigurableMeta
12 from GaudiKernel.Constants import error_explanation, \
13  VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL
14 from GaudiKernel.PropertyProxy import PropertyProxy
15 from GaudiKernel.GaudiHandles import *
17 
18 from GaudiConfig.ControlFlow import (OrNode, AndNode, OrderedNode, ignore,
19  InvertNode, ControlFlowLeaf,
20  ControlFlowNode, CFTrue, CFFalse)
21 
22 # data ---------------------------------------------------------------------
23 __all__ = [
24  'Configurable', 'ConfigurableAlgorithm', 'ConfigurableAlgTool',
25  'ConfigurableAuditor', 'ConfigurableService', 'ConfigurableUser',
26  'VERBOSE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'FATAL',
27  'appendPostConfigAction', 'removePostConfigAction'
28 ]
29 
30 # for messaging
31 import logging
32 log = logging.getLogger('Configurable')
33 
34 
35 def expandvars(data):
36  """
37  Expand environment variables "data".
38  Data can be string, list, tuple and dictionary. For collection, all the
39  contained strings will be manipulated (recursively).
40  """
41  import os.path
42  typ = type(data)
43  if typ is str:
44  return os.path.expandvars(data)
45  elif typ in [list, tuple]:
46  collect = []
47  for i in data:
48  collect.append(expandvars(i))
49  return typ(collect)
50  elif typ is dict:
51  collect = {}
52  for k in data:
53  collect[expandvars(k)] = expandvars(data[k])
54  return collect
55  return data
56 
57 
58 class Error(RuntimeError):
59  """
60  Error occurred in the configuration process.
61  """
62  pass
63 
64 
65 # Allow references to options as in old style
66 
67 
68 class PropertyReference(object):
69  def __init__(self, propname):
70  self.name = propname
71 
72  def __str__(self):
73  return "@%s" % self.name
74 
75  def __resolve__(self):
76  # late binding for property references
77  retval = None
78  refname, refprop = self.name.rsplit('.', 1)
79  if refname in Configurable.allConfigurables:
80  conf = Configurable.allConfigurables[refname]
81  retval = getattr(conf, refprop)
82  if hasattr(retval, "getFullName"):
83  retval = retval.getFullName()
84  else:
85  raise NameError(
86  "name '%s' not found resolving '%s'" % (refname, self))
87  return retval
88 
89  def getFullName(self):
90  """This function allow transparent integration with
91  Configurable.getValuedProperties.
92  """
93  try:
94  return self.__resolve__()
95  except NameError:
96  # ignore the error if we cannot resolve the name yet
97  return self
98  except AttributeError:
99  # ignore the error if we cannot resolve the attribute yet
100  return self
101 
102 
103 # base class for configurable Gaudi algorithms/services/algtools/etc. ======
104 
105 
106 class Configurable(object):
107  """Base class for Gaudi components that implement the IProperty interface.
108  Provides most of the boilerplate code, but the actual useful classes
109  are its derived ConfigurableAlgorithm, ConfigurableService, and
110  ConfigurableAlgTool."""
111 
112  # for detecting the default name
113  class DefaultName:
114  pass
115 
116  propertyNoValue = '<no value>'
117  indentUnit = '| '
118  printHeaderWidth = 100
119  printHeaderPre = 5
120 
122 
123  __slots__ = (
124  '__children', # controlled components, e.g. private AlgTools
125  '__tools', # private AlgTools (#PM-->)
126  '_name', # the (unqualified) component name
127  '_inSetDefaults', # currently setting default values
128  '_initok', # used to enforce base class init
129  '_setupok', # for debugging purposes (temporary)
130  '_unpickling', # flag for actions done during unpickling
131  )
132 
133  allConfigurables = {
134  } # just names would do, but currently refs to the actual
135  configurableServices = {
136  } # just names would do, but currently refs to the actual
137  # configurables is needed for (temporary) backwards
138  # compatibility; will change in the future
139  _configurationLocked = False
140 
141  def __new__(cls, *args, **kwargs):
142  """To Gaudi, any object with the same type/name is the same object. Hence,
143  this is mimicked in the configuration: instantiating a new Configurable
144  of a type with the same name will return the same instance."""
145 
146  global log
147  # try to get the name of the Configurable (having a name is compulsory)
148  if 'name' in kwargs:
149  # simple keyword (by far the easiest)
150  name = kwargs['name']
151  elif 'name' in cls.__init__.func_code.co_varnames:
152  # either positional in args, or default
153  index = list(cls.__init__.func_code.co_varnames).index('name')
154  try:
155  # var names index is offset by one as __init__ is to be called with self
156  name = args[index - 1]
157  except IndexError:
158  # retrieve default value, then
159  name = cls.__init__.func_defaults[index - (len(args) + 1)]
160  else:
161  # positional index is assumed (will work most of the time)
162  try:
163  name = args[1] # '0' is for self
164  except (IndexError, TypeError):
165  raise TypeError('no "name" argument while instantiating "%s"' %
166  cls.__name__)
167 
168  argname = name
169  if name == Configurable.DefaultName:
170  if hasattr(cls, 'DefaultedName'):
171  name = cls.DefaultedName
172  else:
173  name = cls.getType()
174  elif not name or type(name) != str:
175  # unnamed, highly specialized user code, etc. ... unacceptable
176  raise TypeError(
177  'could not retrieve name from %s.__init__ arguments' %
178  cls.__name__)
179 
180  # Handle the case of global tools to prepend ToolSvc in the name.
181  # This is needed for compatibility with old JobOptions files being read
182  if issubclass(cls, ConfigurableAlgTool) and '.' not in name:
183  name = 'ToolSvc.' + name
184 
185  # close backdoor access to otherwise private subalgs/tools
186  # PM if 0 <= name.find( '.' ):
187  # PM # temp protection for old style types
188  # PM from OldStyleConfig import GenericConfigurable
189  # PM if not issubclass( cls, GenericConfigurable ): # except raised for new types only
190  # PM raise NameError( '"%s": backdoor access to private configurables not allowed' % name )
191 
192  # ordinary recycle case
193  if name in cls.configurables:
194  conf = cls.configurables[name]
195  if name != argname: # special case: user derived <-> real ... make same
196  cls.configurables[conf.getType()] = conf
197  # ---PM: Initialize additional properties
198  for n, v in kwargs.items():
199  if n != "name": # it should not be confused with a normal property
200  setattr(conf, n, v)
201  if not cls._configurationLocked and not "_enabled" in kwargs and isinstance(
202  conf, ConfigurableUser):
203  # Ensure that the ConfigurableUser gets enabled if nothing is
204  # specified in the constructor.
205  setattr(conf, "_enabled", True)
206  return conf
207 
208  # a couple of special cases (note that these cases don't mix)
209  spos = name.find('/')
210  ti_name = None
211  if spos < 0:
212  ti_name = "%s/%s" % (name, name)
213  if ti_name in cls.configurables:
214  # support for old-style name as type/name lookup where name==type
215  return cls.configurables[ti_name]
216 
217  i_name = None
218  if spos > 0:
219  i_name = name[:spos]
220  if i_name == name[spos + 1:] and i_name in cls.configurables:
221  # this is the opposite of the above special case
222  return cls.configurables[i_name]
223 
224  # the following is purely for debugging support and should realistically bomb
225  conf = cls.allConfigurables.get(name, None) or\
226  (spos < 0 and cls.allConfigurables.get(ti_name, None)) or\
227  (spos > 0 and i_name == name[spos + 1:]
228  and cls.allConfigurables.get(i_name, None))
229  if conf: # wrong type used?
230  if conf.__class__ is ConfigurableGeneric:
231  # If the instance found is ConfigurableGeneric then
232  # we create a new one with the proper type and fill with
233  # the contents of the generic one
234  newconf = object.__new__(cls)
235  cls.__init__(newconf, *args, **kwargs)
236  # initialize with the properties of generic configurable
237  # (we map the names of the properties to lowercase versions because
238  # old options are not case sensitive)
239  names = {}
240  for n in newconf.__slots__:
241  names[n.lower()] = n
242  for n in conf._properties:
243  if names[n.lower()] != n:
244  log.warning(
245  "Option '%s' was used for %s, but the correct spelling is '%s'"
246  % (n, name, names[n.lower()]))
247  setattr(newconf, names[n.lower()], getattr(conf, n))
248  for n, v in kwargs.items():
249  setattr(newconf, n, v)
250  cls.configurables[name] = newconf
251  cls.allConfigurables[name] = newconf
252  return newconf
253  else:
254  # will be an actual error in the future (now only report as such)
255  log.error(
256  'attempt to redefine type of "%s" (was: %s, new: %s)%s',
257  name, conf.__class__.__name__, cls.__name__,
258  error_explanation)
259  # in the future:
260  # return None # will bomb on use (or go unharmed on non-use)
261  # for now, allow use through allConfigurables lookup
262  # ---PM: Initialize additional properties
263  for n, v in kwargs.items():
264  setattr(conf, n, v)
265  return conf
266 
267  # still here: create a new instance and initialize it
268  conf = object.__new__(cls)
269  cls.__init__(conf, *args, **kwargs)
270 
271  # update normal, per-class cache
272  cls.configurables[name] = conf
273 
274  for base in cls.__bases__:
275  if base.__name__ == 'ConfigurableService':
276  cls.configurableServices[name] = conf
277 
278  # update generics super-cache, if needed
279  cls.allConfigurables[name] = conf
280  # -->PM#if hasattr( cls, 'getType' ) and name.find('/') < 0:
281  # -->PM# cls.allConfigurables[ cls.getType() + '/' + name ] = conf
282 
283  return conf
284 
285  def __init__(self, name=DefaultName):
286  # check class readiness, all required overloads should be there now
287  klass = self.__class__
288 
289  # this is an abstract class
290  if klass == Configurable:
291  raise TypeError, "%s is an ABC and can not be instantiated" % str(
292  Configurable)
293 
294  # the following methods require overloading
295  # NOT YET meths = { 'getServices' : 1, # retrieve list of services to configure
296  meths = {
297  'getDlls': 1, # provide list of Dlls to load
298  'getGaudiType': 1, # return string describing component class
299  'getHandle': 1
300  } # provide access to C++ side component instance
301  # 'getType' : 1 } # return the type of the actual C++ component
302 
303  for meth, nArgs in meths.items():
304  try:
305  f = getattr(klass, meth).im_func
306  except AttributeError:
307  raise NotImplementedError, "%s is missing in class %s" % (
308  meth, str(klass))
309 
310  # in addition, verify the number of arguments w/o defaults
311  nargcount = f.func_code.co_argcount
312  ndefaults = f.func_defaults and len(f.func_defaults) or 0
313  if not nargcount - ndefaults <= nArgs <= nargcount:
314  raise TypeError, "%s.%s requires exactly %d arguments" % (
315  klass, meth, nArgs)
316 
317  # for using this Configurable as a (Gaudi) sequence
318  self.__children = []
319  self.__tools = {}
320 
321  # know who we are
322  if name == Configurable.DefaultName:
323  if hasattr(self.__class__, 'DefaultedName'):
324  self._name = self.__class__.DefaultedName
325  else:
326  self._name = self.getType()
327  else:
328  self._name = name
329 
330  # set to True when collecting defaults, False otherwise
331  self._inSetDefaults = False
332 
333  # for later, in case __init__ itself is overridden
334  self._initok = True
335 
336  # for debugging purposes (temporary)
337  self._setupok = False
338 
339  # used to prevent spurious deprecation warnings when unpickling
340  self._unpickling = False
341 
342  # pickle support
343  def __getstate__(self):
344  dict = {}
345  for name, proxy in self._properties.items():
346  try:
347  dict[name] = proxy.__get__(self)
348  except AttributeError:
349  pass
350 
351  dict['_Configurable__children'] = self.__children
352  dict['_Configurable__tools'] = self.__tools
353  dict['_name'] = self._name
354  return dict
355 
356  def __getnewargs__(self):
357  return (self._name, )
358 
359  def __setstate__(self, dict):
360  self._initok = True
361  from contextlib import contextmanager
362 
363  @contextmanager
364  def unpickling():
365  try:
366  self._unpickling = True
367  yield
368  finally:
369  self._unpickling = False
370 
371  with unpickling():
372  for n, v in dict.items():
373  setattr(self, n, v)
374 
375  # to allow a few basic sanity checks, as well as nice syntax
376  def __len__(self):
377  return len(self.__children)
378 
379  def __iter__(self):
380  return iter(self.__children)
381 
382  # ownership rules of self through copying
383  def __deepcopy__(self, memo):
384  newconf = object.__new__(self.__class__)
385  self.__class__.__init__(newconf, self.getName())
386 
387  for proxy in self._properties.values():
388  try:
389  proxy.__set__(newconf, proxy.__get__(self))
390  except AttributeError:
391  pass # means property was not set for self
392 
393  for c in self.__children:
394  newconf += c # processes proper copy semantics
395 
396  return newconf
397 
398  # hierarchy building, and ownership rules of children
399  def __iadd__(self, configs, descr=None):
400  if not type(configs) in (list, tuple):
401  configs = (configs, )
402 
403  joname = self.getJobOptName()
404 
405  for cfg in configs:
406  # prevent type mismatches
407  if not isinstance(cfg, Configurable):
408  raise TypeError("'%s' is not a Configurable" % str(cfg))
409 
410  cc = self.copyChildAndSetParent(cfg, joname)
411 
412  # filters dupes; usually "ok" (backdoor should catch them)
413  ccjo = cc.getJobOptName()
414  for c in self.__children:
415  if c.getJobOptName() == ccjo:
416  log.error('attempt to add a duplicate ... dupe ignored%s',
417  error_explanation)
418  break
419  else:
420  self.__children.append(cc)
421 
422  try:
423  if descr: # support for tool properties
424  descr.__set__(self, cc)
425  else:
426  setattr(self, cc.getName(), cc)
427  except AttributeError:
428  pass # to allow free addition of tools/subalgorithms
429 
430  return self
431 
432  def __getattr__(self, attr): # until ToolProperties exist ...
433 
434  if attr in self.__tools:
435  return self.__tools[attr]
436 
437  if attr in self._properties:
438  if isinstance(self._properties[attr].__get__(self),
439  DataObjectHandleBase):
440  return self._properties[attr].__get__(self)
441 
442  for c in self.__children:
443  if c.getName() == attr:
444  return c
445 
446  raise AttributeError(
447  "'%s' object has no attribute '%s'" % (self.__class__, attr))
448 
449  def __setattr__(self, name, value):
450  if self._configurationLocked:
451  raise RuntimeError(
452  "%s: Configuration cannot be modified after the ApplicationMgr has been started."
453  % self.name())
454  try:
455  super(Configurable, self).__setattr__(name, value)
456  except AttributeError:
457  raise AttributeError(
458  "Configurable '%s' does not have property '%s'." %
459  (self.__class__.__name__, name))
460 
461  def __delattr__(self, attr):
462  # remove as property, otherwise try as child
463  try:
464  # remove history etc., then reset to default (in case set before)
465  prop = self._properties[attr]
466  prop.__delete__(self)
467  prop.__set__(self, prop.default)
468  return # reaches here? was property: done now
469  except KeyError:
470  pass
471  # otherwise, remove the private tool
472  if attr in self.__tools:
473  del self.__tools[attr]
474 
475  # otherwise, remove child, if one is so named
476  for c in self.__children:
477  if c.getName() == attr:
478  self.__children.remove(c)
479 
480  # potentially, there are left over caches (certain user derived classes)
481  try:
482  del self.__dict__[attr]
483  except (AttributeError, KeyError):
484  pass
485 
486  def __nonzero__(self):
487  return True
488 
489  def remove(self, items):
490  if type(items) != list and type(items) != tuple:
491  items = [items]
492 
493  self.__children = [e for e in self.__children if not e in items]
494 
495  def removeAll(self):
496  self.remove(self.__children)
497 
498  # called by __iadd__; determines child copy semantics
499  def copyChild(self, child):
500  return copy.deepcopy(child)
501 
502  def setParent(self, parentName):
503  pass
504 
505  def getParent(self):
506  return ""
507 
508  def hasParent(self, parent):
509  return False
510 
511  def copyChildAndSetParent(self, cfg, parent):
512  cc = self.copyChild(cfg)
513 
514  if hasattr(cc, 'setParent') and parent:
515  try:
516  cc.setParent(parent)
517  except RuntimeError, e:
518  # temporary backdoor resolution for compatibility
519  log.error(str(e) + '%s', error_explanation)
520  ccbd = cc.configurables[cc.getJobOptName()]
521 
522  # merge properties, new over pre-existing
523  for proxy in self._properties.values():
524  if proxy.history.has_key(cc):
525  proxy.__set__(ccbd, proxy.__get__(cc))
526 
527  # consolidate
528  cc = ccbd
529  return cc
530 
531  def getChildren(self):
532  return self.__children[:] # read only
533 
534  def getTools(self):
535  return self.__tools.values() # read only
536 
537  def children(self):
538  log.error(
539  "children() is deprecated, use getChildren() instead for consistency"
540  )
541  log.error(
542  "getChildren() returns a copy; to add a child, use 'parent += child'%s",
543  error_explanation)
544  return self.__children # by ref, for compatibility
545 
546  def getAllChildren(self):
547  """Get all (private) configurable children, both explicit ones (added with +=)
548  and the ones in the private GaudiHandle properties"""
549  childs = []
550  # add private configurable properties (also inside handles)
551  for proxy in self._properties.values():
552  try:
553  c = proxy.__get__(self)
554  except AttributeError:
555  pass
556  else:
557  if isinstance(c, Configurable) and not c.isPublic():
558  childs.append(c)
559  elif isinstance(c, GaudiHandle):
560  try:
561  conf = c.configurable
562  except AttributeError:
563  pass
564  else:
565  if not conf.isPublic():
566  childs.append(conf)
567  elif isinstance(c, GaudiHandleArray):
568  # only setup private arrays
569  if not c.isPublic():
570  for ci in c:
571  if isinstance(ci, Configurable):
572  childs.append(ci)
573  else:
574  try:
575  conf = ci.configurable
576  except AttributeError:
577  pass
578  else:
579  childs.append(conf)
580 
581  # add explicit children
582  childs += self.__children
583  return childs
584 
585  def getSequence(self):
586  elems = []
587  for c in self.__children:
588  elems.append(c.getFullName())
589  return elems
590 
591  def setup(self):
592  # make sure base class init has been called
593  if not hasattr(self, '_initok') or not self._initok:
594  # could check more, but this is the only explanation
595  raise TypeError, \
596  "Configurable.__init__ not called in %s override" % self.__class__.__name__
597 
598 # log.debug("calling setup() on " + self.getFullJobOptName())
599 
600 # setup self: this collects all values on the python side
601  self.__setupServices()
602  self.__setupDlls()
603  self.__setupDefaults()
604 
605  # setup children
606  for c in self.getAllChildren():
607  c.setup()
608 
609  # now get handle to work with for moving properties into the catalogue
610  handle = self.getHandle()
611  if not handle:
612  log.debug('no handle for %s: not transporting properties',
613  self._name)
614  return # allowed, done early
615 
616  # pass final set of properties on to handle on the C++ side or JobOptSvc
617  for name in self._properties.keys():
618  if hasattr(self,
619  name): # means property has python-side value/default
620  setattr(handle, name, getattr(self, name))
621 
622  # for debugging purposes
623  self._setupok = True
624 
625  def getProperties(self):
626  props = {}
627  for name, proxy in self._properties.items():
628  try:
629  props[name] = proxy.__get__(self)
630  except AttributeError:
631  props[name] = Configurable.propertyNoValue
632 
633  return props
634 
636  """Get all properties with their description string as { name : (value, desc) }."""
637  props = {}
638  for name, proxy in self._properties.items():
639  try:
640  props[name] = (proxy.__get__(self), proxy.__doc__)
641  except AttributeError:
642  props[name] = (Configurable.propertyNoValue, proxy.__doc__)
643  return props
644 
646  props = {}
647  for name, proxy in self._properties.items():
648  if self.isPropertySet(name):
649  value = proxy.__get__(self)
650  if hasattr(value, 'getFullName'):
651  value = value.getFullName()
652  elif type(value) in [list, tuple]:
653  new_value = []
654  for i in value:
655  if hasattr(i, 'getFullName'):
656  new_value.append(i.getFullName())
657  else:
658  new_value.append(i)
659  value = type(value)(new_value)
660  elif type(value) is dict:
661  new_value = {}
662  for i in value:
663  if hasattr(value[i], 'getFullName'):
664  new_value[i] = value[i].getFullName()
665  else:
666  new_value[i] = value[i]
667  value = new_value
668  props[name] = value
669 
670  return props
671 
672  def properties(self):
673  return self.getProperties() # compatibility
674 
675  @classmethod
677  class collector:
678  pass
679 
680  # user provided defaults
681  c = collector()
682  cls.setDefaults(c)
683 
684  # defaults from C++
685  for k, v in cls._properties.items():
686  if not k in c.__dict__ and hasattr(v, 'default'):
687  c.__dict__[k] = v.default
688 
689  return c.__dict__
690 
691  @classmethod
692  def getDefaultProperty(cls, name):
693  class collector:
694  pass
695 
696  # user provided defaults
697  c = collector()
698  cls.setDefaults(c)
699 
700  if name in c.__dict__:
701  return c.__dict__[name]
702 
703  # defaults from C++
704  try:
705  v = cls._properties[name]
706  if hasattr(v, 'default'):
707  return v.default
708  except KeyError:
709  pass
710 
711  return None
712 
713  def getProp(self, name):
714  """Returns the value of the given property.
715  """
716  if hasattr(self, name):
717  return getattr(self, name)
718  else:
719  return self.getDefaultProperties()[name]
720 
721  def setProp(self, name, value):
722  """Set the value of a given property
723  """
724  return setattr(self, name, value)
725 
726  def isPropertySet(self, name):
727  """Tell if the property 'name' has been set or not.
728 
729  Because of a problem with list and dictionary properties, in those cases
730  if the value is equal to the default, the property is considered as not
731  set.
732  """
733  if not hasattr(self, name):
734  return False
735  default = self.getDefaultProperty(name)
736  if isinstance(default, (list, dict, DataObjectHandleBase)):
737  value = getattr(self, name)
738  return value != default
739  return True
740 
741  def getType(cls):
742  return cls.__name__
743 
744  def getName(self):
745  return self._name
746 
747  def name(self):
748  return self.getName()
749 
750  def getJobOptName(self): # full hierachical name
751  return self.getName()
752 
753  def isPublic(self):
754  return True
755 
756  # for a couple of existing uses out there
757  def jobOptName(self):
758  log.error(
759  "jobOptName() is deprecated, use getJobOptName() instead for consistency%s",
760  error_explanation)
761  return self.getJobOptName() # compatibility
762 
763  def getFullName(self):
764  return str(self.getType() + '/' + self.getName())
765 
766  def getFullJobOptName(self):
767  return "%s/%s" % (self.getType(), self.getJobOptName()
768  or self.getName())
769 
770  def getPrintTitle(self):
771  return self.getGaudiType() + ' ' + self.getTitleName()
772 
773  def getTitleName(self):
774  if log.isEnabledFor(logging.DEBUG):
775  return self.getFullJobOptName()
776  else:
777  return self.getFullName()
778 
779  def setDefaults(cls, handle):
780  pass
781 
782  def clone(self, name=None, **kwargs):
783  if not name:
784  if hasattr(self, 'DefaultedName'):
785  name = self.DefaultedName
786  else:
787  name = self.getType()
788 
789  newconf = Configurable.__new__(self.__class__, name)
790  self.__class__.__init__(newconf, name)
791 
792  for proxy in self._properties.values():
793  try:
794  value = proxy.__get__(self)
795  if type(value) in [str, list, dict, tuple]:
796  # clone the values of the properties for basic types
797  value = type(value)(value)
798  proxy.__set__(newconf, value)
799  except AttributeError:
800  pass
801 
802  for c in self.__children:
803  newconf += c # processes proper copy semantics
804 
805  for n, t in self.__tools.items():
806  newconf.addTool(t, n)
807 
808  for name, value in kwargs.items():
809  setattr(newconf, name, value)
810 
811  return newconf
812 
813  def splitName(self):
814  fullname = self.getName()
815  dot = fullname.find('.')
816  if dot != -1:
817  parentname = fullname[:dot]
818  longname = fullname[dot + 1:]
819  else:
820  parentname = ''
821  longname = fullname
822  dot = longname.find('.')
823  if dot != -1:
824  name = longname[:dot]
825  else:
826  name = longname
827  return parentname, name, longname
828 
829  def addTool(self, tool, name=None):
830  if isclass(tool) and issubclass(tool, ConfigurableAlgTool):
831  if name is None:
832  name = tool.__name__
833  priv_tool = tool(self.getName() + '.' + name)
834  elif isinstance(tool, ConfigurableAlgTool):
835  if name is None:
836  name = tool.splitName()[1]
837  priv_tool = tool.clone(self.getName() + '.' + name)
838  else:
839  if isclass(tool):
840  classname = tool.__name__
841  else:
842  classname = type(tool).__name__
843  raise TypeError, "addTool requires AlgTool configurable. Got %s type" % classname
844  self.__tools[name] = priv_tool
845  if name in self.__slots__:
846  # this is to avoid that the property hides the tool
847  setattr(self, name, self.__tools[name])
848  return self.__tools[name]
849 
850  def _isInSetDefaults(self):
851  return self._inSetDefaults
852 
853  def __setupServices(self):
854  #svcs = self.getServices()
855  # if not svcs:
856  svcs = []
857  # elif type(svcs) == types.StringType:
858  # svcs = [ svcs ]
859 
860  import __main__
861  for svc in svcs:
862  handle = __main__.Service(svc)
863  # services should be configurables as well, but aren't for now
864  # handle.setup()
865 
866  # allow Configurable to make some changes
867  if hasattr(self, 'configure' + svc):
868  eval('self.configure' + svc + '( handle )')
869 
870  def __setupDlls(self):
871  dlls = self.getDlls()
872  if not dlls:
873  dlls = []
874  elif type(dlls) == types.StringType:
875  dlls = [dlls]
876 
877  from __main__ import theApp
878  dlls = filter(lambda d: d not in theApp.Dlls, dlls)
879  if dlls:
880  theApp.Dlls += dlls
881 
882  def __setupDefaults(self):
883  # set handle defaults flags to inform __setattr__ that it is being
884  # called during setDefaults of the concrete Configurable
885  self._inSetDefaults = True
886  self.setDefaults(self)
887  self._inSetDefaults = False
888 
889  @staticmethod
890  def _printHeader(indentStr, title):
891  preLen = Configurable.printHeaderPre
892  postLen = Configurable.printHeaderWidth - \
893  preLen - 3 - len(title) # - len(indentStr)
894  postLen = max(preLen, postLen)
895  return indentStr + '/%s %s %s' % (preLen * '*', title, postLen * '*')
896 
897  @staticmethod
898  def _printFooter(indentStr, title):
899  preLen = Configurable.printHeaderPre
900  postLen = Configurable.printHeaderWidth - \
901  preLen - 12 - len(title) # - len(indentStr)
902  postLen = max(preLen, postLen)
903  return indentStr + '\\%s (End of %s) %s' % (preLen * '-', title,
904  postLen * '-')
905 
906  def __repr__(self):
907  return '{0}({1!r})'.format(self.__class__.__name__, self.name())
908 
909  def __str__(self, indent=0, headerLastIndentUnit=indentUnit):
910  global log # to print some info depending on output level
911  indentStr = indent * Configurable.indentUnit
912  # print header
913  title = self.getPrintTitle()
914  # print line to easily see start-of-configurable
915  if indent > 0:
916  headerIndent = (indent - 1) * \
917  Configurable.indentUnit + headerLastIndentUnit
918  else:
919  headerIndent = ''
920  rep = Configurable._printHeader(headerIndent, title)
921  rep += os.linesep
922  # print own properties
923  props = self.getProperties()
924  defs = self.getDefaultProperties()
925  if not props:
926  rep += indentStr + '|-<no properties>' + os.linesep
927  else:
928  # get property name with
929  nameWidth = 0
930  for p in props.keys():
931  nameWidth = max(nameWidth, len(p))
932  for p, v in props.items():
933  # start with indent and property name
934  prefix = indentStr + '|-%-*s' % (nameWidth, p)
935  # add memory address for debugging (not for defaults)
936  if log.isEnabledFor(logging.DEBUG):
937  if v != Configurable.propertyNoValue:
938  address = ' @%11s' % hex(id(v))
939  else:
940  address = 13 * ' '
941  prefix += address
942  # add value and default
943  default = defs.get(p)
944  if v == Configurable.propertyNoValue:
945  # show default value as value, and no extra 'default'
946  strVal = repr(default)
947  strDef = None
948  else:
949  # convert configurable to handle
950  if hasattr(v, "getGaudiHandle"):
951  vv = v.getGaudiHandle()
952  else:
953  vv = v
954  if isinstance(vv, GaudiHandle) or isinstance(
955  vv, GaudiHandleArray):
956  strVal = repr(vv)
957  # the default may not be a GaudiHandle (?)
958  if hasattr(default, "toStringProperty"):
959  strDef = repr(default.toStringProperty())
960  else:
961  strDef = repr(default)
962  if strDef == repr(vv.toStringProperty()):
963  strDef = None
964  else:
965  strVal = repr(vv)
966  strDef = repr(default)
967  # add the value
968  line = prefix + ' = ' + strVal
969  # add default if present
970  if strDef is not None:
971  # put default on new line if too big
972  if len(line) + len(strDef) > Configurable.printHeaderWidth:
973  line += os.linesep + indentStr + '| ' + \
974  (len(prefix) - len(indentStr) - 3) * ' '
975  line += ' (default: %s)' % (strDef, )
976  # add the line to the total string
977  rep += line + os.linesep
978  # print out full private configurables
979 # if isinstance(v,Configurable) and not v.isPublic():
980 ## rep += v.__str__( indent + 1 ) + os.linesep
981 # elif isinstance(v,GaudiHandleArray):
982 # for vi in v:
983 # if isinstance(vi,Configurable) and not vi.isPublic():
984 ## rep += vi.__str__( indent + 1 ) + os.linesep
985 
986 # print configurables + their properties, or loop over sequence
987 # for cfg in self.__children:
988  for cfg in self.getAllChildren():
989  rep += cfg.__str__(indent + 1, '|=') + os.linesep
990 
991  # print line to easily see end-of-configurable. Note: No linesep!
992  rep += Configurable._printFooter(indentStr, title)
993  return rep
994 
995  def isApplicable(self):
996  '''
997  Return True is the instance can be "applied".
998  Always False for plain Configurable instances
999  (i.e. not ConfigurableUser).
1000  '''
1001  return False
1002 
1003 
1004 # classes for generic Gaudi component ===========
1005 
1006 
1007 class DummyDescriptor(object):
1008  def __init__(self, name):
1009  self.__name__ = name # conventional
1010 
1011  def __get__(self, obj, type=None):
1012  return getattr(obj, self.__name__)
1013 
1014  def __set__(self, obj, value):
1015  object.__setattr__(obj, self.__name__, value)
1016 
1017 
1019  #__slots__ = { }
1020 
1021  def __init__(self, name=Configurable.DefaultName):
1022  Configurable.__init__(self, name)
1023  self._name = name
1024  self._properties = {}
1025 
1026  def __deepcopy__(self, memo):
1027  return self # algorithms are always shared
1028 
1029  def getGaudiType(self):
1030  return 'GenericComponent'
1031 
1032  def getDlls(self):
1033  pass
1034 
1035  def getHandle(self):
1036  pass
1037 
1038  def __setattr__(self, name, value):
1039  # filter private (user) variables
1040  if name[0] == '_':
1041  super(ConfigurableGeneric, self).__setattr__(name, value)
1042  return
1043 
1044  # filter configurable types
1045  if isinstance(value, Configurable):
1046  self.__dict__[name] = value
1047  return
1048 
1049  # assume all the rest are properties
1050  if not name in self._properties:
1051  self._properties[name] = PropertyProxy(DummyDescriptor(name))
1052  self._properties[name].__set__(self, value)
1053 
1054  def getJobOptName(self):
1055  return None
1056 
1057 
1058 # base classes for individual Gaudi algorithms/services/algtools ===========
1060  __slots__ = {
1061  '_jobOptName': 0,
1062  'OutputLevel': 0,
1063  'Enable': 1,
1064  'ErrorMax': 1,
1065  'ErrorCount': 0,
1066  'AuditAlgorithms': 0,
1067  'AuditInitialize': 0,
1068  'AuditReinitialize': 0,
1069  'AuditExecute': 0,
1070  'AuditFinalize': 0,
1071  'AuditBeginRun': 0,
1072  'AuditEndRun': 0
1073  }
1074 
1075  def __init__(self, name=Configurable.DefaultName):
1076  super(ConfigurableAlgorithm, self).__init__(name)
1077  name = self.getName()
1078  self._jobOptName = name[name.find('/') + 1:] # strips class
1079 
1080  def __deepcopy__(self, memo):
1081  return self # algorithms are always shared
1082 
1083  def getHandle(self):
1084  return iAlgorithm(self.getJobOptName())
1085 
1086  def getGaudiType(self):
1087  return 'Algorithm'
1088 
1089  def getJobOptName(self):
1090  return self._jobOptName
1091 
1092  # mimick the ControlFlowLeaf interface
1093  def __and__(self, rhs):
1094  if rhs is CFTrue:
1095  return self
1096  elif rhs is CFFalse:
1097  return CFFalse
1098  return AndNode(self, rhs)
1099 
1100  def __or__(self, rhs):
1101  if rhs is CFFalse:
1102  return self
1103  elif rhs is CFTrue:
1104  return CFTrue
1105  return OrNode(self, rhs)
1106 
1107  def __invert__(self):
1108  return InvertNode(self)
1109 
1110  def __rshift__(self, rhs):
1111  return OrderedNode(self, rhs)
1112 
1113  def visitNode(self, visitor):
1114  visitor.enter(self)
1115  self._visitSubNodes(visitor)
1116  visitor.leave(self)
1117 
1118  def _visitSubNodes(self, visitor):
1119  pass
1120 
1121  def __eq__(self, other):
1122  return (repr(self) == repr(other))
1123 
1124 
1126  __slots__ = {
1127  'OutputLevel': 0,
1128  'AuditServices': 0,
1129  'AuditInitialize': 0,
1130  'AuditFinalize': 0
1131  }
1132 
1133  def __deepcopy__(self, memo):
1134  return self # services are always shared
1135 
1136  def copyChild(self, child):
1137  return child # full sharing
1138 
1139  def getHandle(self):
1140  return iService(self._name)
1141 
1142  def getGaudiType(self):
1143  return 'Service'
1144 
1145  def getGaudiHandle(self):
1146  return ServiceHandle(self.toStringProperty())
1147 
1148  def toStringProperty(self):
1149  # called on conversion to a string property for the jocat
1150  return self.getName()
1151 
1152 
1154  __slots__ = {
1155  '_jobOptName': '',
1156  'OutputLevel': 0,
1157  'AuditTools': 0,
1158  'AuditInitialize': 0,
1159  'AuditFinalize': 0
1160  }
1161 
1162  def __init__(self, name=Configurable.DefaultName):
1163  super(ConfigurableAlgTool, self).__init__(name)
1164  if '.' not in self._name:
1165  # Public tools must have ToolSvc as parent
1166  self._name = "ToolSvc." + self._name
1167  name = self.getName()
1168  name = name[name.find('/') + 1:] # strips class, if any
1169  self._jobOptName = name
1170 
1171  def getHandle(self):
1172  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1173  return iProperty(self.getJobOptName())
1174 
1175  def getGaudiType(self):
1176  return 'AlgTool'
1177 
1178  def getGaudiHandle(self):
1179  if self.isPublic():
1180  return PublicToolHandle(self.toStringProperty())
1181  else:
1182  return PrivateToolHandle(self.toStringProperty())
1183 
1184  def getPrintTitle(self):
1185  if self.isPublic():
1186  pop = 'Public '
1187  else:
1188  pop = 'Private '
1189  return pop + Configurable.getPrintTitle(self)
1190 
1191  def setParent(self, parentName):
1192  # print "ConfigurableAlgTool.setParent(%s@%x,%r)" % (self.getName(),id(self),parentName)
1193  # print "Calling stack:"
1194  # import traceback
1195  # traceback.print_stack()
1196  # propagate parent to AlgTools in children
1197  for c in self.getAllChildren():
1198  if isinstance(c, ConfigurableAlgTool):
1199  c.setParent(parentName)
1200 
1201  # update my own parent
1202  name = self.getName()
1203  name = name[name.rfind('.') + 1:] # Name of the instance
1204  self._jobOptName = self._name = parentName + '.' + name
1205 
1206  def getParent(self):
1207  dot = self._jobOptName.rfind('.')
1208  if dot != -1:
1209  return self._jobOptName[:dot]
1210  else:
1211  return ""
1212 
1213  def hasParent(self, parent):
1214  return self._jobOptName.startswith(parent + '.')
1215 
1216  def getJobOptName(self):
1217  return self._jobOptName
1218 
1219  def isPublic(self):
1220  return self.isInToolSvc()
1221 
1222  def isInToolSvc(self):
1223  return self._jobOptName.startswith('ToolSvc.')
1224 
1225  def toStringProperty(self):
1226  # called on conversion to a string property for the jocat
1227  return self.getFullName()
1228 
1229  def getFullName(self):
1230  # for Tools, the "full name" means "Type/LocalName",
1231  # without the names of the parents
1232  name = self.getName()
1233  # strip off everything before the last '.'
1234  name = name[name.rfind('.') + 1:]
1235  return str(self.getType() + '/' + name)
1236 
1237 
1238 # FIXME: this is just a placeholder, waiting for a real implementation
1239 # It is sufficient to get us going... (and import a PkgConf which
1240 # happens to contain an Auditor...)
1242  __slots__ = {'_jobOptName': 0, 'OutputLevel': 0, 'Enable': 1}
1243 
1244  def __init__(self, name=Configurable.DefaultName):
1245  super(ConfigurableAuditor, self).__init__(name)
1246  name = self.getName()
1247  name = name[name.find('/') + 1:] # strips class, if any
1248  self._jobOptName = name
1249 
1250  def getHandle(self):
1251  # iAlgTool isn't useful, unless one knows for sure that the tool exists
1252  return iProperty(self.getJobOptName())
1253 
1254  def getGaudiType(self):
1255  return 'Auditor'
1256 
1257  def getJobOptName(self):
1258  return self._jobOptName
1259 
1260  def toStringProperty(self):
1261  # called on conversion to a string property for the jocat
1262  return self.getType() + '/' + self.getName()
1263 
1264 
1266  __slots__ = {
1267  "__users__": [],
1268  "__used_instances__": [],
1269  "_enabled": True,
1270  "_applied": False
1271  }
1272  # list of ConfigurableUser classes this one is going to modify in the
1273  # __apply_configuration__ method.
1274  # The list may contain class objects, strings representing class objects or
1275  # tuples with the class object (or a string) as first element and the instance
1276  # name as second element.
1277  # If the instance name is None or not present, the function _instanceName()
1278  # is used to determine the name of the instance (the default implementation
1279  # returns "<this name>_<other name>".
1280  __used_configurables__ = []
1281  # list of ConfigurableUser classes this one is going to query in the
1282  # __apply_configuration__ method
1283  __queried_configurables__ = []
1284 
1285  def __init__(self, name=Configurable.DefaultName, _enabled=True, **kwargs):
1286  super(ConfigurableUser, self).__init__(name)
1287  for n, v in kwargs.items():
1288  setattr(self, n, v)
1289  self._enabled = _enabled
1290  self.__users__ = []
1291  self._applied = False
1292 
1293  # Needed to retrieve the actual class if the declaration in __used_configurables__
1294  # and __queried_configurables__ is done with strings.
1295  from GaudiKernel.ConfigurableDb import getConfigurable as confDbGetConfigurable
1296 
1297  # Set the list of users of the used configurables
1298  #
1300  for used in self.__used_configurables__:
1301  # By default we want to use the default name of the instances
1302  # for the used configurables
1303  used_name = Configurable.DefaultName
1304  # If the entry in the list is a tuple, we need a named instance
1305  if type(used) is tuple:
1306  used, used_name = used # we re-set used to re-use the code below
1307  if not used_name:
1308  used_name = self._instanceName(used)
1309  # Check is 'used' is a string or not
1310  if type(used) is str:
1311  used_class = confDbGetConfigurable(used)
1312  else:
1313  used_class = used
1314  # Instantiate the configurable that we are going to use
1315  try:
1316  inst = used_class(name=used_name, _enabled=False)
1317  except AttributeError:
1318  # This cover the case where the used configurable is not a
1319  # ConfigurableUser instance, i.e. id doesn't have the attribute
1320  # '_enabled'.
1321  inst = used_class(name=used_name)
1322  self.__addActiveUseOf(inst)
1323  for queried in self.__queried_configurables__:
1324  try:
1325  if type(queried) is str:
1326  queried = confDbGetConfigurable(used)
1327  inst = queried(_enabled=False)
1328  except AttributeError:
1329  inst = queried()
1330  self.__addPassiveUseOf(inst)
1331 
1332  def __addActiveUseOf(self, other):
1333  """
1334  Declare that we are going to modify the Configurable 'other' in our
1335  __apply_configuration__.
1336  """
1337  self.__used_instances__.append(other)
1338  if hasattr(other, "__users__"): # allow usage of plain Configurables
1339  other.__users__.append(self)
1340 
1341  def __addPassiveUseOf(self, other):
1342  """
1343  Declare that we are going to retrieve property values from the
1344  ConfigurableUser 'other' in our __apply_configuration__.
1345  """
1346  if not isinstance(other, ConfigurableUser):
1347  raise Error(
1348  "'%s': Cannot make passive use of '%s', it is not a ConfigurableUser"
1349  % (self.name(), other.name()))
1350  other.__addActiveUseOf(self)
1351 
1352  def getGaudiType(self):
1353  return 'User'
1354 
1355  def getDlls(self):
1356  return None
1357 
1358  def getHandle(self):
1359  return None
1360 
1361  def __detach_used__(self):
1362  """
1363  Remove this ConfigurableUser instance from the users list of the used
1364  instances.
1365  """
1366  for used in self.__used_instances__:
1367  if hasattr(used,
1368  "__users__"): # allow usage of plain Configurables
1369  used.__users__.remove(self)
1370 
1371  def propagateProperty(self, name, others=None, force=True):
1372  """
1373  Propagate the property 'name' (if set) to other configurables (if possible).
1374  'others' can be:
1375  None:
1376  propagate to all the entries in __used_configurables__
1377  a configurable instance:
1378  propagate only to it
1379  list of configurable instances:
1380  propagate to all of them.
1381 
1382 
1383  The logic is:
1384  - if the local property is set, the other property will be overwritten
1385  - local property not set and other set => keep other
1386  - local property not set and other not set => overwrite the default for
1387  ConfigurableUser instances and set the property for Configurables
1388  """
1389  # transform 'others' to a list of configurable instances
1390  if others is None:
1391  others = self.__used_instances__
1392  elif type(others) not in [list, tuple]:
1393  others = [others]
1394  # these can be computed before the loop
1395  local_is_set = self.isPropertySet(name)
1396  value = self.getProp(name)
1397  # loop over the others that do have 'name' in their slots
1398  for other in [o for o in others if name in o.__slots__]:
1399  # If self property is set, use it
1400  if local_is_set:
1401  if other.isPropertySet(name):
1402  log.warning(
1403  "Property '%(prop)s' is set in both '%(self)s' and '%(other)s', using '%(self)s.%(prop)s'"
1404  % {
1405  "self": self.name(),
1406  "other": other.name(),
1407  "prop": name
1408  })
1409  other.setProp(name, value)
1410  # If not, and other property also not set, propagate the default
1411  elif not other.isPropertySet(name):
1412  if isinstance(other, ConfigurableUser):
1413  otherType = type(other._properties[name].getDefault())
1414  other._properties[name].setDefault(value)
1415  if otherType in [list, dict]:
1416  # Special case for list and dictionaries:
1417  # also set the property to the same value of the default (copy)
1418  other.setProp(name, otherType(value))
1419  else:
1420  other.setProp(name, value)
1421  # If not set and other set, do nothing
1422 
1423  def propagateProperties(self, names=None, others=None, force=True):
1424  """
1425  Call propagateProperty for each property listed in 'names'.
1426  If 'names' is None, all the properties are propagated.
1427  """
1428  if names is None:
1429  # use all the non-private slots
1430  names = [p for p in self.__slots__ if not p.startswith("_")]
1431  for n in names:
1432  self.propagateProperty(n, others, force)
1433 
1435  """
1436  Function to be overridden to convert the high level configuration into a
1437  low level one.
1438  The default implementation calls applyConf, which is the method defined
1439  in some ConfigurableUser implementations.
1440  """
1441  return self.applyConf()
1442 
1443  def applyConf(self):
1444  """
1445  Function to be overridden to convert the high level configuration into a
1446  low level one.
1447  """
1448  pass
1449 
1450  def _instanceName(self, cls):
1451  """
1452  Function used to define the name of the private instance of a given class
1453  name.
1454  This method is used when the __used_configurables_property__ declares the
1455  need of a private used configurable without specifying the name.
1456  """
1457  if type(cls) is str:
1458  clName = cls
1459  else:
1460  clName = cls.__name__
1461  return "%s_%s" % (self.name(), clName)
1462 
1463  def getUsedInstance(self, name):
1464  """
1465  Return the used instance with a given name.
1466  """
1467  for i in self.__used_instances__:
1468  if i.name() == name:
1469  if hasattr(i, "_enabled"):
1470  # ensure that the instances retrieved through the method are
1471  # enabled
1472  i._enabled = True
1473  return i
1474  raise KeyError(name)
1475 
1476  def isApplicable(self):
1477  '''
1478  Return True is the instance can be "applied".
1479  '''
1480  return (not self.__users__) and (not self._applied)
1481 
1482 
1483 # list of callables to be called after all the __apply_configuration__ are called.
1484 postConfigActions = []
1485 
1486 
1488  """
1489  Add a new callable ('function') to the list of post-configuration actions.
1490  If the callable is already in the list, it is moved to the end of the list.
1491  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1492  """
1493  try:
1494  postConfigActions.remove(function)
1495  except:
1496  pass
1497  postConfigActions.append(function)
1498 
1499 
1501  """
1502  Remove a callable from the list of post-config actions.
1503  The list is directly accessible as 'GaudiKernel.Configurable.postConfigActions'.
1504  """
1505  postConfigActions.remove(function)
1506 
1507 
1508 _appliedConfigurableUsers_ = False
1509 
1510 
1512  """
1513  Call the apply method of all the ConfigurableUser instances respecting the
1514  dependencies. First the C.U.s that are not used by anybody, then the used
1515  ones, when they are not used anymore.
1516  """
1517  # Avoid double calls
1518  global _appliedConfigurableUsers_, postConfigActions
1519  if _appliedConfigurableUsers_:
1520  return
1521  _appliedConfigurableUsers_ = True
1522 
1523  def applicableConfUsers():
1524  '''
1525  Generator returning all the configurables that can be applied in the
1526  order in which they can be applied.
1527  '''
1528  # This is tricky...
1529  # We keep on looking for the first configurable that can be applied.
1530  # When we cannot find any, 'next()' raises a StopIteration that is
1531  # propagated outside of the infinite loop and the function, then handled
1532  # correctly from outside (it is the exception that is raised when you
1533  # exit from a generator).
1534  # Checking every time the full list is inefficient, but it is the
1535  # easiest way to fix bug #103803.
1536  # <https://savannah.cern.ch/bugs/?103803>
1537  while True:
1538  yield (c for c in Configurable.allConfigurables.values()
1539  if c.isApplicable()).next()
1540 
1541  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1542  for c in applicableConfUsers():
1543  if c._enabled:
1544  log.info("applying configuration of %s", c.name())
1545  if debugApplyOrder:
1546  sys.stderr.write('applying %r' % c)
1547  c.__apply_configuration__()
1548  log.info(c)
1549  else:
1550  log.info("skipping configuration of %s", c.name())
1551  c._applied = True # flag the instance as already applied
1552  if hasattr(c, "__detach_used__"):
1553  # tells the used configurables that they are not needed anymore
1554  c.__detach_used__()
1555 
1556  # check for dependency loops
1557  leftConfUsers = [
1558  c for c in Configurable.allConfigurables.values() if
1559  hasattr(c, '__apply_configuration__') and c._enabled and not c._applied
1560  ]
1561  # if an enabled configurable has not been applied, there must be a dependency loop
1562  if leftConfUsers:
1563  raise Error("Detected loop in the ConfigurableUser"
1564  " dependencies: %r" % [c.name() for c in leftConfUsers])
1565  # ensure that all the Handles have been triggered
1566  known = set()
1567  unknown = set(Configurable.allConfigurables)
1568  while unknown:
1569  for k in unknown:
1570  if not known: # do not print during the first iteration
1571  log.debug('new configurable created automatically: %s', k)
1572  # this trigger the instantiation from handles
1573  Configurable.allConfigurables[k].properties()
1574  known.add(k)
1575  unknown -= known
1576  # Call post-config actions
1577  for action in postConfigActions:
1578  action()
1579 
1580 
1582  """
1583  Obsolete (buggy) implementation of applyConfigurableUsers(), kept to provide
1584  backward compatibility for configurations that where relying (implicitly) on
1585  bug #103803, or on a specific (non guaranteed) order of execution.
1586 
1587  @see applyConfigurableUsers()
1588  """
1589  # Avoid double calls
1590  global _appliedConfigurableUsers_, postConfigActions
1591  if _appliedConfigurableUsers_:
1592  return
1593  _appliedConfigurableUsers_ = True
1594 
1595  debugApplyOrder = 'GAUDI_DUBUG_CONF_USER' in os.environ
1596  confUsers = [
1597  c for c in Configurable.allConfigurables.values()
1598  if hasattr(c, "__apply_configuration__")
1599  ]
1600  applied = True # needed to detect dependency loops
1601  while applied and confUsers:
1602  newConfUsers = [] # list of conf users that cannot be applied yet
1603  applied = False
1604  for c in confUsers:
1605  if hasattr(c, "__users__") and c.__users__:
1606  newConfUsers.append(c) # cannot use this one yet
1607  else: # it does not have users or the list is empty
1608  applied = True
1609  # the ConfigurableUser is enabled if it doesn't have an _enabled
1610  # property or its value is True
1611  enabled = (not hasattr(c, "_enabled")) or c._enabled
1612  if enabled:
1613  log.info("applying configuration of %s", c.name())
1614  if debugApplyOrder:
1615  sys.stderr.write('applying %r' % c)
1616  c.__apply_configuration__()
1617  log.info(c)
1618  else:
1619  log.info("skipping configuration of %s", c.name())
1620  if hasattr(c, "__detach_used__"):
1621  # tells the used configurables that they are not needed anymore
1622  c.__detach_used__()
1623  confUsers = newConfUsers # list of C.U.s still to go
1624  if confUsers:
1625  # this means that some C.U.s could not be applied because of a dependency loop
1626  raise Error("Detected loop in the ConfigurableUser "
1627  " dependencies: %r" % [c.name() for c in confUsers])
1628  # ensure that all the Handles have been triggered
1629  known = set()
1630  unknown = set(Configurable.allConfigurables)
1631  while unknown:
1632  for k in unknown:
1633  if not known: # do not print during the first iteration
1634  log.debug('new configurable created automatically: %s', k)
1635  # this trigger the instantiation from handles
1636  Configurable.allConfigurables[k].properties()
1637  known.add(k)
1638  unknown -= known
1639  # Call post-config actions
1640  for action in postConfigActions:
1641  action()
1642 
1643 
1645  """
1646  Function to select all and only the configurables that have to be used in
1647  GaudiPython.AppMgr constructor.
1648  This is needed because in Athena the implementation have to be different (the
1649  configuration is used in a different moment).
1650  """
1651  return [
1652  k for k, v in Configurable.allConfigurables.items()
1653  if v.getGaudiType() != "User"
1654  ] # Exclude ConfigurableUser instances
1655 
1656 
1657 def purge():
1658  """
1659  Clean up all configurations and configurables.
1660  """
1661  for c in Configurable.allConfigurables.values():
1662  c.__class__.configurables.clear()
1663  Configurable.allConfigurables.clear()
1664  # FIXME: (MCl) this is needed because instances of ConfigurableGeneric are not
1665  # migrated to the correct class when this is known.
1666  ConfigurableGeneric.configurables.clear()
1667  from ProcessJobOptions import _included_files
1668  import os.path
1669  import sys
1670  for file in _included_files:
1671  dirname, basname = os.path.split(file)
1672  basname, ext = os.path.splitext(basname)
1673  if basname in sys.modules:
1674  del sys.modules[basname]
1675  _included_files.clear()
1676 
1677 
1679  def __init__(self):
1680  self.stack = []
1681 
1682  @property
1683  def sequence(self):
1684  return self.stack[-1]
1685 
1686  def enter(self, visitee):
1687  pass
1688 
1689  def _getUniqueName(self, prefix):
1690  from Gaudi.Configuration import allConfigurables
1691  cnt = 0
1692  name = prefix + str(cnt)
1693  while name in allConfigurables:
1694  cnt += 1
1695  name = prefix + str(cnt)
1696  return name
1697 
1698  def _newSeq(self, prefix='seq_', **kwargs):
1699  from Configurables import GaudiSequencer
1700  return GaudiSequencer(self._getUniqueName('seq_'), **kwargs)
1701 
1702  def leave(self, visitee):
1703  stack = self.stack
1704  if visitee in (CFTrue, CFFalse):
1705  stack.append(self._newSeq(Invert=visitee is CFFalse))
1706  elif isinstance(visitee, (ControlFlowLeaf, ConfigurableAlgorithm)):
1707  stack.append(visitee)
1708  elif isinstance(visitee, (OrNode, AndNode, OrderedNode)):
1709  b = stack.pop()
1710  a = stack.pop()
1711  seq = self._newSeq(
1712  Members=[a, b],
1713  ModeOR=isinstance(visitee, OrNode),
1714  ShortCircuit=not isinstance(visitee, OrderedNode),
1715  MeasureTime=True)
1716  stack.append(seq)
1717  elif isinstance(visitee, ignore):
1718  if hasattr(stack[-1], 'IgnoreFilterPassed'):
1719  stack[-1].IgnoreFilterPassed = True
1720  else:
1721  stack.append(
1722  self._newSeq(
1723  Members=[stack.pop()], IgnoreFilterPassed=True))
1724  elif isinstance(visitee, InvertNode):
1725  if hasattr(stack[-1], 'Invert'):
1726  stack[-1].Invert = True
1727  else:
1728  stack.append(self._newSeq(Members=[stack.pop()], Invert=True))
1729 
1730 
1731 def makeSequences(expression):
1732  '''
1733  Convert a control flow expression to nested GaudiSequencers.
1734  '''
1735  if not isinstance(expression, ControlFlowNode):
1736  raise ValueError('ControlFlowNode instance expected, got %s' %
1737  type(expression).__name__)
1738  visitor = CreateSequencesVisitor()
1739  expression.visitNode(visitor)
1740  return visitor.sequence
1741 
1742 
1743 class SuperAlgorithm(ControlFlowNode):
1744  '''
1745  Helper class to use a ControlFlowNode as an algorithm configurable
1746  instance.
1747  '''
1748 
1749  def __new__(cls, name=None, **kwargs):
1750  if name is None:
1751  name = cls.__name__
1752  if name in Configurable.allConfigurables:
1753  instance = Configurable.allConfigurables[name]
1754  assert type(instance) is cls, \
1755  ('trying to reuse {0!r} as name of a {1} instance while it''s '
1756  'already used for an instance of {2}').format(
1757  name,
1758  cls.__name__,
1759  type(instance).__name__)
1760  return instance
1761  else:
1762  instance = super(SuperAlgorithm, cls).__new__(cls, name, **kwargs)
1763  Configurable.allConfigurables[name] = instance
1764  return instance
1765 
1766  def __init__(self, name=None, **kwargs):
1767  self._name = name or self.getType()
1768  self.graph = self._initGraph()
1769  for key in kwargs:
1770  setattr(self, key, kwargs[key])
1771 
1772  @property
1773  def name(self): # name is a read only property
1774  return self._name
1775 
1776  @classmethod
1777  def getType(cls):
1778  return cls.__name__
1779 
1780  # required to be registered in allConfigurables
1781  def properties(self):
1782  pass
1783 
1784  # required to be registered in allConfigurables
1785  def isApplicable(self):
1786  return False
1787 
1788  # required to be registered in allConfigurables
1789  def getGaudiType(self):
1790  return 'User'
1791 
1792  def _makeAlg(self, typ, **kwargs):
1793  '''
1794  Instantiate and algorithm of type 'typ' with a name suitable for use
1795  inside a SuperAlgorithm.
1796  '''
1797  name = '{0}_{1}'.format(self.name, kwargs.pop('name', typ.getType()))
1798  return typ(name, **kwargs)
1799 
1800  def _initGraph(self):
1801  raise NotImplementedError()
1802 
1803  def __repr__(self):
1804  return '{0}({1!r})'.format(self.getType(), self.name)
1805 
1806  def _visitSubNodes(self, visitor):
1807  if self.graph:
1808  self.graph.visitNode(visitor)
1809 
1810  def __setattr__(self, name, value):
1811  super(SuperAlgorithm, self).__setattr__(name, value)
1812  if name in ('_name', 'graph'):
1813  # do not propagate internal data members
1814  return
1815 
1816  class PropSetter(object):
1817  def enter(self, node):
1818  try:
1819  setattr(node, name, value)
1820  except (ValueError, AttributeError):
1821  # ignore type and name mismatch
1822  pass
1823 
1824  def leave(self, node):
1825  pass
1826 
1827  self._visitSubNodes(PropSetter())
def propagateProperty(self, name, others=None, force=True)
def _newSeq(self, prefix='seq_', kwargs)
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:109
def _printHeader(indentStr, title)
def __init__(self, name=Configurable.DefaultName, _enabled=True, kwargs)
def __init__(self, name=None, kwargs)
def appendPostConfigAction(function)
Sequencer for executing several algorithms, stopping when one is faulty.
def __init__(self, name=DefaultName)
def addTool(self, tool, name=None)
def makeSequences(expression)
def _printFooter(indentStr, title)
def __init__(self, name=Configurable.DefaultName)
def clone(self, name=None, kwargs)
MsgStream & hex(MsgStream &log)
Definition: MsgStream.h:271
EventIDBase max(const EventIDBase &lhs, const EventIDBase &rhs)
Definition: EventIDBase.h:215
def __init__(self, name=Configurable.DefaultName)
def __init__(self, name=Configurable.DefaultName)
def copyChildAndSetParent(self, cfg, parent)
def __iadd__(self, configs, descr=None)
def __new__(cls, name=None, kwargs)
def removePostConfigAction(function)
def __str__(self, indent=0, headerLastIndentUnit=indentUnit)
def __init__(self, name=Configurable.DefaultName)
def propagateProperties(self, names=None, others=None, force=True)
def __setattr__(self, name, value)
def isApplicable(self)
rep += v.__str__( indent + 1 ) + os.linesep elif isinstance(v,GaudiHandleArray): for vi in v: if isin...