Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have cms.Modifier apply immediately #6296

Merged
merged 2 commits into from Nov 9, 2014
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
135 changes: 89 additions & 46 deletions FWCore/ParameterSet/python/Config.py
Expand Up @@ -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")
Expand All @@ -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._setChosen()

def setStrict(self, value):
self.__isStrict = value
Expand Down Expand Up @@ -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._applyNewProcessModifiers(self)

def _dumpConfigNamedList(self,items,typeName,options):
returnValue = ''
for name,item in items:
Expand Down Expand Up @@ -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"""
Expand All @@ -1064,30 +1075,45 @@ 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)
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

class ModifierChain(object):
"""A Modifier made up of a list of Modifiers
"""
self.modify(process)

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
Expand Down Expand Up @@ -1797,42 +1823,59 @@ 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
#check that Modifier not attached to a process doesn't run
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
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"))
#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()