Permalink
Browse files

* put coroutine running logic in own class * do as little of the conf…

…iguration as possible on a per request basis * remove all per request closures

Signed-off-by: Eric Naeseth <enaeseth@gmail.com>
  • Loading branch information...
1 parent 299f68d commit 6ed02a14eb8c4b08fa388707b79484e459c98721 @libscott libscott committed with Eric Naeseth Apr 17, 2010
Showing with 64 additions and 61 deletions.
  1. +64 −61 swirl.py
View
125 swirl.py
@@ -5,20 +5,61 @@
"""
import logging
+import inspect
import functools
from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, asynchronous as web_async
try:
- from inspect import isgenerator
+ from inspect import isgeneratorfunction
except ImportError:
# Python < 2.6
- import types
- def isgenerator(obj):
- return isinstance(obj, types.GeneratorType)
+ def isgeneratorfunction(obj):
+ return bool((inspect.isfunction(object) or inspect.ismethod(object)) and
+ obj.func_code.co_flags & CO_GENERATOR)
__version__ = '0.1.0'
+class CoroutineRunner:
+ def __init__(self, io_loop, gen, web_handler):
+ self.gen = gen
+ self.io_loop = io_loop
+ self.web_handler = web_handler
+ # start the ball rolling...
+ self.callback_proxy()
+
+ def execute_work(self):
+ return self.work(self.callback_proxy)
+
+ def callback_proxy(self, *args):
+ try:
+ if len(args) > 0:
+ if isinstance(args[-1], Exception):
+ self.work = self.gen.throw(args[-1])
+ elif (hasattr(args[0], 'error') and
+ isinstance(args[0].error, Exception)):
+ self.work = self.gen.throw(args[0].error)
+ else:
+ if args[-1] is None:
+ args = args[:-1]
+ if len(args) == 1:
+ self.work = self.gen.send(args[0])
+ else:
+ self.work = self.gen.send(args)
+ else:
+ self.work = self.gen.next()
+ self.io_loop.add_callback(self.execute_work)
+ except StopIteration:
+ if self.web_handler and not self.web_handler._finished:
+ self.web_handler.finish()
+ except Exception, e:
+ if self.web_handler:
+ if self.web_handler._headers_written:
+ logging.error('Exception after headers written',
+ exc_info=True)
+ else:
+ self.web_handler._handle_request_exception(e)
+
def make_asynchronous_decorator(io_loop):
"""
Creates an asynchronous decorator that uses the given I/O loop.
@@ -29,7 +70,9 @@ def make_asynchronous_decorator(io_loop):
For information on how to use such a decorator, see
`swirl.asynchronous`.
"""
-
+ if io_loop is None:
+ io_loop = IOLoop.instance()
+
# hack because Python 2.x lacks the "nonlocal" keyword
io_loop = [io_loop]
@@ -38,67 +81,27 @@ def asynchronous(coroutine):
Allows a function to not use explicit callback functions to respond
to asynchronous events.
"""
+
+ if not isgeneratorfunction(coroutine):
+ # the "coroutine" isn't actually a coroutine; just return
+ # its result like tornado.web.asynchronous would do
+ return coroutine
+
+ web_async_coroutine = web_async(coroutine)
@functools.wraps(coroutine)
def run_async_routine(*args, **kwargs):
- if io_loop[0] is None:
- io_loop[0] = IOLoop.instance()
-
+ # we check if we're an instancemethod of RequestHandler for better
+ # intergration
if len(args) > 0 and isinstance(args[0], RequestHandler):
- routine = web_async(coroutine)
- web_handler = args[0]
+ CoroutineRunner(io_loop[0],
+ web_async_coroutine(*args, **kwargs),
+ args[0])
else:
- routine = coroutine
- web_handler = None
-
- gen = routine(*args, **kwargs)
- if not isgenerator(gen):
- # the "coroutine" isn't actually a coroutine; just return
- # its result like tornado.web.asynchronous would do
- return gen
-
- work = [None]
- def execute_work():
- return work[0](callback_proxy)
-
- def callback_proxy(*args):
- is_err = lambda val: isinstance(val, Exception)
- try:
- if len(args) > 0:
- if is_err(args[-1]):
- work[0] = gen.throw(args[-1])
- elif (hasattr(args[0], 'error') and
- is_err(args[0].error)):
- work[0] = gen.throw(args[0].error)
- else:
- if args[-1] is None:
- args = args[:-1]
- if len(args) == 1:
- work[0] = gen.send(args[0])
- else:
- work[0] = gen.send(args)
- else:
- work[0] = gen.next()
- io_loop[0].add_callback(execute_work)
- except StopIteration:
- if web_handler and not web_handler._finished:
- web_handler.finish()
- except Exception, e:
- if web_handler:
- if web_handler._headers_written:
- logging.error('Exception after headers written',
- exc_info=True)
- else:
- web_handler._handle_request_exception(e)
-
- try:
- work[0] = gen.next()
- except StopIteration:
- return None
-
- io_loop[0].add_callback(execute_work)
- return run_async_routine
-
+ CoroutineRunner(io_loop[0],
+ coroutine(*args, **kwargs), None)
+
+ return run_async_routine
return asynchronous
asynchronous = make_asynchronous_decorator(None)

0 comments on commit 6ed02a1

Please sign in to comment.