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>=256:
00527
00528 result+=" ("
00529 indented = False
00530 for i, v in enumerate(self):
00531 if i == 0:
00532 if hasattr(self, "_nPerLine"):
00533 nPerLine = self._nPerLine
00534 else:
00535 nPerLine = 5
00536 else:
00537 if not indented:
00538 indented = True
00539 options.indent()
00540 result += ', '
00541 if i % nPerLine == 0:
00542 result += '\n'+options.indentation()
00543 result += self.pythonValueForItem(v,options)
00544 if indented:
00545 options.unindent()
00546
00547 if n>=256:
00548 result +=' ) '
00549 result += ')'
00550 return result
00551 @staticmethod
00552 def _itemsFromStrings(strings,converter):
00553 return (converter(x).value() for x in strings)
00554
00555 def saveOrigin(obj, level):
00556
00557 frame = inspect.getframeinfo(inspect.currentframe(level+1))
00558
00559
00560
00561 obj._filename = frame[0]
00562 obj._lineNumber = frame[1]
00563
00564 if __name__ == "__main__":
00565
00566 import unittest
00567 class TestList(_ValidatingParameterListBase):
00568 def _itemIsValid(self,item):
00569 return True
00570 class testMixins(unittest.TestCase):
00571 def testListConstruction(self):
00572 t = TestList(1)
00573 self.assertEqual(t,[1])
00574 t = TestList((1,))
00575 self.assertEqual(t,[1])
00576 t = TestList("one")
00577 self.assertEqual(t,["one"])
00578 t = TestList( [1,])
00579 self.assertEqual(t,[1])
00580 t = TestList( (x for x in [1]) )
00581 self.assertEqual(t,[1])
00582
00583 t = TestList(1,2)
00584 self.assertEqual(t,[1,2])
00585 t = TestList((1,2))
00586 self.assertEqual(t,[1,2])
00587 t = TestList("one","two")
00588 self.assertEqual(t,["one","two"])
00589 t = TestList(("one","two"))
00590 self.assertEqual(t,["one","two"])
00591 t = TestList( [1,2])
00592 self.assertEqual(t,[1,2])
00593 t = TestList( (x for x in [1,2]) )
00594 self.assertEqual(t,[1,2])
00595 t = TestList( iter((1,2)) )
00596 self.assertEqual(t,[1,2])
00597
00598
00599 def testLargeList(self):
00600
00601
00602 args = [i for i in xrange(0,300)]
00603
00604 t = TestList(*args)
00605 pdump= t.dumpPython()
00606 class cms(object):
00607 def __init__(self):
00608 self.TestList = TestList
00609 pythonized = eval( pdump, globals(),{'cms':cms()} )
00610 self.assertEqual(t,pythonized)
00611 def testUsingBlock(self):
00612 a = UsingBlock("a")
00613 self.assert_(isinstance(a, _ParameterTypeBase))
00614 def testCopy(self):
00615 class __Test(_TypedParameterizable):
00616 pass
00617 class __TestType(_SimpleParameterTypeBase):
00618 def _isValid(self,value):
00619 return True
00620 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00621 b = a.copy()
00622 self.assertEqual(b.t.value(),1)
00623 self.assertEqual(b.u.value(),2)
00624 def testClone(self):
00625 class __Test(_TypedParameterizable):
00626 pass
00627 class __TestType(_SimpleParameterTypeBase):
00628 def _isValid(self,value):
00629 return True
00630 a = __Test("MyType",t=__TestType(1), u=__TestType(2))
00631 b = a.clone(t=3, v=__TestType(4))
00632 self.assertEqual(a.t.value(),1)
00633 self.assertEqual(a.u.value(),2)
00634 self.assertEqual(b.t.value(),3)
00635 self.assertEqual(b.u.value(),2)
00636 self.assertEqual(b.v.value(),4)
00637 self.assertRaises(TypeError,a.clone,None,**{"v":1})
00638 def testModified(self):
00639 class __TestType(_SimpleParameterTypeBase):
00640 def _isValid(self,value):
00641 return True
00642 a = __TestType(1)
00643 self.assertEqual(a.isModified(),False)
00644 a.setValue(1)
00645 self.assertEqual(a.isModified(),False)
00646 a.setValue(2)
00647 self.assertEqual(a.isModified(),True)
00648 a.resetModified()
00649 self.assertEqual(a.isModified(),False)
00650 unittest.main()