[PEAK] A couple more Component initialization cases

Grant Baillie grant at osafoundation.org
Mon Aug 13 20:33:45 EDT 2007


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.

(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'. 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? You can  
convince yourself of this by figuring out which methods depend on  
which cells once the component has been initialized. (Or, of course,  
by writing enough unit tests, for some value of 'enough' :).

 >>> class ScheduleItem(trellis.Component):
...
...     trellis.values(
...         startTime = None,
...         duration  = timedelta(minutes=30),
...         endTime   = None,
...     )
...
...     @trellis.rule
...     def startTime(self):
...         if self.startTime is None:
...             if self.endTime is not None:
...                 return self.endTime - self.duration
...             else:
...                 return datetime.now()
...         return self.startTime
...
...     @trellis.rule
...     def duration(self):
...         if None in (self.endTime, self.startTime):
...             return self.duration
...         else:
...             return self.endTime - self.startTime
...
...     @trellis.rule
...     def endTime(self):
...         if self.startTime is not None:
...             return self.startTime + self.duration
...         else:
...             return self.endTime


--Grant




More information about the PEAK mailing list