Package param :: Module parameterized
[hide private]
[frames] | no frames]

Source Code for Module param.parameterized

   1  """ 
   2  Generic support for objects with full-featured Parameters and 
   3  messaging.   
   4   
   5  $Id: parameterized.py 11321 2010-07-28 16:47:04Z ceball $ 
   6  """ 
   7  __version__='$Revision$' 
   8   
   9  import sys 
  10  import copy 
  11  import re 
  12  import __main__ 
  13   
  14  from operator import itemgetter,attrgetter 
  15  from types import FunctionType 
  16   
  17  # JABALERT: Could consider using Python's logging facilities instead. 
  18  SILENT  = 0 
  19  WARNING = 50 
  20  NORMAL  = 100 
  21  MESSAGE = NORMAL 
  22  VERBOSE = 200 
  23  DEBUG   = 300 
  24   
  25  min_print_level = NORMAL 
  26   
  27  # Indicates whether warnings should be raised as errors, stopping 
  28  # processing. 
  29  warnings_as_exceptions = False 
  30   
  31  object_count = 0 
  32  warning_count = 0 
  33   
  34   
  35  import inspect 
36 -def classlist(class_):
37 """ 38 Return a list of the class hierarchy above (and including) the given class. 39 40 Same as inspect.getmro(class_)[::-1] 41 """ 42 return inspect.getmro(class_)[::-1]
43
44 45 -def descendents(class_):
46 """ 47 Return a list of the class hierarchy below (and including) the given class. 48 49 The list is ordered from least- to most-specific. Can be useful for 50 printing the contents of an entire class hierarchy. 51 """ 52 assert isinstance(class_,type) 53 q = [class_] 54 out = [] 55 while len(q): 56 x = q.pop(0) 57 out.insert(0,x) 58 for b in x.__subclasses__(): 59 if b not in q and b not in out: 60 q.append(b) 61 return out[::-1]
62
63 64 65 -def get_all_slots(class_):
66 """ 67 Return a list of slot names for slots defined in this class and 68 its superclasses. 69 """ 70 # A subclass's __slots__ attribute does not contain slots defined 71 # in its superclass (the superclass' __slots__ end up as 72 # attributes of the subclass). 73 all_slots = [] 74 parent_param_classes = [class_ for class_ in classlist(class_)[1::]] 75 for class_ in parent_param_classes: 76 if hasattr(class_,'__slots__'): 77 all_slots+=class_.__slots__ 78 return all_slots
79 80 81 82 83 # CEBALERT: decorators hide the docstring when using help(). Consider 84 # the decorator module: would allow doc to be seen for the actual 85 # method rather than seeing 'partial object at ...'. 86 # Not part of standard library: 87 # http://www.phyast.pitt.edu/~micheles/python/documentation.html 88 89 from functools import partial
90 -class bothmethod(object): # pylint: disable-msg=R0903
91 """ 92 'optional @classmethod' 93 94 A decorator that allows a method to receive either the class 95 object (if called on the class) or the instance object 96 (if called on the instance) as its first argument. 97 98 Code (but not documentation) copied from: 99 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/523033. 100 """ 101 # pylint: disable-msg=R0903 102
103 - def __init__(self, func):
104 self.func = func
105 106 # i.e. this is also a non-data descriptor
107 - def __get__(self, obj, type_=None):
108 if obj is None: 109 return partial(self.func, type_) 110 else: 111 return partial(self.func, obj)
112
113 114 -class ParameterMetaclass(type):
115 """ 116 Metaclass allowing control over creation of Parameter classes. 117 """
118 - def __new__(mcs,classname,bases,classdict):
119 # store the class's docstring in __classdoc 120 if '__doc__' in classdict: 121 classdict['__classdoc']=classdict['__doc__'] 122 # when asking for help on Parameter *object*, return the doc 123 # slot 124 classdict['__doc__']=property(attrgetter('doc')) 125 126 # To get the benefit of slots, subclasses must themselves define 127 # __slots__, whether or not they define attributes not present in 128 # the base Parameter class. That's because a subclass will have 129 # a __dict__ unless it also defines __slots__. 130 if '__slots__' not in classdict: 131 classdict['__slots__']=[] 132 133 return type.__new__(mcs,classname,bases,classdict)
134
135 - def __getattribute__(mcs,name):
136 if name=='__doc__': 137 # when asking for help on Parameter *class*, return the 138 # stored class docstring 139 return type.__getattribute__(mcs,'__classdoc') 140 else: 141 return type.__getattribute__(mcs,name)
142
143 144 145 # CEBALERT: we break some aspects of slot handling for Parameter and 146 # Parameterized. The __new__ methods in the metaclasses for those two 147 # classes omit to handle the case where __dict__ is passed in 148 # __slots__ (and they possibly omit other things too). Additionally, 149 # various bits of code in the Parameterized class assumes that all 150 # Parameterized instances have a __dict__, but I'm not sure that's 151 # guaranteed to be true (although it's true at the moment). 152 153 154 # CB: we could maybe reduce the complexity by doing something to allow 155 # a parameter to discover things about itself when created (would also 156 # allow things like checking a Parameter is owned by a 157 # Parameterized). I have some vague ideas about what to do. 158 -class Parameter(object):
159 """ 160 An attribute descriptor for declaring parameters. 161 162 Parameters are a special kind of class attribute. Setting a 163 Parameterized class attribute to be a Parameter instance causes 164 that attribute of the class (and the class's instances) to be 165 treated as a Parameter. This allows special behavior, including 166 dynamically generated parameter values, documentation strings, 167 constant and read-only parameters, and type or range checking at 168 assignment time. 169 170 For example, suppose someone wants to define two new kinds of 171 objects Foo and Bar, such that Bar has a parameter delta, Foo is a 172 subclass of Bar, and Foo has parameters alpha, sigma, and gamma 173 (and delta inherited from Bar). She would begin her class 174 definitions with something like this: 175 176 class Bar(Parameterized): 177 delta = Parameter(default=0.6, doc='The difference between steps.') 178 ... 179 180 class Foo(Bar): 181 alpha = Parameter(default=0.1, doc='The starting value.') 182 sigma = Parameter(default=0.5, doc='The standard deviation.', 183 constant=True) 184 gamma = Parameter(default=1.0, doc='The ending value.') 185 ... 186 187 Class Foo would then have four parameters, with delta defaulting 188 to 0.6. 189 190 Parameters have several advantages over plain attributes: 191 192 1. Parameters can be set automatically when an instance is 193 constructed: The default constructor for Foo (and Bar) will 194 accept arbitrary keyword arguments, each of which can be used 195 to specify the value of a Parameter of Foo (or any of Foo's 196 superclasses). E.g., if a script does this: 197 198 myfoo = Foo(alpha=0.5) 199 200 myfoo.alpha will return 0.5, without the Foo constructor 201 needing special code to set alpha. 202 203 If Foo implements its own constructor, keyword arguments will 204 still be accepted if the constructor accepts a dictionary of 205 keyword arguments (as in ``def __init__(self,**params):``), and 206 then each class calls its superclass (as in 207 ``super(Foo,self).__init__(**params)``) so that the 208 Parameterized constructor will process the keywords. 209 210 2. A Parameterized class need specify only the attributes of a 211 Parameter whose values differ from those declared in 212 superclasses; the other values will be inherited. E.g. if Foo 213 declares 214 215 delta = Parameter(default=0.2) 216 217 the default value of 0.2 will override the 0.6 inherited from 218 Bar, but the doc will be inherited from Bar. 219 220 3. The Parameter descriptor class can be subclassed to provide 221 more complex behavior, allowing special types of parameters 222 that, for example, require their values to be numbers in 223 certain ranges, generate their values dynamically from a random 224 distribution, or read their values from a file or other 225 external source. 226 227 4. The attributes associated with Parameters provide enough 228 information for automatically generating property sheets in 229 graphical user interfaces, allowing Parameterized instances to 230 be edited by users. 231 232 Note that Parameters can only be used when set as class attributes 233 of Parameterized classes. Parameters used as standalone objects, 234 or as class attributes of non-Parameterized classes, will not have 235 the behavior described here. 236 """ 237 __metaclass__ = ParameterMetaclass 238 239 # Because they implement __get__ and __set__, Parameters are known 240 # as 'descriptors' in Python; see "Implementing Descriptors" and 241 # "Invoking Descriptors" in the 'Customizing attribute access' 242 # section of the Python reference manual: 243 # http://docs.python.org/ref/attribute-access.html 244 # 245 # Overview of Parameters for programmers 246 # ====================================== 247 # 248 # Consider the following code: 249 # 250 # 251 # class A(Parameterized): 252 # p = Parameter(default=1) 253 # 254 # a1 = A() 255 # a2 = A() 256 # 257 # 258 # * a1 and a2 share one Parameter object (A.__dict__['p']). 259 # 260 # * The default (class) value of p is stored in this Parameter 261 # object (A.__dict__['p'].default). 262 # 263 # * If the value of p is set on a1 (e.g. a1.p=2), a1's value of p 264 # is stored in a1 itself (a1.__dict__['_p_param_value']) 265 # 266 # * When a1.p is requested, a1.__dict__['_p_param_value'] is 267 # returned. When a2.p is requested, '_p_param_value' is not 268 # found in a2.__dict__, so A.__dict__['p'].default (i.e. A.p) is 269 # returned instead. 270 # 271 # 272 # Be careful when referring to the 'name' of a Parameter: 273 # 274 # * A Parameterized class has a name for the attribute which is 275 # being represented by the Parameter ('p' in the example above); 276 # in the code, this is called the 'attrib_name'. 277 # 278 # * When a Parameterized instance has its own local value for a 279 # parameter, it is stored as '_X_param_value' (where X is the 280 # attrib_name for the Parameter); in the code, this is called 281 # the internal_name. 282 283 284 # So that the extra features of Parameters do not require a lot of 285 # overhead, Parameters are implemented using __slots__ (see 286 # http://www.python.org/doc/2.4/ref/slots.html). Instead of having 287 # a full Python dictionary associated with each Parameter instance, 288 # Parameter instances have an enumerated list (named __slots__) of 289 # attributes, and reserve just enough space to store these 290 # attributes. Using __slots__ requires special support for 291 # operations to copy and restore Parameters (e.g. for Python 292 # persistent storage pickling); see __getstate__ and __setstate__. 293 __slots__ = ['_attrib_name','_internal_name','default','doc', 294 'precedence','instantiate','constant','readonly', 295 'pickle_default_value'] 296 297 # When created, a Parameter does not know which 298 # Parameterized class owns it. If a Parameter subclass needs 299 # to know the owning class, it can declare an 'objtype' slot 300 # (which will be filled in by ParameterizedMetaclass) 301
302 - def __init__(self,default=None,doc=None,precedence=None, # pylint: disable-msg=R0913 303 instantiate=False,constant=False,readonly=False, 304 pickle_default_value=True):
305 """ 306 Initialize a new Parameter object: store the supplied attributes. 307 308 default: the owning class's value for the attribute 309 represented by this Parameter. 310 311 precedence is a value, usually in the range 0.0 to 1.0, that 312 allows the order of Parameters in a class to be defined (for 313 e.g. in GUI menus). A negative precedence indicates a 314 parameter that should be hidden in e.g. GUI menus. 315 316 default, doc, and precedence default to None. This is to allow 317 inheritance of Parameter slots (attributes) from the owning-class' 318 class hierarchy (see ParameterizedMetaclass). 319 320 In rare cases where the default value should not be pickled, 321 set pickle_default_value=False (e.g. for file search paths). 322 """ 323 self._attrib_name = None 324 self._internal_name = None 325 self.precedence = precedence 326 self.default = default 327 self.doc = doc 328 self.constant = constant or readonly # readonly => constant 329 self.readonly = readonly 330 self._set_instantiate(instantiate) 331 self.pickle_default_value = pickle_default_value
332 333
334 - def _set_instantiate(self,instantiate):
335 """Constant parameters must be instantiated.""" 336 # CB: instantiate doesn't actually matter for read-only 337 # parameters, since they can't be set even on a class. But 338 # this avoids needless instantiation. 339 if self.readonly: 340 self.instantiate = False 341 else: 342 self.instantiate = instantiate or self.constant # pylint: disable-msg=W0201
343 344
345 - def __get__(self,obj,objtype): # pylint: disable-msg=W0613
346 """ 347 Return the value for this Parameter. 348 349 If called for a Parameterized class, produce that 350 class's value (i.e. this Parameter object's 'default' 351 attribute). 352 353 If called for a Parameterized instance, produce that 354 instance's value, if one has been set - otherwise produce the 355 class's value (default). 356 """ 357 # NB: obj can be None (when __get__ called for a 358 # Parameterized class); objtype is never None 359 360 if not obj: 361 result = self.default 362 else: 363 result = obj.__dict__.get(self._internal_name,self.default) 364 return result 365 366
367 - def __set__(self,obj,val):
368 """ 369 Set the value for this Parameter. 370 371 If called for a Parameterized class, set that class's 372 value (i.e. set this Parameter object's 'default' attribute). 373 374 If called for a Parameterized instance, set the value of 375 this Parameter on that instance (i.e. in the instance's 376 __dict__, under the parameter's internal_name). 377 378 379 If the Parameter's constant attribute is True, only allows 380 the value to be set for a Parameterized class or on 381 uninitialized Parameterized instances. 382 383 If the Parameter's readonly attribute is True, only allows the 384 value to be specified in the Parameter declaration inside the 385 Parameterized source code. A read-only parameter also 386 cannot be set on a Parameterized class. 387 388 Note that until we support some form of read-only 389 object, it is still possible to change the attributes of the 390 object stored in a constant or read-only Parameter (e.g. the 391 left bound of a BoundingBox). 392 """ 393 # NB: obj can be None (when __set__ called for a 394 # Parameterized class) 395 if self.constant or self.readonly: 396 if self.readonly: 397 raise TypeError("Read-only parameter '%s' cannot be modified"%self._attrib_name) 398 elif not obj: 399 self.default = val 400 elif not obj.initialized: 401 obj.__dict__[self._internal_name] = val 402 else: 403 raise TypeError("Constant parameter '%s' cannot be modified"%self._attrib_name) 404 405 else: 406 if not obj: 407 self.default = val 408 else: 409 obj.__dict__[self._internal_name] = val
410 411
412 - def __delete__(self,obj):
413 raise TypeError("Cannot delete '%s': Parameters deletion not allowed."%self._attrib_name)
414 415
416 - def _set_names(self,attrib_name):
417 self._attrib_name = attrib_name 418 self._internal_name = "_%s_param_value"%attrib_name 419 420
421 - def __getstate__(self):
422 """ 423 All Parameters have slots, not a dict, so we have to support 424 pickle and deepcopy ourselves. 425 """ 426 state = {} 427 for slot in get_all_slots(type(self)): 428 state[slot] = getattr(self,slot) 429 430 return state
431
432 - def __setstate__(self,state):
433 # set values of __slots__ (instead of in non-existent __dict__) 434 for (k,v) in state.items(): 435 setattr(self,k,v)
436
437 438 # Define one particular type of Parameter that is used in this file 439 -class String(Parameter):
440 __slots__ = ['allow_None'] 441
442 - def __init__(self,default="",allow_None=False,**params):
443 """Initialize a string parameter.""" 444 Parameter.__init__(self,default=default,**params) 445 self.allow_None = (default is None or allow_None)
446
447 - def __set__(self,obj,val):
448 if not isinstance(val,str) and not (self.allow_None and val is None): 449 raise ValueError("String '%s' only takes a string value."%self._attrib_name) 450 451 super(String,self).__set__(obj,val)
452
453 454 455 -class ParameterizedMetaclass(type):
456 """ 457 The metaclass of Parameterized (and all its descendents). 458 459 The metaclass overrides type.__setattr__ to allow us to set 460 Parameter values on classes without overwriting the attribute 461 descriptor. That is, for a Parameterized class of type X with a 462 Parameter y, the user can type X.y=3, which sets the default value 463 of Parameter y to be 3, rather than overwriting y with the 464 constant value 3 (and thereby losing all other info about that 465 Parameter, such as the doc string, bounds, etc.). 466 467 The __init__ method is used when defining a Parameterized class, 468 usually when the module where that class is located is imported 469 for the first time. That is, the __init__ in this metaclass 470 initializes the *class* object, while the __init__ method defined 471 in each Parameterized class is called for each new instance of 472 that class. 473 474 Additionally, a class can declare itself abstract by having an 475 attribute __abstract set to True. The 'abstract' attribute can be 476 used to find out if a class is abstract or not. 477 """
478 - def __init__(mcs,name,bases,dict_):
479 """ 480 Initialize the class object (not an instance of the class, but 481 the class itself). 482 483 Initializes all the Parameters by looking up appropriate 484 default values (see __param_inheritance()) and setting 485 attrib_names (see _set_names()). 486 """ 487 type.__init__(mcs,name,bases,dict_) 488 489 # Give Parameterized classes a useful 'name' attribute. 490 # (Could instead consider changing the instance Parameter 491 # 'name' to '__name__'?) 492 mcs.name = name 493 494 # All objects (with their names) of type Parameter that are 495 # defined in this class 496 parameters = [(name,obj) 497 for (name,obj) in dict_.items() 498 if isinstance(obj,Parameter)] 499 500 for param_name,param in parameters: 501 mcs._initialize_parameter(param_name,param)
502 503
504 - def _initialize_parameter(mcs,param_name,param):
505 # parameter has no way to find out the name a 506 # Parameterized class has for it 507 param._set_names(param_name) 508 mcs.__param_inheritance(param_name,param)
509 510 511 # CBENHANCEMENT: Python 2.6 has abstract base classes. 512 # http://docs.python.org/whatsnew/2.6.html
513 - def __is_abstract(mcs):
514 """ 515 Return True if the class has an attribute __abstract set to True. 516 Subclasses will return False unless they themselves have 517 __abstract set to true. This mechanism allows a class to 518 declare itself to be abstract (e.g. to avoid it being offered 519 as an option in a GUI), without the "abstract" property being 520 inherited by its subclasses (at least one of which is 521 presumably not abstract). 522 """ 523 # Can't just do ".__abstract", because that is mangled to 524 # _ParameterizedMetaclass__abstract before running, but 525 # the actual class object will have an attribute 526 # _ClassName__abstract. So, we have to mangle it ourselves at 527 # runtime. 528 try: 529 return getattr(mcs,'_%s__abstract'%mcs.__name__) 530 except AttributeError: 531 return False
532 533 abstract = property(__is_abstract) 534 535 536
537 - def __setattr__(mcs,attribute_name,value):
538 """ 539 Implements 'self.attribute_name=value' in a way that also supports Parameters. 540 541 If there is already a descriptor named attribute_name, and 542 that descriptor is a Parameter, and the new value is *not* a 543 Parameter, then call that Parameter's __set__ method with the 544 specified value. 545 546 In all other cases set the attribute normally (i.e. overwrite 547 the descriptor). If the new value is a Parameter, once it has 548 been set we make sure that the value is inherited from 549 Parameterized superclasses as described in __param_inheritance(). 550 """ 551 # Find out if there's a Parameter called attribute_name as a 552 # class attribute of this class - if not, parameter is None. 553 parameter,owning_class = mcs.get_param_descriptor(attribute_name) 554 555 if parameter and not isinstance(value,Parameter): 556 if owning_class != mcs: 557 type.__setattr__(mcs,attribute_name,copy.copy(parameter)) 558 mcs.__dict__[attribute_name].__set__(None,value) 559 560 else: 561 type.__setattr__(mcs,attribute_name,value) 562 563 if isinstance(value,Parameter): 564 mcs.__param_inheritance(attribute_name,value) 565 else: 566 # the purpose of the warning below is to catch 567 # mistakes ("thinking you are setting a parameter, but 568 # you're not"). There are legitimate times when 569 # something needs be set on the class, and we don't 570 # want to see a warning then. Such attributes should 571 # presumably be prefixed by at least one underscore. 572 # (For instance, python's own pickling mechanism 573 # caches __slotnames__ on the class: 574 # http://mail.python.org/pipermail/python-checkins/2003-February/033517.html.) 575 # CEBALERT: this warning bypasses the usual 576 # mechanisms, which has have consequences for warning 577 # counts, warnings as exceptions, etc. 578 if not attribute_name.startswith('_'): 579 print ("Warning: Setting non-Parameter class attribute %s.%s = %s " 580 % (mcs.__name__,attribute_name,`value`))
581 582
583 - def __param_inheritance(mcs,param_name,param):
584 """ 585 Look for Parameter values in superclasses of this 586 Parameterized class. 587 588 Ordinarily, when a Python object is instantiated, attributes 589 not given values in the constructor will inherit the value 590 given in the object's class, or in its superclasses. For 591 Parameters owned by Parameterized classes, we have implemented 592 an additional level of default lookup, should this ordinary 593 lookup return only None. 594 595 In such a case, i.e. when no non-None value was found for a 596 Parameter by the usual inheritance mechanisms, we explicitly 597 look for Parameters with the same name in superclasses of this 598 Parameterized class, and use the first such value that we 599 find. 600 601 The goal is to be able to set the default value (or other 602 slots) of a Parameter within a Parameterized class, just as we 603 can set values for non-Parameter objects in Parameterized 604 classes, and have the values inherited through the 605 Parameterized hierarchy as usual. 606 607 Note that instantiate is handled differently: if there is a 608 parameter with the same name in one of the superclasses with 609 instantiate set to True, this parameter will inherit 610 instatiate=True. 611 """ 612 # get all relevant slots (i.e. slots defined in all 613 # superclasses of this parameter) 614 slots = {} 615 for p_class in classlist(type(param))[1::]: 616 slots.update(dict.fromkeys(p_class.__slots__)) 617 618 # Some Parameter classes need to know the owning Parameterized 619 # class. Such classes can declare an 'objtype' slot, and the 620 # owning class will be stored in it. 621 if 'objtype' in slots: 622 setattr(param,'objtype',mcs) 623 del slots['objtype'] 624 625 # instantiate is handled specially 626 for superclass in classlist(mcs)[::-1]: 627 super_param = superclass.__dict__.get(param_name) 628 if super_param is not None and super_param.instantiate is True: 629 param.instantiate=True 630 del slots['instantiate'] 631 632 633 for slot in slots.keys(): 634 superclasses = iter(classlist(mcs)[::-1]) 635 636 # Search up the hierarchy until param.slot (which has to 637 # be obtained using getattr(param,slot)) is not None, or 638 # we run out of classes to search. 639 # 640 # CEBALERT: there's probably a better way than while and 641 # an iterator, but it works. 642 while getattr(param,slot) is None: 643 try: 644 param_super_class = superclasses.next() 645 except StopIteration: 646 break 647 648 new_param = param_super_class.__dict__.get(param_name) 649 if new_param != None and hasattr(new_param,slot): 650 # (slot might not be there because could be a more 651 # general type of Parameter) 652 new_value = getattr(new_param,slot) 653 setattr(param,slot,new_value)
654 655
656 - def get_param_descriptor(mcs,param_name):
657 """ 658 Goes up the class hierarchy (starting from the current class) 659 looking for a Parameter class attribute param_name. As soon as 660 one is found as a class attribute, that Parameter is returned 661 along with the class in which it is declared. 662 """ 663 classes = classlist(mcs) 664 for c in classes[::-1]: 665 attribute = c.__dict__.get(param_name) 666 if isinstance(attribute,Parameter): 667 return attribute,c 668 return None,None
669 670 671 672 673 # JABALERT: Only partially achieved so far -- objects of the same 674 # type and parameter values are treated as different, so anything 675 # for which instantiate == True is reported as being non-default. 676 677 # Whether script_repr should avoid reporting the values of parameters 678 # that are just inheriting their values from the class defaults. 679 script_repr_suppress_defaults=True
680 681 682 # CEBALERT: How about some defaults? 683 # Also, do we need an option to return repr without path, if desired? 684 # E.g. to get 'pre_plot_hooks()' instead of 685 # 'topo.command.analysis.pre_plot_hooks()' in the gui? 686 -def script_repr(val,imports,prefix,settings):
687 """ 688 Variant of repr() designed for generating a runnable script. 689 690 Instances of types that require special handling can use the 691 script_repr_reg dictionary. Using the type as a key, add a 692 function that returns a suitable representation of instances of 693 that type, and adds the required import statement. 694 """ 695 # CB: doc prefix & settings or realize they don't need to be 696 # passed around, etc. 697 if isinstance(val,type): 698 rep = type_script_repr(val,imports,prefix,settings) 699 700 elif type(val) in script_repr_reg: 701 rep = script_repr_reg[type(val)](val,imports,prefix,settings) 702 703 elif hasattr(val,'script_repr'): 704 rep=val.script_repr(imports=imports,prefix=prefix+" ") 705 706 else: 707 rep=repr(val) 708 709 return rep
710 711 712 # see script_repr() 713 script_repr_reg = {}
714 715 716 # currently only handles list and tuple 717 -def container_script_repr(container,imports,prefix,settings):
718 result=[] 719 for i in container: 720 result.append(script_repr(i,imports,prefix,settings)) 721 722 ## (hack to get container brackets) 723 if isinstance(container,list): 724 d1,d2='[',']' 725 elif isinstance(container,tuple): 726 d1,d2='(',')' 727 else: 728 raise NotImplementedError 729 rep=d1+','.join(result)+d2 730 731 # no imports to add for built-in types 732 733 return rep
734
735 # why I have to type prefix and settings? 736 -def function_script_repr(fn,imports,prefix,settings):
737 name = fn.func_name 738 module = fn.__module__ 739 imports.append('import %s'%module) 740 return module+'.'+name
741
742 -def type_script_repr(type_,imports,prefix,settings):
743 module = type_.__module__ 744 if module!='__builtin__': 745 imports.append('import %s'%module) 746 return module+'.'+type_.__name__
747 748 script_repr_reg[list]=container_script_repr 749 script_repr_reg[tuple]=container_script_repr 750 script_repr_reg[FunctionType]=function_script_repr 751 752 753 # If not None, the value of this Parameter will be called (using '()') 754 # before every call to __db_print, and is expected to evaluate to a 755 # string that is suitable for prefixing messages and warnings (such 756 # as some indicator of the global state). 757 dbprint_prefix=None
758 759 760 -def as_uninitialized(fn):
761 """ 762 Decorator: call fn with the parameterized_instance's 763 initialization flag set to False, then revert the flag. 764 765 (Used to decorate Parameterized methods that must alter 766 a constant Parameter.) 767 """ 768 def override_initialization(parameterized_instance,*args,**kw): 769 original_initialized=parameterized_instance.initialized 770 parameterized_instance.initialized=False 771 fn(parameterized_instance,*args,**kw) 772 parameterized_instance.initialized=original_initialized
773 return override_initialization 774
775 776 777 -class Parameterized(object):
778 """ 779 Base class for named objects that support Parameters and message 780 formatting. 781 782 Automatic object naming: Every Parameterized instance has a name 783 parameter. If the user doesn't designate a name=<str> argument 784 when constructing the object, the object will be given a name 785 consisting of its class name followed by a unique 5-digit number. 786 787 Automatic parameter setting: The Parameterized __init__ method 788 will automatically read the list of keyword parameters. If any 789 keyword matches the name of a Parameter (see Parameter class) 790 defined in the object's class or any of its superclasses, that 791 parameter in the instance will get the value given as a keyword 792 argument. For example: 793 794 class Foo(Parameterized): 795 xx = Parameter(default=1) 796 797 foo = Foo(xx=20) 798 799 in this case foo.xx gets the value 20. 800 801 Message formatting: Each Parameterized instance has several methods 802 for optionally printing output according to the current 'print 803 level', such as SILENT, WARNING, MESSAGE, VERBOSE, or DEBUG. Each 804 successive level allows more messages to be printed. For example, 805 when the level is VERBOSE, all warning, message, and verbose 806 output will be printed. When it is WARNING, only warnings will be 807 printed. When it is SILENT, no output will be printed. 808 809 For each level (except SILENT) there's an associated print method: 810 Parameterized.warning(), .message(), .verbose(), and .debug(). 811 812 Each line printed this way is prepended with the name of the 813 object that printed it. The Parameterized.print_level parameter 814 and the module global variable min_print_level combine to 815 determine what gets printed. For example, if foo is a 816 Parameterized: 817 818 foo.message('The answer is',42) 819 820 is equivalent to: 821 822 if max(foo.print_level,parameterized.min_print_level) >= MESSAGE: 823 print foo.name+':', 'The answer is', 42 824 """ 825 826 __metaclass__ = ParameterizedMetaclass 827 828 name = String(default=None,constant=True,doc=""" 829 String identifier for this object.""") 830 831 ### JABALERT: Should probably make this an Enumeration instead. 832 print_level = Parameter(default=MESSAGE,precedence=-1) 833 834
835 - def __init__(self,**params):
836 """ 837 Initialize this Parameterized instance. 838 839 The values of parameters can be supplied as keyword arguments 840 to the constructor (using parametername=parametervalue); these 841 values will override the class default values for this one 842 instance. 843 844 If no 'name' parameter is supplied, self.name defaults to the 845 object's class name with a unique number appended to it. 846 """ 847 global object_count 848 849 # Flag that can be tested to see if e.g. constant Parameters 850 # can still be set 851 self.initialized=False 852 853 self.__generate_name() 854 855 self._setup_params(**params) 856 object_count += 1 857 858 self.nopickle = [] # CEBALERT: remove this - we don't use it 859 self.debug('Initialized',self) 860 861 self.initialized=True
862 863 864 @bothmethod
865 - def _add_parameter(self_or_cls,param_name,param_obj):
866 """ 867 Add a new Parameter object into this object's class. 868 869 Supposed to result in a Parameter equivalent to one declared 870 in the class's source code. 871 """ 872 if isinstance(self_or_cls,type): 873 cls=self_or_cls 874 else: 875 cls=type(self_or_cls) 876 877 # CEBALERT: can't we just do 878 # setattr(cls,param_name,param_obj)? The metaclass's 879 # __setattr__ is actually written to handle that. (Would also 880 # need to do something about the params() cache. That cache 881 # is a pain, but it definitely improved the startup time; it 882 # would be worthwhile making sure no method except for one 883 # "add_param()" method has to deal with it (plus any future 884 # remove_param() method.) 885 type.__setattr__(cls,param_name,param_obj) 886 cls.__metaclass__._initialize_parameter(cls,param_name,param_obj) 887 # delete cached params() 888 try: 889 delattr(cls,'_%s__params'%cls.__name__) 890 except AttributeError: 891 pass
892 893 894 @bothmethod
895 - def set_param(self_or_cls,param_name,val):
896 """ 897 Sets the value of param_name to val, after checking that param_name 898 is a parameter of this object. 899 900 (I.e., same as setattr(obj,param_name,val), except the 901 param_name's existence as a parameter is first checked.) 902 """ 903 if param_name not in self_or_cls.params(): 904 raise ValueError("'%s' is not a parameter of %s"%(param_name,self_or_cls)) 905 setattr(self_or_cls,param_name,val)
906 907 908 # CEBALERT: I think I've noted elsewhere the fact that we 909 # sometimes have a method on Parameter that requires passing the 910 # owning Parameterized instance or class, and other times we have 911 # the method on Parameterized itself. In case I haven't written 912 # that down elsewhere, here it is again. We should clean that up 913 # (at least we should be consistent). 914 915 # cebalert: it's really time to stop and clean up this bothmethod 916 # stuff and repeated code in methods using it. 917 918 # CEBALERT: note there's no state_push method on the class, so 919 # dynamic parameters set on a class can't have state saved. This 920 # is because, to do this, state_push() would need to be a 921 # @bothmethod, but that complicates inheritance in cases where we 922 # already have a state_push() method. I need to decide what to do 923 # about that. (isinstance(g,Parameterized) below is used to exclude classes.)
924 - def state_push(self):
925 """ 926 Save this instance's state. 927 928 For Parameterized instances, this includes the state of 929 dynamically generated values. 930 931 Subclasses that maintain short-term state should additionally 932 save and restore that state using state_push() and 933 state_pop(). 934 935 Generally, this method is used by operations that need to test 936 something without permanently altering the objects' state. 937 """ 938 for pname,p in self.params().items(): 939 g = self.get_value_generator(pname) 940 if hasattr(g,'_Dynamic_last'): 941 g._saved_Dynamic_last.append(g._Dynamic_last) 942 g._saved_Dynamic_time.append(g._Dynamic_time) 943 # CB: not storing the time_fn: assuming that doesn't 944 # change. 945 elif hasattr(g,'state_push') and isinstance(g,Parameterized): 946 g.state_push()
947
948 - def state_pop(self):
949 """ 950 Restore the most recently saved state. 951 952 See state_push() for more details. 953 """ 954 for pname,p in self.params().items(): 955 g = self.get_value_generator(pname) 956 if hasattr(g,'_Dynamic_last'): 957 g._Dynamic_last = g._saved_Dynamic_last.pop() 958 g._Dynamic_time = g._saved_Dynamic_time.pop() 959 elif hasattr(g,'state_pop') and isinstance(g,Parameterized): 960 g.state_pop()
961 962 963 @bothmethod
964 - def set_default(self_or_cls,param_name,value):
965 """ 966 Set the default value of param_name. 967 968 Equivalent to setting param_name on the class. 969 """ 970 if isinstance(self_or_cls,type): 971 cls=self_or_cls 972 else: 973 cls=type(self_or_cls) 974 setattr(cls,param_name,value)
975 976 977 @bothmethod
978 - def set_dynamic_time_fn(self_or_cls,time_fn,sublistattr=None):
979 """ 980 Set time_fn for all Dynamic Parameters of this class or 981 instance object that are currently being dynamically 982 generated. 983 984 Additionally, sets _Dynamic_time_fn=time_fn on this class or 985 instance object, so that any future changes to Dynamic 986 Parmeters can inherit time_fn (e.g. if a Number is changed 987 from a float to a number generator, the number generator will 988 inherit time_fn). 989 990 If specified, sublistattr is the name of an attribute of this 991 class or instance that contains an iterable collection of 992 subobjects on which set_dynamic_time_fn should be called. If 993 the attribute sublistattr is present on any of the subobjects, 994 set_dynamic_time_fn() will be called for those, too. 995 """ 996 self_or_cls._Dynamic_time_fn = time_fn 997 998 if isinstance(self_or_cls,type): 999 a = (None,self_or_cls) 1000 else: 1001 a = (self_or_cls,) 1002 1003 for n,p in self_or_cls.params().items(): 1004 if hasattr(p,'_value_is_dynamic'): 1005 if p._value_is_dynamic(*a): 1006 g = self_or_cls.get_value_generator(n) 1007 g._Dynamic_time_fn = time_fn 1008 1009 if sublistattr: 1010 try: 1011 sublist = getattr(self_or_cls,sublistattr) 1012 except AttributeError: 1013 sublist = [] 1014 1015 for obj in sublist: 1016 obj.set_dynamic_time_fn(time_fn,sublistattr)
1017 1018 1019 @as_uninitialized
1020 - def _set_name(self,name):
1021 self.name=name
1022 1023 1024 @as_uninitialized
1025 - def __generate_name(self):
1026 """ 1027 Set name to a gensym formed from the object's type name and 1028 the object_count. 1029 """ 1030 self._set_name('%s%05d' % (self.__class__.__name__ ,object_count))
1031 1032 # CB: __repr__ is called often; methods it uses should not be too slow
1033 - def __repr__(self):
1034 """ 1035 Provide a nearly valid Python representation that could be used to recreate 1036 the item with its parameters, if executed in the appropriate environment. 1037 1038 Returns 'classname(parameter1=x,parameter2=y,...)', listing 1039 all the parameters of this object. 1040 """ 1041 settings = ['%s=%s' % (name,repr(val)) 1042 for name,val in self.get_param_values()] 1043 return self.__class__.__name__ + "(" + ", ".join(settings) + ")"
1044 1045 1046 1047
1048 - def script_repr(self,imports=[],prefix=" "):
1049 """ 1050 Variant of __repr__ designed for generating a runnable script. 1051 """ 1052 # Suppresses automatically generated names and print_levels. 1053 settings=[] 1054 for