1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Support for the C{L{Model}} object, an object in which C{L{CellAttr}}s
21 may be embedded.
22
23 @var DEBUG: Turns on debugging messages for the model module.
24 """
25
26 import cells
27 from cell import Cell, EphemeralCellUnboundError
28 from cellattr import CellAttr
29 from observer import Observer, ObserverAttr
30
31 DEBUG = False
32
34 """
35 debug() -> None
36
37 Prints debug messages.
38 """
39 msgs = list(msgs)
40 msgs.insert(0, "model".rjust(cells._DECO_OFFSET) + " > ")
41 if DEBUG or cells.DEBUG:
42 print " ".join(( str(msg) for msg in msgs))
43
72
73
75 """
76 A class in which CellAttrs may be used. Models automatically bring
77 their cells up-to-date at C{L{__init__}}-time. Cells may be
78 altered at runtime by passing C{attrname=value}, or
79 C{attrname=hash} to the constructor.
80
81 @ivar model_name: A cell holding The name of this Model. By
82 default, None.
83
84 @ivar model_value: A cell holding the value of this Model. By
85 default, None.
86
87 @ivar parent: A cell for C{L{Family}} graph traversal. By default,
88 None.
89 """
90 __metaclass__ = ModelMetatype
91
92 _initialized = False
93
94 model_name = cells.makecell(value=None, kid_overrides=False)
95 model_value = cells.makecell(value=None, kid_overrides=False)
96 parent = cells.makecell(value=None, kid_overrides=False)
97
99 """
100 __init__(self, [<attrname>=<value, rule or dict>], ...) -> None
101
102 Initialize a Model with optional overrides. By passing a
103 parameter with the same name as a cell attribute, you may
104 override that cell attribute. For example:
105
106 >>> class A(cells.Model):
107 ... x = cells.makecell(value=1)
108 ...
109 >>> a1 = A()
110 >>> a1.x
111 1
112 >>> a2 = A(x="blah")
113 >>> a2.x
114 'blah'
115
116 This override can be arbitrarily complex; for instance, you
117 can make a RuleCell into a ValueCell, change a attribute's
118 celltype ... In short, anything you can do at Model defintion
119 time you can alter at instantiation time:
120
121 >>> class B(cells.Model):
122 ... x = cells.makecell(rule=lambda s,p: 3 * s.y)
123 ... y = cells.makecell(value=2)
124 ...
125 >>> b = B()
126 >>> b.x
127 6
128 >>> b.y = 1
129 >>> b.x
130 3
131 >>> b = B(y=10)
132 >>> b.x
133 30
134 >>> b.y
135 10
136 >>> b = B(x={'celltype': cells.RuleThenInputCell})
137 >>> b.x
138 6
139 >>> b.y
140 2
141 >>> b.x = 5
142 >>> b.x
143 5
144 >>> b.y = 1
145 >>> b.x
146 5
147
148 @param attrname: The name of the attribute you wish to
149 override. If this is set to a callable, it will override
150 the rule for the cell. If it's set to a dictionary with
151 one or more of 'rule', 'value', or 'celltype', those
152 attributes will be overridden in the cell. Otherwise, it
153 will override the value of the target cell.
154 """
155 self._initregistry = {}
156 klass = self.__class__
157
158
159 for k,v in kwargs.iteritems():
160 if k in dir(klass):
161
162 if callable(v):
163 cellinit = {'rule': v}
164 elif 'keys' in dir(v):
165
166
167
168
169 quays = v.keys()
170 for qui in ('rule', 'value', 'celltype'):
171 if qui in quays:
172 cellinit = v
173 break
174 else:
175 cellinit = {'value': v}
176
177 if not cellinit:
178 raise BadInitError("A cell initialization dictionary was not built. Try wrapping your value or rule assignment in a dictionary.")
179
180
181
182 self._initregistry[k] = cellinit
183
184
185 self._observers = []
186 for name in self._observernames:
187 self._observers.append(getattr(self, name))
188
189
190 debug("INITIAL EQUALIZATIONS START")
191 for name in dir(self):
192 try:
193 getattr(self, name)
194 except EphemeralCellUnboundError, e:
195 debug(name, "was an unbound ephemeral")
196 debug("INITIAL EQUALIZATIONS END")
197
198
199 for key in self._noncells:
200 self._run_observers(getattr(self, key))
201
202
203 self._initialized = True
204
206 """
207 Per KT's spec, Models may not set non-cell attributes after
208 __init__.
209
210 @raise NonCellSetError: If you try to set a non-cell attribute
211 """
212
213 if isinstance(self.__dict__.get(key), Cell):
214 object.__setattr__(self, key, value)
215
216 elif not self._initialized:
217 if key not in self._noncells:
218 self._noncells.add(key)
219 object.__setattr__(self, key, value)
220
221 elif key not in self.__dict__.keys():
222 object.__setattr__(self, key, value)
223
224 else:
225 raise NonCellSetError, "Setting non-cell attributes of models " + \
226 "after init is disallowed"
227
229 """Runs each observer in turn. There's some optimization that
230 could go on here, if it turns out to be neccessary.
231 """
232 debug("model running observers -- ", str(len(self._observers)),
233 "to test")
234 for observer in self._observers:
235 observer.run_if_applicable(self, attribute)
236
238 """
239
240 """
241 debug("Building cell: owner:", str(self))
242 debug(" name:", name)
243 debug(" args:", str(args))
244 debug(" kwargs:", str(kwargs))
245
246 if kwargs.has_key('celltype'):
247 celltype = kwargs["celltype"]
248 elif kwargs.has_key('rule'):
249 celltype = cells.RuleCell
250 elif kwargs.has_key('value'):
251 celltype = cells.InputCell
252 else:
253 raise Exception("Could not determine target type for cell " +
254 "given owner: " + str(self) +
255 ", name: " + name +
256 ", args:" + str(args) +
257 ", kwargs:" + str(kwargs))
258
259 kwargs['name'] = name
260 return celltype(self, *args, **kwargs)
261
262 @classmethod
263 - def observer(klass, attrib=None, oldvalue=None, newvalue=None):
264 """
265 observer(attrib=None, oldvalue=None, newvalue=None) -> decorator
266
267 A classmethod to add an observer attribute to a Model. The
268 observer may be set to fire on any change in the model, any
269 change in an attribute, or when a function testing the new or
270 old value of a cell returns true.
271
272 >>> import cells
273 >>> class A(cells.Model):
274 ... x = cells.makecell(value=4)
275 ...
276 >>> @A.observer(attrib="x", newvalue=lambda a: a % 2)
277 ... def odd_x_obs(model):
278 ... print "New value of x is odd!"
279 ...
280 >>> a = A()
281 >>> a.x
282 4
283 >>> a.x = 5
284 New value of x is odd!
285 >>> a.x = 42
286 >>> a.x = 11
287 New value of x is odd!
288
289 @param attrib: An attribute name to attach the observer to
290
291 @param oldvalue: A function to run on the now-out-of-date
292 value of a cell which changed in this datapulse; if the
293 function returns True, the observer will fire. The
294 signature for the function must be C{f(val) -> bool}
295
296 @param newvalue: A function to run on up-to-date value; if the
297 function returns True, the observer will fire. The
298 signature for the function must be C{f(val) -> bool}
299 """
301 klass._observernames.add(func.__name__)
302 setattr(klass, func.__name__, ObserverAttr(func.__name__, attrib,
303 oldvalue, newvalue,
304 func))
305 return observer_decorator
306
307
309 """
310 You may not set a non-cell Model attribute after initialization.
311 """
313 self.value = value
315 return repr(self.value)
316
319 self.value = value
321 return repr(self.value)
322