00001 import inspect
00002
00003 class _ConfigureComponent(object):
00004 """Denotes a class that can be used by the Processes class"""
00005 pass
00006
00007 class PrintOptions(object):
00008 def __init__(self):
00009 self.indent_= 0
00010 self.deltaIndent_ = 4
00011 self.isCfg = True
00012 def indentation(self):
00013 return ' '*self.indent_
00014 def indent(self):
00015 self.indent_ += self.deltaIndent_
00016 def unindent(self):
00017 self.indent_ -= self.deltaIndent_
00018
00019 class _ParameterTypeBase(object):
00020 """base class for classes which are used as the 'parameters' for a ParameterSet"""
00021 def __init__(self):
00022 self.__dict__["_isFrozen"] = False
00023 self.__isTracked = True
00024 self._isModified = False
00025 def isModified(self):
00026 return self._isModified
00027 def resetModified(self):
00028 self._isModified=False
00029 def configTypeName(self):
00030 if self.isTracked():
00031 return type(self).__name__
00032 return 'untracked '+type(self).__name__
00033 def pythonTypeName(self):
00034 if self.isTracked():
00035 return 'cms.'+type(self).__name__
00036 return 'cms.untracked.'+type(self).__name__
00037 def dumpPython(self, options=PrintOptions()):
00038 return self.pythonTypeName()+"("+self.pythonValue(options)+")"
00039 def __repr__(self):
00040 return self.dumpPython()
00041 def isTracked(self):
00042 return self.__isTracked
00043 def setIsTracked(self,trackness):
00044 self.__isTracked = trackness
00045 def isFrozen(self):
00046 return self._isFrozen
00047 def setIsFrozen(self):
00048 self._isFrozen = True
00049
00050 class _SimpleParameterTypeBase(_ParameterTypeBase):
00051 """base class for parameter classes which only hold a single value"""
00052 def __init__(self,value):
00053 super(_SimpleParameterTypeBase,self).__init__()
00054 self._value = value
00055 if not self._isValid(value):
00056 raise ValueError(str(value)+" is not a valid "+str(type(self)))
00057 def value(self):
00058 return self._value
00059 def setValue(self,value):
00060 if not self._isValid(value):
00061 raise ValueError(str(value)+" is not a valid "+str(type(self)))
00062 if value!=self._value:
00063 self._isModified=True
00064 self._value=value
00065 def configValue(self, options=PrintOptions()):
00066 return str(self._value)
00067 def pythonValue(self, options=PrintOptions()):
00068 return self.configValue(options)
00069 def __eq__(self,other):
00070 if isinstance(other,_SimpleParameterTypeBase):
00071 return self._value == other._value
00072 return self._value == other
00073 def __ne__(self,other):
00074 if isinstance(other,_SimpleParameterTypeBase):
00075 return self._value != other._value
00076 return self._value != other
00077
00078
00079 class UsingBlock(_SimpleParameterTypeBase):
00080 """For injection purposes, pretend this is a new parameter type
00081 then have a post process step which strips these out
00082 """
00083 def __init__(self,value, s='', loc=0, file=''):
00084 super(UsingBlock,self).__init__(value)
00085 self.s = s
00086 self.loc = loc
00087 self.file = file
00088 self.isResolved = False
00089 @staticmethod
00090 def _isValid(value):
00091 return isinstance(value,str)
00092 def _valueFromString(value):
00093 """only used for cfg-parsing"""
00094 return string(value)
00095 def insertInto(self, parameterSet, myname):
00096 value = self.value()
00097
00098
00099
00100 parameterSet.addString(self.isTracked(), myname, value)
00101 def dumpPython(self, options):
00102 if options.isCfg:
00103 return "process."+self.value()
00104 else:
00105 return self.value()
00106
00107
00108 class _Parameterizable(object):
00109 """Base class for classes which allow addition of _ParameterTypeBase data"""
00110 def __init__(self,*arg,**kargs):
00111 self.__dict__['_Parameterizable__parameterNames'] = []
00112 self.__dict__["_isFrozen"] = False
00113 """The named arguments are the 'parameters' which are added as 'python attributes' to the object"""
00114 if len(arg) != 0:
00115
00116 for block in arg:
00117 if type(block).__name__ != "PSet":
00118 raise ValueError("Only PSets can be passed as unnamed argument blocks. This is a "+type(block).__name__)
00119 self.__setParameters(block.parameters_())
00120 self.__setParameters(kargs)
00121 self._isModified = False
00122
00123 def parameterNames_(self):
00124 """Returns the name of the parameters"""
00125 return self.__parameterNames[:]
00126 def isModified(self):
00127 if self._isModified:
00128 return True
00129 for name in self.parameterNames_():
00130 param = self.__dict__[name]
00131 if isinstance(param, _Parameterizable) and param.isModified():
00132 self._isModified = True
00133 return True
00134 return False
00135
00136 def hasParameter(self, params):
00137 """
00138 _hasParameter_
00139
00140 check that pset provided has the attribute chain
00141 specified.
00142
00143 Eg, if params is [ 'attr1', 'attr2', 'attr3' ]
00144 check for pset.attr1.attr2.attr3
00145
00146 returns True if parameter exists, False if not
00147 """
00148 return (self.getParameter(params) != None)
00149
00150 def getParameter(self, params):
00151 """
00152 _getParameter_
00153
00154 Retrieve the specified parameter from the PSet Provided
00155 given the attribute chain
00156
00157 returns None if not found
00158 """
00159 lastParam = self
00160
00161 if type(params).__name__ == 'str':
00162 return getattr(self, params, None)
00163 for param in params:
00164 lastParam = getattr(lastParam, param, None)
00165 print str(lastParam)
00166 if lastParam == None:
00167 return None
00168 return lastParam
00169
00170 def parameters_(self):
00171 """Returns a dictionary of copies of the user-set parameters"""
00172 import copy
00173 result = dict()
00174 for name in self.parameterNames_():
00175 result[name]=copy.deepcopy(self.__dict__[name])
00176 return result
00177
00178 def __addParameter(self, name, value):
00179 if not isinstance(value,_ParameterTypeBase):
00180 self.__raiseBadSetAttr(name)
00181 if name in self.__dict__:
00182 message = "Duplicate insert of member " + name
00183 message += "\nThe original parameters are:\n"
00184 message += self.dumpPython() + '\n'
00185 raise ValueError(message)
00186 self.__dict__[name]=value
00187 self.__parameterNames.append(name)
00188 self._isModified = True
00189
00190 def __setParameters(self,parameters):
00191 for name,value in parameters.iteritems():
00192 self.__addParameter(name, value)
00193
00194 def __setattr__(self,name,value):
00195
00196
00197 if self.isFrozen() and not (name in ["_Labelable__label","_isFrozen"] or name.startswith('_')):
00198 message = "Object already added to a process. It is read only now\n"
00199 message += " %s = %s" %(name, value)
00200 message += "\nThe original parameters are:\n"
00201 message += self.dumpPython() + '\n'
00202 raise ValueError(message)
00203
00204 if name[0]=='_':
00205 super(_Parameterizable,self).__setattr__(name,value)
00206 elif not name in self.__dict__:
00207 self.__addParameter(name, value)
00208 self._isModified = True
00209 else:
00210
00211 if isinstance(value,_ParameterTypeBase):
00212 self.__dict__[name] = value
00213 else:
00214 self.__dict__[name].setValue(value)
00215 self._isModified = True
00216
00217 def isFrozen(self):
00218 return self._isFrozen
00219 def setIsFrozen(self):
00220 self._isFrozen = True
00221 for name in self.parameterNames_():
00222 self.__dict__[name].setIsFrozen()
00223 def __delattr__(self,name):
00224 if self.isFrozen():
00225 raise ValueError("Object already added to a process. It is read only now")
00226 super(_Parameterizable,self).__delattr__(name)
00227 self.__parameterNames.remove(name)
00228 @staticmethod
00229 def __raiseBadSetAttr(name):
00230 raise TypeError(name+" does not already exist, so it can only be set to a CMS python configuration type")
00231 def dumpPython(self, options=PrintOptions()):
00232 others = []
00233 usings = []
00234 for name in self.parameterNames_():
00235 param = self.__dict__[name]
00236
00237 name2 = name.replace('-','_')
00238 options.indent()
00239
00240 if name.startswith("using_"):
00241 usings.append(options.indentation()+param.dumpPython(options))
00242 else:
00243 others.append(options.indentation()+name2+' = '+param.dumpPython(options))
00244 options.unindent()
00245
00246 resultList = usings
00247 resultList.extend(others)
00248 return ',\n'.join(resultList)+'\n'
00249 def __repr__(self):
00250 return self.dumpPython()
00251 def insertContentsInto(self, parameterSet):
00252 for name in self.parameterNames_():
00253 param = getattr(self,name)
00254 param.insertInto(parameterSet, name)
00255
00256
00257 class _TypedParameterizable(_Parameterizable):
00258 """Base class for classes which are Parameterizable and have a 'type' assigned"""
00259 def __init__(self,type_,*arg,**kargs):
00260 self.__dict__['_TypedParameterizable__type'] = type_
00261
00262
00263
00264
00265
00266 arg = tuple([x for x in arg if x != None])
00267 super(_TypedParameterizable,self).__init__(*arg,**kargs)
00268 saveOrigin(self, 1)
00269 def _place(self,name,proc):
00270 self._placeImpl(name,proc)
00271 def type_(self):
00272 """returns the type of the object, e.g. 'FooProducer'"""
00273 return self.__type
00274 def copy(self):
00275 returnValue =_TypedParameterizable.__new__(type(self))
00276 params = self.parameters_()
00277 args = list()
00278 if len(params) == 0:
00279 args.append(None)
00280 returnValue.__init__(self.__type,*args,
00281 **params)
00282 returnValue._isModified = self._isModified
00283 return returnValue
00284 def clone(self, *args, **params):
00285 """Copies the object and allows one to modify the parameters of the clone.
00286 New parameters may be added by specify the exact type
00287 Modifying existing parameters can be done by just specifying the new
00288 value without having to specify the type.
00289 """
00290 returnValue =_TypedParameterizable.__new__(type(self))
00291 myparams = self.parameters_()
00292 if len(myparams) == 0 and len(params) and len(args):
00293 args.append(None)
00294 if len(params):
00295
00296 for key,value in params.iteritems():
00297 if key in myparams:
00298 if isinstance(value,_ParameterTypeBase):
00299 myparams[key] =value
00300 else:
00301 myparams[key].setValue(value)
00302 else:
00303 if isinstance(value,_ParameterTypeBase):
00304 myparams[key]=value
00305 else:
00306 self._Parameterizable__raiseBadSetAttr(key)
00307
00308 returnValue.__init__(self.__type,*args,
00309 **myparams)
00310 returnValue._isModified = False
00311 returnValue._isFrozen = False
00312 saveOrigin(returnValue, 1)
00313 return returnValue
00314
00315 @staticmethod
00316 def __findDefaultsFor(label,type):
00317
00318 import sys
00319 import glob
00320 choices = list()
00321 for d in sys.path:
00322 choices.extend(glob.glob(d+'/*/*/'+label+'.py'))
00323 if not choices:
00324 return None
00325
00326
00327
00328 for c in choices:
00329
00330 name='.'.join(c[:-3].split('/')[-3:])
00331
00332 mod = __import__(name)
00333 components = name.split('.')
00334 for comp in components[1:]:
00335 mod = getattr(mod,comp)
00336 if hasattr(mod,label):
00337 default = getattr(mod,label)
00338 if isinstance(default,_TypedParameterizable):
00339 if(default.type_() == type):
00340 params = dict()
00341 for name in default.parameterNames_():
00342 params[name] = getattr(default,name)
00343 return params
00344 return None
00345
00346 def dumpConfig(self, options=PrintOptions()):
00347 config = self.__type +' { \n'
00348 for name in self.parameterNames_():
00349 param = self.__dict__[name]
00350 options.indent()
00351 config+=options.indentation()+param.configTypeName()+' '+name+' = '+param.configValue(options)+'\n'
00352 options.unindent()
00353 config += options.indentation()+'}\n'
00354 return config
00355
00356 def dumpPython(self, options=PrintOptions()):
00357 result = "cms."+str(type(self).__name__)+'("'+self.type_()+'"'
00358 nparam = len(self.parameterNames_())
00359 if nparam == 0:
00360 result += ")\n"
00361 elif nparam < 256:
00362 result += ",\n"+_Parameterizable.dumpPython(self,options)+options.indentation() + ")\n"
00363 else:
00364
00365
00366
00367
00368 label = ""
00369 try:
00370 label = "process."+self.label_()
00371 except:
00372 label = "FIX-THIS"
00373 result += ")\n" + self.dumpPythonAttributes(label, options)
00374 return result
00375
00376 def dumpPythonAttributes(self, myname, options):
00377 """ dumps the object with all attributes declared after the constructor"""
00378 result = ""
00379 for name in self.parameterNames_():
00380 param = self.__dict__[name]
00381 result += options.indentation() + myname + "." + name + " = " + param.dumpPython(options) + "\n"
00382 return result
00383
00384 def nameInProcessDesc_(self, myname):
00385 return myname;
00386 def moduleLabel_(self, myname):
00387 return myname
00388 def insertInto(self, parameterSet, myname):
00389 newpset = parameterSet.newPSet()
00390 newpset.addString(True, "@module_label", self.moduleLabel_(myname))
00391 newpset.addString(True, "@module_type", self.type_())
00392 newpset.addString(True, "@module_edm_type", type(self).__name__)
00393 self.insertContentsInto(newpset)
00394 parameterSet.addPSet(True, self.nameInProcessDesc_(myname), newpset)
00395
00396
00397
00398 class _Labelable(object):
00399 """A 'mixin' used to denote that the class can be paired with a label (e.g. an EDProducer)"""
00400 def label_(self):
00401 if not hasattr(self, "_Labelable__label"):
00402 raise RuntimeError("module has no label. Perhaps it wasn't inserted into the process?")
00403 return self.__label
00404 def hasLabel_(self):
00405 return hasattr(self, "_Labelable__label") and self.__label is not None
00406 def setLabel(self,label):
00407 if self.hasLabel_() :
00408 if self.label_() != label and label is not None :
00409 msg100 = "Attempting to change the label of a Labelable object, possibly an attribute of the Process\n"
00410 msg101 = "Old label = "+self.label_()+" New label = "+label+"\n"
00411 msg102 = "Type = "+str(type(self))+"\n"
00412 msg103 = "Some possible solutions:\n"
00413 msg104 = " 1. Clone modules instead of using simple assignment. Cloning is\n"
00414 msg105 = " also preferred for other types when possible.\n"
00415 msg106 = " 2. Declare new names starting with an underscore if they are\n"
00416 msg107 = " for temporaries you do not want propagated into the Process. The\n"
00417 msg108 = " underscore tells \"from x import *\" and process.load not to import\n"
00418 msg109 = " the name.\n"
00419 msg110 = " 3. Reorganize so the assigment is not necessary. Giving a second\n"
00420 msg111 = " name to the same object usually causes confusion and problems.\n"
00421 msg112 = " 4. Compose Sequences: newName = cms.Sequence(oldName)\n"
00422 raise ValueError(msg100+msg101+msg102+msg103+msg104+msg105+msg106+msg107+msg108+msg109+msg110+msg111+msg112)
00423 self.__label = label
00424 def label(self):
00425
00426 return self.__label
00427 def __str__(self):
00428
00429
00430
00431 return str(self.__label)
00432 def dumpSequenceConfig(self):
00433 return str(self.__label)
00434 def dumpSequencePython(self):
00435 return 'process.'+str(self.__label)
00436 def _findDependencies(self,knownDeps,presentDeps):
00437
00438 myDeps=knownDeps.get(self.label_(),None)
00439 if myDeps!=None:
00440 if presentDeps != myDeps:
00441 raise RuntimeError("the module "+self.label_()+" has two dependencies \n"
00442 +str(presentDeps)+"\n"
00443 +str(myDeps)+"\n"
00444 +"Please modify sequences to rectify this inconsistency")
00445 else:
00446 myDeps=set(presentDeps)
00447 knownDeps[self.label_()]=myDeps
00448 presentDeps.add(self.label_())
00449
00450
00451 class _Unlabelable(object):
00452 """A 'mixin' used to denote that the class can be used without a label (e.g. a Service)"""
00453 pass
00454
00455 class _ValidatingListBase(list):
00456 """Base class for a list which enforces that its entries pass a 'validity' test"""
00457 def __init__(self,*arg,**args):
00458 super(_ValidatingListBase,self).__init__(arg)
00459 if 0 != len(args):
00460 raise SyntaxError("named arguments ("+','.join([x for x in args])+") passsed to "+str(type(self)))
00461 if not self._isValid(iter(self)):
00462 raise TypeError("wrong types ("+','.join([str(type(value)) for value in iter(self)])+
00463 ") added to "+str(type(self)))
00464 def __setitem__(self,key,value):
00465 if isinstance(key,slice):
00466 if not self._isValid(value):
00467 raise TypeError("wrong type being inserted into this container "+self._labelIfAny())
00468 else:
00469 if not self._itemIsValid(value):
00470 raise TypeError("can not insert the type "+str(type(value))+" in container "+self._labelIfAny())
00471 super(_ValidatingListBase,self).__setitem__(key,value)
00472 def _isValid(self,seq):
00473
00474 if isinstance(seq, str):
00475 return False
00476 for item in seq:
00477 if not self._itemIsValid(item):
00478 return False
00479 return True
00480 def append(self,x):
00481 if not self._itemIsValid(x):
00482 raise TypeError("wrong type being appended to container "+self._labelIfAny())
00483 super(_ValidatingListBase,self).append(x)
00484 def extend(self,x):
00485 if not self._isValid(x):
00486 raise TypeError("wrong type being extended to container "+self._labelIfAny())
00487 super(_ValidatingListBase,self).extend(x)
00488 def __add__(self,rhs):
00489 if not self._isValid(rhs):
00490 raise TypeError("wrong type being added to container "+self._labelIfAny())
00491 import copy
00492 value = copy.copy(self)
00493 value.extend(rhs)
00494 return value
00495 def insert(self,i,x):
00496 if not self._itemIsValid(x):
00497 raise TypeError("wrong type being inserted to container "+self._labelIfAny())
00498 super(_ValidatingListBase,self).insert(i,x)
00499 def _labelIfAny(self):
00500 result = type(self).__name__
00501 if hasattr(self, '__label'):
00502 result += ' ' + self.__label
00503 return result
00504
00505 class _ValidatingParameterListBase(_ValidatingListBase,_ParameterTypeBase):
00506 def __init__(self,*arg,**args):
00507 _ParameterTypeBase.__init__(self)
00508 if len (arg) == 1 and not isinstance(arg[0],str):
00509 try:
00510 arg = iter(arg[0])
00511 except TypeError:
00512 pass
00513 super(_ValidatingParameterListBase,self).__init__(*arg,**args)
00514 def value(self):
00515 return list(self)
00516 def setValue(self,v):
00517 self[:] = []
00518 self.extend(v)
00519 self._isModified=True
00520 def configValue(self, options=PrintOptions()):
00521 config = '{\n'
00522 first = True
00523 for value in iter(self):
00524 options.indent()
00525 config += options.indentation()
00526 if not first:
00527 config+=', '
00528 config+= self.configValueForItem(value, options)+'\n'
00529 first = False
00530 options.unindent()
00531 config += options.indentation()+'}\n'
00532 return config
00533 def configValueForItem(self,item, options):
00534 return str(item)
00535 def pythonValueForItem(self,item, options):
00536 return self.configValueForItem(item, options)
00537 def __repr__(self):
00538 return self.dumpPython()
00539 def dumpPython(self, options=PrintOptions()):
00540 result = self.pythonTypeName()+"("
00541 n = len(self)
00542 if n>=256:
00543
00544 result+=" ("
00545 indented = False
00546 for i, v in enumerate(self):
00547 if i == 0:
00548 if hasattr(self, "_nPerLine"):
00549 nPerLine = self._nPerLine
00550 else:
00551 nPerLine = 5
00552 else:
00553 if not indented:
00554 indented = True
00555 options.indent()
00556 result += ', '
00557 if i % nPerLine == 0:
00558 result += '\n'+options.indentation()
00559 result += self.pythonValueForItem(v,options)
00560 if indented:
00561 options.unindent()
00562
00563 if n>=256:
00564 result +=' ) '
00565 result += ')'
00566 return result
00567 @staticmethod
00568 def _itemsFromStrings(strings,converter):
00569 return (converter(x).value() for x in strings)
00570
00571 def saveOrigin(obj, level):
00572
00573 frame = inspect.getframeinfo(inspect.currentframe(level+1))
00574
00575
00576
00577 obj._filename = frame[0]
00578 obj._lineNumber = frame[1]
00579
00580 if __name__ == "__main__":
00581
00582 import unittest
00583 class TestList(_ValidatingParameterListBase):
00584 def _itemIsValid(self,item):
00585 return True
00586 class testMixins(unittest.TestCase):
00587 def testListConstruction(self):
00588 t = TestList(1)
00589 self.assertEqual(t,[1])
00590 t = TestList((1,))
00591 self.assertEqual(t,[1])
00592 t = TestList("one")
00593 self.assertEqual(t,["one"])
00594 t = TestList( [1,])
00595 self.assertEqual(t,[1])
00596 t = TestList( (x for x in [1]) )
00597 self.assertEqual(t,[1])
00598
00599 t = TestList(1,2)
00600 self.assertEqual(t,[1,2])
00601 t = TestList((1,2))
00602 self.assertEqual(t,[1,2])
00603 t = TestList("one","two")
00604 self.assertEqual(t,["one","two"])
00605 t = TestList(("one","two"))
00606 self.assertEqual(t,["one","two"])
00607 t = TestList( [1,2])
00608 self.assertEqual(t,[1,2])
00609 t = TestList( (x for x in [1,2]) )
00610 self.assertEqual(t,[1,2])
00611 t = TestList( iter((1,2)) )
00612 self.assertEqual(t,[1,2])
00613
00614
00615 def testLargeList(self):
00616
00617
00618 args = [i for i in xrange(0,300)]
00619
00620 t = TestList(*args)
00621 pdump= t.dumpPython()
00622 class cms(object):
00623 def __init__(self):
00624 self.TestList = TestList
00625 pythonized = eval( pdump, globals(),{'cms':cms()} )
00626 self.assertEqual(t,pythonized)
00627 def testUsingBlock(self):
00628 a = UsingBlock("a")
00629 self.assert_(isinstance(a, _ParameterTypeBase))
00630 def testCopy(self):
00631 class __Test(_TypedParameterizable):
00632 pass
00633 class __TestType(_SimpleParameterTypeBase):
00634 def _isValid(self,value):
00635 return True
00636 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00637 b = a.copy()
00638 self.assertEqual(b.t.value(),1)
00639 self.assertEqual(b.u.value(),2)
00640 def testClone(self):
00641 class __Test(_TypedParameterizable):
00642 pass
00643 class __TestType(_SimpleParameterTypeBase):
00644 def _isValid(self,value):
00645 return True
00646 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00647 b = a.clone(t=3, v=__TestType(4))
00648 self.assertEqual(a.t.value(),1)
00649 self.assertEqual(a.u.value(),2)
00650 self.assertEqual(b.t.value(),3)
00651 self.assertEqual(b.u.value(),2)
00652 self.assertEqual(b.v.value(),4)
00653 self.assertRaises(TypeError,a.clone,None,**{"v":1})
00654 def testModified(self):
00655 class __TestType(_SimpleParameterTypeBase):
00656 def _isValid(self,value):
00657 return True
00658 a = __TestType(1)
00659 self.assertEqual(a.isModified(),False)
00660 a.setValue(1)
00661 self.assertEqual(a.isModified(),False)
00662 a.setValue(2)
00663 self.assertEqual(a.isModified(),True)
00664 a.resetModified()
00665 self.assertEqual(a.isModified(),False)
00666 unittest.main()