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