From 556716d4086c3c0d659b2fc31ce88ea1622e84b6 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sat, 8 Nov 2014 17:46:21 +0100 Subject: [PATCH 1/2] Apply changes from cms.Modifier immediately As discussed in the Core software meeting, the cms.Modifier class now modifies the objects immediately if that cms.Modifier was passed to the constructor of the cms.Process instance. --- FWCore/ParameterSet/python/Config.py | 106 +++++++++++++++------------ 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/FWCore/ParameterSet/python/Config.py b/FWCore/ParameterSet/python/Config.py index 5310e256467a4..ea173f045f56c 100644 --- a/FWCore/ParameterSet/python/Config.py +++ b/FWCore/ParameterSet/python/Config.py @@ -99,8 +99,11 @@ class Temp(object): class Process(object): """Root class for a CMS configuration process""" - def __init__(self,name): - """The argument 'name' will be the name applied to this Process""" + def __init__(self,name,*Mods): + """The argument 'name' will be the name applied to this Process + Can optionally pass as additional arguments cms.Modifier instances + which will be used ot modify the Process as it is built + """ self.__dict__['_Process__name'] = name if not name.isalnum(): raise RuntimeError("Error: The process name is an empty string or contains non-alphanumeric characters") @@ -127,6 +130,9 @@ def __init__(self,name): self.__dict__['_Process__InExtendCall'] = False self.__dict__['_Process__partialschedules'] = {} self.__isStrict = False + self.__dict__['_Process__modifiers'] = Mods + for m in self.__modifiers: + m._Modifier__setChosen() def setStrict(self, value): self.__isStrict = value @@ -545,6 +551,10 @@ def extend(self,other,items=()): #now put in proper bucket newSeq._place(name,self) self.__dict__['_Process__InExtendCall'] = False + # apply any newly acquired process modifiers + for m in self.__modifiers: + m._Modifier__applyNewProcessModifiers(self) + def _dumpConfigNamedList(self,items,typeName,options): returnValue = '' for name,item in items: @@ -1046,11 +1056,12 @@ class Modifier(object): reconfigure items in a process to match our expectation of running in 2017. Once declared, these Modifier instances are imported into a configuration and items which need to be modified are then associated with the Modifier and with the action to do the modification. - The registered modifications will only occur if the modify() method is called. + The registered modifications will only occur if the Modifier was passed to + the cms.Process' constructor. """ def __init__(self): - self.__objectToModifiers = [] self.__processModifiers = [] + self.__chosen = False def toModifyProcess(self,func): """This is used to register actions to be performed on the process as a whole. This takes as argument a callable object (e.g. function) which takes as its sole argument an instance of Process""" @@ -1064,30 +1075,25 @@ def toModify(self,obj, func=None,**kw): """ if func is not None and len(kw) != 0: raise TypeError("toModify takes either two arguments or one argument and key/value pairs") + if not self.isChosen(): + return if func is not None: - self.__objectToModifiers.append( (obj,func)) + func(obj) else: - self.__objectToModifiers.append( (obj, _ParameterModifier(kw))) - def modify(self,process): - """This applies all the registered modifiers to the passed in process""" + temp =_ParameterModifier(kw) + temp(obj) + def __applyNewProcessModifiers(self,process): + """Should only be called by cms.Process instances + applies list of accumulated changes to the process""" for m in self.__processModifiers: - m(process) - for o,m in self.__objectToModifiers: - if isinstance(o,_Labelable): - if o.hasLabel_(): - m(o) - else: - m(o) - return process - def __call__(self,process): - """Forwards to modify call. The presence of a __call__ allows Modifiers to be chained together. - E.g. Have bar inherit all modifiers of foo - foo = Modifier() - bar = Modifier() - foo.toModifyProcess(bar) - """ - self.modify(process) - + m(process) + self.__processModifiers = list() + def __setChosen(self): + """Should only be called by cms.Process instances""" + self.__chosen = True; + def isChosen(self): + return self.__chosen + if __name__=="__main__": import unittest import copy @@ -1797,42 +1803,48 @@ def testPrune(self): self.assert_(hasattr(p, 'pth')) def testModifier(self): m1 = Modifier() - p = Process("test") + p = Process("test",m1) p.a = EDAnalyzer("MyAnalyzer", fred = int32(1)) def _mod_fred(obj): obj.fred = 2 m1.toModify(p.a,_mod_fred) + self.assertEqual(p.a.fred.value(),2) p.b = EDAnalyzer("YourAnalyzer", wilma = int32(1)) m1.toModify(p.b, wilma = 2) - self.assertEqual(p.a.fred.value(),1) - self.assertEqual(p.b.wilma.value(),1) - m1.modify(p) - self.assertEqual(p.a.fred.value(),2) self.assertEqual(p.b.wilma.value(),2) - #check that items not attached to process are unchanged - m1 = Modifier() - p = Process("test") - p.a = EDAnalyzer("MyAnalyzer", fred = int32(1)) - m1.toModify(p.a,_mod_fred) - b = EDAnalyzer("YourAnalyzer", wilma = int32(1)) - m1.toModify(b, wilma = 2) - self.assertEqual(p.a.fred.value(),1) - self.assertEqual(b.wilma.value(),1) - m1.modify(p) - self.assertEqual(p.a.fred.value(),2) - self.assertEqual(b.wilma.value(),1) - #make sure chains of modifiers work + #check that Modifier not attached to a process doesn't run m1 = Modifier() - m2 = Modifier() - m2.toModifyProcess(m1) p = Process("test") p.a = EDAnalyzer("MyAnalyzer", fred = int32(1)) m1.toModify(p.a,_mod_fred) p.b = EDAnalyzer("YourAnalyzer", wilma = int32(1)) m1.toModify(p.b, wilma = 2) - m2.toModify(p.b, wilma = 3) - m2.modify(p) + self.assertEqual(p.a.fred.value(),1) + self.assertEqual(p.b.wilma.value(),1) + #make sure clones get the changes + m1 = Modifier() + p = Process("test",m1) + p.a = EDAnalyzer("MyAnalyzer", fred = int32(1), wilma = int32(1)) + m1.toModify(p.a, fred = int32(2)) + p.b = p.a.clone(wilma = int32(3)) self.assertEqual(p.a.fred.value(),2) + self.assertEqual(p.a.wilma.value(),1) + self.assertEqual(p.b.fred.value(),2) self.assertEqual(p.b.wilma.value(),3) + #test that load causes process wide methods to run + def _rem_a(proc): + del proc.a + class DummyMod(object): + def __init__(self): + self.a = EDAnalyzer("Dummy") + testMod = DummyMod() + p.extend(testMod) + self.assert_(hasattr(p,"a")) + m1 = Modifier() + p = Process("test",m1) + m1.toModifyProcess(_rem_a) + p.extend(testMod) + self.assert_(not hasattr(p,"a")) + unittest.main() From d8d8c48097be01069c1835bcda7cf90c1ca61ff0 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Sun, 9 Nov 2014 07:56:38 -0600 Subject: [PATCH 2/2] Added ModifierChain Allow Modifiers to be grouped together in via a ModifierChain --- FWCore/ParameterSet/python/Config.py | 43 ++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/FWCore/ParameterSet/python/Config.py b/FWCore/ParameterSet/python/Config.py index ea173f045f56c..54e8ce425d0aa 100644 --- a/FWCore/ParameterSet/python/Config.py +++ b/FWCore/ParameterSet/python/Config.py @@ -132,7 +132,7 @@ def __init__(self,name,*Mods): self.__isStrict = False self.__dict__['_Process__modifiers'] = Mods for m in self.__modifiers: - m._Modifier__setChosen() + m._setChosen() def setStrict(self, value): self.__isStrict = value @@ -553,7 +553,7 @@ def extend(self,other,items=()): self.__dict__['_Process__InExtendCall'] = False # apply any newly acquired process modifiers for m in self.__modifiers: - m._Modifier__applyNewProcessModifiers(self) + m._applyNewProcessModifiers(self) def _dumpConfigNamedList(self,items,typeName,options): returnValue = '' @@ -1082,18 +1082,38 @@ def toModify(self,obj, func=None,**kw): else: temp =_ParameterModifier(kw) temp(obj) - def __applyNewProcessModifiers(self,process): + def _applyNewProcessModifiers(self,process): """Should only be called by cms.Process instances applies list of accumulated changes to the process""" for m in self.__processModifiers: m(process) self.__processModifiers = list() - def __setChosen(self): + def _setChosen(self): """Should only be called by cms.Process instances""" - self.__chosen = True; + self.__chosen = True def isChosen(self): return self.__chosen +class ModifierChain(object): + """A Modifier made up of a list of Modifiers + """ + def __init__(self, *chainedModifiers): + self.__chosen = False + self.__chain = chainedModifiers + def _applyNewProcessModifiers(self,process): + """Should only be called by cms.Process instances + applies list of accumulated changes to the process""" + for m in self.__chain: + m._applyNewProcessModifiers(process) + def _setChosen(self): + """Should only be called by cms.Process instances""" + self.__chosen = True + for m in self.__chain: + m._setChosen() + def isChosen(self): + return self.__chosen + + if __name__=="__main__": import unittest import copy @@ -1845,6 +1865,17 @@ def __init__(self): m1.toModifyProcess(_rem_a) p.extend(testMod) self.assert_(not hasattr(p,"a")) - + #test ModifierChain + m1 = Modifier() + mc = ModifierChain(m1) + p = Process("test",mc) + testMod = DummyMod() + m1.toModifyProcess(_rem_a) + p.b = EDAnalyzer("Dummy2", fred = int32(1)) + m1.toModify(p.b, fred = int32(3)) + p.extend(testMod) + self.assert_(not hasattr(p,"a")) + self.assertEqual(p.b.fred.value(),3) + unittest.main()