1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Cell, and subclasses of Cell. You will almost certainly never use
21 these directly, instead relying on C{L{Model}} and its subclasses to
22 instantiate these into objects for you.
23
24 You should know what these do, though, as you can specify
25 C{L{cells.makecell}} or C{L{cells.fun2cell}} instantiate a specific
26 type of cell for you:
27
28 >>> class A(cells.Model):
29 ... l = cells.makecell(value=[1,3,5], celltype=cells.ListCell)
30 ... @cells.fun2cell(celltype=cells.RuleThenInputCell)
31 ... def from_a_global(self, prev):
32 ... global e
33 ... return e["something"]
34
35
36 @var DEBUG: Turns on debugging messages for the cell module.
37
38 @group Only Inherited: Cell, LazyCell
39
40 @group Cell Types: RuleCell, InputCell, RuleThenInputCell,
41 AlwaysLazyCell, UntilAskedLazyCell, DictCell, ListCell
42
43 @group Exceptions: EphemeralCellUnboundError, InputCellRunError,
44 RuleAndValueInitError, RuleCellSetError, SetDuringNotificationError
45 """
46
47 DEBUG = False
48
49 import cells
50 import weakref
51 import copy
52 import UserDict
53
55 """
56 debug() -> None
57
58 Prints debug messages.
59 """
60 msgs = [ str(_) for _ in msgs ]
61 msgs.insert(0, "cell".rjust(cells._DECO_OFFSET) + " > ")
62 if DEBUG or cells.DEBUG:
63 print " ".join(msgs)
64
65
67 """
68 The base Cell class. Does everything interesting.
69 """
71 """
72 __init__(self, owner, name=None, rule=None, value=None,
73 unchanged_if=None) -> None
74
75 Initializes a Cell object. You must not specify both C{rule}
76 and C{value}.
77
78 @param name: This cell's name. When using a C{Cell} with
79 C{L{Model}}s, this parameter is assigned automatically.
80
81 @param rule: Define a rule which backs this cell. You must
82 only define one of C{rule} or C{value}. Lacking C{celltype},
83 this creates a L{RuleCell}. This must be passed a
84 callable with the signature C{f(self, prev) -> value},
85 where C{self} is the model instance the cell is in and
86 C{prev} is the cell's out-of-date value.
87
88 @param value: Define a value for this cell. You must only
89 define one of C{rule} or C{value}. Lacking C{celltype}, this
90 creates an L{InputCell}.
91
92 @param unchanged_if: Sets a function to determine if a cell's
93 value has changed. For example,
94
95 >>> class A(cells.Model):
96 ... x = cells.makecell(value=1,
97 ... unchanged_if=lambda n,o:abs(n-o)>5)
98 ... y = cells.makecell(rule=lambda s,p: s.x * 2)
99 ...
100 >>> a = A()
101 >>> a.x
102 1
103 >>> a.y
104 2
105 >>> a.x = 3
106 >>> a.x
107 3
108 >>> a.y
109 6
110 >>> a.x = 90
111 >>> a.x
112 3
113 >>> a.y
114 6
115
116 The signature for the passed function is C{f(old, new) ->
117 bool}.
118
119 @param ephemeral: Causes the cell to reset its value to None
120 after propogating when it is set. Only makes sense when
121 applied to InputCells
122
123 @raise RuleAndValueInitError: If both C{rule} and C{value} are
124 passed, raise an exception
125
126 """
127 _debug("running cell init for", kwargs.get("name") or 'anonymous')
128
129 if kwargs.get("value", None) and kwargs.get("rule", None):
130 raise RuleAndValueInitError(
131 "Cell.__init__ was passed both rule and value parameters")
132
133 self.owner = owner
134 self.name = kwargs.get("name", None)
135 self.rule = kwargs.get("rule", lambda s,p: None)
136 self.value = kwargs.get("value", None)
137 self.ephemeral = kwargs.get("ephemeral", False)
138 self.unchanged_if = kwargs.get("unchanged_if", lambda o,n: o == n)
139
140 self.called_by = set([])
141 self.calls = set([])
142
143 self.dp = 0
144 self.changed_dp = 0
145 self.bound = False
146
147 self.constant = False
148 self.notifying = False
149
150 self.propogate_to = None
151 self.lazy = False
152 self.last_value = None
153
154
155 self.synapse_space = {}
156
157 if kwargs.has_key("value"):
158 self.bound = True
159 self.changed_dp = cells.cellenv.dp
160 self.dp = cells.cellenv.dp
161
163 """
164 getvalue(self, init=False) -> value
165
166 Returns this cell's up-to-date value.
167 """
168
169
170 if cells.cellenv.curr:
171 cells.cellenv.curr.add_calls(self)
172 self.add_called_by(cells.cellenv.curr)
173
174 self.updatecell()
175 return self.value
176
177 - def set(self, value):
178 """
179 set(self, value) -> None
180
181 Sets this cell's value and begins propogation of the change,
182 if neccessary.
183
184 @param value: The value to set this cell's value to.
185 """
186 if cells.cellenv.curr_propogator:
187 _debug(self.name, "sees in-progress propogation; deferring set.")
188
189 cells.cellenv.deferred_sets.append((self, ("set",
190 ((value,), {}))))
191 else:
192 _debug(self.name, "setting")
193 if not self.unchanged_if(self.value, value):
194 _debug(self.name, "new value is different; propogating change")
195 self.last_value = self.value
196 self.value = value
197
198 cells.cellenv.dp += 1
199 self.dp = cells.cellenv.dp
200
201 self.propogate()
202
203 if self.ephemeral:
204 self.value = None
205
207 """
208 updatecell(self, queryer=None) -> bool
209
210 Updates this cell to the current global DP (datapulse),
211 returning True if it changed, False otherwise.
212
213 @param queryer: The cell to L{propogate} to first, if
214 neccessary
215 """
216 _debug(self.name, "updating")
217 if queryer:
218 self.propogate_to = queryer
219
220 if not self.bound:
221 _debug(self.name, "unbound, rerunning")
222 self.run()
223 return True
224 if self.changed():
225 _debug(self.name, "changed, telling queryer to recalc")
226 return True
227 if self.dp == cells.cellenv.dp:
228 _debug(self.name, "is current.")
229 return False
230 if not cells.cellenv.curr_propogator:
231 if not self.lazy:
232 _debug(self.name, "sees system is not propogating; is current.")
233 self.dp = cells.cellenv.dp
234 return False
235
236
237
238 for cell in self.calls_list():
239 _debug(self.name, "asking", cell.name, "to update")
240 if cell.updatecell(self):
241 _debug(self.name, "got recalc command from", cell.name)
242 if not self.dp == cells.cellenv.dp:
243 if self.run():
244
245
246
247 pt = queryer
248 if self.propogate_to:
249 pt = self.propogate_to
250 self.propogate(pt)
251 self.propogate_to = None
252
253
254
255
256
257 return False
258
259
260
261 _debug(self.name,
262 "finished asking called cells to update without getting recalc;",
263 "is current.")
264 self.dp = cells.cellenv.dp
265 return False
266
268 """
269 propogate(self, propogate_first=None) -> None
270
271 Propogates an updatecell command to the set of cells which call
272 this cell.
273
274 @param propogate_first: If cell C{A} asks cell C{B} to update,
275 and cell C{B}'s value changes (causing a C{propogate}
276 call), it must propogate to C{A} first, before any of the
277 other cells which call C{B}.
278 """
279 if cells.cellenv.curr_propogator:
280 _debug(self.name, "propogating. Old propogator was",
281 cells.cellenv.curr_propogator.name)
282 else:
283 _debug(self.name, "propogating. No old propogator")
284
285 prev_propogator = cells.cellenv.curr_propogator
286 cells.cellenv.curr_propogator = self
287 self.changed_dp = cells.cellenv.dp
288 self.notifying = True
289
290 if self.owner:
291 self.owner._run_observers(self)
292
293
294 if propogate_first:
295
296
297 cells.cellenv.queued_updates.extend(
298 Cell.propogation_list(self, propogate_first))
299
300 _debug(self.name, "first propogating to", propogate_first.name,
301 "then adding",
302 [ cell.name for cell in
303 Cell.propogation_list(self, propogate_first) ],
304 "to deferred")
305
306 _debug(self.name, "asking", propogate_first.name, "to update first")
307 propogate_first.updatecell()
308 _debug(self.name, "finished propogating to first update",
309 propogate_first.name)
310
311 else:
312 _debug(self.name, "propogating to",
313 [ cell.name for cell in
314 Cell.propogation_list(self, propogate_first) ])
315
316
317 for cell in Cell.propogation_list(self, propogate_first):
318 if cell.lazy:
319 _debug(self.name, "saw", cell.name,
320 ", but it's lazy -- not updating")
321 else:
322 _debug(self.name, "asking", cell.name, "to update")
323 cell.updatecell(self)
324
325 self.notifying = False
326 cells.cellenv.curr_propogator = prev_propogator
327
328 if cells.cellenv.curr_propogator:
329 _debug(self.name, "finished propogating; switching to propogating",
330 str(cells.cellenv.curr_propogator.name))
331 else:
332 _debug(self.name, "finished propogating. No old propogator")
333
334
335 if not cells.cellenv.curr_propogator:
336
337
338 cells.cellenv.curr_propogator = cells.Cell(None,
339 name="queued propogation dummy")
340
341 _debug("no cell propogating! running deferred updates.")
342 to_update = cells.cellenv.queued_updates
343 cells.cellenv.queued_updates = []
344 for cell in to_update:
345 if cell.lazy:
346 _debug(self.name, "saw", cell.name,
347 ", but it's lazy -- not running deferred updated")
348 else:
349 _debug("Running deferred update on", cell.name)
350 cell.updatecell(self)
351
352 cells.cellenv.curr_propogator = None
353
354
355 _debug("running deferred sets")
356 to_set = cells.cellenv.deferred_sets
357 cells.cellenv.deferred_sets = []
358 for cell, cmdargtuple in to_set:
359 cmd, argtuple = cmdargtuple
360 args, kwargs = argtuple
361 _debug("running deferred", cmd, "on", cell.name)
362 args, kwargs = argtuple
363 getattr(cell, cmd)(*args, **kwargs)
364
365
367 """
368 run(self) -> bool
369
370 Runs the backing function (rule) for this cell. The sequence is:
371
372 1. Remove this cell from all other cell's called-by sets
373
374 2. Empty this cell's calls set.
375
376 3. Run the function, which as a side effect may add this
377 cell to other cells' called-by sets and add links in this
378 cell's calls set
379
380 4. If this cell's value changes,
381
382 4.1 Run any L{observer}s in this cell's Model
383
384 4.2 Return C{True}
385 """
386 _debug(self.name, "running")
387
388 oldcurr = cells.cellenv.curr
389 cells.cellenv.curr = self
390
391
392
393 self.remove_called_bys()
394 self.reset_calls()
395
396 self.dp = cells.cellenv.dp
397 newvalue = self.rule(self.owner, self.value)
398 self.bound = True
399
400
401 cells.cellenv.curr = oldcurr
402
403
404 if self.unchanged_if(self.value, newvalue):
405 _debug(self.name, "unchanged.")
406 return False
407 else:
408 _debug(self.name, "changed.")
409 self.last_value = self.value
410 self.value = newvalue
411
412
413 if self.owner:
414 self.owner._run_observers(attribute=self)
415
416 return True
417
419 """
420 remove_called_bys(self) -> None
421
422 Remove this cell from the called_by lists of all cells this cell calls
423 """
424 for cell in self.calls_list():
425 _debug(self.name, "removing c-b link from", cell.name)
426 cell.remove_cb(self)
427
429 """
430 changed(self) -> bool
431
432 Did this cell's value change in this DP (datapulse)?
433 """
434 return cells.cellenv.dp == self.changed_dp
435
437 """
438 calls_list(self) -> generator
439
440 Returns a generator of cell objects whose rules call this cell
441 """
442 return (r() for r in self.calls)
443
445 """
446 called_by_list(self) -> generator
447
448 Returns a generator of cell objects which this cell's rule calls
449 """
450 return (r() for r in self.calls)
451
453 """
454 propogation_list(self, elide=None) -> generator
455
456 Returns a generator of cell objects which this cell should
457 propogate to, minus any cell passed in C{elide}.
458
459 @param elide: Remove a cell from the list of cells to
460 propogate a change to. Used by L{propogate} to remove a
461 cell it had to propogate to first.
462 """
463 return (r() for r in self.called_by - set([elide]))
464
466 """Appends the passed list of cells to this cell's calls list"""
467 self.calls.update(set([ weakref.ref(cell) for cell in calls_cells ]))
468
470 """Appends the passed list of cells to this cell's called-by list"""
471 self.called_by.update(set([ weakref.ref(cell) for cell in cb_cells ]))
472
474 """Removes the passed list of cells from this cell's called-by list"""
475 self.called_by.difference_update(set(
476 [ weakref.ref(cell) for cell in cb_cells ]))
477
479 """Resets the calls list to empty"""
480 self.calls = set([])
481
482
483
484 _nonerule = lambda s,p: None
485
487 """A cell whose value is determined by a function (a rule)."""
489 """
490 __init__(self, owner, name=None, rule=lambda s,p: None,
491 unchanged_if=None) -> None
492
493 Initializes a RuleCell object, which may not be C{set} and
494 whose value is determined by a rule.
495
496 @param name: This cell's name. When using a C{Cell} with
497 C{L{Model}}s, this parameter is assigned automatically.
498
499 @param rule: Define a rule which backs this cell. You must
500 only define one of C{rule} or C{value}. Lacking C{celltype},
501 this creates a L{RuleCell}. This must be passed a
502 callable with the signature C{f(self, prev) -> value},
503 where C{self} is the model instance the cell is in and
504 C{prev} is the cell's out-of-date value.
505
506 @param unchanged_if: Sets a function to determine if a cell's
507 value has changed. The signature for the passed function
508 is C{f(old, new) -> bool}.
509
510 @raise RuleCellSetError: If C{value} is passed as a parameter
511 """
512 if kwargs.get("value", None):
513 raise RuleCellSetError("cannot define a RuleCell's value")
514 Cell.__init__(self, owner, rule=rule, *args, **kwargs)
515
516 - def set(self, value):
517 """
518 set(self, value) -> None
519
520 You may not C{set} a L{RuleCell}
521
522 @raise RuleCellSetError: Always raises this exception.
523 """
524 raise RuleCellSetError("cannot set() a rule cell")
525
526
562
563
614
615
617 """
618 A RuleCell which does not update upon propogation. When it has
619 C{L{get}()} run, it updates as other Cells do.
620 """
622 """
623 __init__(self, owner, name=None, rule=lambda s,p: None,
624 unchanged_if=None) -> None
625
626 Initializes a LazyCell object, which may not be C{set} and
627 whose value is determined by a rule.
628
629 @param name: This cell's name. When using a C{Cell} with
630 C{L{Model}}s, this parameter is assigned automatically.
631
632 @param rule: Define a rule which backs this cell. You must
633 only define one of C{rule} or C{value}. Lacking C{celltype},
634 this creates a L{RuleCell}. This must be passed a
635 callable with the signature C{f(self, prev) -> value},
636 where C{self} is the model instance the cell is in and
637 C{prev} is the cell's out-of-date value.
638
639 @param unchanged_if: Sets a function to determine if a cell's
640 value has changed. The signature for the passed function
641 is C{f(old, new) -> bool}.
642
643 @raise RuleCellSetError: If C{value} is passed as a parameter
644 """
645
646 RuleCell.__init__(self, *args, **kwargs)
647 self.lazy = True
648
649
650
651
653 """
654 A LazyCell variant that is *always* Lazy
655 """
656 pass
657
659 """
660 A LazyCell who converts to a normal RuleCell after its first
661 post-init C{L{get}()}
662 """
663 - def getvalue(self, init=False, *args, **kwargs):
664 v = LazyCell.getvalue(self, *args, **kwargs)
665 if not init:
666 self.lazy = False
667
668 return v
669
670 -class DictCell(InputCell, UserDict.DictMixin):
671 """
672 A input cell whose value is initialized to {}. An ordinary
673 InputCell doesn't act like we'd like it to in this case:
674
675 >>> class A(cells.Model):
676 ... x = cells.makecell(value={})
677 ... @cells.fun2cell()
678 ... def xkeys(self, prev):
679 ... return self.x.keys()
680 ...
681 >>> a = A()
682 >>> a.x
683 {}
684 >>> a.xkeys
685 []
686 >>> a.x['foo'] = 'bar'
687 >>> a.x
688 {'foo': 'bar'}
689 >>> a.xkeys
690 []
691
692 But if we use a DictCell, this will act like we'd like it to:
693
694 >>> class A(cells.Model):
695 ... x = cells.makecell(value={}, type=DictCell)
696 ... @cells.fun2cell()
697 ... def xkeys(self, prev):
698 ... return self.x.keys()
699 ...
700 >>> a = A()
701 >>> a.x
702 {}
703 >>> a.xkeys
704 []
705 >>> a.x['foo'] = 'bar'
706 >>> a.x
707 {'foo': 'bar'}
708 >>> a.xkeys
709 ['foo']
710
711 Note that C{unchanged_if} now operates on dictionary values,
712 rather than the dictionary itself.
713 """
714 - def __init__(self, owner, *args, **kwargs):
715 """
716 __init__(self, owner, name=None, rule=None, value=None,
717 unchanged_if=None) -> None
718
719 Initializes an DictCell object. You may not pass a C{rule}.
720
721 @param name: This cell's name. When using a C{Cell} with
722 C{L{Model}}s, this parameter is assigned automatically.
723
724 @param value: Define a value for this cell. This must be a
725 dictionary.
726
727 @param unchanged_if: Sets a function to determine if the
728 dictionary's value has changed. The signature for the
729 passed function is C{f(old, new) -> bool}.
730
731 @raise InputCellRunError: If C{rule} is passed as a parameter
732 """
733 if kwargs.get("rule", None):
734 raise InputCellRunError("You may not give an InputCell a rule")
735 Cell.__init__(self, owner, value=kwargs.pop("value", {}),
736 *args, **kwargs)
737
741
743 """
744 __setitem__(self, key, value) -> None
745
746 Sets this cell's value's key's value and begins propogation of
747 the change to the dict, if neccessary.
748
749 @param key: The value to get out of the cell.value dict
750
751 @param value: The value to set this cell's value's key's value to.
752 """
753 _debug(self.name, "setting setitem as dictcell")
754 if cells.cellenv.curr_propogator:
755 _debug(self.name, "sees in-progress propogation; deferring set.")
756
757 cells.cellenv.deferred_sets.append((self, ("__setitem__",
758 ((key, value), {}))))
759 else:
760 _debug(self.name, "setting")
761 if not self.value.has_key(key) or \
762 not self.unchanged_if(self.value[key], value):
763 _debug(self.name, "new value is different; propogating change")
764 self.last_value = copy.copy(self.value)
765 self.value[key] = value
766
767 cells.cellenv.dp += 1
768 self.dp = cells.cellenv.dp
769
770 if self.owner:
771 self.owner._run_observers(self)
772
773 self.propogate()
774
784
786 return repr(self.value)
787
788 - def get(self, key, default=None):
789
790
791 _debug(self.name, "getting", repr(key))
792 if cells.cellenv.curr:
793 cells.cellenv.curr.add_calls(self)
794 self.add_called_by(cells.cellenv.curr)
795
796 self.updatecell()
797
798 return self.value.get(key, default)
799
801 """
802 __getitem__(self, key) -> value
803
804 Gets the value in self.value[key]
805
806 @param key: lookup
807 """
808 if cells.cellenv.curr:
809 cells.cellenv.curr.add_calls(self)
810 self.add_called_by(cells.cellenv.curr)
811
812 self.updatecell()
813 return self.value[key]
814
816 """
817 keys(self) -> list
818
819 Gets self.value.keys()
820 """
821 if cells.cellenv.curr:
822 cells.cellenv.curr.add_calls(self)
823 self.add_called_by(cells.cellenv.curr)
824
825 self.updatecell()
826 return self.value.keys()
827
835
843
851
853 """
854 A input cell whose value is initialized to []. An ordinary
855 InputCell doesn't act like we'd like it to in this case:
856
857 >>> import cells
858 >>> class A(cells.Model):
859 ... x = cells.makecell(value=[])
860 ... @cells.fun2cell()
861 ... def xlen(self, prev):
862 ... return len(self.x)
863 ...
864 >>> a = A()
865 >>> a.x
866 []
867 >>> a.xlen
868 0
869 >>> a.x.append("foo")
870 >>> a.x
871 ['foo']
872 >>> a.xlen
873 0
874
875 But if we specify a ListCell, it should work as we expect it:
876
877 >>> import cells
878 >>> class A(cells.Model):
879 ... x = cells.makecell(value=[])
880 ... @cells.fun2cell()
881 ... def xlen(self, prev):
882 ... return len(self.x)
883 ...
884 >>> a = A()
885 >>> a.x
886 []
887 >>> a.xlen
888 0
889 >>> a.x.append("foo")
890 >>> a.x
891 ['foo']
892 >>> a.xlen
893 1
894
895 Note that C{unchanged_if} acts on list elements rather than the
896 entire value.
897 """
898
899 - def __init__(self, owner, *args, **kwargs):
900 """
901 __init__(self, owner, name=None, rule=None, value=None,
902 unchanged_if=None) -> None
903
904 Initializes an DictCell object. You may not pass a C{rule}.
905
906 @param name: This cell's name. When using a C{Cell} with
907 C{L{Model}}s, this parameter is assigned automatically.
908
909 @param value: Define a value for this cell. This must be a
910 dictionary.
911
912 @param unchanged_if: Sets a function to determine if the
913 dictionary's value has changed. The signature for the
914 passed function is C{f(old, new) -> bool}.
915
916 @raise InputCellRunError: If C{rule} is passed as a parameter
917 """
918 if kwargs.get("rule", None):
919 raise InputCellRunError("You may not give an InputCell a rule")
920
921 Cell.__init__(self, owner, value=kwargs.pop("value", []),
922 *args, **kwargs)
923
925 if self.owner: self.owner._run_observers(self)
926 self.propogate()
927
934
936 _debug(self.name, "wonders if it should defer a", name)
937 if cells.cellenv.curr_propogator:
938 _debug(self.name, "sees in-progress propogation; deferring set.")
939
940 cells.cellenv.deferred_sets.append((self, (name, argtuple)))
941 return True
942
943 return False
944
945
949
950 - def index(self, v, start=None, stop=None):
951 self._pregets()
952 if start is None: start = 0
953 if stop is None: stop = len(self.value)
954
955 return self.value.index(v, start, stop)
956
960
964
968
969
970 - def pop(self, index=None):
971 """
972 Warning: ListCell.pop() does not act quite like you may expect
973 it in certain circumstances. If a pop occurs during a
974 propogation, the value will be returned, but the list will not
975 be altered until the end of the propogation (thus ensuring all
976 cells "see" the same value of this cell during the DP).
977 """
978 if not self._should_defer("pop", (index,)):
979
980 r = self.value.pop(index)
981 cells.cellenv.dp += 1
982 self.dp = cells.cellenv.dp
983 self._onchanges()
984 else:
985
986 if index is None:
987 index = -1
988 r = self.value[index]
989
990 return r
991
993 - def fn(self, *args, **kwargs):
999 fn.__name__ = name
1000 return fn
1001
1002 append = _make_listfun("append")
1003 extend = _make_listfun("extend")
1004 insert = _make_listfun("insert")
1005 remove = _make_listfun("remove")
1006 reverse = _make_listfun("reverse")
1007 sort = _make_listfun("sort")
1008 __add__ = _make_listfun("__add__")
1009 __iadd__ = _make_listfun("__iadd__")
1010 __mul__ = _make_listfun("__mul__")
1011 __rmul__ = _make_listfun("__rmul__")
1012 __imul__ = _make_listfun("__imul__")
1013 __setitem__ = _make_listfun("__setitem__")
1014 __delitem__ = _make_listfun("__delitem__")
1015
1016
1019 self.value = value
1021 return repr(self.value)
1022
1024 """
1025 RuleCells may not be set.
1026 """
1027 pass
1028
1030 """
1031 An EphemeralCell was C{L{get}}ted without being bound.
1032 """
1033 pass
1034
1036 """
1037 An attempt to C{L{run}()} an InputCell was made.
1038 """
1039 pass
1040
1042 """
1043 An attempt at a non-deferred C{L{set}()} happened during
1044 propogation.
1045 """
1046 pass
1047
1049 """
1050 Both C{rule} and C{value} were passed to C{L{__init__}}.
1051 """
1052 pass
1053