diff --git a/Control/AthenaConfiguration/python/ComponentAccumulator.py b/Control/AthenaConfiguration/python/ComponentAccumulator.py index 65124a53af8e4d0ee058dfabcde4d24edad25c57..b08120e3ecfde2dc733a5ac949c1abceb4d0a528 100644 --- a/Control/AthenaConfiguration/python/ComponentAccumulator.py +++ b/Control/AthenaConfiguration/python/ComponentAccumulator.py @@ -26,13 +26,13 @@ def printProperties(msg, c, nestLevel = 0, printDefaults=False, onlyComponentsOn # Iterate in sorted order. propnames= sorted(c._descriptors.keys()) for propname in propnames: - + if not printDefaults and not c.is_property_set(propname): continue propval=getattr(c,propname) # Ignore empty lists - + if isinstance(propval,(GaudiConfig2.semantics._ListHelper,GaudiConfig2.semantics._DictHelper)) and propval.data is None: continue # Printing EvtStore could be relevant for Views? @@ -77,13 +77,13 @@ class ComponentAccumulator(object): self._msg=logging.getLogger('ComponentAccumulator') AthSequencer=CompFactory.AthSequencer self._sequence=AthSequencer(sequenceName,Sequential=True) #(Nested) default sequence of event processing algorithms per sequence + their private tools - self._allSequences = [ self._sequence ] + self._allSequences = [ self._sequence ] self._algorithms = {} #Flat algorithms list, useful for merging self._conditionsAlgs=[] #Unordered list of conditions algorithms + their private tools self._services=[] #List of service, not yet sure if the order matters here in the MT age self._servicesToCreate=[] self._privateTools=None #A placeholder to carry a private tool(s) not yet attached to its parent - self._primaryComp=None #A placeholder to designate the primary service + self._primaryComp=None #A placeholder to designate the primary service self._theAppProps=dict() #Properties of the ApplicationMgr @@ -93,7 +93,7 @@ class ComponentAccumulator(object): #To check if this accumulator was merged: self._wasMerged=False self._isMergable=True - self._lastAddedComponent="Unknown" + self._lastAddedComponent="Unknown" self._debugStage=DbgStage() @@ -104,14 +104,14 @@ class ComponentAccumulator(object): def _inspect(self): #Create a string some basic info about this CA, useful for debugging summary="This CA contains {0} service, {1} conditions algorithms, {2} event algorithms and {3} public tools\n"\ .format(len(self._services),len(self._conditionsAlgs),len(self._algorithms),len(self._publicTools)) - if (self._privateTools): + if (self._privateTools): if (isinstance(self._privateTools, list)): summary+=" Private AlgTool: "+ self._privateTools[-1].getFullJobOptName()+"\n" else: summary+=" Private AlgTool: "+ self._privateTools.getFullJobOptName()+"\n" - if (self._primaryComp): - summary+=" Primary Component: " + self._primaryComp.getFullJobOptName()+"\n" - summary+=" Last component added: "+self._lastAddedComponent+"\n" + if (self._primaryComp): + summary+=" Primary Component: " + self._primaryComp.getFullJobOptName()+"\n" + summary+=" Last component added: "+self._lastAddedComponent+"\n" return summary @@ -152,7 +152,7 @@ class ComponentAccumulator(object): if summariseProps and flag: printProperties(self._msg, c, 1, printDefaults, printComponentsOnly) return - + # If onlyComponents is set, then only print components with names # that appear in the onlyComponents list. If a name is present @@ -163,37 +163,40 @@ class ComponentAccumulator(object): self._msg.info( "Event Inputs" ) self._msg.info( "Event Algorithm Sequences" ) - - if withDetails:# The WithDetails option does work with GaudiConfi2 (for now) - self._msg.info( self._sequence ) - else: - def printSeqAndAlgs(seq, nestLevel = 0, - onlyComponents = []): - def __prop(name): - if name in seq._properties: - return seq._properties[name] - return seq._descriptors[name].default - + def printSeqAndAlgs(seq, nestLevel = 0, + onlyComponents = []): + def __prop(name): + if name in seq._properties: + return seq._properties[name] + return seq._descriptors[name].default + if withDetails: self._msg.info( "%s\\__ %s (seq: %s %s)", " "*nestLevel, seq.name, "SEQ" if __prop("Sequential") else "PAR", "OR" if __prop("ModeOR") else "AND" ) - nestLevel += 3 - for (c, flag) in filterComponents(seq.Members, onlyComponents): - if isSequence(c): - printSeqAndAlgs(c, nestLevel, onlyComponents = onlyComponents ) + else: + self._msg.info( "%s\\__ %s", " "*nestLevel, seq.name) + + nestLevel += 3 + for (c, flag) in filterComponents(seq.Members, onlyComponents): + if isSequence(c): + printSeqAndAlgs(c, nestLevel, onlyComponents = onlyComponents ) + else: + if withDetails: + self._msg.info( "%s\\__ %s (alg)", " "*nestLevel, c.getFullJobOptName() ) else: - self._msg.info( "%s\\__ %s (alg)", " "*nestLevel, c.name ) - if summariseProps and flag: - printProperties(self._msg, c, nestLevel, printDefaults, printComponentsOnly) + self._msg.info( "%s\\__ %s", " "*nestLevel, c.name ) + if summariseProps and flag: + printProperties(self._msg, c, nestLevel, printDefaults, printComponentsOnly) + - for n,s in enumerate(self._allSequences): - self._msg.info( "Top sequence %s", n ) - printSeqAndAlgs(s, onlyComponents = onlyComponents) + for n,s in enumerate(self._allSequences): + self._msg.info( "Top sequence %d", n ) + printSeqAndAlgs(s, onlyComponents = onlyComponents) self.printCondAlgs (summariseProps = summariseProps, onlyComponents = onlyComponents) self._msg.info( "Services" ) - self._msg.info( [ s[0].name + (" (created) " if s[0].name in self._servicesToCreate else "") + self._msg.info( [ s[0].name + (" (created) " if s[0].name in self._servicesToCreate else "") for s in filterComponents (self._services, onlyComponents) ] ) self._msg.info( "Public Tools" ) self._msg.info( "[" ) @@ -280,31 +283,31 @@ class ComponentAccumulator(object): return findSubSequence(self._sequence,sequenceName) def setPrivateTools(self,privTool): - """Use this method to carry private AlgTool(s) to the caller when returning this ComponentAccumulator. + """Use this method to carry private AlgTool(s) to the caller when returning this ComponentAccumulator. The method accepts either a single private AlgTool or a list of private AlgTools (typically assigned to ToolHandleArray) """ if self._privateTools is not None: raise ConfigurationError("This ComponentAccumulator holds already a (list of) private tool. Only one (list of) private tool(s) is allowed") - if isinstance(privTool,collections.Sequence): + if isinstance(privTool,collections.abc.Sequence): for t in privTool: if t.__component_type__ != 'AlgTool': raise ConfigurationError("ComponentAccumulator.setPrivateTools accepts only ConfigurableAlgTools or lists of ConfigurableAlgTools. Encountered %s in a list" % type(t)) - else: + else: if privTool.__component_type__ != "AlgTool": raise ConfigurationError("ComponentAccumulator.setPrivateTools accepts only cCnfigurableAlgTools or lists of ConfigurableAlgTools. Encountered %s " % type(privTool)) - + self._privateTools=privTool return - + def popPrivateTools(self): - """Get the (list of) private AlgTools from this ComponentAccumulator. + """Get the (list of) private AlgTools from this ComponentAccumulator. The CA will not keep any reference to the AlgTool. """ tool=self._privateTools self._privateTools=None return tool - + def popToolsAndMerge(self, other): """ Merging in the other accumulator and getting the (list of) private AlgTools from this CompoentAccumulator. """ @@ -315,7 +318,7 @@ class ComponentAccumulator(object): return tool def addEventAlgo(self, algorithms,sequenceName=None,primary=False): - if not isinstance(algorithms,collections.Sequence): + if not isinstance(algorithms,collections.abc.Sequence): #Swallow both single algorithms as well as lists or tuples of algorithms algorithms=[algorithms,] @@ -328,7 +331,7 @@ class ComponentAccumulator(object): raise ConfigurationError("Can not find sequence %s" % sequenceName ) for algo in algorithms: - if algo.__component_type__ != "Algorithm": + if algo.__component_type__ != "Algorithm": raise TypeError("Attempt to add wrong type: %s as event algorithm" % type( algo ).__name__) if algo.name in self._algorithms: @@ -343,9 +346,9 @@ class ComponentAccumulator(object): if primary: if len(algorithms)>1: self._msg.warning("Called addEvenAlgo with a list of algorithms and primary==True. Designating the first algorithm as primary component") - if self._primaryComp: + if self._primaryComp: self._msg.warning("Overwriting primary component of this CA. Was %s/%s, now %s/%s", - self._primaryComp.__cpp_type__, self._primaryComp.name, + self._primaryComp.__cpp_type__, self._primaryComp.name, algorithms[0].__cpp_type__, algorithms[0].name) #keep a ref of the algorithm as primary component self._primaryComp=algorithms[0] @@ -369,8 +372,8 @@ class ComponentAccumulator(object): raise TypeError("Attempt to add wrong type: %s as conditions algorithm" % type( algo ).__name__) pass deduplicate(algo,self._conditionsAlgs) #will raise on conflict - if primary: - if self._primaryComp: + if primary: + if self._primaryComp: self._msg.warning("Overwriting primary component of this CA. Was %s/%s, now %s/%s", self._primaryComp.__cpp_type__,self._primaryComp.name,algo.__cpp_type__,algo.name) #keep a ref of the de-duplicated conditions algorithm as primary component @@ -390,12 +393,12 @@ class ComponentAccumulator(object): raise TypeError("Attempt to add wrong type: %s as service" % newSvc.__component_type__) pass deduplicate(newSvc,self._services) #will raise on conflict - if primary: - if self._primaryComp: + if primary: + if self._primaryComp: self._msg.warning("Overwriting primary component of this CA. Was %s/%s, now %s/%s", self._primaryComp.__cpp_type__,self._primaryComp.name,newSvc.__cpp_type__,newSvc.name) #keep a ref of the de-duplicated public tool as primary component - self._primaryComp=self.__getOne( self._services, newSvc.name, "Services") + self._primaryComp=self.__getOne( self._services, newSvc.name, "Services") self._lastAddedComponent=newSvc.name if create: @@ -407,14 +410,14 @@ class ComponentAccumulator(object): def addPublicTool(self,newTool,primary=False): if newTool.__component_type__ != "AlgTool": raise TypeError("Attempt to add wrong type: %s as AlgTool" % type( newTool ).__name__) - + deduplicate(newTool,self._publicTools) - if primary: - if self._primaryComp: + if primary: + if self._primaryComp: self._msg.warning("Overwriting primary component of this CA. Was %s/%s, now %s/%s", self._primaryComp.__cpp_type__,self._primaryComp.name,newTool.__cpp_type__,newTool.name) #keep a ref of the de-duplicated service as primary component - self._primaryComp=self.__getOne( self._publicTools, newTool.name, "Public Tool") + self._primaryComp=self.__getOne( self._publicTools, newTool.name, "Public Tool") self._lastAddedComponent=newTool.name return @@ -436,20 +439,20 @@ class ComponentAccumulator(object): def __getOne(self, allcomps, name=None, typename="???"): selcomps = allcomps if name is None else [ t for t in allcomps if t.name == name ] if len( selcomps ) == 1: - return selcomps[0] + return selcomps[0] raise ConfigurationError("Number of %s available %d which is != 1 expected by this API" % (typename, len(selcomps)) ) - + def getPublicTools(self): return self._publicTools - def getPublicTool(self, name=None): + def getPublicTool(self, name=None): """Returns single public tool, exception if either not found or to many found""" return self.__getOne( self._publicTools, name, "PublicTools") def getServices(self): return self._services - def getService(self, name=None): + def getService(self, name=None): """Returns single service, exception if either not found or to many found""" if name is None: return self._primarySvc @@ -465,7 +468,7 @@ class ComponentAccumulator(object): else: if self._theAppProps[key] == value: self._msg.debug("ApplicationMgr property '%s' already set to '%s'.", key, value) - elif isinstance(self._theAppProps[key],collections.Sequence) and not isinstance(self._theAppProps[key],str): + elif isinstance(self._theAppProps[key],collections.abc.Sequence) and not isinstance(self._theAppProps[key],str): value=self._theAppProps[key] + [el for el in value if el not in self._theAppProps[key]] self._msg.info("ApplicationMgr property '%s' already set to '%s'. Overwriting with %s", key, self._theAppProps[key], value) self._theAppProps[key]=value @@ -491,14 +494,14 @@ class ComponentAccumulator(object): raise TypeError("Attempt merge wrong type %s. Only instances of ComponentAccumulator can be added" % type(other).__name__) if (other._privateTools is not None): - if isinstance(other._privateTools,collections.Sequence): + if isinstance(other._privateTools,collections.abc.Sequence): raise RuntimeError("merge called with a ComponentAccumulator a dangling (array of) private tools") else: raise RuntimeError("merge called with a ComponentAccumulator a dangling private tool %s/%s" % (other._privateTools.__cpp_type__,other._privateTools.name)) - - + + if not other._isMergable: raise ConfigurationError("Attempted to merge the ComponentAccumulator that was unsafely manipulated (likely with foreach_component, ...) or is a top level ComponentAccumulator, in such case revert the order") @@ -543,7 +546,7 @@ class ComponentAccumulator(object): # if sequenceName is provided it means we should be ignoring the actual main seq name there and use the sequenceName # that means the first search in the destination seqence needs to be cheated # the sequenceName argument is ambigous when the other CA has more than one sequence and this is checked - + if sequenceName is None: for otherSeq in other._allSequences: found=False @@ -565,7 +568,7 @@ class ComponentAccumulator(object): mergeSequences(destSeq, other._sequence) - + # Additional checking and updating other accumulator's algorithms list for name in other._algorithms: if name not in self._algorithms: @@ -596,7 +599,7 @@ class ComponentAccumulator(object): raise ConfigurationError('It is not allowed for the storable CA to have more than one top sequence, now it has: {}'\ .format(','.join([ s.name for s in self._allSequences]))) - + def wasMerged(self): """ Declares CA as merged @@ -775,7 +778,7 @@ class ComponentAccumulator(object): else: maxEvents=-1 - if (self._debugStage.value == "init"): + if (self._debugStage.value == "init"): hookDebugger() sc = app.initialize() if not sc.isSuccess(): @@ -787,7 +790,7 @@ class ComponentAccumulator(object): self._msg.error("Failed to start AppMgr") return sc - if (self._debugStage.value=="exec"): + if (self._debugStage.value=="exec"): hookDebugger() sc = app.run(maxEvents) if not sc.isSuccess(): @@ -796,7 +799,7 @@ class ComponentAccumulator(object): app.stop().ignore() - if (self._debugStage.value == "fini"): + if (self._debugStage.value == "fini"): hookDebugger() app.finalize().ignore() @@ -805,14 +808,14 @@ class ComponentAccumulator(object): def foreach_component(self, path): """ Utility to set properties of components using wildcards - + Example: forcomps(ca, "*/HLTTop/*/*Hypo*").OutputLevel=VERBOSE - - The compoments name & locations in the CF tree are translated into the unix like path. + + The compoments name & locations in the CF tree are translated into the unix like path. Components of matching path are taken under consideration in setting the property. - If the property is set succesfully an INFO message is printed. Else, a warning is printed. - + If the property is set succesfully an INFO message is printed. Else, a warning is printed. + The convention for path of nested components is as follows: Sequencer - only the name is used in the path Algorithm - full name - type/instance_name (aka full name) is used @@ -939,9 +942,9 @@ def conf2toConfigurable( comp, indent="", suppressDupes=False ): def __areSettingsSame( existingConfigurableInstance, newConf2Instance, indent="" ): _log.debug( "%sChecking if settings are the same %s %s", indent, existingConfigurableInstance.getFullName(), newConf2Instance.getFullJobOptName() ) - if (existingConfigurableInstance.getType() != newConf2Instance.__cpp_type__): + if (existingConfigurableInstance.getType() != newConf2Instance.__cpp_type__): raise ConfigurationError("Old/new ({} | {}) cpp types are not the same for ({} | {}) !".format( - existingConfigurableInstance.getType(),newConf2Instance.__cpp_type__, + existingConfigurableInstance.getType(),newConf2Instance.__cpp_type__, existingConfigurableInstance.getFullName(), newConf2Instance.getFullJobOptName() ) ) alreadySetProperties = existingConfigurableInstance.getValuedProperties().copy() @@ -976,13 +979,13 @@ def conf2toConfigurable( comp, indent="", suppressDupes=False ): configurableClass = __findConfigurableClass( className ) # Do not create with existing name, or it will try to get an existing public tool, if available # (and public tools cannot be added to a PrivateToolHandleArray) - instance = configurableClass( newC + className + str(len(indent)) ) + instance = configurableClass( newC + className + str(len(indent)) ) # Now give it the correct name instance._name = newCdict[newC].name __setProperties( instance, newCdict[newC], __indent( indent ) ) - _log.debug('%s will now add %s to array.',indent, instance) + _log.debug('%s will now add %s to array.',indent, instance) alreadySetProperties[pname].append(instance) elif "PublicToolHandleArray" in propType: toolSet = {_.getName() for _ in alreadySetProperties[pname]} @@ -996,7 +999,7 @@ def conf2toConfigurable( comp, indent="", suppressDupes=False ): alreadySetProperties[pname].append(klass( pubtoolname )) else: _log.warning('Not handling actual Configurable2s for public tool merging yet') - raise Exception() + raise Exception() elif "PrivateToolHandle" in propType or "GaudiConfig2.Configurables" in propType or "ServiceHandle" in propType: existingVal = getattr(existingConfigurableInstance, pname) if isinstance( pvalue, str ): @@ -1020,7 +1023,7 @@ def conf2toConfigurable( comp, indent="", suppressDupes=False ): setattr(existingConfigurableInstance, pname, pvalue) except AttributeError: _log.info("%s Could not set attribute. Type of existingConfigurableInstance %s.",indent, type(existingConfigurableInstance) ) - raise + raise elif alreadySetProperties[pname] != pvalue: _log.info( "%sMerging property: %s for %s", indent, pname, newConf2Instance.getName() ) # create surrogate @@ -1042,10 +1045,10 @@ def conf2toConfigurable( comp, indent="", suppressDupes=False ): "for property %s of %s", indent, alreadySetProperties[pname], pvalue, pname, updatedPropValue, existingConfigurable.getFullName()) - + _log.debug( "%s Conf2 Full name: %s ", indent, comp.getFullJobOptName() ) existingConfigurable = __alreadyConfigured( comp.name ) - + if existingConfigurable: # if configurable exists we try to merge with it _log.debug( "%sPre-existing configurable %s was found, checking if has the same properties", indent, comp.getName() ) __areSettingsSame( existingConfigurable, comp ) @@ -1115,7 +1118,7 @@ def appendCAtoAthena(ca): origPropValue = getattr(theApp, propName) if origPropValue == propValue: _log.debug("ApplicationMgr property '%s' already set to '%s'.", propName, propValue) - elif isinstance(origPropValue, collections.Sequence) and not isinstance(origPropValue, str): + elif isinstance(origPropValue, collections.abc.Sequence) and not isinstance(origPropValue, str): propValue = origPropValue + [el for el in propValue if el not in origPropValue] _log.info("ApplicationMgr property '%s' already set to '%s'. Overwriting with %s", propName, origPropValue, propValue) setattr(theApp, propName, propValue) diff --git a/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py b/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py index 49e1531a915686fe19cf43b2fe070391e30351fa..e6ce148d1fc478ed9f8db3de614c1e7208f54a40 100644 --- a/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py +++ b/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py @@ -32,8 +32,8 @@ class TestComponentAccumulator( unittest.TestCase ): def AlgsConf1(flags): acc = ComponentAccumulator() - a1=TestAlgo("Algo1") - a2=TestAlgo("Algo2") + a1=TestAlgo("Algo1", MyInt = 12345, MyBool = False, MyDouble=2.718, MyStringVec=["Very", "important"]) + a2=TestAlgo("Algo2", MyInt = 98765, MyBool = True, MyDouble=1.41, MyStringVec=["unimportant"]) return acc,[a1,a2] @@ -75,9 +75,9 @@ class TestComponentAccumulator( unittest.TestCase ): accNA1=AlgsConf4(dummyCfgFlags) acc.merge(accNA1[0]) acc.addEventAlgo(accNA1[1:],"sub2Sequence1" ) - outf = open("testFile.pkl", "wb") - acc.store(outf) - outf.close() + with open("testFile.pkl", "wb") as outf: + acc.store(outf) + acc.printConfig(withDetails=True, summariseProps=True) self.acc = acc @@ -100,8 +100,8 @@ class TestComponentAccumulator( unittest.TestCase ): def test_readBackConfiguration( self ): import pickle - f = open('testFile.pkl', 'rb') - s = pickle.load( f ) + with open('testFile.pkl', 'rb') as f: + s = pickle.load( f ) self.assertIsNotNone( s, "The pickle has no content") def test_foreach_component( self ): @@ -302,24 +302,25 @@ class MergeMovingAlgorithms( unittest.TestCase ): class TestComponentAccumulatorAccessors( unittest.TestCase ): def runTest( self ): ca = ComponentAccumulator() + ca.wasMerged() ca.addEventAlgo(TestAlgo("alg1")) self.assertEqual( len(ca.getEventAlgos()), 1 , "Found single alg") -# no idea why this assersts do not recognise exceptions -# self.assertRaises(ConfigurationError, ca.getEventAlgo("alg2")) + from .ComponentAccumulator import ConfigurationError + self.assertRaises(ConfigurationError, lambda: ca.getEventAlgo("alg2")) ca.addEventAlgo(TestAlgo("alg2")) self.assertIsNotNone( ca.getEventAlgo("alg2"), "Found single alg") self.assertEqual( len(ca.getEventAlgos()), 2 , "Found single alg") - # self.assertRaises(ConfigurationError, ca.getEventAlgo(), "Single Alg API ambiguity") + self.assertRaises(ConfigurationError, lambda: ca.getEventAlgo()) # Single Alg API ambiguity ca.addPublicTool( dummyTool(name="tool1") ) self.assertIsNotNone( ca.getPublicTool(), "Found single tool") ca.addPublicTool( dummyTool(name="tool2") ) -# self.assertRaises(ConfigurationError, ca.getPublicTool(), "Found single tool") + self.assertRaises(ConfigurationError, lambda: ca.getPublicTool()) # Found single tool class TestDeduplication( unittest.TestCase ): def runTest( self ):