[PEAK] A couple more Component initialization cases

Phillip J. Eby pje at telecommunity.com
Tue Aug 14 12:17:32 EDT 2007


At 08:33 PM 8/13/2007, Grant Baillie wrote:
>So, I re-ran some unit tests I had (more or less accidentally)
>disabled, and came across some initialization-related behaviours that
>I found interesting. Mainly, this is kind of a code review request
>for a circular dependency initialization example: I'm not sure my
>final product below is either beautiful or readable :).
>
>(1) The following little doctest no longer works, due to the stated
>behaviour when rules get run (and take effect) at init time. (The
>behaviour means the keyword startTime 'assignment' will get
>overwritten by the rule).
>
> >>> import peak.events.trellis as trellis
> >>> from datetime import *
> >>>
> >>> class ScheduleItem(trellis.Component):
>...    startTime = trellis.value(None)
>...    trellis.rules(startTime = lambda self: datetime.now())
>...
> >>> ScheduleItem(startTime=datetime(2005, 3, 15, 13, 0)).startTime
>datetime.datetime(2005, 3, 15, 13, 0)
>
>To fix this, I can change the rule to be
>
>...    startTime = lambda self: datetime.now() if self.startTime is
>None else self.startTime
>
>or use a similar @action to assign startTime, I suppose. Maybe
>there's a better way I'm missing.

No, it's still me that's wrong here.  The new initialization rules 
are still borked.  The new rule works fine for initializing 
circularly-dependent rules, but not for overriding rule 
defaults.  I'm afraid it's back to the drawing board on that 
one.  :(  I've been working on the updated algorithm (until my PC's 
hard drive crashed yesterday), but that algorithm assumed the new 
initialization rules, so I'm going to have to think it through some 
more.  I could've sworn there was some reason we weren't forcing the 
assignment to override the computed value after calculation as well 
as before.  I'll look into it once I have my PC up and running 
again.  (I'm working off a laptop at the moment, while trying to 
restore my PC.)

>(2) The above was a smaller part of a larger circular dependency
>example, which dates back all the way to the original API examples on
>this list:
>
>
>     class ScheduleItem(trellis.Component):
>
>         trellis.values(
>             startTime = None,
>             duration  = timedelta(minutes=30),
>             endTime   = None,
>         )
>
>         trellis.rules(
>             startTime = lambda self: datetime.now(),
>             duration  = lambda self: self.endTime - self.startTime,
>             endTime   = lambda self: self.startTime + self.duration,
>         )
>
>The above won't work because of (1), and also because of the duration
>and endTime rules will try to do illegal arithmetic with 'None'.

That's quite odd; it seems like the duration and endtime rules should 
read the starttime rule's computed value in that case.

>So,
>once I add a bunch of None checking, I end up with the code below.
>It's more complicated, but it does do the right thing with various
>combination of keyword arguments.
>
>However, a reader of the code might be concerned that the system is
>not evaluation order-dependent. For example, if I modify endTime, is
>it obvious that duration and not startTime will change?

No, it isn't.  If you have N-way recursive rules, you must set at 
least N-1 of the values in a @modifier to ensure the result.  It's 
therefore best to avoid values of N>2.  :)




More information about the PEAK mailing list