[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