Commits

Anonymous committed e0579b4

do not resolve already assigned Configurables; improve detection of default vs. set Handles

  • Participants
  • Parent commits cd3d325

Comments (0)

Files changed (1)

File python/ConfiguredFactory.py

         # set value
         return getattr(configurable,property)
     except AttributeError:
-        # C++ default
         retVal = configurable.getDefaultProperty(property)
         if retVal is None: raise
         return retVal
 
+def getPropertyNoAutoRetrieve(configurable,property):
+    """Helper function to get a property of a configurable: the set value, user set default or C++ default."""
+    if hasPropertyBeenSet(configurable,property):
+        return getattr(configurable,property)
+    else:
+        retVal = configurable.getDefaultProperty(property)
+        if retVal is None: raise AttributeError("%r does not have attribute %s", configurable.__class__, property)
+        return retVal
+
 
 def getPropertyType(configurable,property):
     try:
     except KeyError:
         raise AttributeError("Configurable %r does not have property %s" % (configurable.__class__.__name__,property) ) 
 
-    ## works one-way even for GaudiHandles
-    if configurable not in proxy.history:
-        return False
-
-    ## Due to the auto-retrieval of Handles (possibly triggered by Configurable.__str__ : this needs fixing!)
-    ## GaudiHandles may appear in history, even if not set
-    defValue = proxy.default
-    if not isinstance(defValue,GaudiHandle):
-        return True
-    
-    # Nasty hacked fall-back solution for GaudiHandles
-    # CAVEAT: if user sets it equal to the default, it will be considered not set
-    oldISD = configurable._inSetDefaults
-    configurable._inSetDefaults = True # avoid adding this getattr() call to the history due to auto-retrieve
-    value = getattr(configurable,property) # will auto-retrieve GaudiHandles if not yet set
-    configurable._inSetDefaults = oldISD
-
-    return defValue.getFullName() != value.getFullName()
+    return configurable in proxy.history
 
 
 #
                 except AttributeError:
                     pass
 
-        if typ is None:
-            return "%s%r  maker=%s" % (indent,name, self.getMakerType())
-        else:
-            return "%s%r  type=%s  maker=%s" % (indent,name, typ, self.getMakerType())
-    
+        mess = "%s%r  maker=%s" % (indent, name, self.getMakerType())
+        if typ is not None: mess += "  type=%s" % (typ,)
+
+        return mess
+
 
     def createInstance(self,name,argsOverride=None):
         theMaker = self._getConfigurableMaker()
 
-        parent=self
-        # turn Tool/Service names into real Tool/Service configurables
         args = dict(self.configurableArgs)
         if argsOverride: args.update(argsOverride)
         # make an instance of the class
         # One done for C++ defaults, and only if requireAllDependencies=False (otherwise exception is thrown)
         self._ignoredDependencyErrors = []
         # list of properties where the default has not been explicitly set
-        self._excludedDefaultProperties = []
+        self._excludedDefaultProperties = {}
         # list of default instances that we not created because they had only the type
         self._excludedOnlyTypes = []
 
 
     def _isExcludedDefaultProperty(self,propName):
         if propName in self._propertiesToExcludeDefaults:
-            addToListIfNotIn( self._excludedDefaultProperties, propName )
+            n = self._excludedDefaultProperties.setdefault(propName,0)
+            n += 1
+            self._excludedDefaultProperties[propName] = n
             return True
 
         return False
         resolvingString = ""
         # for debug printout
         if self.logger().isEnabledFor(logging.VERBOSE):
-            resolvingString = indent+"Resolving "
+            resolvingString = indent+"%s "
             if ( isPropDefault ):
                 resolvingString += "default "
             else:
 
         try:
             if isValueArray:
-                self.logger().verbose(resolvingString)
+                self.logger().verbose(resolvingString % "Resolving")
                 newPropertyValue = [] # propertyType()
                 for v in propertyValue:
                     conf = self._resolveConfigurable(parent,propertyName,v,getterFunc,checkType,indent,propStack)
