Package Ganga :: Package GPIDev :: Package Schema :: Module Schema'
[hide private]
[frames] | no frames]

Source Code for Module Ganga.GPIDev.Schema.Schema'

  1  ################################################################################ 
  2  # Ganga Project. http://cern.ch/ganga 
  3  # 
  4  # $Id: Schema.py,v 1.3 2009-05-20 13:40:22 moscicki Exp $ 
  5  ################################################################################ 
  6   
  7  import Ganga.Utility.logging 
  8  logger = Ganga.Utility.logging.getLogger() 
  9   
 10  from Ganga.Utility.logic import implies 
 11   
 12  # 
 13  # Ganga Public Interface Schema 
 14  # 
 15   
 16  # Version of the schema. 
 17   
18 -class Version:
19 - def __init__(self,major,minor):
20 self.major = major 21 self.minor = minor
22
23 - def __eq__(self,v):
24 return self.major == v.major and self.minor == v.minor
25
26 - def __ne__(self,v):
27 return not self == v
28
29 - def isCompatible(self,v):
30 return v.major == self.major and v.minor <= self.minor
31
32 -def defaultConfigSectionName(name):
33 return 'defaults_'+name #_Properties
34 35 # Schema defines the logical model of the Ganga Public Interface (GPI) 36 # for jobs and pluggable job components such as applications and 37 # submission backends. 38 39 # Schema contains the data description (properties) which is the basis 40 # of the efficient management of the persistent data in a generic way. 41 # Any Ganga component is a container of properties (a property 42 # may be a component as well). Properties may be protected (read-only 43 # by the users). Persistent properties are stored with the job 44 # whereas transient are not. This additional information is contained 45 # in metaproperties. Large fractions of Ganga GPI may be generated 46 # automatically based on the metaproperties definition in a schema. 47 48 # Each component belongs to a certain category (for example 49 # 'application'). There may be many different types of applications, 50 # each one defined by a separate component. 51 52 # Schema of each component provides a version information (it is 53 # important for persistent storage) and compatibility of the scripts. 54 55 # Internal Note: Schema object must be additionally initialized with 56 # the _pluginclass object which is used as the source of information 57 # about name and category of the schema deepcopy of Schema objects is 58 # possible however the _pluginclass objects are shared unless 59 # overriden explicitly. 60
61 -class Schema:
62 # Schema constructor is used by Ganga plugin developers. 63 # Ganga will automatically set a reference to the plugin class which corresponds to this schema, hence 64 # defining the schema's name and category. 65 # 66 # datadict: dictionary of properties (schema items) 67 # version: the version information
68 - def __init__(self,version,datadict):
69 self.datadict = datadict 70 self.version = version 71 self._pluginclass = None
72
73 - def __getitem__(self,name):
74 return self.datadict[name]
75 76 category = property(lambda self: self._pluginclass._category) 77 name = property(lambda self: self._pluginclass._name) 78
79 - def allItems(self):
80 return zip(self.datadict.keys(),self.datadict.values())
81
82 - def simpleItems(self):
83 return self._filter(SimpleItem)
84
85 - def componentItems(self):
86 return self._filter(ComponentItem)
87
88 - def hasAttribute(self,name):
89 return self.datadict.has_key(name)
90
91 - def getItem(self,name):
92 return self.__getitem__(name)
93
94 - def _filter(self,klass):
95 r = [] 96 97 for n,c in zip(self.datadict.keys(),self.datadict.values()): 98 if issubclass(c.__class__,klass): 99 r.append((n,c)) 100 return r
101
102 - def isEqual(self,schema):
103 return self.name == schema.name and self.category == schema.category
104 105 ## # NOT TESTED 106 ## def isCompatible(self,schema): 107 ## if len(self.datadict.keys()) != len(schema.datadict.keys()): 108 ## return 0 109 110 ## for k in self.datadict.keys(): 111 ## if not self[k].isA(schema[k]) 112 ## return 0 113 ## return 1 114
115 - def getPluginClass(self):
116 return self._pluginclass
117 118 # make a schema copy for a derived class, does not copy the pluginclass
119 - def inherit_copy(self):
120 import copy 121 return Schema(copy.deepcopy(self.version), copy.deepcopy(self.datadict))
122
123 - def createDefaultConfig(self):
124 import Ganga.Utility.Config 125 # create a configuration unit for default values of object properties 126 # take the defaults from schema defaults 127 config = Ganga.Utility.Config.makeConfig( defaultConfigSectionName(self.name), "default attribute values for %s objects"%self.name) #self._pluginclass._proxyClass.__doc__ ) 128 129 for name,item in self.allItems(): 130 if not item['protected'] and not item['hidden']: #and not item['sequence']: #FIXME: do we need it or not?? 131 try: 132 types = item['typelist'] 133 if types == []: 134 types = None 135 except: 136 types = None 137 if item['sequence']: 138 if not types is None: 139 types.append('list') # bugfix 36398: allow to assign a list in the configuration 140 if type(item['defvalue']) is type({}): 141 if not types is None: 142 types.append('dict') 143 config.addOption(name,item['defvalue'],item['doc'],override=False,typelist=types) 144 145 146 def prehook(name,x): 147 errmsg = "Cannot set %s=%s in [%s]: "%(name,repr(x),config.name) 148 149 try: 150 item = self.getItem(name) 151 except KeyError,x: 152 raise Ganga.Utility.Config.ConfigError(errmsg+"attribute not defined in the schema") 153 except Exception,x: 154 raise Ganga.Utility.Config.ConfigError(errmsg+str(x)) 155 156 if item.isA(ComponentItem): 157 if not type(x) is type('') and not x is None: 158 raise Ganga.Utility.Config.ConfigError(errmsg+"only strings and None allowed as a default value of Component Item.") 159 try: 160 self._getDefaultValueInternal(name,x,check=True) 161 except: 162 raise Ganga.Utility.Config.ConfigError(errmsg+str(x)) 163 164 if item['protected'] or item['hidden']: 165 raise Ganga.Utility.Config.ConfigError(errmsg+"protected or hidden property") 166 167 # FIXME: File() == 'x' triggers AttributeError 168 #try: 169 # if x == '': x = None 170 #except AttributeError: 171 # pass 172 173 return x
174 175 config.attachUserHandler(prehook,None) 176 config.attachSessionHandler(prehook,None)
177
178 - def getDefaultValue(self,attr):
179 """ Get the default value of a schema item, both simple and component. 180 """ 181 return self._getDefaultValueInternal(attr)
182
183 - def _getDefaultValueInternal(self,attr, val=None, check=False):
184 """ Get the default value of a schema item, both simple and component. 185 If check is True then val is used instead of default value: this is used to check if the val may be used as a default value (e.g. if it is OK to use it as a value in the config file) 186 """ 187 import Ganga.Utility.Config 188 config = Ganga.Utility.Config.getConfig( defaultConfigSectionName(self.name) ) 189 190 item = self.getItem(attr) 191 192 # hidden, protected and sequence values are not represented in config 193 try: 194 defvalue = config[attr] 195 except Ganga.Utility.Config.ConfigError,x: 196 defvalue = item['defvalue'] 197 198 # in the checking mode, use the provided value instead 199 if check: 200 defvalue = val 201 202 if item.isA(ComponentItem): 203 204 # FIXME: limited support for initializing non-empty sequences (i.e. apps => ['DaVinci','Executable'] is NOT correctly initialized) 205 206 if not item['sequence']: 207 if defvalue is None: 208 if not item['load_default']: 209 assert(item['optional']) 210 return None 211 212 # if a defvalue of a component item is an object (not string) just process it as for SimpleItems (useful for FileItems) 213 # otherwise do a lookup via plugin registry 214 215 if type(defvalue) is type('') or defvalue is None: 216 from Ganga.Utility.Plugin import allPlugins 217 return allPlugins.find(item['category'],defvalue)() 218 219 # make a copy of the default value (to avoid strange effects if the original modified) 220 import copy 221 return copy.deepcopy(defvalue)
222 223 224 225 # Items in schema may be either Components,Simples, Files or BindingItems. 226 # 227 # Component, Simple, File are data items and have implicit storage. 228 # 229 # Metaproperties of all Items: 230 # 231 # transient : never stored on persistent media 232 # protected : not modifiable via GPI 233 # hidden : not visible in the GPI 234 # comparable: taken into account for equality checks 235 # sequence: an item is a sequence (algorithms traversing the component tree dive into sequences as well) 236 # strict_sequence: if not strict sequence then assignment of a single item will be automatically converted to a 1-element sequence, i.e. obj.x = v => obj.x = [v] 237 # defvalue : default value, if item is a sequence the defvalue must be a list 238 # for component this must be either None or a string with a name of the component in the same category 239 # copyable : if 0 then the property value will not be copied and the default value from schema will be set in the destination 240 # implied initialization rule (unless the "copyable" metaproperty is explicitly set) 241 # protected == 1 => copyable == 0 242 # protected == 0 => copyable == 1 243 # doc : a docstring 244 # checkset : a bound checkset method, restrict write access at the object level (for example job.status 245 # may not be modified directly in backend handlers, instead updateStatus() method should be used) 246 # 247 # filter : a bound method can be used to change the values of an attribute. The attribute will be set to the return value 248 # 249 # visitable : if false then all algorithms based on the visitor patter will not accept this item [true] 250 # this is needed in certain cases (such as job.master) to avoid infinite recursion (and loops) 251 # 252 # summary_print: An bound method name (string). Will be passed an attribute value and a verbosity_level. Should 253 # return a (Python parsable) string summarising the state of the value. 254 # 255 # summary_sequence_maxlen: An integer longer than which a sequence will be summerised when doing a summary 256 # print. If the value is -1, the sequence will never be summerised. 257 # 258 # changable_at_resubmit: if true than the value may be changed at job resubmission, see Job.resubmit() 259 # 260 # 261 # Metaproperties of SimpleItems 262 # 263 # typelist : a list of type names (strings) indicating allowed types of the property (e.g. ["str","int","Ganga.GPIDev.Lib.File.File.File"]), see: http://twiki.cern.ch/twiki/bin/view/ArdaGrid/GangaTypes 264 # 265 # 266 # Metaproperties of ComponentItems: 267 # 268 # category : category of the component ('applications','backends',...) 269 # optional : if true then None may be used as a legal value of the item, [false] 270 # load_default: if true and defvalue is None then load default plugin, [true] 271 # 272 # defvalue is None load_default==true => load default plugin 273 # defvalue is None load_default==false => use None (optional MUST be true, otherwise error) 274 # 275 # defvalue is 'x' => load 'x' 276 # 277 # getter : a bound getter method, this implies that the component does not have associated storage and cannot be neither set nor deleted [None] 278 # getter implies: transient=1, protected=1, sequence=0, defvalue=None, load_default=0, optional=1, copyable=0 279 # proxy_get: a bound getter method for proxy decoration, allows to customize completely the creation of the proxy 280 # 281 # 282 # Metaproperties of FileItems 283 # 284 # FIXME: under development 285 # in/out : direction wrt to the job submission operation 286 # ref/copy : reference vs copy semantics for file contents 287 # 288 289 ## # BindingItems enable to hook arbitrary getter methods and do not have any implicit storage. Therefore 290 ## # the BindingItems are always transient, cannot be copied, cannot be sequences and have None default value. 291
292 -class Item:
293 # default values of common metaproperties 294 _metaproperties = {'transient' : 0, 'protected' : 0, 'hidden' : 0, 'comparable' : 1, 'sequence' : 0, 'defvalue' : None, 'copyable' : 1, 'doc' : '','visitable':1, 'checkset':None, 'filter':None,'strict_sequence':1, 'summary_print':None, 'summary_sequence_maxlen':5,'proxy_get':None,'getter':None, 'changable_at_resubmit':0} 295
296 - def __init__(self):
297 self._meta = Item._metaproperties.copy()
298
299 - def __getitem__(self,key):
300 return self._meta[key]
301 302 # compare the kind of item: 303 # all calls are equivalent: 304 # item.isA('SimpleItem') 305 # item.isA(SimpleItem) 306 # item.isA(SimpleItem())
307 - def isA(self,what):
308 # for backwards compatibility with Ganga3 CLIP: if a string -- first convert to the class name 309 if type(what) is type(''): 310 import Schema # get access to all Item classes defined in this module (schema) 311 try: 312 what = getattr(Schema,what) 313 except AttributeError: 314 # class not found 315 return 0 316 317 import types 318 if type(what) is types.InstanceType: 319 what = what.__class__ 320 321 return issubclass(self.__class__,what)
322
323 - def _update(self,kwds,forced=None):
324 """ Add new metaproperties/override old values. To be used by derived contructors only. 325 'forced' is an (optional) dictionary containing all values which cannot be modified by 326 the user of the derived contructor. 327 """ 328 if forced: 329 # find intersection 330 forbidden = [k for k in forced if k in kwds] 331 if len(forbidden) > 0: 332 raise TypeError('%s received forbidden (forced) keyword arguments %s' %(str(self.__class__),str(forbidden))) 333 self._meta.update(forced) 334 335 self._meta.update(kwds) 336 337 # conditional initial value logic... 338 # unless 'copyable' explicitly set (or forced), protected==1 => copyable==0 339 if not kwds.has_key('copyable') and (not forced or not forced.has_key('copyable')): 340 if self._meta['protected']: 341 logger.debug('applied implicit conditional rule: protected==1 => copyable==0') 342 self._meta['copyable'] = 0
343 344 # return a description of the property including a docstring
345 - def describe(self):
346 347 specdoc = '('+self._describe() + ')' 348 349 if self['doc']: 350 s = "%s. %s"% (self['doc'],specdoc) 351 else: 352 s = specdoc 353 354 return s
355 356 # return a meta description of the property
357 - def _describe(self):
358 first = ',' 359 #txt = ' [' 360 txt = '' 361 for m in ['transient','protected','comparable','optional']: 362 try: 363 if self[m]: 364 txt += '%s%s'%(first,m) 365 first = ',' 366 except KeyError: 367 pass 368 #txt += ']' 369 txt = " default="+repr(self['defvalue'])+txt 370 if self['sequence']: txt = " list," + txt 371 return txt
372
373 - def _check_type(self, val, name, enableGangaList=True):
374 from Ganga.Core import GangaAttributeError,ProtectedAttributeError,ReadOnlyObjectError,TypeMismatchError,SchemaError 375 376 if enableGangaList: 377 from Ganga.GPIDev.Lib.GangaList.GangaList import GangaList 378 else: 379 GangaList = list 380 381 item = self 382 383 # type checking does not make too much sense for Component Items because component items are 384 # always checked at the object (_impl) level for category compatibility 385 386 if item.isA(ComponentItem): 387 return 388 389 validTypes = item._meta['typelist'] 390 391 # setting typelist explicitly to None results in disabling the type checking completely 392 if validTypes is None: 393 return 394 395 def check(isAllowedType): 396 if not isAllowedType: 397 raise TypeMismatchError( 'Attribute "%s" expects a value of the following types: %s' % ( name, validTypes ) )
398 399 if item._meta['sequence']: 400 if not isinstance( item._meta['defvalue'], (list,GangaList) ): 401 raise SchemaError( 'Attribute "%s" defined as a sequence but defvalue is not a list.' % name ) 402 403 if not isinstance( val, (GangaList,list) ): 404 raise TypeMismatchError( 'Attribute "%s" expects a list.' % name ) 405 406 if validTypes: 407 if item._meta[ 'sequence' ]: 408 409 for valItem in val: 410 if not valueTypeAllowed( valItem, validTypes ): 411 raise TypeMismatchError( 'List entry %s for %s is invalid. Valid types: %s' % ( valItem, name, validTypes ) ) 412 413 414 return 415 else: # Non-sequence 416 if isinstance( item._meta['defvalue'], dict ): 417 if isinstance( val, dict ): 418 for dKey, dVal in val.iteritems(): 419 if not valueTypeAllowed( dKey, validTypes ) or not valueTypeAllowed( dVal, validTypes ): 420 raise TypeMismatchError( 'Dictionary entry %s:%s for attribute %s is invalid. Valid types for key/value pairs: %s' % ( dKey, dVal, name, validTypes ) ) 421 else: # new value is not a dict 422 raise TypeMismatchError( 'Attribute "%s" expects a dictionary.' % name ) 423 return 424 else: # a 'simple' (i.e. non-dictionary) non-sequence value 425 check(valueTypeAllowed( val, validTypes )) 426 return 427 428 # typelist is not defined, use the type of the default value 429 if item._meta['defvalue'] is None: 430 logger.warning('type-checking disabled: type information not provided for %s, contact plugin developer',name) 431 else: 432 if item._meta['sequence']: 433 logger.warning('type-checking is incomplete: type information not provided for a sequence %s, contact plugin developer',name) 434 else: 435 check(isinstance( val, type(item._meta['defvalue']) ))
436 437
438 -class ComponentItem(Item):
439 # schema user of a ComponentItem cannot change the forced values below 440 _forced = {} 441
442 - def __init__(self,category,optional=0,load_default=1,**kwds):
443 Item.__init__(self) 444 kwds['category'] = category 445 kwds['optional'] = optional 446 kwds['load_default'] = load_default 447 #kwds['getter'] = getter 448 self._update(kwds,forced=ComponentItem._forced) 449 assert(implies(self['defvalue'] is None and not self['load_default'], self['optional'])) 450 451 assert(implies(self['getter'], self['transient'] and self['defvalue'] is None and self['protected'] and not self['sequence'] and not self['copyable']))
452
453 - def _describe(self):
454 return "'"+self['category']+"' object,"+Item._describe(self)
455 456 from Ganga.GPIDev.TypeCheck import _valueTypeAllowed 457 valueTypeAllowed = lambda val,valTypeList: _valueTypeAllowed(val,valTypeList,logger) 458 459 460
461 -class SimpleItem(Item):
462 - def __init__(self,defvalue,typelist=[],**kwds):
463 Item.__init__(self) 464 kwds['defvalue'] = defvalue 465 kwds['typelist'] = typelist 466 self._update(kwds)
467
468 - def _describe(self):
469 return 'simple property,' + Item._describe(self)
470 471 472 # def type_match(self,v): 473 # return (self['sequence'] and v == []) or \ 474 # (not self['typelist'] or valueTypeAllowed( v, self['typelist'])) 475 476 ## class BindingItem(Item): 477 ## _forced = {'transient' : 1, 'sequence' : 0, 'defvalue' : None, 'copyable' : 0} 478 479 ## def __init__(self,getter,**kwds): 480 ## Item.__init__(self) 481 ## assert(not getter is None) 482 ## kwds['getter'] = getter 483 ## ## kwds['setter'] = setter 484 ## ## assert(not setter is None) 485 ## self._update(kwds,forced=BindingItem._forced) 486 487 488 489 # Files are important and common enough to merit a special support for defining their metaproperties
490 -class FileItem(ComponentItem):
491 - def __init__(self,**kwds):
492 ComponentItem.__init__(self,'files') 493 self._update(kwds)
494
495 - def _describe(self):
496 return "'files' object,"+ Item._describe(self)
497 498 # a helper class which gives a human readible representation of schema items 499 # for example suitable for python interactive help()
500 -def make_helper(item):
501 class SchemaItemHelper: 502 def __repr__(self): 503 return item.describe()
504 return SchemaItemHelper() 505 506 if __name__=='__main__': 507 508 # a simple test 509 510 dd = { 511 'application' : ComponentItem(category='applications'), 512 'backend' : ComponentItem(category='backends'), 513 'name' : SimpleItem('',comparable=0), 514 'workdir' : SimpleItem(defvalue=None,type='string',transient=1,protected=1,comparable=0), 515 'status' : SimpleItem(defvalue='new', protected=1, comparable=0), 516 'id': SimpleItem(defvalue=None,type='string',protected=1, comparable=0), 517 'inputbox': FileItem(defvalue=[],sequence=1), 518 'outputbox': FileItem(defvalue=[],sequence=1), 519 'overriden_copyable' : SimpleItem(defvalue=None,protected=1,copyable=1), 520 'plain_copyable' : SimpleItem(defvalue=None,copyable=0) 521 } 522 523 schema = Schema(Version(1,0),dd) 524 525 # NOT a public interface: emulate the Ganga Plugin object for test purposes 526 # Note that pclass MUST be a new-style class in order to support deepcopy
527 - class pclass(object):
528 _category = 'jobs' 529 _name = 'Job'
530 schema._pluginclass = pclass 531 # end of emulating code 532 #allSchemas.add(schema) 533 534 assert(schema.name == 'Job') 535 assert(schema.category == 'jobs') 536 537 assert(schema.allItems() == dd.items()) 538 539 cc = (schema.componentItems() + schema.simpleItems()).sort() 540 cc2 = dd.items().sort() 541 assert(cc == cc2) 542 543 for i in schema.allItems(): 544 assert(schema[i[0]] == schema.getItem(i[0])) 545 546 assert(schema['id'].isA(SimpleItem)) 547 assert(schema['application'].isA(ComponentItem)) 548 assert(schema['inputbox'].isA(ComponentItem)) 549 assert(schema['inputbox'].isA(FileItem)) 550 551 assert(schema['id']['protected']) 552 assert(not schema['id']['comparable']) 553 assert(schema['id']['type'] == 'string') 554 555 print schema['application']['category'], schema['application']['defvalue'] 556 557 import copy 558 schema2 = copy.deepcopy(schema) 559 560 assert(schema2 is not schema) 561 assert(schema.datadict is not schema2.datadict) 562 assert(schema._pluginclass is schema2._pluginclass) 563 564 for i in schema.allItems(): 565 assert(schema.getItem(i[0]) is not schema2.getItem(i[0])) 566 567 # check the implied rules 568 569 assert(schema['overriden_copyable']['copyable'] == 1) 570 assert(schema['plain_copyable']['copyable'] == 0) 571 assert(schema['id']['copyable'] == 0) 572 assert(schema['application']['copyable'] == 1) 573 574 print 'Schema tested OK.' 575 576 577 # 578 # 579 # $Log: not supported by cvs2svn $ 580 # Revision 1.2 2008/09/09 14:37:16 moscicki 581 # bugfix #40220: Ensure that default values satisfy the declared types in the schema 582 # 583 # factored out type checking into schema module, fixed a number of wrongly declared schema items in the core 584 # 585 # Revision 1.1 2008/07/17 16:40:55 moscicki 586 # migration of 5.0.2 to HEAD 587 # 588 # the doc and release/tools have been taken from HEAD 589 # 590 # Revision 1.15.26.8 2008/05/15 06:31:38 moscicki 591 # # bugfix 36398: allow to assign a list in the configuration 592 # 593 # Revision 1.15.26.7 2008/04/18 13:42:18 moscicki 594 # remove obsolete printout 595 # 596 # Revision 1.15.26.6 2008/04/18 10:52:13 moscicki 597 # 1) typechecking fix 598 # 2) Ganga/test/GPI/ConfigSetEmptyVOMSString.gpi fix 599 # 600 # Revision 1.15.26.5 2008/04/18 08:14:38 moscicki 601 # bugfix 18272 (reintroduced in Ganga 5): add typelist information to the configuration option 602 # 603 # Revision 1.15.26.4 2007/12/18 09:07:06 moscicki 604 # integrated typesystem from Alvin 605 # 606 # Revision 1.15.26.3 2007/11/07 17:02:13 moscicki 607 # merged against Ganga-4-4-0-dev-branch-kuba-slices with a lot of manual merging 608 # 609 # Revision 1.15.26.2 2007/11/07 15:10:03 moscicki 610 # merged in pretty print and GangaList support from ganga-5-dev-branch-4-4-1-will-print branch 611 # 612 # 613 # Revision 1.15.26.1 2007/10/12 13:56:24 moscicki 614 # merged with the new configuration subsystem 615 # 616 # Revision 1.15.28.1 2007/09/25 09:45:11 moscicki 617 # merged from old config branch 618 # 619 # Revision 1.15.6.1 2007/06/18 07:44:55 moscicki 620 # config prototype 621 # 622 # Revision 1.15.30.1 2007/10/30 12:12:08 wreece 623 # First version of the new print_summary functionality. Lots of changes, but some known limitations. Will address in next version. 624 # 625 # Revision 1.15.8.1 2007/06/18 10:16:36 moscicki 626 # slices prototype 627 # 628 # Revision 1.15 2007/03/05 12:04:18 moscicki 629 # explicit switch for strict_sequence (default is True), if the sequence is non-strict then a single value v will be converted to [v] on assignment, for example non-strict File sequence yields obj.x = 'a' <=> obj.x = [File('a')] <=> obj.x = File('a') 630 # 631 # Revision 1.14 2006/10/02 13:11:18 moscicki 632 # added extra check when setting default values: try to apply the value and raise ConfigError if it fails (for example unknown plugin) 633 # 634 # Revision 1.13 2006/07/28 12:53:36 moscicki 635 # fixed default value for ComponentItem (was broken) 636 # 637 # Revision 1.12 2006/07/28 08:26:23 moscicki 638 # allow defvalue to be an object for component items (not only a string or None) 639 # 640 # Revision 1.11 2006/07/27 20:15:35 moscicki 641 # createDefaultConfig() 642 # getDefaultValue() 643 # "checkset" metaproperty 644 # 645 # Revision 1.10 2005/12/02 15:35:06 moscicki 646 # visitable and getter metaproperties 647 # 648 # Revision 1.9 2005/08/24 15:42:21 moscicki 649 # automatically generated help for properties, disabled the SchemaHelper and few other improvements to the help system 650 # 651 # 652 # 653