1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Classes which deal with observers, bits of code which fire when a
21 C{L{Model}} is updated and updated cells match certain conditions of
22 the observer, such as cell name or statements about the cell's value.
23
24 @var DEBUG: Turns on debugging messages for the observer module.
25 """
26
27 import cells
28 from cells import Cell
29
30 DEBUG = False
31
37
38
39
41 """
42 Wrapper for Observers within Models. Will auto-vivify an Observer
43 within a Model instance the first time it's called.
44 """
45 - def __init__(self, name, *args, **kwargs):
46 self.name, self.args, self.kwargs = name, args, kwargs
47
48 - def __get__(self, owner, ownertype):
49 if not owner: return self
50
51 _debug("got request for observer", self.name,
52 "args =", str(self.args),
53 "kwargs =", str(self.kwargs))
54 if self.name not in owner.__dict__.keys():
55 owner.__dict__[self.name] = Observer(*self.args,
56 **self.kwargs)
57 return owner.__dict__[self.name]
58
60 """
61 Wrapper for a function which fires when a C{L{Model}} updates and
62 certain conditions are met. Observers may be bound to specific
63 attributes or whether a function returns true when handed a cell's
64 old value or new value, or any combination of the above. An
65 observer that has no conditions on its running runs whenever the
66 Model updates. Observers with multiple conditions will only fire
67 when all the conditions pass. Observers run at most once per
68 datapulse.
69
70 You should use the C{L{Model.observer}} decorator to add Observers
71 to Models:
72
73 >>> import cells
74 >>> class A(cells.Model):
75 ... x = cells.makecell(value=4)
76 ...
77 >>> @A.observer(attrib="x",
78 ... newvalue=lambda a: a % 2)
79 ... def odd_x_obs(model):
80 ... print "New value of x is odd!"
81 ...
82 >>> @A.observer(attrib="x")
83 ... def x_obs(model):
84 ... print "x got changed!"
85 ...
86 >>> @A.observer()
87 ... def model_obs(model):
88 ... print "something in the model changed"
89 ...
90 >>> @A.observer(attrib="x",
91 ... newvalue=lambda a: a % 2,
92 ... oldvalue=lambda a: not (a % 2))
93 ... def was_even_now_odd_x_obs(model):
94 ... print "New value of x is odd, and it was even!"
95 ...
96 >>> a = A()
97 something in the model changed
98 x got changed!
99 >>> a.x = 5
100 something in the model changed
101 x got changed!
102 New value of x is odd!
103 New value of x is odd, and it was even!
104 >>> a.x = 11
105 something in the model changed
106 x got changed!
107 New value of x is odd!
108 >>> a.x = 42
109 something in the model changed
110 x got changed!
111
112
113 @ivar attrib: (optional) The cell name this observer watches. Only
114 when a cell with this name changes will the observer fire. You
115 may also pass a list of cell names to "watch".
116
117 @ivar oldvalue: A function (signature: C{f(val) -> bool}) which,
118 if it returns C{True} when passed a changed cell's out-of-date
119 value, allows the observer to fire.
120
121 @ivar newvalue: A function (signature: C{f(val) -> bool}) which,
122 if it returns C{True} when passed a changed cell's out-of-date
123 value, allows the observer to fire.
124
125 @ivar func: The function to run when the observer
126 fires. Signature: C{f(model_instance) -> (ignored)}
127
128 @ivar last_ran: The DP this observer last ran in. Observers only
129 run once per DP.
130 """
131
132 - def __init__(self, attrib, oldvalue, newvalue, func):
133 """
134 __init__(self, attrib, oldvalue, newvalue, func)
135
136 Initializes a new Observer. All arguments are required, but
137 only func is required to be anything but none.
138
139 See attrib, oldvalue, and newvalue instance variable docs for
140 explanation of their utility.
141 """
142 self.attrib_name = attrib
143 self.oldvalue = oldvalue
144 self.newvalue = newvalue
145 self.func = func
146 self.last_ran = 0
147
149 """
150 Determine whether this observer should fire, and fire if
151 appropriate.
152
153 @param model: the model instance to search for matching cells
154 within.
155
156 @param attr: the attribute which "asked" this observer to run.
157 """
158 _debug("running observer", self.func.__name__)
159 if self.last_ran == cells.cellenv.dp:
160 _debug(self.func.__name__, "already ran in this dp")
161 return
162
163 if self.attrib_name:
164 if isinstance(self.attrib_name, str):
165 attrs = (self.attrib_name,)
166 else:
167 attrs = self.attrib_name
168
169 for attrib_name in attrs:
170 if isinstance(attr, Cell):
171 if attr.name == attrib_name:
172 _debug("found a cell with matching name!")
173 break
174 elif getattr(model, attrib_name) is attr:
175 _debug(self.func.__name__, "looked in its model for an " +
176 "attrib with its desired name; found one that " +
177 "matched passed attr.")
178 break
179 else:
180 _debug("Attribute name tests failed")
181 return
182
183 if self.newvalue:
184 if isinstance(attr, Cell):
185 if not self.newvalue(attr.value):
186 _debug(self.func.__name__,
187 "function didn't match cell's new value")
188 return
189 else:
190 if not self.newvalue(attr):
191 _debug(self.func.__name__, "function didn't match non-cell")
192 return
193
194
195
196 if self.oldvalue:
197 if isinstance(attr, Cell):
198 if not self.oldvalue(attr.last_value):
199 _debug(self.func.__name__,
200 "function didn't match old value")
201 return
202
203
204 _debug(self.func.__name__, "running")
205 self.func(model)
206 self.last_ran = cells.cellenv.dp
207