CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_5_3_1/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 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         #print "WARNING: _Labelable::label() needs to be changed to label_()"
00410         return self.__label
00411     def __str__(self):
00412         #this is probably a bad idea
00413         # I added this so that when we ask a path to print
00414         # we will see the label that has been assigned
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         #print 'in labelled'
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         # see if strings get reinterpreted as lists
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             #result+=', '.join((self.pythonValueForItem(v,options) for v in iter(self)))
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     #frame = inspect.stack()[level+1]
00566     frame = inspect.getframeinfo(inspect.currentframe(level+1))
00567     # not safe under old python versions
00568     #obj._filename = frame.filename
00569     #obj._lineNumber = frame.lineno
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             #lists larger than 255 entries can not be initialized
00610             #using the constructor
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()