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