diff --git a/Control/AthenaConfiguration/CMakeLists.txt b/Control/AthenaConfiguration/CMakeLists.txt index 9f1ab05d497938c6a9f6c6c4f9ad97ec76fd7aea..d7316c7fbbd39f9ca49e3c3fbe083b38fa645c67 100644 --- a/Control/AthenaConfiguration/CMakeLists.txt +++ b/Control/AthenaConfiguration/CMakeLists.txt @@ -14,7 +14,7 @@ atlas_install_scripts( share/confTool.py python/iconfTool/iconfTool ) atlas_install_data( share/*.ref ) atlas_add_test( ComponentAccumulatorTest - SCRIPT python -m unittest -v AthenaConfiguration.ComponentAccumulator + SCRIPT python -m unittest -v AthenaConfiguration.ComponentAccumulatorTest POST_EXEC_SCRIPT nopost.sh ) atlas_add_test( UnifyPropertiesTest diff --git a/Control/AthenaConfiguration/python/ComponentAccumulator.py b/Control/AthenaConfiguration/python/ComponentAccumulator.py index 40c4b80b7cb06f21ae8df50de6e59ce698e2a535..6c44b4f06cd1a6fa4f89dc6dffd4a58ed7c23613 100644 --- a/Control/AthenaConfiguration/python/ComponentAccumulator.py +++ b/Control/AthenaConfiguration/python/ComponentAccumulator.py @@ -5,7 +5,6 @@ from AthenaCommon.Configurable import Configurable,ConfigurableService,Configura from AthenaCommon.CFElements import isSequence,findSubSequence,findAlgorithm,flatSequencers,findOwningSequence,checkSequenceConsistency from AthenaCommon.AlgSequence import AthSequencer -from AthenaConfiguration.AthConfigFlags import AthConfigFlags import GaudiKernel.GaudiHandles as GaudiHandles from GaudiKernel.GaudiHandles import PublicToolHandle, PublicToolHandleArray, ServiceHandle, PrivateToolHandle, PrivateToolHandleArray import ast @@ -40,6 +39,7 @@ class ComponentAccumulator(object): #To check if this accumulator was merged: self._wasMerged=False + self._isMergable=True def empty(self): @@ -465,7 +465,9 @@ class ComponentAccumulator(object): if not isinstance(other,ComponentAccumulator): raise TypeError("Attempt merge wrong type %s. Only instances of ComponentAccumulator can be added" % type(other).__name__) - + + if not other._isMergable: + raise ConfigurationError("Attempted to merge the accumulator that was unsafely manipulated (likely with foreach_component, ...)") if not Configurable.configurableRun3Behavior: raise ConfigurationError("discoverd Configurable.configurableRun3Behavior=False while working woth ComponentAccumulator") @@ -603,6 +605,7 @@ class ComponentAccumulator(object): def store(self,outfile,nEvents=10,useBootStrapFile=True,threaded=False): from AthenaCommon.Utils.unixtools import find_datafile import pickle + self._isMergable = True # this is CA we store, it is ok it is not mergable if useBootStrapFile: import glob # first load basics from the bootstrap-pickle @@ -738,7 +741,6 @@ class ComponentAccumulator(object): self._wasMerged=True def createApp(self,OutputLevel=3): - log = logging.getLogger("ComponentAccumulator") self._wasMerged=True from Gaudi.Main import BootstrapHelper bsh=BootstrapHelper() @@ -774,14 +776,14 @@ class ComponentAccumulator(object): name=comp.getJobOptName() for k, v in comp.getValuedProperties().items(): if isinstance(v,Configurable): - log.debug("Adding "+name+"."+k+" = "+v.getFullName()) + self._msg.debug("Adding "+name+"."+k+" = "+v.getFullName()) bsh.addPropertyToCatalogue(jos,name,k,v.getFullName()) addCompToJos(v) elif isinstance(v,GaudiHandles.GaudiHandleArray): bsh.addPropertyToCatalogue(jos,name,k,str([ v1.getFullName() for v1 in v ])) else: if not isSequence(comp) and k!="Members": #This property his handled separatly - log.debug("Adding "+name+"."+k+" = "+str(v)) + self._msg.debug("Adding "+name+"."+k+" = "+str(v)) bsh.addPropertyToCatalogue(jos,name,k,str(v)) pass pass @@ -796,7 +798,7 @@ class ComponentAccumulator(object): #Add tree of algorithm sequences: for seqName, algoList in flatSequencers( self._sequence ).iteritems(): - log.debug("Members of %s : %s" % (seqName,str([alg.getFullName() for alg in algoList]))) + self._msg.debug("Members of %s : %s" % (seqName,str([alg.getFullName() for alg in algoList]))) bsh.addPropertyToCatalogue(jos,seqName,"Members",str( [alg.getFullName() for alg in algoList])) for alg in algoList: addCompToJos(alg) @@ -821,7 +823,6 @@ class ComponentAccumulator(object): def run(self,maxEvents=None,OutputLevel=3): - log = logging.getLogger("ComponentAccumulator") app = self.createApp (OutputLevel) #Determine maxEvents @@ -831,22 +832,22 @@ class ComponentAccumulator(object): else: maxEvents=-1 - print "INITIALIZE STEP" + self._msg.info("INITIALIZE STEP") sc = app.initialize() if not sc.isSuccess(): - log.error("Failed to initialize AppMgr") + self._msg.error("Failed to initialize AppMgr") return sc app.printAlgsSequences() #could be removed later .... sc = app.start() if not sc.isSuccess(): - log.error("Failed to start AppMgr") + self._msg.error("Failed to start AppMgr") return sc sc = app.run(maxEvents) if not sc.isSuccess(): - log.error("Failure running application") + self._msg.error("Failure running application") return sc app.stop().ignore() @@ -856,6 +857,27 @@ class ComponentAccumulator(object): sc1 = app.terminate() return sc1 + 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. + 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. + + 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 + PrivateTools - the name of the property + the type/instance_name are added + PublicTools - are located under ToolSvc/ and type/instance_name is used + Services - located under SvcMgr/ and type/instance_name is used + """ + from AthenaConfiguration.PropSetterProxy import PropSetterProxy + self._isMergable=False + return PropSetterProxy(self, path) + def CAtoGlobalWrapper(cfgmethod,flags): @@ -865,304 +887,3 @@ def CAtoGlobalWrapper(cfgmethod,flags): result.appendToGlobals() return - - - -# self test -import unittest - -class TestComponentAccumulator( unittest.TestCase ): - def setUp(self): - - Configurable.configurableRun3Behavior+=1 - # trivial case without any nested sequences - from AthenaCommon.Configurable import ConfigurablePyAlgorithm # guinea pig algorithms - from AthenaCommon.CFElements import seqAND, parOR - from AthenaCommon.Logging import log - from AthenaCommon.Constants import DEBUG - - log.setLevel(DEBUG) - - dummyCfgFlags=AthConfigFlags() - dummyCfgFlags.lock() - - class Algo(ConfigurablePyAlgorithm): - def __init__(self, name): - super( ConfigurablePyAlgorithm, self ).__init__( name ) - - def AlgsConf1(flags): - acc = ComponentAccumulator() - a1=Algo("Algo1") - a2=Algo("Algo2") - return acc,[a1,a2] - - - def AlgsConf2(flags): - acc = ComponentAccumulator() - result,algs=AlgsConf1( flags ) - acc.merge(result) - algs.append(Algo("Algo3")) - return acc,algs - - acc = ComponentAccumulator() - - # top level algs - acc1,algs=AlgsConf2(dummyCfgFlags) - acc.merge(acc1) - acc.addEventAlgo(algs) - - def AlgsConf3(flags): - acc = ComponentAccumulator() - na1=Algo("NestedAlgo1") - return acc,na1 - - def AlgsConf4(flags): - acc,na1= AlgsConf3( flags ) - NestedAlgo2 = Algo("NestedAlgo2") - NestedAlgo2.OutputLevel=7 - return acc,na1,NestedAlgo2 - - acc.addSequence( seqAND("Nest") ) - acc.addSequence( seqAND("subSequence1"), parentName="Nest" ) - acc.addSequence( parOR("subSequence2"), parentName="Nest" ) - - acc.addSequence( seqAND("sub2Sequence1"), parentName="subSequence1") - acc.addSequence( seqAND("sub3Sequence1"), parentName="subSequence1") - acc.addSequence( seqAND("sub4Sequence1"), parentName="subSequence1") - - accNA1=AlgsConf4(dummyCfgFlags) - acc.merge(accNA1[0]) - acc.addEventAlgo(accNA1[1:],"sub2Sequence1" ) - acc.store(open("testFile.pkl", "w")) - self.acc = acc - - - def test_algorihmsAreAdded( self ): - self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo1", 1).name(), "Algo1", "Algorithm not added to a top sequence" ) - self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo2", 1).name(), "Algo2", "Algorithm not added to a top sequence" ) - self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo3", 1).name(), "Algo3", "Algorithm not added to a top sequence" ) - - def test_sequencesAreAdded( self ): - self.assertIsNotNone( self.acc.getSequence("subSequence1" ), "Adding sub-sequence failed" ) - self.assertIsNotNone( self.acc.getSequence("subSequence2" ), "Adding sub-sequence failed" ) - self.assertIsNotNone( self.acc.getSequence("sub2Sequence1"), "Adding sub-sequence failed" ) - self.assertIsNotNone( findSubSequence( self.acc.getSequence("subSequence1"), "sub2Sequence1"), "Adding sub-sequence done in a wrong place" ) - - def test_algorithmsInNestedSequences( self ): - self.assertIsNotNone( findAlgorithm( self.acc.getSequence(), "NestedAlgo1" ), "Algorithm added to nested sequence" ) - self.assertIsNotNone( findAlgorithm( self.acc.getSequence(), "NestedAlgo1", 1 ) is None, "Algorithm mistakenly in top sequence" ) - self.assertIsNotNone( findAlgorithm( findSubSequence( self.acc.getSequence(), "sub2Sequence1"), "NestedAlgo1", 1 ), "Algorithm not in right sequence" ) - - - def test_readBackConfiguration( self ): - import pickle - with open( "testFile.pkl" ) as f: - s = pickle.load( f ) - self.assertIsNotNone( s, "The pickle has no content") - -class TestHLTCF( unittest.TestCase ): - def runTest( self ): - # replicate HLT issue, it occured because the sequnces were recorded in the order of storing in the dict and thus the - # some of them (in this case hltSteps) did not have properties recorded - from AthenaCommon.CFElements import seqAND, seqOR, parOR - from AthenaCommon.Configurable import ConfigurablePyAlgorithm # guinea pig algorithms - Configurable.configurableRun3Behavior=1 - acc = ComponentAccumulator() - acc.addSequence( seqOR("hltTop") ) - algos2 = ConfigurablePyAlgorithm( "RecoAlgInTop" ) - acc.addEventAlgo( algos2, sequenceName="hltTop" ) # some algo - acc.addSequence( seqAND("hltSteps"), parentName="hltTop" ) - acc.addSequence( parOR("hltStep_1"), parentName="hltSteps" ) - acc.addSequence( seqAND("L2CaloEgammaSeq"), "hltStep_1" ) - acc.addSequence( parOR("hltStep_2"), parentName="hltSteps" ) - acc.moveSequence( "L2CaloEgammaSeq", "hltStep_2" ) - - acc.store(open("testFile2.pkl", "w")) - import pickle - f = open("testFile2.pkl") - s = pickle.load(f) - f.close() - self.assertNotEqual( s['hltSteps']['Members'], '[]', "Empty set of members in hltSteps, Sequences recording order metters" ) - - -class MultipleParentsInSequences( unittest.TestCase ): - def runTest( self ): - # test if an algorithm (or sequence) can be controlled by more than one sequence - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - from AthenaCommon.Configurable import ConfigurablePyAlgorithm # guinea pig algorithms - accTop = ComponentAccumulator() - - recoSeq = seqAND("seqReco") - recoAlg = ConfigurablePyAlgorithm( "recoAlg" ) - recoSeq += recoAlg - - acc1 = ComponentAccumulator() - acc1.addSequence( seqAND("seq1") ) - acc1.addSequence( recoSeq, parentName="seq1" ) - - acc2 = ComponentAccumulator() - acc2.addSequence( seqAND("seq2") ) - acc2.addSequence( recoSeq, parentName="seq2" ) - - accTop.merge( acc1 ) - accTop.merge( acc2 ) - - accTop.printConfig() - - self.assertIsNotNone( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), "Algorithm missing in the first sequence" ) - self.assertIsNotNone( findAlgorithm( accTop.getSequence( "seq2" ), "recoAlg" ), "Algorithm missing in the second sequence" ) - s = accTop.getSequence( "seqReco" ) - self.assertEqual( len( s.getChildren() ), 1, "Wrong number of algorithms in reco seq: %d " % len( s.getChildren() ) ) - self.assertIs( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), findAlgorithm( accTop.getSequence( "seq2" ), "recoAlg" ), "Algorithms are cloned" ) - self.assertIs( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), recoAlg, "Clone of the original inserted in sequence" ) - - accTop.store( open("dummy.pkl", "w") ) - import pickle - # check if the recording did not harm the sequences - with open("dummy.pkl") as f: - s = pickle.load( f ) - self.assertEquals( s['seq1']["Members"], "['AthSequencer/seqReco']", "After pickling recoSeq missing in seq1 " + s['seq1']["Members"]) - self.assertEquals( s['seq2']["Members"], "['AthSequencer/seqReco']", "After pickling recoSeq missing in seq2 " + s['seq2']["Members"]) - self.assertEquals( s['seqReco']["Members"], "['ConfigurablePyAlgorithm/recoAlg']", "After pickling seqReco is corrupt " + s['seqReco']["Members"] ) - -class ForbidRecursiveSequences( unittest.TestCase ): - def runTest( self ): - #Can't add a sequence with the same name below itself, e.g. - # \__ AthAlgSeq (seq: PAR AND) - # \__ seq1 (seq: SEQ AND) - # \__ seq1 (seq: SEQ AND) - def selfSequence(): - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - accTop = ComponentAccumulator() - accTop.wasMerged() - seq1 = seqAND("seq1") - seq1_again = seqAND("seq1") - accTop.addSequence(seq1) - accTop.addSequence(seq1_again, parentName = "seq1") - - #Allowed to add a sequence with the same name at same depth, e.g. - # \__ AthAlgSeq (seq: PAR AND) - # \__ seq1 (seq: SEQ AND) - # \__ seq2 (seq: SEQ AND) - # \__ seq2 (seq: SEQ AND) - # should not raise any exceptions - def selfTwinSequence(): - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - accTop = ComponentAccumulator() - accTop.wasMerged() - seq1 = seqAND("seq1") - seq2 = seqAND("seq2") - seq2_again = seqAND("seq1") - accTop.addSequence(seq1) - accTop.addSequence(seq2, parentName = "seq1") - accTop.addSequence(seq2_again, parentName = "seq1") - accTop.wasMerged() - - - #Can't add a sequence with the same name two steps below itself, e.g. - # \__ AthAlgSeq (seq: PAR AND) - # \__ seq1 (seq: SEQ AND) - # \__ seq2 (seq: SEQ AND) - # \__ seq1 (seq: SEQ AND) - def selfGrandParentSequence(): - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - accTop = ComponentAccumulator() - accTop.store( open("test.pkl", "w") )#silence RuntimeError - seq1 = seqAND("seq1") - seq2 = seqAND("seq2") - seq1_again = seqAND("seq1") - accTop.addSequence(seq1) - accTop.addSequence(seq2, parentName = "seq1") - accTop.addSequence(seq1_again, parentName = "seq2") - accTop.wasMerged() - - #Can't merge sequences with the same name two steps below itself, e.g. - # \__ AthAlgSeq (seq: PAR AND) - # \__ seq1 (seq: SEQ AND) - # \__ seq2 (seq: SEQ AND) - # \__ seq1 (seq: SEQ AND) - def selfMergedGrandParentSequence(): - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - acc1=ComponentAccumulator() - acc1.wasMerged() - acc1.addSequence(seqAND("seq1")) - acc2=ComponentAccumulator() - acc2.wasMerged() - acc2.addSequence(seqAND("seq2")) - acc2.addSequence(seqAND("seq1"), "seq2") - acc1.merge(acc2) - - self.assertRaises(RuntimeError, selfSequence ) - self.assertRaises(RuntimeError, selfGrandParentSequence ) - self.assertRaises(RuntimeError, selfMergedGrandParentSequence ) - -class FailedMerging( unittest.TestCase ): - def runTest( self ): - topCA = ComponentAccumulator() - - def badMerge(): - someCA = ComponentAccumulator() - topCA.merge( (someCA, 1, "hello") ) - self.assertRaises(RuntimeError, badMerge ) - topCA.wasMerged() - - -class MergeMovingAlgorithms( unittest.TestCase ): - def runTest( self ): - Configurable.configurableRun3Behavior=1 - from AthenaCommon.CFElements import seqAND - from AthenaCommon.Configurable import ConfigurablePyAlgorithm # guinea pig algorithms - destinationCA = ComponentAccumulator() - destinationCA.addSequence( seqAND("dest") ) - - sourceCA = ComponentAccumulator() - sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg1")) - sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg2")) - sourceCA.addSequence( seqAND("innerSeq") ) - sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg3"), sequenceName="innerSeq" ) - - destinationCA.merge( sourceCA, sequenceName="dest" ) - - #destinationCA.merge( sourceCA ) - self.assertIsNotNone( findAlgorithm( destinationCA.getSequence("dest"), "alg1" ), "Algorithm not placed in sub-sequence" ) - self.assertIsNotNone( findSubSequence( destinationCA.getSequence(), "innerSeq" ), "The sequence is not added" ) - self.assertIsNotNone( findAlgorithm( destinationCA.getSequence("dest"), "alg3" ), "Algorithm deep in thesource CA not placed in sub-sequence of destiantion CA" ) - destinationCA.wasMerged() - sourceCA.wasMerged() - -class TestComponentAccumulatorAccessors( unittest.TestCase ): - def runTest( self ): - ca = ComponentAccumulator() - from AthenaCommon.Configurable import ConfigurablePyAlgorithm # guinea pig algorithms - ca.addEventAlgo(ConfigurablePyAlgorithm("alg1")) - - self.assertIsNotNone( ca.getEventAlgo(), "Found single alg") - self.assertEquals( len(ca.getEventAlgos()), 1 , "Found single alg") -# no idea why this assersts do not recognise exceptions -# self.assertRaises(ConfigurationError, ca.getEventAlgo("alg2")) - - ca.addEventAlgo(ConfigurablePyAlgorithm("alg2")) - - self.assertIsNotNone( ca.getEventAlgo("alg2"), "Found single alg") - self.assertEquals( len(ca.getEventAlgos()), 2 , "Found single alg") - # self.assertRaises(ConfigurationError, ca.getEventAlgo(), "Single Alg API ambiguity") - - class Tool(ConfigurableAlgTool): - def __init__(self, *args, **kwargs): - super(Tool, self).__init__(*args, **kwargs) - def getDlls(self): - return None - - - ca.addPublicTool( Tool(name="tool1") ) - self.assertIsNotNone( ca.getPublicTool(), "Found single tool") - ca.addPublicTool( Tool(name="tool2") ) -# self.assertRaises(ConfigurationError, ca.getPublicTool(), "Found single tool") - -if __name__ == "__main__": - unittest.main() diff --git a/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py b/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py new file mode 100644 index 0000000000000000000000000000000000000000..b97d2d485fe2385ad1061f7d73f15270b2ac87da --- /dev/null +++ b/Control/AthenaConfiguration/python/ComponentAccumulatorTest.py @@ -0,0 +1,320 @@ +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + +# self test of ComponentAccumulator + +from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator +from AthenaConfiguration.AthConfigFlags import AthConfigFlags +from AthenaCommon.CFElements import findSubSequence,findAlgorithm +from AthenaCommon.Configurable import Configurable, ConfigurablePyAlgorithm # guinea pig algorithms +from AthenaCommon.CFElements import seqAND, seqOR, parOR +from AthenaCommon.Logging import log +from AthenaCommon.Constants import DEBUG, INFO + +import unittest + +class TestComponentAccumulator( unittest.TestCase ): + def setUp(self): + + + # trivial case without any nested sequences + + Configurable.configurableRun3Behavior+=1 + + log.setLevel(DEBUG) + + dummyCfgFlags=AthConfigFlags() + dummyCfgFlags.lock() + + class Algo(ConfigurablePyAlgorithm): + def __init__(self, name): + super( ConfigurablePyAlgorithm, self ).__init__( name ) + + def AlgsConf1(flags): + acc = ComponentAccumulator() + a1=Algo("Algo1") + a2=Algo("Algo2") + return acc,[a1,a2] + + + def AlgsConf2(flags): + acc = ComponentAccumulator() + result,algs=AlgsConf1( flags ) + acc.merge(result) + a = Algo("Algo3") + print "algo3 when created", id(a) + algs.append(a) + return acc,algs + + acc = ComponentAccumulator() + + # top level algs + acc1,algs=AlgsConf2(dummyCfgFlags) + acc.merge(acc1) + acc.addEventAlgo(algs) + + def AlgsConf3(flags): + acc = ComponentAccumulator() + na1=Algo("NestedAlgo1") + return acc,na1 + + def AlgsConf4(flags): + acc,na1= AlgsConf3( flags ) + NestedAlgo2 = Algo("NestedAlgo2") + NestedAlgo2.OutputLevel=7 + return acc,na1,NestedAlgo2 + + acc.addSequence( seqAND("Nest") ) + acc.addSequence( seqAND("subSequence1"), parentName="Nest" ) + acc.addSequence( parOR("subSequence2"), parentName="Nest" ) + + acc.addSequence( seqAND("sub2Sequence1"), parentName="subSequence1") + acc.addSequence( seqAND("sub3Sequence1"), parentName="subSequence1") + acc.addSequence( seqAND("sub4Sequence1"), parentName="subSequence1") + + accNA1=AlgsConf4(dummyCfgFlags) + acc.merge(accNA1[0]) + acc.addEventAlgo(accNA1[1:],"sub2Sequence1" ) + acc.store(open("testFile.pkl", "w")) + self.acc = acc + + + def test_algorihmsAreAdded( self ): + self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo1", 1).name(), "Algo1", "Algorithm not added to a top sequence" ) + self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo2", 1).name(), "Algo2", "Algorithm not added to a top sequence" ) + self.assertEqual( findAlgorithm( self.acc.getSequence(), "Algo3", 1).name(), "Algo3", "Algorithm not added to a top sequence" ) + + def test_sequencesAreAdded( self ): + self.assertIsNotNone( self.acc.getSequence("subSequence1" ), "Adding sub-sequence failed" ) + self.assertIsNotNone( self.acc.getSequence("subSequence2" ), "Adding sub-sequence failed" ) + self.assertIsNotNone( self.acc.getSequence("sub2Sequence1"), "Adding sub-sequence failed" ) + self.assertIsNotNone( findSubSequence( self.acc.getSequence("subSequence1"), "sub2Sequence1"), "Adding sub-sequence done in a wrong place" ) + + def test_algorithmsInNestedSequences( self ): + self.assertIsNotNone( findAlgorithm( self.acc.getSequence(), "NestedAlgo1" ), "Algorithm added to nested sequence" ) + self.assertIsNotNone( findAlgorithm( self.acc.getSequence(), "NestedAlgo1", 1 ) is None, "Algorithm mistakenly in top sequence" ) + self.assertIsNotNone( findAlgorithm( findSubSequence( self.acc.getSequence(), "sub2Sequence1"), "NestedAlgo1", 1 ), "Algorithm not in right sequence" ) + + + def test_readBackConfiguration( self ): + import pickle + with open( "testFile.pkl" ) as f: + s = pickle.load( f ) + self.assertIsNotNone( s, "The pickle has no content") + + def test_foreach_component( self ): + from AthenaCommon.Logging import logging + logging.getLogger('foreach_component').setLevel(DEBUG) + algo3 = self.acc.getEventAlgo("Algo3") + algo3.OutputLevel = INFO + self.acc.foreach_component("*/Algo3").OutputLevel = DEBUG # restet to debug level + self.assertEqual( algo3.OutputLevel, DEBUG, "wrong OutputLevel value for Algo3") + self.acc.foreach_component("*sub2*/*").OutputLevel = INFO + self.assertEqual(self.acc.getEventAlgo("NestedAlgo1").OutputLevel, INFO, "wrong OutputLevel value for NestedAlgo1") + self.assertEqual(self.acc.getEventAlgo("NestedAlgo2").OutputLevel, INFO, "wrong OutputLevel value for NestedAlgo1") + + + +class TestHLTCF( unittest.TestCase ): + def runTest( self ): + # replicate HLT issue, it occured because the sequnces were recorded in the order of storing in the dict and thus the + # some of them (in this case hltSteps) did not have properties recorded + + + Configurable.configurableRun3Behavior=1 + acc = ComponentAccumulator() + acc.addSequence( seqOR("hltTop") ) + algos2 = ConfigurablePyAlgorithm( "RecoAlgInTop" ) + acc.addEventAlgo( algos2, sequenceName="hltTop" ) # some algo + acc.addSequence( seqAND("hltSteps"), parentName="hltTop" ) + acc.addSequence( parOR("hltStep_1"), parentName="hltSteps" ) + acc.addSequence( seqAND("L2CaloEgammaSeq"), "hltStep_1" ) + acc.addSequence( parOR("hltStep_2"), parentName="hltSteps" ) + acc.moveSequence( "L2CaloEgammaSeq", "hltStep_2" ) + + acc.store(open("testFile2.pkl", "w")) + import pickle + f = open("testFile2.pkl") + s = pickle.load(f) + f.close() + self.assertNotEqual( s['hltSteps']['Members'], '[]', "Empty set of members in hltSteps, Sequences recording order metters" ) + + +class MultipleParentsInSequences( unittest.TestCase ): + def runTest( self ): + # test if an algorithm (or sequence) can be controlled by more than one sequence + Configurable.configurableRun3Behavior=1 + + accTop = ComponentAccumulator() + + recoSeq = seqAND("seqReco") + recoAlg = ConfigurablePyAlgorithm( "recoAlg" ) + recoSeq += recoAlg + + acc1 = ComponentAccumulator() + acc1.addSequence( seqAND("seq1") ) + acc1.addSequence( recoSeq, parentName="seq1" ) + + acc2 = ComponentAccumulator() + acc2.addSequence( seqAND("seq2") ) + acc2.addSequence( recoSeq, parentName="seq2" ) + + accTop.merge( acc1 ) + accTop.merge( acc2 ) + + accTop.printConfig() + + self.assertIsNotNone( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), "Algorithm missing in the first sequence" ) + self.assertIsNotNone( findAlgorithm( accTop.getSequence( "seq2" ), "recoAlg" ), "Algorithm missing in the second sequence" ) + s = accTop.getSequence( "seqReco" ) + self.assertEqual( len( s.getChildren() ), 1, "Wrong number of algorithms in reco seq: %d " % len( s.getChildren() ) ) + self.assertIs( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), findAlgorithm( accTop.getSequence( "seq2" ), "recoAlg" ), "Algorithms are cloned" ) + self.assertIs( findAlgorithm( accTop.getSequence( "seq1" ), "recoAlg" ), recoAlg, "Clone of the original inserted in sequence" ) + + accTop.store( open("dummy.pkl", "w") ) + import pickle + # check if the recording did not harm the sequences + with open("dummy.pkl") as f: + s = pickle.load( f ) + self.assertEquals( s['seq1']["Members"], "['AthSequencer/seqReco']", "After pickling recoSeq missing in seq1 " + s['seq1']["Members"]) + self.assertEquals( s['seq2']["Members"], "['AthSequencer/seqReco']", "After pickling recoSeq missing in seq2 " + s['seq2']["Members"]) + self.assertEquals( s['seqReco']["Members"], "['ConfigurablePyAlgorithm/recoAlg']", "After pickling seqReco is corrupt " + s['seqReco']["Members"] ) + +class ForbidRecursiveSequences( unittest.TestCase ): + def runTest( self ): + #Can't add a sequence with the same name below itself, e.g. + # \__ AthAlgSeq (seq: PAR AND) + # \__ seq1 (seq: SEQ AND) + # \__ seq1 (seq: SEQ AND) + def selfSequence(): + Configurable.configurableRun3Behavior=1 + from AthenaCommon.CFElements import seqAND + accTop = ComponentAccumulator() + accTop.wasMerged() + seq1 = seqAND("seq1") + seq1_again = seqAND("seq1") + accTop.addSequence(seq1) + accTop.addSequence(seq1_again, parentName = "seq1") + + #Allowed to add a sequence with the same name at same depth, e.g. + # \__ AthAlgSeq (seq: PAR AND) + # \__ seq1 (seq: SEQ AND) + # \__ seq2 (seq: SEQ AND) + # \__ seq2 (seq: SEQ AND) + # should not raise any exceptions + def selfTwinSequence(): + Configurable.configurableRun3Behavior=1 + from AthenaCommon.CFElements import seqAND + accTop = ComponentAccumulator() + accTop.wasMerged() + seq1 = seqAND("seq1") + seq2 = seqAND("seq2") + seq2_again = seqAND("seq1") + accTop.addSequence(seq1) + accTop.addSequence(seq2, parentName = "seq1") + accTop.addSequence(seq2_again, parentName = "seq1") + accTop.wasMerged() + + + #Can't add a sequence with the same name two steps below itself, e.g. + # \__ AthAlgSeq (seq: PAR AND) + # \__ seq1 (seq: SEQ AND) + # \__ seq2 (seq: SEQ AND) + # \__ seq1 (seq: SEQ AND) + def selfGrandParentSequence(): + Configurable.configurableRun3Behavior=1 + from AthenaCommon.CFElements import seqAND + accTop = ComponentAccumulator() + seq1 = seqAND("seq1") + seq2 = seqAND("seq2") + seq1_again = seqAND("seq1") + accTop.addSequence(seq1) + accTop.addSequence(seq2, parentName = "seq1") + accTop.addSequence(seq1_again, parentName = "seq2") + accTop.wasMerged() + + #Can't merge sequences with the same name two steps below itself, e.g. + # \__ AthAlgSeq (seq: PAR AND) + # \__ seq1 (seq: SEQ AND) + # \__ seq2 (seq: SEQ AND) + # \__ seq1 (seq: SEQ AND) + def selfMergedGrandParentSequence(): + Configurable.configurableRun3Behavior=1 + from AthenaCommon.CFElements import seqAND + acc1=ComponentAccumulator() + acc1.wasMerged() + acc1.addSequence(seqAND("seq1")) + acc2=ComponentAccumulator() + acc2.wasMerged() + acc2.addSequence(seqAND("seq2")) + acc2.addSequence(seqAND("seq1"), "seq2") + acc1.merge(acc2) + + self.assertRaises(RuntimeError, selfSequence ) + self.assertRaises(RuntimeError, selfGrandParentSequence ) + self.assertRaises(RuntimeError, selfMergedGrandParentSequence ) + +class FailedMerging( unittest.TestCase ): + def runTest( self ): + topCA = ComponentAccumulator() + + def badMerge(): + someCA = ComponentAccumulator() + topCA.merge( (someCA, 1, "hello") ) + self.assertRaises(RuntimeError, badMerge ) + topCA.wasMerged() + + +class MergeMovingAlgorithms( unittest.TestCase ): + def runTest( self ): + Configurable.configurableRun3Behavior=1 + destinationCA = ComponentAccumulator() + destinationCA.addSequence( seqAND("dest") ) + + sourceCA = ComponentAccumulator() + sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg1")) + sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg2")) + sourceCA.addSequence( seqAND("innerSeq") ) + sourceCA.addEventAlgo(ConfigurablePyAlgorithm("alg3"), sequenceName="innerSeq" ) + + destinationCA.merge( sourceCA, sequenceName="dest" ) + + #destinationCA.merge( sourceCA ) + self.assertIsNotNone( findAlgorithm( destinationCA.getSequence("dest"), "alg1" ), "Algorithm not placed in sub-sequence" ) + self.assertIsNotNone( findSubSequence( destinationCA.getSequence(), "innerSeq" ), "The sequence is not added" ) + self.assertIsNotNone( findAlgorithm( destinationCA.getSequence("dest"), "alg3" ), "Algorithm deep in thesource CA not placed in sub-sequence of destiantion CA" ) + destinationCA.wasMerged() + sourceCA.wasMerged() + + +class TestComponentAccumulatorAccessors( unittest.TestCase ): + def runTest( self ): + ca = ComponentAccumulator() + from AthenaCommon.Configurable import ConfigurablePyAlgorithm,ConfigurableAlgTool + ca.addEventAlgo(ConfigurablePyAlgorithm("alg1")) + + self.assertIsNotNone( ca.getEventAlgo(), "Found single alg") + self.assertEquals( len(ca.getEventAlgos()), 1 , "Found single alg") +# no idea why this assersts do not recognise exceptions +# self.assertRaises(ConfigurationError, ca.getEventAlgo("alg2")) + + ca.addEventAlgo(ConfigurablePyAlgorithm("alg2")) + + self.assertIsNotNone( ca.getEventAlgo("alg2"), "Found single alg") + self.assertEquals( len(ca.getEventAlgos()), 2 , "Found single alg") + # self.assertRaises(ConfigurationError, ca.getEventAlgo(), "Single Alg API ambiguity") + + class Tool(ConfigurableAlgTool): + def __init__(self, *args, **kwargs): + super(Tool, self).__init__(*args, **kwargs) + def getDlls(self): + return None + + + ca.addPublicTool( Tool(name="tool1") ) + self.assertIsNotNone( ca.getPublicTool(), "Found single tool") + ca.addPublicTool( Tool(name="tool2") ) +# self.assertRaises(ConfigurationError, ca.getPublicTool(), "Found single tool") + + +if __name__ == "__main__": + unittest.main() diff --git a/Control/AthenaConfiguration/python/PropSetterProxy.py b/Control/AthenaConfiguration/python/PropSetterProxy.py new file mode 100644 index 0000000000000000000000000000000000000000..e55e6e32fa2b15c2c6517c5c0f2c36a0f60d5676 --- /dev/null +++ b/Control/AthenaConfiguration/python/PropSetterProxy.py @@ -0,0 +1,79 @@ +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +from AthenaCommon.Logging import logging +from AthenaCommon.CFElements import isSequence +from AthenaCommon.Configurable import ConfigurableAlgTool +from GaudiKernel.GaudiHandles import PrivateToolHandle, PrivateToolHandleArray +class PropSetterProxy(object): + __compPaths = {} + __scannedCA = None + + def __init__(self, ca, path): + self.__path = path + self.__findComponents( ca ) + + def __setattr__(self, name, value): + if name.startswith("_PropSetterProxy"): + return super(PropSetterProxy, self).__setattr__(name, value) + + msg = logging.getLogger('foreach_component') + if name != "OutputLevel": + msg.warning( "Only OutputLevel is allowed to be changed with the foreach_component at the moment" ) + return + + + import fnmatch + for component_path, component in PropSetterProxy.__compPaths.iteritems(): + if fnmatch.fnmatch( component_path, self.__path ): + if name in component.getProperties(): + try: + setattr( component, name, value ) + msg.info( "Set property: %s to value %s of component %s because it matched %s " % ( name, str(value), component_path, self.__path ) ) + except Exception, ex: + msg.warning( "Failed to set property: %s to value %s of component %s because it matched %s, reason: %s" % ( name, str(value), component_path, self.__path, str(ex) ) ) + pass + else: + msg.warning( "No such a property: %s in component %s, tried to set it because it matched %s" % ( name, component_path, self.__path ) ) + + + def __findComponents(self, ca): + if ca is not PropSetterProxy.__scannedCA: + PropSetterProxy.__scannedCA = ca + PropSetterProxy.__compPaths = {} + def __add(path, comp): + if comp.getName() == "": + return + PropSetterProxy.__compPaths[ path ] = comp + + + for svc in ca._services: + PropSetterProxy.__compPaths['SvcMgr/'+svc.getFullName()] = svc + for t in ca._publicTools: + PropSetterProxy.__compPaths['ToolSvc/'+t.getFullName()] = t + + def __nestAlg(startpath, comp): # it actually dives inside the algorithms and (sub) tools + if comp.getName() == "": + return + for name, value in comp.getProperties().iteritems(): + if isinstance( value, ConfigurableAlgTool ) or isinstance( value, PrivateToolHandle ): + __add( startpath+"/"+name+"/"+value.getFullName(), value ) + __nestAlg( startpath+"/"+name+"/"+value.getName(), value ) + if isinstance( value, PrivateToolHandleArray): + for toolIndex,t in enumerate(value): + __add( startpath+"/"+name+"/"+t.getFullName(), t ) + __nestAlg( startpath+"/"+name+"/"+t.getName(), value[toolIndex] ) + + + def __nestSeq( startpath, seq ): + for c in seq.getChildren(): + if isSequence(c): + __nestSeq( startpath+"/"+c.getName(), c ) + else: # the algorithm or tool + __add( startpath+"/"+c.getFullName(), c ) + __nestAlg( startpath+"/"+c.getFullName(), c ) + + __nestSeq("", ca._sequence) + + + + + diff --git a/Trigger/TrigValidation/TrigUpgradeTest/share/newJOtest.py b/Trigger/TrigValidation/TrigUpgradeTest/share/newJOtest.py index 52d08bb197a1867bcebbcdcc4025ddaafe42608c..f569bc47d3e2f77eae00af5ccba93ea8c9078b2e 100644 --- a/Trigger/TrigValidation/TrigUpgradeTest/share/newJOtest.py +++ b/Trigger/TrigValidation/TrigUpgradeTest/share/newJOtest.py @@ -36,7 +36,7 @@ flags.Trigger.L1Decoder.forceEnableAllChains = True flags.lock() -from AthenaCommon.Constants import INFO,DEBUG +from AthenaCommon.Constants import INFO,DEBUG,WARNING acc = ComponentAccumulator() from ByteStreamCnvSvc.ByteStreamConfig import TrigBSReadCfg @@ -62,12 +62,16 @@ acc.getEventAlgo( "TrigSignatureMoniMT" ).OutputLevel=DEBUG print acc.getEventAlgo( "TrigSignatureMoniMT" ) -# from TrigUpgradeTest.TestUtils import applyMenu -# applyMenu( acc.getEventAlgo( "L1Decoder" ) ) -#acc.getEventAlgo( "L1Decoder" ).OutputLevel=DEBUG -#acc.getEventAlgo( "L2ElectronCaloHypo" ).OutputLevel=DEBUG -#acc.getEventAlgo( "FastEMCaloAlgo" ).OutputLevel=DEBUG -#acc.getEventAlgo( "Filter_for_L2PhotonCaloHypo" ).OutputLevel=DEBUG +from AthenaCommon.Logging import logging +logging.getLogger('forcomps').setLevel(DEBUG) +acc.foreach_component("*/L1Decoder").OutputLevel = DEBUG +acc.foreach_component("*/L1Decoder/*Tool").OutputLevel = DEBUG # tools +acc.foreach_component("*HLTTop/*Hypo*").OutputLevel = DEBUG # hypo algs +acc.foreach_component("*HLTTop/*Hypo*/*Tool*").OutputLevel = DEBUG # hypo tools +acc.foreach_component("*HLTTop/RoRSeqFilter/*").OutputLevel = DEBUG # filters +acc.foreach_component("*HLTTop/*Input*").OutputLevel = DEBUG # input makers +acc.foreach_component("*HLTTop/*GenericMonitoringTool*").OutputLevel = WARNING # silcence mon tools (addressing by type) + acc.printConfig() @@ -80,3 +84,4 @@ with file(fname, "w") as p: +