1 """
2 Parameters are a kind of class attribute allowing special behavior,
3 including dynamically generated parameter values, documentation
4 strings, constant and read-only parameters, and type or range checking
5 at assignment time.
6
7 Potentially useful for any large Python program that needs
8 user-modifiable object attributes; see the parameterized.Parameter and
9 parameterized.Parameterized classes for more information.
10
11
12 This file contains subclasses of Parameter, implementing specific
13 parameter types (e.g. Number).
14
15 $Id: __init__.py 9414 2008-10-08 10:08:23Z ceball $
16 """
17 __version__='$Revision: 9414 $'
18
19
20
21
22 import types
23
24 from parameterized import Parameterized, Parameter, descendents
25
27 """
28 Enumeration is a Parameter with a list of available values.
29
30 An Enumeration's value is always one from its list of available values.
31 """
32 __slots__ = ['available']
33
34 - def __init__(self, default=None, available=[], **params):
46
48 """
49 Set to the given value, raising an exception if that value is
50 not in the list of available ones.
51 """
52 self.__check_value(val)
53 super(Enumeration,self).__set__(obj,val)
54
56 """
57 Raises an error if the given value isn't in the list of available ones.
58 """
59 if val not in self.available:
60 raise ValueError("%s not in %s's list of available values."%(val,self._attrib_name))
61
62
64 """
65 A helper function that produces an actual parameter from a stored
66 object: if the object is callable, call it, otherwise return the
67 object.
68 """
69 if callable(value_obj):
70 return value_obj()
71 else:
72 return value_obj
73
74
76 """
77 Parameter whose value can be generated dynamically by a callable
78 object.
79
80 If a Parameter is declared as Dynamic, it can be set a callable
81 object (such as a function or callable class), and getting the
82 parameter's value will call that callable.
83
84 Note that at present, the callable object must allow attributes
85 to be set on itself.
86
87 [Python 2.4 limitation: the callable object must be an instance of a
88 callable class, rather than a named function or a lambda function,
89 otherwise the object will not be picklable or deepcopyable.]
90
91
92 Setting Dynamic.time_fn allows the production of dynamic values to
93 be controlled: a new value will be produced only if the current
94 value of time_fn is greater than what it was last time the
95 parameter value was requested.
96
97 If time_fn is set to None, a new value is always produced.
98
99 If Dynamic.time_fn is set to something other than None, it must,
100 when called, produce a number.
101 """
102
103
104
105
106
107
108 time_fn = None
109
110
111
112
113
114
125
126
128 """
129 Add 'last time' and 'last value' attributes to the generator.
130 """
131
132 if hasattr(obj,"_Dynamic_time_fn"):
133 gen._Dynamic_time_fn = obj._Dynamic_time_fn
134
135 gen._Dynamic_last = None
136
137
138 gen._Dynamic_time = -1
139
140 gen._saved_Dynamic_last = []
141 gen._saved_Dynamic_time = []
142
143
145 """
146 Call the superclass's __get__; if the result is not dynamic
147 return that result, otherwise ask that result to produce a
148 value and return it.
149 """
150 gen = super(Dynamic,self).__get__(obj,objtype)
151
152 if not hasattr(gen,'_Dynamic_last'):
153 return gen
154 else:
155 return self._produce_value(gen)
156
157
159 """
160 Call the superclass's set and keep this parameter's
161 instantiate value up to date (dynamic parameters
162 must be instantiated).
163
164 If val is dynamic, initialize it as a generator.
165 """
166 super(Dynamic,self).__set__(obj,val)
167
168 dynamic = callable(val)
169 if dynamic: self._initialize_generator(val,obj)
170 if not obj: self._set_instantiate(dynamic)
171
172
174 """
175 Return a value from gen.
176
177 If there is no time_fn, then a new value will be returned
178 (i.e. gen will be asked to produce a new value).
179
180 If force is True, or the value of time_fn() is greater than
181 what it was was last time produce_value was called, a
182 new value will be produced and returned. Otherwise,
183 the last value gen produced will be returned.
184 """
185 if hasattr(gen,"_Dynamic_time_fn"):
186 time_fn = gen._Dynamic_time_fn
187 else:
188 time_fn = self.time_fn
189
190 if time_fn is None:
191 value = produce_value(gen)
192 gen._Dynamic_last = value
193 else:
194
195 time = time_fn()
196
197 if force or time>gen._Dynamic_time:
198 value = produce_value(gen)
199 gen._Dynamic_last = value
200 gen._Dynamic_time = time
201 else:
202 value = gen._Dynamic_last
203
204 return value
205
206
208 """
209 Return True if the parameter is actually dynamic (i.e. the
210 value is being generated).
211 """
212 return hasattr(super(Dynamic,self).__get__(obj,objtype),'_Dynamic_last')
213
214
216 """Return the last generated value for this parameter."""
217 gen=super(Dynamic,self).__get__(obj,objtype)
218
219 if hasattr(gen,'_Dynamic_last'):
220 return gen._Dynamic_last
221 else:
222 return gen
223
224
225 - def _force(self,obj,objtype=None):
233
234
235
236 import operator
237 is_number = operator.isNumberType
238
239
240
242 """
243 Number is a numeric parameter. Numbers have a default value and
244 optional bounds. There are two types of bounds: ``bounds`` and
245 ``softbounds``. ``bounds`` are hard bounds: the parameter must
246 have a value within the specified range. The default bounds are
247 (None,None), meaning there are actually no hard bounds. One or
248 both bounds can be set by specifying a value
249 (e.g. bounds=(None,10) means there is no lower bound, and an upper
250 bound of 10).
251
252 Number is also a type of Dynamic parameter, so its value
253 can be set to a callable to get a dynamically generated
254 number (see Dynamic).
255
256 When not being dynamically generated, bounds are checked when a
257 Number is created or set. Using a default value outside the hard
258 bounds, or one that is not numeric, results in an exception. When
259 being dynamically generated, bounds are checked when a the value
260 of a Number is requested. A generated value that is not numeric,
261 or is outside the hard bounds, results in an exception.
262
263 As a special case, if allow_None=True (which is true by default if
264 the parameter has a default of None when declared) then a value
265 of None is also allowed.
266
267 A separate function set_in_bounds() is provided that will
268 silently crop the given value into the legal range, for use
269 in, for instance, a GUI.
270
271 ``softbounds`` are present to indicate the typical range of
272 the parameter, but are not enforced. Setting the soft bounds
273 allows, for instance, a GUI to know what values to display on
274 sliders for the Number.
275
276 Example of creating a Number::
277 AB = Number(default=0.5, bounds=(None,10), softbounds=(0,1), doc='Distance from A to B.')
278 """
279 __slots__ = ['bounds','_softbounds','allow_None']
280
281 - def __init__(self,default=0.0,bounds=None,softbounds=None,allow_None=False,**params):
293
294
303
304
306 """
307 Set to the given value, raising an exception if out of bounds.
308 """
309 if not callable(val): self._check_value(val)
310 super(Number,self).__set__(obj,val)
311
312
314 """
315 Set to the given value, but cropped to be within the legal bounds.
316 All objects are accepted, and no exceptions will be raised. See
317 crop_to_bounds for details on how cropping is done.
318 """
319 if not callable(val):
320 bounded_val = self.crop_to_bounds(val)
321 else:
322 bounded_val = val
323
324 super(Number,self).__set__(obj,bounded_val)
325
326
328 """
329 Return the given value cropped to be within the hard bounds
330 for this parameter.
331
332 If a numeric value is passed in, check it is within the hard
333 bounds. If it is larger than the high bound, return the high
334 bound. If it's smaller, return the low bound. In either case, the
335 returned value could be None. If a non-numeric value is passed
336 in, set to be the default value (which could be None). In no
337 case is an exception raised; all values are accepted.
338 """
339
340
341
342 if (is_number(val)):
343 if self.bounds==None:
344 return val
345 vmin, vmax = self.bounds
346 if vmin != None:
347 if val < vmin:
348 return vmin
349
350 if vmax != None:
351 if val > vmax:
352 return vmax
353
354 elif self.allow_None and val==None:
355 return val
356
357 else:
358
359 return self.default
360
361 return val
362
363
365 """
366 Checks that the value is numeric and that it is within the hard
367 bounds; if not, an exception is raised.
368 """
369 if self.allow_None and val==None:
370 return
371
372 if not (is_number(val)):
373 raise ValueError("Parameter '%s' only takes numeric values"%(self._attrib_name))
374 if self.bounds!=None:
375 vmin,vmax = self.bounds
376 if vmin != None and vmax != None:
377 if not (vmin <= val <= vmax):
378 raise ValueError("Parameter '%s' must be between %s and %s (inclusive)"%(self._attrib_name,vmin,vmax))
379 elif vmin != None:
380 if not vmin <= val:
381 raise ValueError("Parameter '%s' must be at least %s"%(self._attrib_name,vmin))
382 elif vmax != None:
383 if not val <=vmax:
384 raise ValueError("Parameter '%s' must be at most %s"%(self._attrib_name,vmax))
385
386
388 """
389 For each soft bound (upper and lower), if there is a defined bound (not equal to None)
390 then it is returned, otherwise it defaults to the hard bound. The hard bound could still be None.
391 """
392 if self.bounds==None:
393 hl,hu=(None,None)
394 else:
395 hl,hu=self.bounds
396
397 if self._softbounds==None:
398 sl,su=(None,None)
399 else:
400 sl,su=self._softbounds
401
402
403 if (sl==None): l = hl
404 else: l = sl
405
406 if (su==None): u = hu
407 else: u = su
408
409 return (l,u)
410
411
412
419
420
422
423 - def __init__(self,default=1.0,softbounds=None,**params):
425
426
427
429 __slots__ = ['bounds','allow_None']
430
431
432 - def __init__(self,default=False,bounds=(0,1),allow_None=False,**params):
436
438 if self.allow_None:
439 if not isinstance(val,bool) and val is not None:
440 raise ValueError("Boolean '%s' only takes a Boolean value or None."
441 %self._attrib_name)
442
443 if val is not True and val is not False and val is not None:
444 raise ValueError("Boolean '%s' must be True, False, or None."%self._attrib_name)
445 else:
446 if not isinstance(val,bool):
447 raise ValueError("Boolean '%s' only takes a Boolean value."%self._attrib_name)
448
449 if val is not True and val is not False:
450 raise ValueError("Boolean '%s' must be True or False."%self._attrib_name)
451
452 super(Boolean,self).__set__(obj,val)
453
454
456
457 - def __init__(self,default="",**params):
460
462 if not isinstance(val,str):
463 raise ValueError("String '%s' only takes a string value."%self._attrib_name)
464
465 super(String,self).__set__(obj,val)
466
467
469 __slots__ = ['length']
470
471 - def __init__(self,default=(0,0),length=None,**params):
484
486 if not isinstance(val,tuple):
487 raise ValueError("NumericTuple '%s' only takes a tuple value."%self._attrib_name)
488
489 if not len(val)==self.length:
490 raise ValueError("%s: tuple is not of the correct length (%d instead of %d)." %
491 (self._attrib_name,len(val),self.length))
492 for n in val:
493 if not is_number(n):
494 raise ValueError("%s: tuple element is not numeric: %s." % (self._attrib_name,str(n)))
495
499
500
502
503 - def __init__(self,default=(0.0,0.0),**params):
505
506
508 """
509 Parameter holding a value that is a callable object, such as a function.
510
511 A keyword argument instantiate=True should be provided when a
512 function object is used that might have state. On the other hand,
513 regular standalone functions cannot be deepcopied as of Python
514 2.4, so instantiate must be False for those values.
515 """
516 - def __init__(self,default=None,**params):
518
523
524
526 """
527 Wrap a callable object in an InstanceMethodWrapper, if necessary.
528
529 If c is an instancemethod, then wrap it and return the wrapper,
530 otherwise return c.
531 """
532 if isinstance(c,types.MethodType):
533 return InstanceMethodWrapper(c)
534 else:
535 return c
536
537
538
540 """
541 Return a dictionary containing all subclasses of the specified
542 parentclass, including the parentclass. Only classes that are
543 defined in scripts that have been run or modules that have been
544 imported are included, so the caller will usually first do ``from
545 package import *``.
546
547 If the class has an attribute ``abstract``, and it is True, the
548 class will not be included.
549 """
550 return dict([(c.__name__,c) for c in descendents(parentclass)
551 if not c.abstract])
552
553
555 """
556 A parameter that is in fact a composite of a set of other
557 parameters or attributes of the class. The constructor argumentt
558 'attribs' takes a list of attribute names. Getting the parameter
559 returns a list of the values of the constituents of the composite,
560 in the order specified. Likewise, setting the parameter takes a
561 sequence of values and sets the value of the constituent
562 attributes sets all the constituents
563 """
564 __slots__=['attribs','objtype']
565
569
571 """
572 Return the values of all the attribs, as a list.
573 """
574 if not obj:
575 return [getattr(objtype,a) for a in self.attribs]
576 else:
577 return [getattr(obj,a) for a in self.attribs]
578
580 """
581 Set the values of all the attribs.
582 """
583 assert len(val) == len(self.attribs),"Compound parameter '%s' got the wrong number of values (needed %d, but got %d)." % (self._attrib_name,len(self.attribs),len(val))
584
585 if not obj:
586 for a,v in zip(self.attribs,val):
587 setattr(self.objtype,a,v)
588 else:
589 for a,v in zip(self.attribs,val):
590 setattr(obj,a,v)
591
592
594 """
595 Parameter whose value is set to some form of one of the
596 possibilities in its range.
597
598 Subclasses must implement get_range().
599 """
600 __abstract = True
601
603 raise NotImplementedError("get_range() must be implemented in subclasses.")
604
605
607 """
608 Parameter whose value is set to an object from its list of possible objects.
609 """
610 __slots__ = ['objects','allow_None']
611
612 - def __init__(self,default=None,objects=[],instantiate=True,allow_None=False,**params):
617
618
619
620
622 """
623 val must be None or one of the objects in self.objects.
624 """
625 if not (val in self.objects) and not (val is None and self.allow_None):
626 raise ValueError("%s not in Parameter %s's list of possible objects" \
627 %(val,self._attrib_name))
628
629
630
631
632
633
634
635
636
637
638
639
641 """
642 Return the possible objects to which this parameter could be set.
643
644 (Returns the dictionary {object.name:object}.)
645 """
646 d=dict([(obj.name,obj) for obj in self.objects])
647 if self.allow_None:
648 d['None']=None
649 return d
650
651
653 """
654 Parameter whose value is an instance of the specified class.
655 """
656
657 __slots__ = ['class_','allow_None']
658
659 - def __init__(self,class_,default=None,instantiate=True,allow_None=False,**params):
664
665
667 """val must be None or an instance of self.class_"""
668 if not (isinstance(val,self.class_)) and not (val is None and self.allow_None):
669 raise ValueError(
670 "Parameter '%s' value must be an instance of %s, not '%s'" %
671 (self._attrib_name, self.class_.__name__, val))
672
676
677
679 """
680 Return the possible types for this parameter's value.
681
682 (I.e. return {name: <class>} for all classes that are
683 concrete_descendents() of self.class_.)
684
685 Only classes from modules that have been imported are added
686 (see concrete_descendents()).
687 """
688 classes = concrete_descendents(self.class_)
689 d=dict([(name,class_) for name,class_ in classes.items()])
690 if self.allow_None:
691 d['None']=None
692 return d
693
694
695 -class List(Parameter):
696 """
697 Parameter whose value is a list of objects, usually of a specified type.
698
699 The bounds allow a minimum and/or maximum length of
700 list to be enforced. If the class is non-None, all
701 items in the list are checked to be of that type.
702 """
703 __slots__ = ['class_','bounds']
704
705 - def __init__(self,default=[],class_=None,instantiate=True,
706 bounds=(0,None),**params):
712
713
714
715
717 """Set to the given value, raising an exception if out of bounds."""
718 self._check_bounds(val)
719 super(List,self).__set__(obj,val)
720
722 """
723 Checks that the list is of the right length and has the right contents.
724 Otherwise, an exception is raised.
725 """
726 if not (isinstance(val,list)):
727 raise ValueError("List '%s' must be a list."%(self._attrib_name))
728
729 if self.bounds!=None:
730 min_length,max_length = self.bounds
731 l=len(val)
732 if min_length != None and max_length != None:
733 if not (min_length <= l <= max_length):
734 raise ValueError("%s: list length must be between %s and %s (inclusive)"%(self._attrib_name,min_length,max_length))
735 elif min_length != None:
736 if not min_length <= l:
737 raise ValueError("%s: list length must be at least %s."%(self._attrib_name,min_length))
738 elif max_length != None:
739 if not l <= max_length:
740 raise ValueError("%s: list length must be at most %s."%(self._attrib_name,max_length))
741
742 self._check_type(val)
743
745 if self.class_!=None:
746 for v in val:
747 assert isinstance(v,self.class_),repr(v)+" is not an instance of " + repr(self.class_) + "."
748
749
750
752 """
753 Parameter whose value is a list of callable objects.
754
755 This type of List Parameter is typically used to provide a place
756 for users to register a set of commands to be called at a
757 specified place in some sequence of processing steps.
758 """
759 __slots__ = ['class_','bounds']
760
762 for v in val:
763 assert callable(v),repr(v)+" is not callable."
764
765
766
767 -class Dict(ClassSelector):
768 """
769 Parameter whose value is a dictionary.
770 """
773
774
775
777 """
778 Wrapper for pickling instance methods.
779
780 The constructor takes an instance method (e.g. for an object
781 'sim', method sim.time) as its only argument. The wrapper
782 instance is callable, picklable, etc.
783 """
784
785
786
788 return repr(self.im.im_func)
789
790
792 return self.im.im_func.func_name
793 __name__ = property(_fname)
794
797
799 return (self.im.im_self,
800 self.im.im_func.func_name)
801
803 obj,func_name = state
804 self.im = getattr(obj,func_name)
805
807 return self.im(*args,**kw)
808