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
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
28
29 warnings_as_exceptions = False
30
31 object_count = 0
32 warning_count = 0
33
34
35 import inspect
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
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
66 """
67 Return a list of slot names for slots defined in this class and
68 its superclasses.
69 """
70
71
72
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
84
85
86
87
88
89 from functools import partial
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
102
105
106
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
134
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 __slots__ = ['_attrib_name','_internal_name','default','doc',
294 'precedence','instantiate','constant','readonly',
295 'pickle_default_value']
296
297
298
299
300
301
302 - def __init__(self,default=None,doc=None,precedence=None,
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
329 self.readonly = readonly
330 self._set_instantiate(instantiate)
331 self.pickle_default_value = pickle_default_value
332
333
343
344
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
358
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
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
394
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
413 raise TypeError("Cannot delete '%s': Parameters deletion not allowed."%self._attrib_name)
414
415
417 self._attrib_name = attrib_name
418 self._internal_name = "_%s_param_value"%attrib_name
419
420
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
436
437
438
439 -class String(Parameter):
440 __slots__ = ['allow_None']
441
442 - def __init__(self,default="",allow_None=False,**params):
446
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
669
670
671
672
673
674
675
676
677
678
679 script_repr_suppress_defaults=True
680
681
682
683
684
685
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
696
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
713 script_repr_reg = {}
718 result=[]
719 for i in container:
720 result.append(script_repr(i,imports,prefix,settings))
721
722
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
732
733 return rep
734
737 name = fn.func_name
738 module = fn.__module__
739 imports.append('import %s'%module)
740 return module+'.'+name
741
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
754
755
756
757 dbprint_prefix=None
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
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
832 print_level = Parameter(default=MESSAGE,precedence=-1)
833
834
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
850
851 self.initialized=False
852
853 self.__generate_name()
854
855 self._setup_params(**params)
856 object_count += 1
857
858 self.nopickle = []
859 self.debug('Initialized',self)
860
861 self.initialized=True
862
863
864 @bothmethod
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
878
879
880
881
882
883
884
885 type.__setattr__(cls,param_name,param_obj)
886 cls.__metaclass__._initialize_parameter(cls,param_name,param_obj)
887
888 try:
889 delattr(cls,'_%s__params'%cls.__name__)
890 except AttributeError:
891 pass
892
893
894 @bothmethod
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
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
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
944
945 elif hasattr(g,'state_push') and isinstance(g,Parameterized):
946 g.state_push()
947
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
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
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
1022
1023
1024 @as_uninitialized
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
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
1049 """
1050 Variant of __repr__ designed for generating a runnable script.
1051 """
1052
1053 settings=[]
1054 for