CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_5/src/FWCore/ParameterSet/python/Mixins.py

Go to the documentation of this file.
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         #  doesn't seem to handle \0 correctly
00098         #if value == '\0':
00099         #    value = ''
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             #raise ValueError("unnamed arguments are not allowed. Please use the syntax 'name = value' when assigning arguments.")
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         # Don't accidentally iterate over letters in a string
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         #since labels are not supposed to have underscores at the beginning
00196         # I will assume that if we have such then we are setting an internal variable
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         # underscored names bypass checking for _ParameterTypeBase
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             # handle the case where users just replace with a value, a = 12, rather than a = cms.int32(12)
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             # we don't want minuses in names
00237             name2 = name.replace('-','_')
00238             options.indent()
00239             #_UsingNodes don't get assigned variables
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         # usings need to go first
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         #the 'type' is also placed in the 'arg' list and we need to remove it
00262         #if 'type_' not in kargs:
00263         #    arg = arg[1:]
00264         #else:
00265         #    del args['type_']
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             #need to treat items both in params and myparams specially
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         #This routine is no longer used, but I might revive it in the future
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         #now see if any of them have what we want
00326         #the use of __import__ is taken from an example
00327         # from the www.python.org documentation on __import__
00328         for c in choices:
00329             #print " found file "+c
00330             name='.'.join(c[:-3].split('/')[-3:])
00331             #name = c[:-3].replace('/','.')
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             # too big.  Need to dump externally
00365             #NOTE: in future should explore just creating a dict
00366             #  {'foo':cms.uint32(1), .... }
00367             # and pass it to the constructor using the **{...} notation
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         #print "WARNING: _Labelable::label() needs to be changed to label_()"
00426         return self.__label
00427     def __str__(self):
00428         #this is probably a bad idea
00429         # I added this so that when we ask a path to print
00430         # we will see the label that has been assigned
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         #print 'in labelled'
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         # see if strings get reinterpreted as lists
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             #wrap in a tuple since they don't have a size constraint
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         #result+=', '.join((self.pythonValueForItem(v,options) for v in iter(self)))
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     #frame = inspect.stack()[level+1]
00573     frame = inspect.getframeinfo(inspect.currentframe(level+1))
00574     # not safe under old python versions
00575     #obj._filename = frame.filename
00576     #obj._lineNumber = frame.lineno
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             #lists larger than 255 entries can not be initialized
00617             #using the constructor
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()