On Feb 22, 2013, at 8:30 AM, Christopher Armstrong <ra...@twistedmatrix.com> 
wrote:

> I think it's a reasonable change to make, and I don't foresee any problems 
> with it, so I think it's fine to submit a bug about it. But I do question the 
> architecture that needs to make use of it. I would probably avoid scenarios 
> like that in my own code.

I disagree; the behavior of result consumption is intentional - although it 
could be better documented.  Changing it would very definitely be incompatible 
(<http://twistedmatrix.com/trac/wiki/CompatibilityPolicy>); this is possible, 
of course, if the deprecation/migration is worth it, but the behavior being 
requested here would be worse in a number of ways.

If we re-populated the result, every failed Deferred yielded by an 
inlineCallbacks function would log its traceback twice: once when the unhandled 
exception propagated out of the inlineCallbacks function causing its Deferred 
to fail, and once when the unhandled exception propagated from the yielded 
Deferred itself, since nothing would have consumed it when that Deferred would 
be GC'd.

Speaking of GC, similarly, any large objects in Deferred results processed by 
inlineCallbacks functions would live longer, and continue participating in any 
reference cycles they're part of, possibly causing memory leaks, or at least, 
longer collection times and less favorable memory usage behavior, especially in 
long-lived processes.

Basically, you can't treat a Deferred as an event broadcaster.  It isn't one.  
It's a single-shot representation of an asynchronous result, which a single 
consumer can consume with its current value, possibly yielding a new value.  
Some consumers can be diligent about not modifying the Deferred's state, so 
that it can be passed on down the chain, but inlineCallbacks can never be such 
a consumer: since each inlineCallbacks-decorated function generates its own 
Deferred return value, it is naturally the terminal consumer of any Deferreds 
that it yields, and should clear out their results.

Ultimately, _every_ Deferred ought to have a terminal consumer, that takes the 
result and does useful work with it - persists it, shows it in some UI to a 
user - rather than continuing to pass it along.  Since 'yield x' is not 
sufficiently expressive to say what else to do with 'x' and what state to leave 
it in, we must assume that the intention of a coroutine is to take the value 
and do some work with it.  Any inlineCallbacks function which wants to express 
its intent more precisely can do this, instead:

    def fork(d):
        d2 = Deferred()
        def fire(x):
            d2.callback(x)
            return x
        d.addBoth(fire)
        return d2

    @inlineCallbacks
    def foo():
        result = yield fork(somethingAsync())

Maybe putting that function in Twisted (this is not the first time it's come 
up) would be a useful addition.

-glyph

_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to