[PEAK] Trellis: propagating constants
Sergey Schetinin
maluke at gmail.com
Tue Apr 14 18:58:36 EDT 2009
Some Trellis cell types know they can become constants but that
"constness" doesn't propagate immediately. Illustration:
>>> from peak.events.trellis import LazyCell
>>> c1 = LazyCell(lambda: 1)
>>> c2 = LazyCell(lambda: c1.value)
>>> c2.value
1
>>> c1, c2
(Constant(1), LazyCell(<function <lambda> at 0x00B6CBB0>, 1 [inactive]))
>>> c2.value
1
>>> c1, c2
(Constant(1), Constant(1))
The problem is that if c2 had listeners it would never become
constant, because on_commit(self._check_const) is called from run
method and that would never get called as there are no subjects.
The reason for the whole thing is that _check_const only runs if rule
didn't read any cells and c2 did read c1. So the solution seems to
iterate through listeners and check their constness as well, like
this:
for listener in self.iter_listeners():
if isinstance(listener, ReadOnlyCell):
on_commit(listener._check_const)
change_attr(self, 'next_listener', None)
That doesn't work either because the link is not broken from the
listener's side because it is stored in undo log (change_attr(self,
'next_listener', None)), so the unlink wasn't called yet. So lets try
this:
link = self.next_listener
while link is not None:
nxt = link.next_listener
listener = link()
if isinstance(listener, ReadOnlyCell):
on_commit(listener._check_const)
on_undo(stm.Link, self, listener)
link.unlink()
link = nxt
It worked, so I decided to put together a little bigger test:
from peak.events import trellis
c1 = trellis.LazyCell(lambda: 1)
c2 = trellis.LazyCell(c1.get_value)
c3 = trellis.LazyCell(c1.get_value)
c4 = trellis.LazyCell(lambda: (c2.value, c3.value))
c5 = trellis.Cell(c4.get_value)
c5.value
assert (c1.__class__ is c2.__class__
is c3.__class__ is c4.__class__
is trellis.LazyConstant)
and it failed with
Traceback (most recent call last):
File "check_autoconst.py", line 7, in <module>
c5.value
File "c:\files\checkouts\trellis\peak\events\trellis.py", line 192,
in get_value
atomically(schedule, self)
File "c:\files\checkouts\trellis\peak\events\stm.py", line 467, in atomically
return super(Controller,self).atomically(self._process, func, args, kw)
File "c:\files\checkouts\trellis\peak\events\stm.py", line 187, in atomically
self.cleanup(*sys.exc_info())
File "c:\files\checkouts\trellis\peak\events\stm.py", line 221, in cleanup
self.rollback_to(0)
File "c:\files\checkouts\trellis\peak\events\stm.py", line 260, in rollback_to
f(*a)
File "c:\files\checkouts\trellis\peak\events\trellis.py", line 255,
in __setattr__
raise AttributeError("Constants can't be changed", self)
AttributeError: ("Constants can't be changed", Constant((1, 1)))
Which means something was rolling back and trying to set _set_by to
NO_VALUE (seen in debug prints). I guess the reason is that
c4._check_const is in commit queue twice (added by both c2 and c3), so
I have an idea: replace this
if isinstance(listener, ReadOnlyCell):
on_commit(listener._check_const)
with this:
if isinstance(listener, ReadOnlyCell) and
listener.next_subject is None:
listener._check_const()
and now the test passes. In fact it has two fixes at once :) Either
would suffice:
if isinstance(listener, ReadOnlyCell):
listener._check_const()
or
if isinstance(listener, ReadOnlyCell) and
listener.next_subject is None:
on_commit(listener._check_const)
The former seems a little better as there are no assumptions on what
are the criteria for constness, but on the other hand can there be any
criteria not requiring absence of subjects? And on_commit seems
unnecessary anyway, so left both of them in.
See patch in attachment and test above.
--
Best Regards,
Sergey Schetinin
http://s3bk.com/ -- S3 Backup
http://word-to-html.com/ -- Word to HTML Converter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: const.patch
Type: application/octet-stream
Size: 934 bytes
Desc: not available
Url : http://www.eby-sarna.com/pipermail/peak/attachments/20090415/b9f87cc4/const.obj
More information about the PEAK
mailing list