python - Releasing references to deferred: what's the point -
this pattern mentioned in twisted tutorial standard way avoid firing same deferred twice:
class a(clientfactory): ... def finished(self, result): if self.deferred not none: d, self.deferred = self.deferred, none d.callback(result)
but understand, deferred won't let (i.e., raise exception if do) call same instance twice. why recreate safety mechanism repeating code everywhere?
as say, second call deferred.callback
raise alreadycallederror
. idiomatic write code doesn't trigger exception, though. deferred-using code i've written or read tries reserve alreadycallederror
indicate programming errors rather normal runtime conditions can safely ignored.
so:
def finished(self, result): try: self.deferred.callback(result) except alreadycallederror: pass
is not preferred spelling. think because there might other reasons alreadycallederror
raised don't stem finished
being called more 1 , kind of error handling obscure cases, potentially hiding bugs.
a second reason throw away deferred
instance unexpected exception logging. when deferred
garbage collected , has unhandled failure
result, failure
logged. indicates programming error. deferred
can't garbage collected long there references it, though, throwing away reference in factory @ least ensures factory won't keep deferred
alive (though application code using deferred
still could).
an alternate approach write this:
def finished(self, result): if self.deferred not none: self.deferred.callback(result) self.deferred = none
this still throws away deferred
without tuple unpacking. however, there's problem here.
consider more complete version of a
:
class a(clientfactory): def waituntilfinished(self): self.deferred = deferred() return self.deferred def finished(self, result): if self.deferred not none: d, self.deferred = self.deferred, none d.callback(result)
as can see, not safe call a.waituntilfinished
2 times before finished
has happened once. is, if write:
a = a() x = a.waituntilfinished() y = a.waituntilfinished()
then can pretty x
never receive result. perhaps sad, reasonably document limitation on of api.
now, consider different usage pattern:
a = a() x = a.waituntilfinished() def dosomething(result): return a.waituntilfinished() x.addcallback(dosomething)
this code no longer calls waituntilfinished
twice before finished
called once. waits until finished
called before calling waituntilfinished
second time. if documented api being safe call once until resulting deferred fires, might think usage reasonable.
with simpler implementation:
def finished(self, result): if self.deferred not none: self.deferred.callback(result) self.deferred = none
there's problem. dosomething
called result of self.deferred.callback(result)
. more specifically, dosomething
called before self.deferred.callback
returns (put way, dosomething
called synchronously statement). dosomething
calls waituntilfinished
creates new deferred
, assigns factory's deferred
attribute. self.deferred.callback(result)
finishes , self.deferred = noneruns - , new
deferred` thrown away, never called.
by setting self.deferred
none
before invoking callback chain, case avoided.
Comments
Post a Comment