-                    if conf: newPropertyValue.append( conf )
+                    if conf:
+                        newPropertyValue.append( conf )
+                    else: # keep existing one
+                        newPropertyValue.append( v )
 
             else: # not an array
-                if isPropDefault and self._isExcludedDefaultValue(propertyValue): return propertyValue
-                if not isPropertyArray: self.logger().verbose(resolvingString)
+                if isPropDefault and self._isExcludedDefaultValue(propertyValue):
+                    self.logger().verbose(resolvingString % "Keeping existing")
+                    return propertyValue
+                if not isPropertyArray: self.logger().verbose(resolvingString % "Resolving")
 
                 if isinstance(propertyValue,Configurable):
-                    doCheck=True
-                    tryDefault=False
+                    # do not change already assigned configurables
+                    self.logger().verbose(resolvingString % "Keeping existing")
+                    return propertyValue
                 elif isinstance(propertyValue,GaudiHandle) or type(propertyValue) == str:
                     doCheck=checkType
                     # tryDefaultConfigurable None instead of False to always retrieve it for the sake of dependencies
                     raise TypeError("%r is not a Configurable, Handle, HandleArray, string or list" % propertyValue)
 
                 nextIndent=indent+" "
-                newPropertyValue = getterFunc(propertyValue, tryDefaultConfigurable=tryDefault, checkType=doCheck,
+                newPropertyValue = getterFunc(propertyValue, tryDefaultConfigurable=tryDefault, checkType=doCheck, allowNone=True,
                                               indent=nextIndent,propStack=propStack)
 
                 # for printout at the end
 
     ## @arg @c parent : the parent (@c ConfiguredPlaceHolder) of the property
     def _resolveProperty(self,parent,propertyName,propertyValue,checkType=False,indent="",propStack=None):
-        """For Tool/ServiceHandles: turn name of tool/service into configurable instances.a
-        For any other, just return value."""
+        """For Tool/ServiceHandles: turn name of tool/service into configurable instances.
+        For any other, return None so it does not get re-assigned"""
         if propertyValue is None: return None # valid also for Handles. Nothing special to do.
         cls = parent.__class__
         try:
             confGetter = self.getPrivateTool
         elif isinstance(cxxdefault,(ServiceHandle,ServiceHandleArray)):
             confGetter = self.getService
-        else: # any other type of property
+        else: # any other type of property left untouched
             return propertyValue
 
         return self._resolveConfigurable(parent,propertyName,propertyValue,confGetter,checkType,indent,propStack)
         confName = conf.getName()
         defaultProps = conf.getDefaultProperties()
         for name,value in defaultProps.items():
-            # use set value if available
-            try:
-                value = getattr(conf,name)
-            except AttributeError:
-                # value not set, so we stick with the default value
-                pass
-
             # skip non-configurables, since they don't need to be resolved
             propertyType = getPropertyType(conf,name)
             if not issubclass(propertyType,(GaudiHandle,GaudiHandleArray,Configurable)):
                 if not self._configSetDependencies: continue
             else:
                 if not self._configDefaultDependencies: continue
+                if self._isExcludedDefaultProperty( name ): continue
 
-            # skip some defaults
-            if not isPropSet and self._isExcludedDefaultProperty( name ):
+            # to avoid auto-retrieve of private AlgTools, only call getattr() if already set elsewhere
+            if isPropSet:
+                value = getattr(conf,name)
+            else:
+                value = defaultProps[name]
+
+            resolvedValue = self._resolveProperty(conf,name,value,checkType=isPropSet,indent=indent,propStack=propStack)
+            if resolvedValue is None or resolvedValue == value:
+                self.logger().verbose("%sProperty %s.%s=%r left unchanged", indent, conf.getFullName(),name, value)
                 continue
 
-
-            resolvedValue = self._resolveProperty(conf,name,value,checkType=isPropSet,indent=indent,propStack=propStack)
-            if resolvedValue is None: continue
             try:
                 setattr(conf,name,resolvedValue)
             except Exception,err:
-                raise ConfigurationError("ERROR in setting %s(%r).%s = %r\n  Error message: %s" %
+                raise ConfigurationError("ERROR in setting %s(%r).%s = %r\n  Exception raised: %s" %
                                          (conf.getType(),conf.getName(),name,value,err) )
 
         
         newValue = None
         for n,v in propsToCheck.items():
             try:
-                oldProp = getProperty(configInstance,n)
+                oldProp = getPropertyNoAutoRetrieve(configInstance,n)
             except AttributeError:
                 continue
             
         # first look in available instances
         confType,confName = getTypeAndName(instanceName)
         if not confName: return None
-        self.logger().verbose("%sGoing to instantiate %s(%r)",indent,confType,confName)
+        self.logger().verbose("%sTrying to instantiate %s(%r)",indent,confType,confName)
         nextIndent=indent+" "
         if propStack is None: propStack = PropertyStack()
         conf = None
         maker = self._availableConfigurables.get(confName)
         if maker is not None:
             conf = maker.getInstance()
-            if conf is None:
+            if conf is not None:
+                self.logger().debug("%sUsing already configured %s(%r)",indent,conf.getType(),conf.getName())            
+            else: # create it
                 conf = maker.createInstance(confName)
+                self.logger().verbose("%sInstantiated %s(%r). Starting to resolve dependencies",indent,conf.getType(),conf.getName())
+                self._resolveAllProperties(conf,nextIndent,propStack)
+                if checkType and confType != confName:
+                    # explicit type given, check that existing type is as requested
+                    self.checkConfigurableType(conf,confType)
 
-            self._resolveAllProperties(conf,nextIndent,propStack)
-            if checkType and confType != confName:
-                # explicit type given, check that existing type is as requested
-                self.checkConfigurableType(conf,confType)
-
-            self.logger().debug("%sInstantiated configured %s(%r)",indent,conf.getType(),conf.getName())
+                self.logger().debug("%sInstantiated configured %s(%r)",indent,conf.getType(),conf.getName())
 
         else:
             # look in default configurables (if requested)
             if tryDefaultConfigurable or tryDefaultConfigurable is None:
                 # do not make defaults if only the type is given
-                if self._excludeIfOnlyType(instanceName): return None
+                if self._excludeIfOnlyType(instanceName):
+                    self.logger().verbose("%sNot instantiated because %r is only a type",indent,instanceName)
+                    return None
                 # skip explicitly excludes ones
                 if self._skipIfNotAvailable(instanceName):
+                    self.logger().verbose("%sNot instantiated because %r is not in database",indent,instanceName)
                     return None
 
                 configurableClass = ConfigurableDb.getConfigurable(confType)
                     if tryDefaultConfigurable:
                         raise ConfigurableClassNotFoundError("Could not find configurable class for %s" % instanceName)
                     elif tryDefaultConfigurable is None:
+                        self.logger().verbose("%sNot instantiated because configurable class %r does not exist",indent,confType)
                         return None
 
                 else:
                     
 
         if conf is None:
+            self.logger().verbose("%sCould not find configurable instance %r", indent, instanceName)
             raise ConfigurableInstanceNotFoundError("Could not find configurable instance %r" % (instanceName) )
 
-
         return conf
 
 
         if not self._hasReadDB: self.read()
         # check if original is available (to catch mistakes like swapping cloneName and originalName)
         if type(originalOrName) == str and originalOrName not in self._availableConfigurables:
-            raise ConfigurationError("Can not make clone %r of non-existing configurable %r" % (cloneName,originalOrName))
-
+            raise ConfigurationError("Can not make clone %r of non-existing configurable %r" % (cloneName,originalOrName) )
 
         conf = None
         # first see if cloneName is already present in list of available configurables
             if conf is None:
                 conf = maker.createInstance(cloneName)
                 self._resolveAllProperties(conf)
+                self.logger().info("Instantiated configurable %s(%r), requested as clone of %r", conf.__class__.__name__, conf.name(), originalOrName)
+            else:
+                self.logger().debug("Using existing configurable %s(%r), requested as clone of %r", conf.__class__.__name__, conf.name(), originalOrName)
                 
             problem = self.checkProperties(conf,kwargs)
             if problem:
                 raise ConfigurationError("Existing clone requested with incompatible properties: "+problem)
 
-            return conf
 
-        # if we get here, clone needs to me made
-        if type(originalOrName) == str:
-            originalName = originalOrName # for printout later
-            self._addConfiguredClone(originalName,cloneName,**kwargs)
-            conf = self.getConfigured(cloneName,tryDefaultConfigurable=False,checkType=True)
+        if conf is None:
+            # if we get here, clone needs to me made
+            if type(originalOrName) == str:
+                originalName = originalOrName # for printout later
+                self._addConfiguredClone(originalName,cloneName,**kwargs)
+                conf = self.getConfigured(cloneName,tryDefaultConfigurable=False,checkType=True)
+                self.logger().info("Instantiated configurable %s(%r) as clone of %r", conf.__class__.__name__, conf.name(), originalName)
 
-        elif isinstance(originalOrName,Configurable):
-            originalName = originalOrName.name() # for printout later
-            conf = originalOrName.clone(cloneName)
-            for n,v in kwargs.items():
-                setattr(conf,n,v)
-            self._resolveAllProperties(conf)
-            
+            elif isinstance(originalOrName,Configurable):
+                originalName = originalOrName.name() # for printout later
+                conf = originalOrName.clone(cloneName)
+                for n,v in kwargs.items():
+                    setattr(conf,n,v)
+                self._resolveAllProperties(conf)
+                # add to list for later retrieval
+                self._availableConfigurables[cloneName] = ConfiguredPlaceHolder(conf)
+                self.logger().info("Instantiated direct resolved clone %s(%r) from original %r", conf.__class__.__name__, conf.name(), originalName)
+
 
         if conf is None:
             # complete failure
             raise RuntimeError("Could not find configurable %r as source for clone %r" % (originalOrName,cloneName) )
 
-        self.logger().info("Instantiating configurable %s(%r) as clone of %r", conf.__class__.__name__, conf.name(), originalName)
-        # also store clones for later usage
         return conf
         
 
         return tool
 
 
-    def getPublicTool(self, name, tryDefaultConfigurable=None, checkType=False, indent="", propStack=None):
+    def getPublicTool(self, name, tryDefaultConfigurable=None, checkType=False, allowNone=False, indent="", propStack=None):
         """Get public tool"""
-        if not name: return None
+        if not name:
+            if allowNone: return None
+            else: raise ConfigurationError("Requested Public Tool with empty name")
+
         global ToolSvc
         confType,confName = getTypeAndName(name)
         if not confName: return None
         except AttributeError:
             # make it and add to ToolSvc
             theTool = self._getTool(name, tryDefaultConfigurable, checkType, indent, propStack)
-            if theTool is None: return None
+            if theTool is None:
+                if allowNone: return None
+                else: raise ConfigurationError("Public Tool %r not found" % (name,) )
+                    
             # some configurable makers already add the tool to ToolSvc
             if not hasattr(ToolSvc,theTool.getName()):
                 ToolSvc += theTool
             return theTool
 
 
-    def getPrivateTool(self, name, tryDefaultConfigurable = None, checkType = True, indent = "", propStack=None, **toolProps):
+    def getPrivateTool(self, name, tryDefaultConfigurable = None, checkType = True, allowNone=False, indent = "", propStack=None, **toolProps):
         """Get private tool. Returns a (deep) copy of the tool, so really private"""
-        if not name: return None
+        if not name:
+            if allowNone: return None
+            else: raise ConfigurationError("Requested Private Tool with empty name")
+            
         tool = self._getTool(name, tryDefaultConfigurable, checkType, indent, propStack)
-        if tool is None: return None
+        if tool is None:
+            if allowNone: return None
+            else: raise ConfigurationError("Private Tool %r not found" % (name,) )
+
         # start with a copy
         tool = copy.deepcopy( tool )
         # overwrite with user properties
         """Add a clone of a possible service to the list of possible services."""
         self._addConfiguredClone(originalName, cloneName, **kwargs)
 
-    def getService( self, name, tryDefaultConfigurable=None, checkType=True, indent="", propStack=None ):
-        if not name: return None
+    def getService( self, name, tryDefaultConfigurable=None, checkType=True, allowNone=False, indent="", propStack=None ):
+        if not name:
+            if allowNone: return None
+            else: raise ConfigurationError("Requested Service with empty name")
+
         confType,confName = getTypeAndName(name)
-        if not confName: return None
+        if not confName:
+            if allowNone: return None
+            else: raise ConfigurationError("Requested Service with empty instance name: %r" % (name,) )
+
+
         global ServiceMgr
         # first try to get it from ServiceMgr
         try:
                 tryDefault = tryDefaultConfigurable
             # make it and add to ServiceMgr
             theSvc = self.getConfigured( name, tryDefault, checkType, indent, propStack )
-            if theSvc is None: return None
+            if theSvc is None:
+                if allowNone: return None
+                else: raise ConfigurationError("Service %r not found" % (name,) )
+
             self.checkService(theSvc)
             ServiceMgr += theSvc
             return theSvc
         self._addConfiguredClone(originalName, cloneName, **kwargs)
 
 
-    def getAlgorithm( self, name, tryDefaultConfigurable=None, checkType=True, indent="", propStack=None ):
-        if not name: return None
+    def getAlgorithm( self, name, tryDefaultConfigurable=None, checkType=True, allowNone=False, indent="", propStack=None ):
+        if not name:
+            if allowNone: return None
+            else: raise ConfigurationError("Requested Algorithm with empty name")
+
         if tryDefaultConfigurable is None:
             tryDefault = self._tryDefaultAlgorithm or None
         else:
             tryDefault = tryDefaultConfigurable
 
         theAlg = self.getConfigured( name, tryDefault, checkType, indent, propStack )
-        if theAlg is None: return None
+        if theAlg is None:
+            if allowNone: return None
+            else: raise ConfigurationError("Algorithm %r not found" % (name,) )
+
         self.checkAlgorithm(theAlg)        
         return theAlg
 
         if self._excludedDefaultProperties:
             lines = [ "%i properties with the following names and default values were not explicitly configured:" % \
                       len(self._excludedDefaultProperties) ]
-            for c in self._excludedDefaultProperties:
-                lines.append("  %s" % c)
+            for c,count in self._excludedDefaultProperties.items():
+                lines.append("  %s (%d times)" % (c,count) )
             log.info( os.linesep.join(lines) )
 
         if self._excludedOnlyTypes:
                 try:
                     mod = __import__( confDbModule, globals(), locals(), [dbFile] )
                 except Exception, err:
-                    self.logger().warning( "Could not import module %s !", confDbModule )
+                    self.logger().warning( "Error importing module %s !", confDbModule )
                     self.logger().warning( "Reason: %s", err )
                 else:
                     nFiles += 1
 
         stopTime = time.time()
-        self.logger().debug( "importing configDb modules... DONE" )
-        self.logger().debug( "imported %i confDb modules in %.2f seconds" % (nFiles,stopTime-startTime) )
+        self.logger().info( "imported %i confDb modules in %.2f seconds" % (nFiles,stopTime-startTime) )
         self._hasReadDB = True