Coverage for tw2/core/params.py : 94%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import absolute_import
3from . import core
4import itertools
5import copy
6import six
9class ParameterError(core.WidgetError):
10 "Errors related to parameters."
13class _Required(object):
14 """This class is used to mark a widget parameter as being required, by
15 setting the default value to this."""
16 def __repr__(self):
17 return 'Required'
18Required = _Required()
21class _Auto(object):
22 """
23 Used to explicitly mark a parameter as being automatically generated.
24 """
25 def __repr__(self):
26 return 'Auto'
27Auto = _Auto()
30class Deferred(object):
31 """This class is used as a wrapper around a parameter value. It takes a
32 callable, which will be called every time the widget is displayed, with
33 the returned value giving the parameter value."""
35 def __init__(self, fn):
36 self.fn = fn
38 def __repr__(self):
39 doc = self.fn.__doc__ if self.fn.__doc__ else '<Deferred>'
40 return '<Deferred: %s>' % doc
43class _Default(object):
44 def __repr__(self):
45 return 'Default'
46Default = _Default()
48_param_seq = itertools.count(0)
51class Param(object):
52 """A parameter for a widget.
54 `description`
55 A string to describe the parameter. When overriding a parameter
56 description, the string can include ``$$`` to insert the previous
57 description.
59 `default`
60 The default value for the parameter. If no defalt is specified,
61 the parameter is a required parameter. This can also be specified
62 explicitly using tw.Required.
64 `request_local`
65 Can the parameter be overriden on a per-request basis? (default:
66 True)
68 `attribute`
69 Should the parameter be automatically included as an attribute?
70 (default: False)
72 `view_name`
73 The name used for the attribute. This is useful for attributes like
74 *class* which are reserved names in Python. If this is None, the name
75 is used. (default: None)
77 The class takes care to record which arguments have been explictly
78 specifed, even if to their default value. If a parameter from a base
79 class is updated in a subclass, arguments that have been explicitly
80 specified will override the base class.
81 """
83 child_param = False
84 internal = False
85 name = None
86 defined_on = None
87 view_name = None
89 def __init__(self, description=Default, default=Default,
90 request_local=Default, attribute=Default,
91 view_name=Default):
92 self._seq = six.advance_iterator(_param_seq)
94 self.description = None
95 if description is not Default:
96 self.description = description
97 self.default = Required
98 if default is not Default:
99 self.default = default
100 self.request_local = True
101 if request_local is not Default:
102 self.request_local = request_local
103 self.attribute = False
104 if attribute is not Default:
105 self.attribute = attribute
106 self.view_name = None
107 if view_name is not Default:
108 self.view_name = view_name
110 self.specified = []
111 args = [
112 'description',
113 'default',
114 'request_local',
115 'attribute',
116 'view_name',
117 ]
118 for arg in args:
119 if locals()[arg] is not Default:
120 self.specified.append(arg)
122 def __repr__(self):
123 return '%s: %s (default: %s, defined on: %s)' % (
124 self.name, self.description, self.default, self.defined_on)
127class Variable(Param):
128 """A variable - a parameter that is passed from the widget to the template,
129 but cannot be controlled by the user. These do not appear in the concise
130 documentation for the widget.
131 """
132 internal = True
134 def __init__(self, description=Default, **kw):
135 kw.setdefault('default', None)
136 super(Variable, self).__init__(description, **kw)
137 self.specified.append('internal')
140class ChildParam(Param):
141 """A parameter that applies to children of this widget
143 This is useful for situations such as a layout widget, which adds a
144 :attr:`label` parameter to each of its children. When a Widget subclass is
145 defined with a parent, the widget picks up the defaults for any child
146 parameters from the parent.
147 """
148 child_param = True
151class ChildVariable(Variable, ChildParam):
152 """A variable that applies to children of this widget
153 """
154 pass
157class ParamMeta(type):
158 "Meta class the collects parameters."
160 def __new__(meta, name, bases, dct):
161 """Create a new `Widget` subclass. This detects `Param` instances
162 defined declaratively, updates with information from the containing
163 class, and stores the objects in `_params`."""
165 params = {}
166 for b in bases:
167 if hasattr(b, '_params'):
168 params.update(b._all_params)
170 for pname, prm in list(dct.items()):
171 if isinstance(prm, Param):
172 if pname in params:
173 newprm = prm
174 prm = copy.copy(params[pname])
175 for a in newprm.specified:
176 setattr(prm, a, getattr(newprm, a))
177 else:
178 prm.name = pname
179 if prm.view_name is None:
180 prm.view_name = pname
181 prm.defined_on = name
183 params[pname] = prm
184 if not prm.child_param and prm.default is not Required:
185 dct[pname] = prm.default
186 else:
187 del dct[pname]
188 elif pname in params:
189 params[pname] = copy.copy(params[pname])
190 params[pname].default = prm
191 if prm is Required and pname != 'validator':
192 del dct[pname]
194 ins = type.__new__(meta, name, bases, dct)
195 ins._all_params = params
196 ins._params = dict((p.name, p) for p in params.values()
197 if not p.child_param)
198 return ins
201class Parametered(six.with_metaclass(ParamMeta, object)):
202 pass