[PEAK] Trellis: undetected circularity via optional rules

Sergey Schetinin maluke at gmail.com
Wed Apr 15 14:31:50 EDT 2009


First an illustration:

from peak.events.trellis import *

class C(Component):
    attrs(run=False, switch=True)
    @maintain
    def a(self):
        if self.switch:
            if self.run:
                self.b
                return True

    @maintain(optional=True)
    def b(self):
        self.switch = False

c = C()
c.run = True
print c.a, c.switch #prints True False

This is a similar problem to "inconsistent init" I reported before,
but is cause by a different part of code. Unlike the older case here
trellis already knows that `a` depends on `switch`, but the
circularity is still not detected, that happens because of how
stm.Controller.run_rule processes cell initialization -- when the cell
is being initialized (like the optional `b` cell here) its writes are
not processed immediately but rather postponed to the time when the
parent cell writes will be processed, which is `a` in this case.

When that processing takes place there's a special condition ("if
dependent is not listener:" in _process_writes) included to avoid
maintain rules that read then write some value being detected as a
circularity. However it doesn't handle the case where the rule reads
some cell and then some other cell being initialized writes that read
value -- just like `b` does in my example -- then the `dependent` of
that is the cell for `a` and the `listener` is `a` as well, because we
are processing writes of the initializing cell in _process_writes for
`a`. So, if we change the condition to "if writer is not listener or
dependent is not listener:" the circularity is detected.

I though that there might be an issue that this new condition would
detect the non-existent circularity when the rule being initialized
reads then writes some value, so that this would be invalid:

class C2(Component):
    attrs(run=False, counter=0)
    @maintain
    def a(self):
        if self.run:
            self.b

    @maintain(optional=True)
    def b(self):
        self.counter += 1

c2 = C2()
c2.run = True
print c2.counter # prints 2


However that is not the case, the reason for that is that there's no
_retry to raise an exception and instead `b` runs twice. So the
correct condition seems to be "if dependent is not writer:" instead,
at least both cases described work well.

        while writes:
            subject, writer = writes.popitem()
            for dependent in subject.iter_listeners():
                if dependent is not writer: # changed from dependent
is not listener:
                    if dependent.dirty():
                        self.schedule(dependent, layer) #, writer
                        notified[dependent] = 1

Now I wonder if this will break any trellis properties, like circular
maintain rules that don't set conflicting values (the rules might
initialize each other even if they are not optional, when the
component is being initialized). The tests fail, but they always hang
up for me anyway, so I can't really use that as a reference.


So I have a question: for how many people in this list trellis tests
do run successfully?



-- 
Best Regards,
Sergey Schetinin

http://s3bk.com/ -- S3 Backup
http://word-to-html.com/ -- Word to HTML Converter


More information about the PEAK mailing list