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 setLabel(self,label):
00401 self.__label = label
00402 def label_(self):
00403 if not hasattr(self, "_Labelable__label"):
00404 raise RuntimeError("module has no label. Perhaps it wasn't inserted into the process?")
00405 return self.__label
00406 def hasLabel_(self):
00407 return hasattr(self, "_Labelable__label") and self.__label is not None
00408 def label(self):
00409
00410 return self.__label
00411 def __str__(self):
00412
00413
00414
00415 return str(self.__label)
00416 def dumpSequenceConfig(self):
00417 return str(self.__label)
00418 def dumpSequencePython(self):
00419 return 'process.'+str(self.__label)
00420 def _findDependencies(self,knownDeps,presentDeps):
00421
00422 myDeps=knownDeps.get(self.label_(),None)
00423 if myDeps!=None:
00424 if presentDeps != myDeps:
00425 raise RuntimeError("the module "+self.label_()+" has two dependencies \n"
00426 +str(presentDeps)+"\n"
00427 +str(myDeps)+"\n"
00428 +"Please modify sequences to rectify this inconsistency")
00429 else:
00430 myDeps=set(presentDeps)
00431 knownDeps[self.label_()]=myDeps
00432 presentDeps.add(self.label_())
00433
00434
00435 class _Unlabelable(object):
00436 """A 'mixin' used to denote that the class can be used without a label (e.g. a Service)"""
00437 pass
00438
00439 class _ValidatingListBase(list):
00440 """Base class for a list which enforces that its entries pass a 'validity' test"""
00441 def __init__(self,*arg,**args):
00442 super(_ValidatingListBase,self).__init__(arg)
00443 if 0 != len(args):
00444 raise SyntaxError("named arguments ("+','.join([x for x in args])+") passsed to "+str(type(self)))
00445 if not self._isValid(iter(self)):
00446 raise TypeError("wrong types ("+','.join([str(type(value)) for value in iter(self)])+
00447 ") added to "+str(type(self)))
00448 def __setitem__(self,key,value):
00449 if isinstance(key,slice):
00450 if not self._isValid(value):
00451 raise TypeError("wrong type being inserted into this container "+self._labelIfAny())
00452 else:
00453 if not self._itemIsValid(value):
00454 raise TypeError("can not insert the type "+str(type(value))+" in container "+self._labelIfAny())
00455 super(_ValidatingListBase,self).__setitem__(key,value)
00456 def _isValid(self,seq):
00457
00458 if isinstance(seq, str):
00459 return False
00460 for item in seq:
00461 if not self._itemIsValid(item):
00462 return False
00463 return True
00464 def append(self,x):
00465 if not self._itemIsValid(x):
00466 raise TypeError("wrong type being appended to container "+self._labelIfAny())
00467 super(_ValidatingListBase,self).append(x)
00468 def extend(self,x):
00469 if not self._isValid(x):
00470 raise TypeError("wrong type being extended to container "+self._labelIfAny())
00471 super(_ValidatingListBase,self).extend(x)
00472 def __add__(self,rhs):
00473 if not self._isValid(rhs):
00474 raise TypeError("wrong type being added to container "+self._labelIfAny())
00475 import copy
00476 value = copy.copy(self)
00477 value.extend(rhs)
00478 return value
00479 def insert(self,i,x):
00480 if not self._itemIsValid(x):
00481 raise TypeError("wrong type being inserted to container "+self._labelIfAny())
00482 super(_ValidatingListBase,self).insert(i,x)
00483 def _labelIfAny(self):
00484 result = type(self).__name__
00485 if hasattr(self, '__label'):
00486 result += ' ' + self.__label
00487 return result
00488
00489 class _ValidatingParameterListBase(_ValidatingListBase,_ParameterTypeBase):
00490 def __init__(self,*arg,**args):
00491 _ParameterTypeBase.__init__(self)
00492 if len (arg) == 1 and not isinstance(arg[0],str):
00493 try:
00494 arg = iter(arg[0])
00495 except TypeError:
00496 pass
00497 super(_ValidatingParameterListBase,self).__init__(*arg,**args)
00498 def value(self):
00499 return list(self)
00500 def setValue(self,v):
00501 self[:] = []
00502 self.extend(v)
00503 self._isModified=True
00504 def configValue(self, options=PrintOptions()):
00505 config = '{\n'
00506 first = True
00507 for value in iter(self):
00508 options.indent()
00509 config += options.indentation()
00510 if not first:
00511 config+=', '
00512 config+= self.configValueForItem(value, options)+'\n'
00513 first = False
00514 options.unindent()
00515 config += options.indentation()+'}\n'
00516 return config
00517 def configValueForItem(self,item, options):
00518 return str(item)
00519 def pythonValueForItem(self,item, options):
00520 return self.configValueForItem(item, options)
00521 def __repr__(self):
00522 return self.dumpPython()
00523 def dumpPython(self, options=PrintOptions()):
00524 result = self.pythonTypeName()+"("
00525 n = len(self)
00526 if n<255:
00527 indented = False
00528 for i, v in enumerate(self):
00529 if i == 0:
00530 if hasattr(self, "_nPerLine"):
00531 nPerLine = self._nPerLine
00532 else:
00533 nPerLine = 5
00534 else:
00535 if not indented:
00536 indented = True
00537 options.indent()
00538
00539 result += ', '
00540 if i % nPerLine == 0:
00541 result += '\n'+options.indentation()
00542 result += self.pythonValueForItem(v,options)
00543 if indented:
00544 options.unindent()
00545
00546 else:
00547 result = '('
00548 start = 0
00549 l=list()
00550 while(start < len(self)):
00551 v=self.pythonTypeName()+'('
00552 v+=', '.join((self.pythonValueForItem(v,options) for v in self[start:start+255]))
00553 v+=')'
00554 l.append(v)
00555 start+=255
00556 result+='+'.join(l)
00557 pass
00558 result += ')'
00559 return result
00560 @staticmethod
00561 def _itemsFromStrings(strings,converter):
00562 return (converter(x).value() for x in strings)
00563
00564 def saveOrigin(obj, level):
00565
00566 frame = inspect.getframeinfo(inspect.currentframe(level+1))
00567
00568
00569
00570 obj._filename = frame[0]
00571 obj._lineNumber = frame[1]
00572
00573 if __name__ == "__main__":
00574
00575 import unittest
00576 class TestList(_ValidatingParameterListBase):
00577 def _itemIsValid(self,item):
00578 return True
00579 class testMixins(unittest.TestCase):
00580 def testListConstruction(self):
00581 t = TestList(1)
00582 self.assertEqual(t,[1])
00583 t = TestList((1,))
00584 self.assertEqual(t,[1])
00585 t = TestList("one")
00586 self.assertEqual(t,["one"])
00587 t = TestList( [1,])
00588 self.assertEqual(t,[1])
00589 t = TestList( (x for x in [1]) )
00590 self.assertEqual(t,[1])
00591
00592 t = TestList(1,2)
00593 self.assertEqual(t,[1,2])
00594 t = TestList((1,2))
00595 self.assertEqual(t,[1,2])
00596 t = TestList("one","two")
00597 self.assertEqual(t,["one","two"])
00598 t = TestList(("one","two"))
00599 self.assertEqual(t,["one","two"])
00600 t = TestList( [1,2])
00601 self.assertEqual(t,[1,2])
00602 t = TestList( (x for x in [1,2]) )
00603 self.assertEqual(t,[1,2])
00604 t = TestList( iter((1,2)) )
00605 self.assertEqual(t,[1,2])
00606
00607
00608 def testLargeList(self):
00609
00610
00611 args = [i for i in xrange(0,300)]
00612
00613 t = TestList(*args)
00614 pdump= t.dumpPython()
00615 class cms(object):
00616 def __init__(self):
00617 self.TestList = TestList
00618 pythonized = eval( pdump, globals(),{'cms':cms()} )
00619 self.assertEqual(t,pythonized)
00620 def testUsingBlock(self):
00621 a = UsingBlock("a")
00622 self.assert_(isinstance(a, _ParameterTypeBase))
00623 def testCopy(self):
00624 class __Test(_TypedParameterizable):
00625 pass
00626 class __TestType(_SimpleParameterTypeBase):
00627 def _isValid(self,value):
00628 return True
00629 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00630 b = a.copy()
00631 self.assertEqual(b.t.value(),1)
00632 self.assertEqual(b.u.value(),2)
00633 def testClone(self):
00634 class __Test(_TypedParameterizable):
00635 pass
00636 class __TestType(_SimpleParameterTypeBase):
00637 def _isValid(self,value):
00638 return True
00639 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00640 b = a.clone(t=3, v=__TestType(4))
00641 self.assertEqual(a.t.value(),1)
00642 self.assertEqual(a.u.value(),2)
00643 self.assertEqual(b.t.value(),3)
00644 self.assertEqual(b.u.value(),2)
00645 self.assertEqual(b.v.value(),4)
00646 self.assertRaises(TypeError,a.clone,None,**{"v":1})
00647 def testModified(self):
00648 class __TestType(_SimpleParameterTypeBase):
00649 def _isValid(self,value):
00650 return True
00651 a = __TestType(1)
00652 self.assertEqual(a.isModified(),False)
00653 a.setValue(1)
00654 self.assertEqual(a.isModified(),False)
00655 a.setValue(2)
00656 self.assertEqual(a.isModified(),True)
00657 a.resetModified()
00658 self.assertEqual(a.isModified(),False)
00659 unittest.main()