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

Source Code for Module topo.param.parameterized

   1  """ 
   2  Generic support for objects with full-featured Parameters and 
   3  messaging.   
   4   
   5  $Id: parameterized.py 9417 2008-10-08 20:23:33Z ceball $ 
   6  """ 
   7  __version__='$Revision: 9417 $' 
   8   
   9  import sys 
  10  import copy 
  11  import re 
  12   
  13  from operator import itemgetter,attrgetter 
  14  from types import FunctionType 
  15   
  16  # JABALERT: Could consider using Python's logging facilities instead. 
  17  SILENT  = 0 
  18  WARNING = 50 
  19  NORMAL  = 100 
  20  MESSAGE = NORMAL 
  21  VERBOSE = 200 
  22  DEBUG   = 300 
  23   
  24  min_print_level = NORMAL 
  25   
  26  # Indicates whether warnings should be raised as errors, stopping 
  27  # processing. 
  28  warnings_as_exceptions = False 
  29   
  30  object_count = 0 
  31   
  32   
  33  import inspect 
34 -def classlist(class_):
35 """ 36 Return a list of the class hierarchy above (and including) the given class. 37 38 Same as inspect.getmro(class_)[::-1] 39 """ 40 return inspect.getmro(class_)[::-1]
41
42 43 -def descendents(class_):
44 """ 45 Return a list of the class hierarchy below (and including) the given class. 46 47 The list is ordered from least- to most-specific. Can be useful for 48 printing the contents of an entire class hierarchy. 49 """ 50 assert isinstance(class_,type) 51 q = [class_] 52 out = [] 53 while len(q): 54 x = q.pop(0) 55 out.insert(0,x) 56 for b in x.__subclasses__(): 57 if b not in q and b not in out: 58 q.append(b) 59 return out[::-1]
60
61 62 63 -def get_all_slots(class_):
64 """ 65 Return a list of slot names for slots defined in this class and 66 its superclasses. 67 """ 68 # A subclass's __slots__ attribute does not contain slots defined 69 # in its superclass (the superclass' __slots__ end up as 70 # attributes of the subclass). 71 all_slots = [] 72 parent_param_classes = [class_ for class_ in classlist(class_)[1::]] 73 for class_ in parent_param_classes: 74 if hasattr(class_,'__slots__'): 75 all_slots+=class_.__slots__ 76 return all_slots
77 78 79 80 81 # CEBALERT: decorators hide the docstring when using help(). Consider 82 # the decorator module: would allow doc to be seen for the actual 83 # method rather than seeing 'partial object at ...'. 84 # Not part of standard library: 85 # http://www.phyast.pitt.edu/~micheles/python/documentation.html 86 87 from functools import partial
88 -class bothmethod(object): # pylint: disable-msg=R0903
89 """ 90 'optional @classmethod' 91 92 A decorator that allows a method to receive either the class 93 object (if called on the class) or the instance object 94 (if called on the instance) as its first argument. 95 96 Code (but not documentation) copied from: 97 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/523033. 98 """ 99 # pylint: disable-msg=R0903 100
101 - def __init__(self, func):
102 self.func = func
103 104 # i.e. this is also a non-data descriptor
105 - def __get__(self, obj, type_=None):
106 if obj is None: 107 return partial(self.func, type_) 108 else: 109 return partial(self.func, obj)
110
111 112 -class ParameterMetaclass(type):
113 """ 114 Metaclass allowing control over creation of Parameter classes. 115 """
116 - def __new__(mcs,classname,bases,classdict):
117 # store the class's docstring in __classdoc 118 if '__doc__' in classdict: 119 classdict['__classdoc']=classdict['__doc__'] 120 # when asking for help on Parameter *object*, return the doc 121 # slot 122 classdict['__doc__']=property(attrgetter('doc')) 123 124 # To get the benefit of slots, subclasses must themselves define 125 # __slots__, whether or not they define attributes not present in 126 # the base Parameter class. That's because a subclass will have 127 # a __dict__ unless it also defines __slots__. 128 if '__slots__' not in classdict: 129 classdict['__slots__']=[] 130 131 return type.__new__(mcs,classname,bases,classdict)
132
133 - def __getattribute__(mcs,name):
134 if name=='__doc__': 135 # when asking for help on Parameter *class*, return the 136 # stored class docstring 137 return type.__getattribute__(mcs,'__classdoc') 138 else: 139 return type.__getattribute__(mcs,name)
140
141 142 143 # CEBALERT: we break some aspects of slot handling for Parameter and 144 # Parameterized. The __new__ methods in the metaclasses for those two 145 # classes omit to handle the case where __dict__ is passed in 146 # __slots__ (and they possibly omit other things too). Additionally, 147 # various bits of code in the Parameterized class assumes that all 148 # Parameterized instances have a __dict__, but I'm not sure that's 149 # guaranteed to be true (although it's true at the moment). 150 151 152 # CB: we could maybe reduce the complexity by doing something to allow 153 # a parameter to discover things about itself when created (would also 154 # allow things like checking a Parameter is owned by a 155 # Parameterized). I have some vague ideas about what to do. 156 -class Parameter(object):
157 """ 158 An attribute descriptor for declaring parameters. 159 160 Parameters are a special kind of class attribute. Setting a 161 Parameterized class attribute to be a Parameter instance causes 162 that attribute of the class (and the class's instances) to be 163 treated as a Parameter. This allows special behavior, including 164 dynamically generated parameter values, documentation strings, 165 constant and read-only parameters, and type or range checking at 166 assignment time. 167 168 For example, suppose someone wants to define two new kinds of 169 objects Foo and Bar, such that Bar has a parameter delta, Foo is a 170 subclass of Bar, and Foo has parameters alpha, sigma, and gamma 171 (and delta inherited from Bar). She would begin her class 172 definitions with something like this: 173 174 class Bar(Parameterized): 175 delta = Parameter(default=0.6, doc='The difference between steps.') 176 ... 177 178 class Foo(Bar): 179 alpha = Parameter(default=0.1, doc='The starting value.') 180 sigma = Parameter(default=0.5, doc='The standard deviation.', 181 constant=True) 182 gamma = Parameter(default=1.0, doc='The ending value.') 183 ... 184 185 Class Foo would then have four parameters, with delta defaulting 186 to 0.6. 187 188 Parameters have several advantages over plain attributes: 189 190 1. Parameters can be set automatically when an instance is 191 constructed: The default constructor for Foo (and Bar) will 192 accept arbitrary keyword arguments, each of which can be used 193 to specify the value of a Parameter of Foo (or any of Foo's 194 superclasses). E.g., if a script does this: 195 196 myfoo = Foo(alpha=0.5) 197 198 myfoo.alpha will return 0.5, without the Foo constructor 199 needing special code to set alpha. 200 201 If Foo implements its own constructor, keyword arguments will 202 still be accepted if the constructor accepts a dictionary of 203 keyword arguments (as in ``def __init__(self,**params):``), and 204 then each class calls its superclass (as in 205 ``super(Foo,self).__init__(**params)``) so that the 206 Parameterized constructor will process the keywords. 207 208 2. A Parameterized class need specify only the attributes of a 209 Parameter whose values differ from those declared in 210 superclasses; the other values will be inherited. E.g. if Foo 211 declares 212 213 delta = Parameter(default=0.2) 214 215 the default value of 0.2 will override the 0.6 inherited from 216 Bar, but the doc will be inherited from Bar. 217 218 3. The Parameter descriptor class can be subclassed to provide 219 more complex behavior, allowing special types of parameters 220 that, for example, require their values to be numbers in 221 certain ranges, generate their values dynamically from a random 222 distribution, or read their values from a file or other 223 external source. 224 225 4. The attributes associated with Parameters provide enough 226 information for automatically generating property sheets in 227 graphical user interfaces, allowing Parameterized instances to 228 be edited by users. 229 230 Note that Parameters can only be used when set as class attributes 231 of Parameterized classes. Parameters used as standalone objects, 232 or as class attributes of non-Parameterized classes, will not have 233 the behavior described here. 234 """ 235 __metaclass__ = ParameterMetaclass 236 237 # Because they implement __get__ and __set__, Parameters are known 238 # as 'descriptors' in Python; see "Implementing Descriptors" and 239 # "Invoking Descriptors" in the 'Customizing attribute access' 240 # section of the Python reference manual: 241 # http://docs.python.org/ref/attribute-access.html 242 # 243 # Overview of Parameters for programmers 244 # ====================================== 245 # 246 # Consider the following code: 247 # 248 # 249 # class A(Parameterized): 250 # p = Parameter(default=1) 251 # 252 # a1 = A() 253 # a2 = A() 254 # 255 # 256 # * a1 and a2 share one Parameter object (A.__dict__['p']). 257 # 258 # * The default (class) value of p is stored in this Parameter 259 # object (A.__dict__['p'].default). 260 # 261 # * If the value of p is set on a1 (e.g. a1.p=2), a1's value of p 262 # is stored in a1 itself (a1.__dict__['_p_param_value']) 263 # 264 # * When a1.p is requested, a1.__dict__['_p_param_value'] is 265 # returned. When a2.p is requested, '_p_param_value' is not 266 # found in a2.__dict__, so A.__dict__['p'].default (i.e. A.p) is 267 # returned instead. 268 # 269 # 270 # Be careful when referring to the 'name' of a Parameter: 271 # 272 # * A Parameterized class has a name for the attribute which is 273 # being represented by the Parameter ('p' in the example above); 274 # in the code, this is called the 'attrib_name'. 275 # 276 # * When a Parameterized instance has its own local value for a 277 # parameter, it is stored as '_X_param_value' (where X is the 278 # attrib_name for the Parameter); in the code, this is called 279 # the internal_name. 280 281 282 # So that the extra features of Parameters do not require a lot of 283 # overhead, Parameters are implemented using __slots__ (see 284 # http://www.python.org/doc/2.4/ref/slots.html). Instead of having 285 # a full Python dictionary associated with each Parameter instance, 286 # Parameter instances have an enumerated list (named __slots__) of 287 # attributes, and reserve just enough space to store these 288 # attributes. Using __slots__ requires special support for 289 # operations to copy and restore Parameters (e.g. for Python 290 # persistent storage pickling); see __getstate__ and __setstate__. 291 __slots__ = ['_attrib_name','_internal_name','default','doc', 292 'precedence','instantiate','constant','readonly'] 293 294 # When created, a Parameter does not know which 295 # Parameterized class owns it. If a Parameter subclass needs 296 # to know the owning class, it can declare an 'objtype' slot 297 # (which will be filled in by ParameterizedMetaclass) 298
299 - def __init__(self,default=None,doc=None,precedence=None, # pylint: disable-msg=R0913 300 instantiate=False,constant=False,readonly=False):
301 """ 302 Initialize a new Parameter object: store the supplied attributes. 303 304 default: the owning class's value for the attribute 305 represented by this Parameter. 306 307 precedence is a value, usually in the range 0.0 to 1.0, that 308 allows the order of Parameters in a class to be defined (for 309 e.g. in GUI menus). A negative precedence indicates a 310 parameter that should be hidden in e.g. GUI menus. 311 312 default, doc, and precedence default to None. This is to allow 313 inheritance of Parameter slots (attributes) from the owning-class' 314 class hierarchy (see ParameterizedMetaclass). 315 """ 316 self._attrib_name = None 317 self._internal_name = None 318 self.precedence = precedence 319 self.default = default 320 self.doc = doc 321 self.constant = constant or readonly # readonly => constant 322 self.readonly = readonly 323 self._set_instantiate(instantiate)
324 325
326 - def _set_instantiate(self,instantiate):
327 """Constant parameters must be instantiated.""" 328 # CB: instantiate doesn't actually matter for read-only 329 # parameters, since they can't be set even on a class. But 330 # this avoids needless instantiation. 331 if self.readonly: 332 self.instantiate = False 333 else: 334 self.instantiate = instantiate or self.constant # pylint: disable-msg=W0201
335 336
337 - def __get__(self,obj,objtype): # pylint: disable-msg=W0613
338 """ 339 Return the value for this Parameter. 340 341 If called for a Parameterized class, produce that 342 class's value (i.e. this Parameter object's 'default' 343 attribute). 344 345 If called for a Parameterized instance, produce that 346 instance's value, if one has been set - otherwise produce the 347 class's value (default). 348 """ 349 # NB: obj can be None (when __get__ called for a 350 # Parameterized class); objtype is never None 351 352 if not obj: 353 result = self.default 354 else: 355 result = obj.__dict__.get(self._internal_name,self.default) 356 return result 357 358
359 - def __set__(self,obj,val):
360 """ 361 Set the value for this Parameter. 362 363 If called for a Parameterized class, set that class's 364 value (i.e. set this Parameter object's 'default' attribute). 365 366 If called for a Parameterized instance, set the value of 367 this Parameter on that instance (i.e. in the instance's 368 __dict__, under the parameter's internal_name). 369 370 371 If the Parameter's constant attribute is True, only allows 372 the value to be set for a Parameterized class or on 373 uninitialized Parameterized instances. 374 375 If the Parameter's readonly attribute is True, only allows the 376 value to be specified in the Parameter declaration inside the 377 Parameterized source code. A read-only parameter also 378 cannot be set on a Parameterized class. 379 380 Note that until we support some form of read-only 381 object, it is still possible to change the attributes of the 382 object stored in a constant or read-only Parameter (e.g. the 383 left bound of a BoundingBox). 384 """ 385 # NB: obj can be None (when __set__ called for a 386 # Parameterized class) 387 if self.constant or self.readonly: 388 if self.readonly: 389 raise TypeError("Read-only parameter '%s' cannot be modified"%self._attrib_name) 390 elif not obj: 391 self.default = val 392 elif not obj.initialized: 393 obj.__dict__[self._internal_name] = val 394 else: 395 raise TypeError("Constant parameter '%s' cannot be modified"%self._attrib_name) 396 397 else: 398 if not obj: 399 self.default = val 400 else: 401 obj.__dict__[self._internal_name] = val
402 403
404 - def __delete__(self,obj):
405 raise TypeError("Cannot delete '%s': Parameters deletion not allowed."%self._attrib_name)
406 407
408 - def _set_names(self,attrib_name):
409 self._attrib_name = attrib_name 410 self._internal_name = "_%s_param_value"%attrib_name 411 412
413 - def __getstate__(self):
414 """ 415 All Parameters have slots, not a dict, so we have to support 416 pickle and deepcopy ourselves. 417 """ 418 state = {} 419 for slot in get_all_slots(type(self)): 420 state[slot] = getattr(self,slot) 421 422 return state
423
424 - def __setstate__(self,state):
425 # set values of __slots__ (instead of in non-existent __dict__) 426 for (k,v) in state.items(): 427 setattr(self,k,v)
428
429 430 431 -class ParameterizedMetaclass(type):
432 """ 433 The metaclass of Parameterized (and all its descendents). 434 435 The metaclass overrides type.__setattr__ to allow us to set 436 Parameter values on classes without overwriting the attribute 437 descriptor. That is, for a Parameterized class of type X with a 438 Parameter y, the user can type X.y=3, which sets the default value 439 of Parameter y to be 3, rather than overwriting y with the 440 constant value 3 (and thereby losing all other info about that 441 Parameter, such as the doc string, bounds, etc.). 442 443 The __init__ method is used when defining a Parameterized class, 444 usually when the module where that class is located is imported 445 for the first time. That is, the __init__ in this metaclass 446 initializes the *class* object, while the __init__ method defined 447 in each Parameterized class is called for each new instance of 448 that class. 449 450 Additionally, a class can declare itself abstract by having an 451 attribute __abstract set to True. The 'abstract' attribute can be 452 used to find out if a class is abstract or not. 453 """
454 - def __init__(mcs,name,bases,dict_):
455 """ 456 Initialize the class object (not an instance of the class, but 457 the class itself). 458 459 Initializes all the Parameters by looking up appropriate 460 default values; see __param_inheritance(). 461 """ 462 type.__init__(mcs,name,bases,dict_) 463 464 # All objects (with their names) of type Parameter that are 465 # defined in this class 466 parameters = [(name,obj) 467 for (name,obj) in dict_.items() 468 if isinstance(obj,Parameter)] 469 470 for param_name,param in parameters: 471 # parameter has no way to find out the name a 472 # Parameterized class has for it 473 param._set_names(param_name) 474 mcs.__param_inheritance(param_name,param)
475 476
477 - def __is_abstract(mcs):
478 """ 479 Return True if the class has an attribute __abstract set to True. 480 Subclasses will return False unless they themselves have 481 __abstract set to true. This mechanism allows a class to 482 declare itself to be abstract (e.g. to avoid it being offered 483 as an option in a GUI), without the "abstract" property being 484 inherited by its subclasses (at least one of which is 485 presumably not abstract). 486 """ 487 # Can't just do ".__abstract", because that is mangled to 488 # _ParameterizedMetaclass__abstract before running, but 489 # the actual class object will have an attribute 490 # _ClassName__abstract. So, we have to mangle it ourselves at 491 # runtime. 492 try: 493 return getattr(mcs,'_%s__abstract'%mcs.__name__) 494 except AttributeError: 495 return False
496 497 abstract = property(__is_abstract) 498 499 500
501 - def __setattr__(mcs,attribute_name,value):
502 """ 503 Implements 'self.attribute_name=value' in a way that also supports Parameters. 504 505 If there is already a descriptor named attribute_name, and 506 that descriptor is a Parameter, and the new value is *not* a 507 Parameter, then call that Parameter's __set__ method with the 508 specified value. 509 510 In all other cases set the attribute normally (i.e. overwrite 511 the descriptor). If the new value is a Parameter, once it has 512 been set we make sure that the value is inherited from 513 Parameterized superclasses as described in __param_inheritance(). 514 """ 515 # Find out if there's a Parameter called attribute_name as a 516 # class attribute of this class - if not, parameter is None. 517 parameter,owning_class = mcs.get_param_descriptor(attribute_name) 518 519 if parameter and not isinstance(value,Parameter): 520 if owning_class != mcs: 521 type.__setattr__(mcs,attribute_name,copy.copy(parameter)) 522 mcs.__dict__[attribute_name].__set__(None,value) 523 524 else: 525 type.__setattr__(mcs,attribute_name,value) 526 527 if isinstance(value,Parameter): 528 mcs.__param_inheritance(attribute_name,value) 529 else: 530 # the purpose of the warning below is to catch 531 # mistakes ("thinking you are setting a parameter, but 532 # you're not"). There are legitimate times when 533 # something needs be set on the class, and we don't 534 # want to see a warning then. Such attributes should 535 # presumably be prefixed by at least one underscore. 536 # (For instance, python's own pickling mechanism 537 # caches __slotnames__ on the class: 538 # http://mail.python.org/pipermail/python-checkins/2003-February/033517.html.) 539 if not attribute_name.startswith('_'): 540 print ("Warning: Setting non-Parameter class attribute %s.%s = %s " 541 % (mcs.__name__,attribute_name,`value`))
542 543
544 - def __param_inheritance(mcs,param_name,param):
545 """ 546 Look for Parameter values in superclasses of this 547 Parameterized class. 548 549 Ordinarily, when a Python object is instantiated, attributes 550 not given values in the constructor will inherit the value 551 given in the object's class, or in its superclasses. For 552 Parameters owned by Parameterized classes, we have implemented 553 an additional level of default lookup, should this ordinary 554 lookup return only None. 555 556 In such a case, i.e. when no non-None value was found for a 557 Parameter by the usual inheritance mechanisms, we explicitly 558 look for Parameters with the same name in superclasses of this 559 Parameterized class, and use the first such value that we 560 find. 561 562 The goal is to be able to set the default value (or other 563 slots) of a Parameter within a Parameterized class, just as we 564 can set values for non-Parameter objects in Parameterized 565 classes, and have the values inherited through the 566 Parameterized hierarchy as usual. 567 """ 568 # get all relevant slots (i.e. slots defined in all 569 # superclasses of this parameter) 570 slots = {} 571 for p_class in classlist(type(param))[1::]: 572 slots.update(dict.fromkeys(p_class.__slots__)) 573 574 # Some Parameter classes need to know the owning Parameterized 575 # class. Such classes can declare an 'objtype' slot, and the 576 # owning class will be stored in it. 577 if 'objtype' in slots: 578 setattr(param,'objtype',mcs) 579 del slots['objtype'] 580 581 for slot in slots.keys(): 582 superclasses = iter(classlist(mcs)[::-1]) 583 584 # Search up the hierarchy until param.slot (which has to 585 # be obtained using getattr(param,slot)) is not None, or 586 # we run out of classes to search. 587 # 588 # CEBALERT: there's probably a better way than while and 589 # an iterator, but it works. 590 while getattr(param,slot) is None: 591 try: 592 param_super_class = superclasses.next() 593 except StopIteration: 594 break 595 596 new_param = param_super_class.__dict__.get(param_name) 597 if new_param != None and hasattr(new_param,slot): 598 # (slot might not be there because could be a more 599 # general type of Parameter) 600 new_value = getattr(new_param,slot) 601 setattr(param,slot,new_value)
602 603
604 - def get_param_descriptor(mcs,param_name):
605 """ 606 Goes up the class hierarchy (starting from the current class) 607 looking for a Parameter class attribute param_name. As soon as 608 one is found as a class attribute, that Parameter is returned 609 along with the class in which it is declared. 610 """ 611 classes = classlist(mcs) 612 for c in classes[::-1]: 613 attribute = c.__dict__.get(param_name) 614 if isinstance(attribute,Parameter): 615 return attribute,c 616 return None,None
617
618 619 620 621 # JABALERT: Only partially achieved so far -- objects of the same 622 # type and parameter values are treated as different, so anything 623 # for which instantiate == True is reported as being non-default. 624 625 # Whether script_repr should avoid reporting the values of parameters 626 # that are just inheriting their values from the class defaults. 627 script_repr_suppress_defaults=True 628 629 630 631 -def script_repr(val,imports,prefix,settings):
632 """ 633 Variant of repr() designed for generating a runnable script. 634 635 Types that require special handling can use the script_repr_reg 636 dictionary. Using the type as a key, add a function that returns a 637 suitable representation of instances of that type, and adds the 638 required import statement. 639 """ 640 # CB: doc prefix & settings or realize they don't need to be 641 # passed around, etc. 642 if type(val) in script_repr_reg: 643 rep = script_repr_reg[type(val)](val,imports,prefix,settings) 644 645 elif hasattr(val,'script_repr'): 646 rep=val.script_repr(imports=imports,prefix=prefix+" ") 647 648 else: 649 rep=repr(val) 650 651 return rep
652
653 654 # see script_repr() 655 script_repr_reg = {} 656 657 658 # currently only handles list and tuple 659 -def container_script_repr(container,imports,prefix,settings):
660 result=[] 661 for i in container: 662 result.append(script_repr(i,imports,prefix,settings)) 663 664 ## (hack to get container brackets) 665 if isinstance(container,list): 666 d1,d2='[',']' 667 elif isinstance(container,tuple): 668 d1,d2='(',')' 669 else: 670 raise NotImplementedError 671 rep=d1+','.join(result)+d2 672 673 # no imports to add for built-in types 674 675 return rep
676
677 # why I have to type prefix and settings? 678 -def function_script_repr(fn,imports,prefix,settings):
679 name = fn.func_name 680 module = fn.__module__ 681 imports.append('import %s'%module) 682 return module+'.'+name
683 684 685 script_repr_reg[list]=container_script_repr 686 script_repr_reg[tuple]=container_script_repr 687 script_repr_reg[FunctionType]=function_script_repr
688 689 690 # If not None, the value of this Parameter will be called (using '()') 691 # before every call to __db_print, and is expected to evaluate to a 692 # string that is suitable for prefixing messages and warnings (such 693 # as some indicator of the global state). 694 dbprint_prefix=None 695 696 697 -def as_uninitialized(fn):
698 """ 699 Decorator: call fn with the parameterized_instance's 700 initialization flag set to False, then revert the flag. 701 702 (Used to decorate Parameterized methods that must alter 703 a constant Parameter.) 704 """ 705 def override_initialization(parameterized_instance,*args,**kw): 706 original_initialized=parameterized_instance.initialized 707 parameterized_instance.initialized=False 708 fn(parameterized_instance,*args,**kw) 709 parameterized_instance.initialized=original_initialized
710 return override_initialization 711
712 713 714 -class Parameterized(object):
715 """ 716 Base class for named objects that support Parameters and message 717 formatting. 718 719 Automatic object naming: Every Parameterized instance has a name 720 parameter. If the user doesn't designate a name=<str> argument 721 when constructing the object, the object will be given a name 722 consisting of its class name followed by a unique 5-digit number. 723 724 Automatic parameter setting: The Parameterized __init__ method 725 will automatically read the list of keyword parameters. If any 726 keyword matches the name of a Parameter (see Parameter class) 727 defined in the object's class or any of its superclasses, that 728 parameter in the instance will get the value given as a keyword 729 argument. For example: 730 731 class Foo(Parameterized): 732 xx = Parameter(default=1) 733 734 foo = Foo(xx=20) 735 736 in this case foo.xx gets the value 20. 737 738 Message formatting: Each Parameterized instance has several methods 739 for optionally printing output according to the current 'print 740 level', such as SILENT, WARNING, MESSAGE, VERBOSE, or DEBUG. Each 741 successive level allows more messages to be printed. For example, 742 when the level is VERBOSE, all warning, message, and verbose 743 output will be printed. When it is WARNING, only warnings will be 744 printed. When it is SILENT, no output will be printed. 745 746 For each level (except SILENT) there's an associated print method: 747 Parameterized.warning(), .message(), .verbose(), and .debug(). 748 749 Each line printed this way is prepended with the name of the 750 object that printed it. The Parameterized.print_level parameter 751 and the module global variable min_print_level combine to 752 determine what gets printed. For example, if foo is a 753 Parameterized: 754 755 foo.message('The answer is',42) 756 757 is equivalent to: 758 759 if max(foo.print_level,parameterized.min_print_level) >= MESSAGE: 760 print foo.name+':', 'The answer is', 42 761 """ 762 763 __metaclass__ = ParameterizedMetaclass 764 765 ## CEBALERT: should be StringParameter, right? 766 name = Parameter(default=None,constant=True,doc=""" 767 String identifier for this object.""") 768 769 ### JABALERT: Should probably make this an Enumeration instead. 770 print_level = Parameter(default=MESSAGE,precedence=-1) 771 772
773 - def __init__(self,**params):
774 """ 775 Initialize this Parameterized instance. 776 777 The values of parameters can be supplied as keyword arguments 778 to the constructor (using parametername=parametervalue); these 779 values will override the class default values for this one 780 instance. 781 782 If no 'name' parameter is supplied, self.name defaults to the 783 object's class name with a unique number appended to it. 784 """ 785 global object_count 786 787 # Flag that can be tested to see if e.g. constant Parameters 788 # can still be set 789 self.initialized=False 790 791 self.__generate_name() 792 793 self._setup_params(**params) 794 object_count += 1 795 796 self.nopickle = [] # CEBALERT: remove this - we don't use it 797 self.debug('Initialized',self) 798 799 self.initialized=True
800 801 802 # CEBALERT: I think I've noted elsewhere the fact that we 803 # sometimes have a method on Parameter that requires passing the 804 # owning Parameterized instance or class, and other times we have 805 # the method on Parameterized itself. In case I haven't written 806 # that down elsewhere, here it is again. We should clean that up 807 # (at least we should be consistent). 808 809 # cebalert: it's really time to stop and clean up this bothmethod 810 # stuff and repeated code in methods using it. 811 812 # CEBALERT: note there's no state_push method on the class, so 813 # dynamic parameters set on a class can't have state saved. This 814 # is because, to do this, state_push() would need to be a 815 # @bothmethod, but that complicates inheritance in cases where we 816 # already have a state_push() method. I need to decide what to do 817 # about that. (isinstance(g,Parameterized) below is used to exclude classes.)
818 - def state_push(self):
819 """ 820 Save this instance's state. 821 822 For Parameterized instances, this includes the state of 823 dynamically generated values. 824 825 Subclasses that maintain short-term state should additionally 826 save and restore that state using state_push() and 827 state_pop(). 828 829 Generally, this method is used by operations that need to test 830 something without permanently altering the objects' state. 831 """ 832 for pname,p in self.params().items(): 833 g = self.get_value_generator(pname) 834 if hasattr(g,'_Dynamic_last'): 835 g._saved_Dynamic_last.append(g._Dynamic_last) 836 g._saved_Dynamic_time.append(g._Dynamic_time) 837 # CB: not storing the time_fn: assuming that doesn't 838 # change. 839 elif hasattr(g,'state_push') and isinstance(g,Parameterized): 840 g.state_push()
841
842 - def state_pop(self):
843 """ 844 Restore the most recently saved state. 845 846 See state_push() for more details. 847 """ 848 for pname,p in self.params().items(): 849 g = self.get_value_generator(pname) 850 if hasattr(g,'_Dynamic_last'): 851 g._Dynamic_last = g._saved_Dynamic_last.pop() 852 g._Dynamic_time = g._saved_Dynamic_time.pop() 853 elif hasattr(g,'state_pop') and isinstance(g,Parameterized): 854 g.state_pop()
855 856 857 858 859 @bothmethod
860 - def set_dynamic_time_fn(self_or_cls,time_fn,sublistattr=None):
861 """ 862 Set time_fn for all Dynamic Parameters of this class or 863 instance object that are currently being dynamically 864 generated. 865 866 Additionally, sets _Dynamic_time_fn=time_fn on this class or 867 instance object, so that any future changes to Dynamic 868 Parmeters can inherit time_fn (e.g. if a Number is changed 869 from a float to a number generator, the number generator will 870 inherit time_fn). 871 872 If specified, sublistattr is the name of an attribute of this 873 class or instance that contains an iterable collection of 874 subobjects on which set_dynamic_time_fn should be called. If 875 the attribute sublistattr is present on any of the subobjects, 876 set_dynamic_time_fn() will be called for those, too. 877 """ 878 self_or_cls._Dynamic_time_fn = time_fn 879 880 if isinstance(self_or_cls,type): 881 a = (None,self_or_cls) 882 else: 883 a = (self_or_cls,) 884 885 for n,p in self_or_cls.params().items(): 886 if hasattr(p,'_value_is_dynamic'): 887 if p._value_is_dynamic(*a): 888 g = self_or_cls.get_value_generator(n) 889 g._Dynamic_time_fn = time_fn 890 891 if sublistattr: 892 try: 893 sublist = getattr(self_or_cls,sublistattr) 894 except AttributeError: 895 sublist = [] 896 897 for obj in sublist: 898 obj.set_dynamic_time_fn(time_fn,sublistattr)
899 900 901 @as_uninitialized
902 - def _set_name(self,name):
903 self.name=name
904 905 906 @as_uninitialized
907 - def __generate_name(self):
908 """ 909 Set name to a gensym formed from the object's type name and 910 the object_count. 911 """ 912 self._set_name('%s%05d' % (self.__class__.__name__ ,object_count))
913 914 # CB: __repr__ is called often; methods it uses should not be too slow
915 - def __repr__(self):
916 """ 917 Provide a nearly valid Python representation that could be used to recreate 918 the item with its parameters, if executed in the appropriate environment. 919 920 Returns 'classname(parameter1=x,parameter2=y,...)', listing 921 all the parameters of this object. 922 """ 923 settings = ['%s=%s' % (name,repr(val)) 924 for name,val in self.get_param_values()] 925 return self.__class__.__name__ + "(" + ", ".join(settings) + ")"
926 927 928 929
930 - def script_repr(self,imports=[],prefix=" "):
931 """ 932 Variant of __repr__ designed for generating a runnable script. 933 """ 934 # Suppresses automatically generated names and print_levels. 935 settings=[] 936 for name,val in self.get_param_values(onlychanged=script_repr_suppress_defaults): 937 938 if name == 'name' and re.match('^'+self.__class__.__name__+'[0-9]+$',str(val)): 939 rep=None 940 elif name == 'print_level': 941 rep=None 942 else: 943 rep=script_repr(val,imports,prefix,settings) 944 945 if rep is not None: 946 settings.append('%s=%s' % (name,rep)) 947 948 949 # Generate import statement 950 cls = self.__class__.__name__ 951 mod = self.__module__ 952 953 bits = mod.split('.') 954 955 imports.append("import %s"%mod) 956 imports.append("import %s"%bits[0]) 957 958 # CB: Doesn't give a nice repr, but I don't see what to do 959 # otherwise that will work in all cases. Also I haven't 960 # updated this code in other places (e.g. simulation). 961 return mod+'.'+self.__class__.__name__ + "(" + (",\n"+prefix).join(settings) + ")"
962 963
964 - def __str__(self):
965 """Return a short representation of the name and class of this object.""" 966 return "<%s %s>" % (self.__class__.__name__,self.name)
967 968 969 # CB: I'm not sure why we allow multiple args here rather than just one 970 # message (given that the caller can easily use string substitution with 971 # "%s"%var etc). I think this makes things more complicated, but I might 972 # be missing something.
973 - def __db_print(self,level=NORMAL,*args):
974 """ 975 Print each of the given args iff print_level or 976 self.db_print_level is greater than or equal to the given 977 level. 978 979 Any of args may be functions, in which case they will be 980 called. This allows delayed execution, preventing 981 time-consuming code from being called unless the print level 982 requires it. 983 """ 984 if level <= max(min_print_level,self.print_level): 985 986 # call any args that are functions 987 args = list(args) 988 for a in args: 989 if isinstance(a,FunctionType): args[args.index(a)]=a() 990 991 s = ' '.join([str(x) for x in args]) 992 993 if dbprint_prefix and callable(dbprint_prefix): 994 prefix=dbprint_prefix() # pylint: disable-msg=E1102 995 else: 996 prefix="" 997 998 print "%s%s: %s" % (prefix,self.name,s) 999 1000 sys.stdout.flush()
1001 1002
1003 - def warning(self,*args):
1004 """ 1005 Print the arguments as a warning, unless module variable 1006 warnings_as_exceptions is True, then raise an Exception 1007 containing the arguments. 1008 """ 1009 if not warnings_as_exceptions: 1010 self.__db_print(WARNING,"Warning:",*args) 1011 else: 1012 raise Exception, ' '.join(["Warning:",]+[str(x) for x in args])
1013 1014
1015 - def message(self,*args):
1016 """Print the arguments as a message.""" 1017 self.__db_print(MESSAGE,*args)
1018
1019 - def verbose(self,*args):
1020 """Print the arguments as a verbose message.""" 1021 self.__db_print(VERBOSE,*args)
1022
1023 - def debug(self,*args):
1024 """Print the arguments as a debugging statement.""" 1025 self.__db_print(DEBUG,*args)
1026 1027 # CEBALERT: this is a bit ugly
1028 - def _instantiate_param(self,param_obj,dict_=None,key=None):
1029 # deepcopy param_obj.default into self.__dict__ (or dict_ if supplied) 1030 # under the parameter's _internal_name (or key if supplied) 1031 dict_ = dict_ or self.__dict__ 1032 key = key or param_obj._internal_name 1033 new_object = copy.deepcopy(param_obj.default) 1034 dict_[key]=new_object 1035 1036 if isinstance(new_object,Parameterized): 1037 global object_count 1038 object_count+=1 1039 # CB: writes over name given to the original object; 1040 # should it instead keep the same name? 1041 new_object.__generate_name()
1042 1043 1044 @as_uninitialized
1045 - def _setup_params(self,**params):
1046 """ 1047 Initialize default and keyword parameter values. 1048 1049 First, ensures that all Parameters with 'instantiate=True' 1050 (typically used for mutable Parameters) are copied directly 1051 into each object, to ensure that there is an independent copy 1052 (to avoid suprising aliasing errors). Then sets each of the 1053 keyword arguments, warning when any of them are not defined as 1054 parameters. 1055 1056 Constant Parameters can be set during calls to this method. 1057 """ 1058 # Deepcopy all 'instantiate=True' parameters 1059 for class_ in classlist(type(self)): 1060 for (k,v) in class_.__dict__.items(): 1061 # (avoid replacing name with the default of None) 1062 if isinstance(v,Parameter) and v.instantiate and k!="name": 1063 self._instantiate_param(v) 1064 1065 for name,val in params.items(): 1066 desc = self.__class__.get_param_descriptor(name)[0] # pylint: disable-msg=E1101 1067 if desc: 1068 self.debug("Setting param %s=%s"% (name, val)) 1069 else: 1070 self.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters" % (name, val)) 1071 # i.e. if not desc it's setting an attribute in __dict__, not a Parameter 1072 setattr(self,name,val)
1073 1074
1075 - def _check_params(self,params):
1076 """ 1077 Print a warning if params contains something that is 1078 not a Parameter of this object. 1079 1080 Typically invoked by a __call__() method that accepts keyword 1081 arguments for parameter setting. 1082 """ 1083 self_params = self.params() 1084 for item in params: 1085 if item not in self_params: 1086 self.warning("'%s' will be ignored (not a Parameter)."%item)
1087 1088
1089 - def get_param_values(self,onlychanged=False):
1090 """Return a list of name,value pairs for all Parameters of this object""" 1091 vals = [] 1092 for name,val in self.params().items(): 1093 value = self.get_value_generator(name) 1094 if (not onlychanged or value != val.default): 1095 vals.append((name,value)) 1096 1097 vals.sort(key=itemgetter(0)) 1098 return vals
1099 1100 # CB: is there a more obvious solution than making these 1101 # 'bothmethod's? 1102 # An alternative would be to lose these methods completely and 1103 # make users do things via the Parameter object directly. 1104 1105 # CB: is there a performance hit for doing this decoration? It 1106 # would show up in lissom_oo_or because separated composite uses 1107 # this method. 1108 @bothmethod
1109 - def force_new_dynamic_value(cls_or_slf,name): # pylint: disable-msg=E0213
1110 """ 1111 Force a new value to be generated for the dynamic attribute 1112 name, and return it. 1113 1114 If name is not dynamic, its current value is returned 1115 (i.e. equivalent to getattr(name). 1116 """ 1117 param_obj = cls_or_slf.params().get(name) 1118 1119 if not param_obj: 1120 return getattr(cls_or_slf,name) 1121 1122 cls,slf=None,None 1123 if isinstance(cls_or_slf,type): 1124 cls = cls_or_slf 1125 else: 1126 slf = cls_or_slf 1127 1128 if not hasattr(param_obj,'_force'): 1129 return param_obj.__get__(slf,cls) 1130 else: 1131 return param_obj._force(slf,cls)
1132 1133 1134 @bothmethod
1135 - def get_value_generator(cls_or_slf,name): # pylint: disable-msg=E0213
1136 """ 1137 Return the value or value-generating object of the named 1138 attribute. 1139 1140 For most parameters, this is simply the parameter's value 1141 (i.e. the same as getattr()), but Dynamic parameters have 1142 their value-generating object returned. 1143 """ 1144 param_obj = cls_or_slf.params().get(name) 1145 1146 if not param_obj: 1147 value = getattr(cls_or_slf,name) 1148 1149 # CompositeParameter detected by being a Parameter and having 'attribs' 1150 elif hasattr(param_obj,'attribs'): 1151 value = [cls_or_slf.get_value_generator(a) for a in param_obj.attribs] 1152 1153 # not a Dynamic Parameter 1154 elif not hasattr(param_obj,'_value_is_dynamic'): 1155 value = getattr(cls_or_slf,name) 1156 1157 # Dynamic Parameter... 1158 else: 1159 internal_name = "_%s_param_value"%name 1160 if hasattr(cls_or_slf,internal_name): 1161 # dealing with object and it's been set on this object 1162 value = getattr(cls_or_slf,internal_name) 1163 else: 1164 # dealing with class or isn't set on the object 1165 value = param_obj.default 1166 1167 return value 1168 1169 1170 @bothmethod
1171 - def inspect_value(cls_or_slf,name): # pylint: disable-msg=E0213
1172 """ 1173 Return the current value of the named attribute without modifying it. 1174 1175 Same as getattr() except for Dynamic parameters, which have their 1176 last generated value returned. 1177 """ 1178 param_obj = cls_or_slf.params().get(name) 1179 1180 if not param_obj: 1181 value = getattr(cls_or_slf,name) 1182 elif hasattr(param_obj,'attribs'): 1183 value = [cls_or_slf.inspect_value(a) for a in param_obj.attribs] 1184 elif not hasattr(param_obj,'_inspect'): 1185 value = getattr(cls_or_slf,name) 1186 else: 1187 if isinstance(cls_or_slf,type): 1188 value = param_obj._inspect(None,cls_or_slf) 1189 else: 1190 value = param_obj._inspect(cls_or_slf,None) 1191 1192 return value 1193 1194 1195
1196 - def print_param_values(self):
1197 """Print the values of all this object's Parameters.""" 1198 for name,val in self.get_param_values(): 1199 print '%s.%s = %s' % (self.name,name,val)
1200 1201
1202 - def __getstate__(self):
1203 """ 1204 Save the object's state: return a dictionary that is a shallow 1205 copy of the object's __dict__ and that also includes the 1206 object's __slots__ (if it has any). 1207 """ 1208 # remind me, why is it a copy? why not just state.update(self.__dict__)? 1209 state = self.__dict__.copy() 1210 1211 for slot in get_all_slots(type(self)): 1212 state[slot] = getattr(self,slot) 1213 1214 # Note that Parameterized object pickling assumes that 1215 # attributes to be saved are only in __dict__ or __slots__ 1216 # (the standard Python places to store attributes, so that's a 1217 # reasonable assumption). (Additionally, class attributes that 1218 # are Parameters are also handled, even when they haven't been 1219 # instantiated - see PickleableClassAttributes.) 1220 1221 return state
1222 1223
1224 - def __setstate__(self,state):
1225 """ 1226 Restore objects from the state dictionary to this object. 1227 1228 During this process the object is considered uninitialized. 1229 """ 1230 self.initialized=False 1231 for name,value in state.items(): 1232 setattr(self,name,value) 1233 self.initialized=True
1234 1235 1236 @classmethod
1237 - def params(cls):
1238 """ 1239 Return the Parameters of this class as the 1240 dictionary {name: parameter_object} 1241 1242 Includes Parameters from this class and its 1243 superclasses. 1244 """ 1245 # CB: we cache the parameters because this method is called 1246 # often, and new parameters cannot be added (or deleted) 1247 try: 1248 return getattr(cls,'_%s__params'%cls.__name__) 1249 except AttributeError: 1250 paramdict = {} 1251 for class_ in classlist(cls): 1252 for name,val in class_.__dict__.items(): 1253 if isinstance(val,Parameter): 1254 paramdict[name] = val 1255 1256 # We only want the cache to be visible to the cls on which 1257 # params() is called, so we mangle the name ourselves at 1258 # runtime (if we were to mangle it now, it would be 1259 # _Parameterized.__params for all classes). 1260 setattr(cls,'_%s__params'%cls.__name__,paramdict) 1261 return paramdict
1262 1263 1264 1265 @classmethod
1266 - def print_param_defaults(cls):
1267 """Print the default values of all cls's Parameters.""" 1268 for key,val in cls.__dict__.items(): 1269 if isinstance(val,Parameter): 1270 print cls.__name__+'.'+key, '=', repr(val.default)
1271 1272
1273 - def defaults(self):
1274 """ 1275 Return {parameter_name:parameter.default} for all non-constant 1276 Parameters. 1277 1278 Note that a Parameter for which instantiate==True has its default 1279 instantiated. 1280 """ 1281 d = {} 1282 for param_name,param in self.params().items(): 1283 if param.constant: 1284 pass 1285 elif param.instantiate: 1286 self._instantiate_param(param,dict_=d,key=param_name) 1287 else: 1288 d[param_name]=param.default 1289 return d
1290 1305
1306 1307 1308 1309 1310 1311 1312 1313 # Support for changing parameter names 1314 _param_name_changes = {} 1315 # e.g. you change topo.pattern.basic.Gaussian.aspect_ratio to aspect_ration 1316 # _param_name_changes['topo.pattern.basic.Gaussian']={'aspect_ratio':'aspect_ration'} 1317 # 1318 # (not yet finished - do we need to add information about version numbers?) 1319 1320 # CEBALERT: Can't this stuff move to the ParameterizedMetaclass? 1321 import __main__ 1322 -class PicklableClassAttributes(object):
1323 """ 1324 Supports pickling of Parameterized class attributes for a given module. 1325 1326 When requested to be pickled, stores a module's PO classes' attributes, 1327 and any given startup_commands. On unpickling, executes the startup 1328 commands and sets the class attributes. 1329 """ 1330 # pylint: disable-msg=R0903 1331 1332 # CB: might have mixed up module and package in the docs.
1333 - def __init__(self,module,exclusions=(),startup_commands=()):
1334 """ 1335 module: a module object, such as topo 1336 1337 Any submodules listed by name in exclusions will not have their 1338 classes' attributes saved. 1339 """ 1340 self.module=module 1341 self.exclude=exclusions 1342 self.startup_commands=startup_commands
1343
1344 - def __getstate__(self):
1345 """ 1346 Return a dictionary of self.module's PO classes' attributes, plus 1347 self.startup_commands. 1348 """ 1349 # warn that classes & functions defined in __main__ won't unpickle 1350 import types 1351 for k,v in __main__.__dict__.items(): 1352 # there's classes and functions...what else? 1353 if isinstance(v,type) or isinstance(v,types.FunctionType): 1354 if v.__module__ == "__main__": 1355 Parameterized().warning("%s (type %s) has source in __main__; it will only be found on unpickling if the class is explicitly defined (e.g. by running the same script first) before unpickling."%(k,type(v))) 1356 1357 1358 class_attributes = {} 1359 self.get_PO_class_attributes(self.module,class_attributes,[],exclude=self.exclude) 1360 1361 # CB: we don't want to pickle anything about this object except what 1362 # we want to have executed on unpickling (this object's not going to be hanging around). 1363 return {'class_attributes':class_attributes, 1364 'startup_commands':self.startup_commands}
1365 1366
1367 - def __setstate__(self,state):
1368 """ 1369 Execute the startup commands and set class attributes. 1370 """ 1371 self.startup_commands = state['startup_commands'] 1372 1373 for cmd in self.startup_commands: 1374 exec cmd in __main__.__dict__ 1375 1376 for class_name,state in state['class_attributes'].items(): 1377 # from e.g. "topo.base.parameter.Parameter", we want "topo.base.parameter" 1378 module_path = class_name[0:class_name.rindex('.')] 1379 1380 ### ? globals()[module.split('.')[0]] = __import__(module) 1381 # exec 'import '+module_path in __main__.__dict__ 1382 1383 exec 'import '+module_path in __main__.__dict__ 1384 1385 try: 1386 class_ = eval(class_name,__main__.__dict__) 1387 except: 1388 Parameterized().warning("Could not find class %s to restore its parameter values (class might have been removed or renamed)."%class_name) 1389 break 1390 1391 # now restore class Parameter values 1392 for p_name,p in state.items(): 1393 1394 if class_name in _param_name_changes: 1395 if p_name in _param_name_changes[class_name]: 1396 new_p_name = _param_name_changes[class_name][p_name] 1397 Parameterized().message("%s's %s parameter has been renamed to %s."%(class_name,p_name,new_p_name)) 1398 p_name = new_p_name 1399 1400 try: 1401 setattr(class_,p_name,p) 1402 except: 1403 Parameterized().warning('Problem restoring parameter %s=%s for class %s (Parameter object representing %s may have changed since the snapshot was created).' % (p_name,repr(p),class_name,p_name))
1404 1405 1406 1407 # CB: I guess this could be simplified
1408 - def get_PO_class_attributes(self,module,class_attributes,processed_modules,exclude=()):
1409 """ 1410 Recursively search module and get attributes of Parameterized classes within it. 1411 1412 class_attributes is a dictionary {module.path.and.Classname: state}, where state 1413 is the dictionary {attribute: value}. 1414 1415 Something is considered a module for our purposes if inspect says it's a module, 1416 and it defines __all__. We only search through modules listed in __all__. 1417 1418 Keeps a list of processed modules to avoid looking at the same one 1419 more than once (since e.g. __main__ contains __main__ contains 1420 __main__...) 1421 1422 Modules can be specifically excluded if listed in exclude. 1423 """ 1424 dict_ = module.__dict__ 1425 for (k,v) in dict_.items(): 1426 if '__all__' in dict_ and inspect.ismodule(v) and k not in exclude: 1427 if k in dict_['__all__'] and v not in processed_modules: 1428 self.get_PO_class_attributes(v,class_attributes,processed_modules,exclude) 1429 processed_modules.append(v) 1430 1431 else: 1432 if isinstance(v,type) and issubclass(v,Parameterized): 1433 1434 # Note: we take the class name as v.__name__, not 1435 # k, because k might be just a label for the true 1436 # class. For example, if someone imports a class 1437 # using 'as', the name in the local namespace 1438 # could be different from the name when the class 1439 # was defined. It is correct to set the 1440 # attributes on the true class. 1441 full_class_path = v.__module__+'.'+v.__name__ 1442 class_attributes[full_class_path] = {} 1443 # POs always have __dict__, never slots 1444 for (name,obj) in v.__dict__.items(): 1445 if isinstance(obj,Parameter): 1446 class_attributes[full_class_path][name] = obj
1447
1448 1449 1450 # CEBALERT: we should incorporate overridden._check_params() here 1451 # rather than making __call__ methods do it 1452 # (that is, if we want to keep _check_params at all). 1453 -class ParamOverrides(dict):
1454 """ 1455 A dictionary that returns the attribute of an object if that attribute is not 1456 present in itself. 1457 1458 Used to override the parameters of an object. 1459 """
1460 - def __init__(self,overridden,dict_):
1461 # we'd like __init__ to be fast because it's going to be 1462 # called a lot. What's the fastest way to move the existing 1463 # params dictionary into this one? Would 1464 # def __init__(self,overridden,**kw): 1465 # ... 1466 # dict.__init__(self,**kw) 1467 # be faster/easier to use? 1468 self.overridden = overridden 1469 dict.__init__(self,dict_)
1470
1471 - def __missing__(self,attr):
1472 """Return the attribute from overridden object.""" 1473 return getattr(self.overridden,attr)
1474
1475 - def __repr__(self):
1476 """As dict.__repr__, but indicate the overridden object.""" 1477 # something like... 1478 return dict.__repr__(self)+" overriding params from %s"%repr(self.overridden)
1479 1480 1481 1482 ## CB: would allow all instance methods to pickle, but we use cPickle 1483 ## rather than pickle (for speed), so we can't use this. 1484 ## 1485 ## # from http://code.activestate.com/recipes/572213/ 1486 ## import pickle,new 1487 ## def save_instancemethod(self, obj): 1488 ## """ Save an instancemethod object """ 1489 ## # Instancemethods are re-created each time they are accessed so this will not be memoized 1490 ## args = (obj.im_func, obj.im_self, obj.im_class) 1491 ## self.save_reduce(new.instancemethod, args) 1492 ## pickle.Pickler.dispatch[new.instancemethod] = save_instancemethod 1493