[Twisted-Python] Asynchronous context in Twisted
Hi, I tried to make python.context work in asynchronous code between main loops. Anyone has similar experience to share please? Not sure if I am rebuilding a wheel :P http://code.google.com/p/little-site/source/browse/littlesite/custom_reactor... BR, Fantix.
On Mar 3, 2011, at 7:31 AM, Fantix King wrote:
Hi,
I tried to make python.context work in asynchronous code between main loops. Anyone has similar experience to share please?
Not sure if I am rebuilding a wheel :P
http://code.google.com/p/little-site/source/browse/littlesite/custom_reactor...
This is something I've often thought about doing in Twisted itself, actually :). But I wasn't sure that chaining context would actually do anything practically useful most of the time. Have you found that it's actually useful? Have you managed to leverage this to, for example, get more informative error messages out of Deferred failures? Doing it as a subclass like this is not optimal, as it limits you to one reactor (and the Select reactor is not really the best one). A wrapper would be slightly more tricky (you'd have to deal with the places that the reactor passes itself through to things like Process and Port, so you'd have to create wrappers for those as well) but much more general.
On Mar 3, 2011, at 2:39 PM, Glyph Lefkowitz wrote:
On Mar 3, 2011, at 7:31 AM, Fantix King wrote:
Hi,
I tried to make python.context work in asynchronous code between main loops. Anyone has similar experience to share please?
Not sure if I am rebuilding a wheel :P
http://code.google.com/p/little-site/source/browse/littlesite/custom_reactor...
This is something I've often thought about doing in Twisted itself, actually :). But I wasn't sure that chaining context would actually do anything practically useful most of the time. Have you found that it's actually useful? Have you managed to leverage this to, for example, get more informative error messages out of Deferred failures?
Doing it as a subclass like this is not optimal, as it limits you to one reactor (and the Select reactor is not really the best one). A wrapper would be slightly more tricky (you'd have to deal with the places that the reactor passes itself through to things like Process and Port, so you'd have to create wrappers for those as well) but much more general.
Thanks for replying! :) Yes! That's a wonderful idea to use this context for asynchronous traceback! I made some small changes to the code and wrote a patch for Twisted (as addReader and addWriter is quite different from one impl to another, I changed SelectReactor only. I haven't got a better idea for this, please advise), please see attachment. With a simple example of raising exception in deferLater-ed function (a-b-c-deferLater-d-e-f-g): from twisted.internet import reactor from twisted.internet.task import deferLater reactor.usingAsyncTraceback = True def g(): raise Exception('Something happened inside.') def f(): return g() def e(): return f() def d(): return e() def c(): deferred = deferLater(reactor, 1, lambda: None) deferred.addCallback(lambda x: d()) return deferred def b(): return c() def a(): return b() if __name__ == '__main__': deferred = a() def errback(failure): failure.printTraceback() deferred.addErrback(errback) deferred.addBoth(lambda x: reactor.stop()) reactor.run() I could get this: Traceback (most recent call last): File "test.py", line 31, in <module> deferred = a() File "test.py", line 28, in a return b() File "test.py", line 25, in b return c() File "test.py", line 20, in c deferred = deferLater(reactor, 1, lambda: None) File "/home/fantix/ac/twisted/internet/task.py", line 751, in deferLater delayedCall = clock.callLater(delay, d.callback, None) File "/home/fantix/ac/twisted/internet/base.py", line 701, in callLater _f, args, kw = self._chainContext(_f, args, kw) *--- <asynchronous break point> ---* File "/home/fantix/ac/twisted/python/context.py", line 59, in callWithContext return self.currentContext().callWithContext(ctx, func, *args, **kw) File "/home/fantix/ac/twisted/python/context.py", line 37, in callWithContext return func(*args,**kw) File "/home/fantix/ac/twisted/internet/defer.py", line 361, in callback self._startRunCallbacks(result) File "/home/fantix/ac/twisted/internet/defer.py", line 455, in _startRunCallbacks self._runCallbacks() --- <exception caught here> --- File "/home/fantix/ac/twisted/internet/defer.py", line 542, in _runCallbacks current.result = callback(current.result, *args, **kw) File "test.py", line 21, in <lambda> deferred.addCallback(lambda x: d()) File "test.py", line 17, in d return e() File "test.py", line 14, in e return f() File "test.py", line 11, in f return g() File "test.py", line 8, in g raise Exception('Something happened inside.') Additionally, in my scenario of a 5 years old asynchronous Twisted web application, we need the "request" object available throughout all code between asynchronous network accesses and database accesses because our global configuration system needs the request object. It would greatly reduce our manual work to pass through the request object here and there to have a context working in the asynchronous way. BR, Fantix.
On Mar 15, 2011, at 2:54 AM, Fantix King wrote:
Thanks for replying! :)
No problem, I wish I had time for more replies, but my stack runneth over.
--- <asynchronous break point> ---
This is completely awesome, and I have had this message sitting around waiting for me to appropriately consider it and reply. Hopefully I'll have some time in the future though. Can you perhaps file a ticket in the Twisted tracker for "asynchronous tracebacks that show me what has happened to a Deferred" or something like that? I think that we've all been afraid that such a feature would have too high of a performance cost, but like the creation/invocation stacks that are used when Deferred debugging is on, we could at least make use of it in unit tests or in debug mode. Thanks again for this awesome demonstration, and I'm sorry I haven't had more time to look at it.
Additionally, in my scenario of a 5 years old asynchronous Twisted web application, we need the "request" object available throughout all code between asynchronous network accesses and database accesses because our global configuration system needs the request object. It would greatly reduce our manual work to pass through the request object here and there to have a context working in the asynchronous way.
Okay, _this_ sounds terrible to me. Implicitly requiring an out-of-band parameter that is retrieved via some global variable is a maintenance nightmare. This is one of the reasons I haven't written and published more about trying to do more with asynchronous context: I don't want idiomatic Twisted applications to become a big mess of spaghetti code which only works if your call stacks are all just so. Availability of an implicit/shared context object was one of the things that made maintaining old Woven and Nevow code such a pain, and I consider its complete elimination in twisted.web.template a major step forward. So let's not go down that road again :).
participants (4)
-
Fantix King
-
Fantix King
-
Glyph Lefkowitz
-
Johan Rydberg