dieseldev / diesel

Greenlet-based event I/O Framework for Python

Home Page:http://diesel.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using Twisted's reactor to run a DieselFlask app doesn't work

qoh opened this issue · comments

commented

(Using Ubuntu 12.10 and packages from the official apt repositories)

When using WSGIResource/Site in order to run a DieselFlask app (in these examples, "app"), you'll always end up with getting some exception from Diesel and Twisted's internal code.

from twisted.internet import reactor
from twisted.web.wsgi import WSGIResource
from twisted.web.server import Site

resource = WSGIResource(reactor, reactor.getThreadPool(), app)
site = Site(resource)

reactor.listenTCP(8080, site)
reactor.run()

With this method (app is a simple "hello world" route on /), going to / throws this exception:

WSGI application error
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/python/threadpool.py", line 167, in _worker
    result = context.call(ctx, function, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
  File "/usr/lib/python2.7/dist-packages/twisted/web/wsgi.py", line 332, in run
    self.reactor.callFromThread(wsgiError, self.started, *exc_info())
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/web/wsgi.py", line 315, in run
    appIterator = self.application(self.environ, self.startResponse)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1518, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1502, in wsgi_app
    with self.request_context(environ):
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1463, in request_context
    return RequestContext(self, environ)
  File "/usr/lib/python2.7/dist-packages/flask/ctx.py", line 87, in __init__
    self.url_adapter = app.create_url_adapter(self.request)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1348, in create_url_adapter
    return self.url_map.bind_to_environ(request.environ,
exceptions.AttributeError: 'dict' object has no attribute 'environ'

After several tries, @dontcare4free managed to make a WSGI middleware that fixes that case. More specifically:

from twisted.internet import reactor
from twisted.web.wsgi import WSGIResource
from twisted.web.server import Site

from flask.wrappers import Request
from UserDict import UserDict

class _RequestDict(UserDict, Request):
    def __init__(self, environ):
        Request.__init__(self, environ)
        UserDict.__init__(self)

    @property
    def data(self):
        return self.environ

    @data.setter
    def data(self, data):
        pass  # Bad, but required for this

def environFixerMiddleware(f):
    def wrapper(environ, start_response):
        return f(_RequestDict(environ), start_response)

    return wrapper

resource = WSGIResource(reactor, reactor.getThreadPool(), environFixerMiddleware(app))
site = Site(resource)

reactor.listenTCP(8080, site)
reactor.run()

This works for a simple "hello world" example. However, when throwing more complicated stuff like WebSockets and such (taken from examples) into the cocktail, another exception arises, which ends up in the actual code that handles exceptions:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/python/threadpool.py", line 167, in _worker
    result = context.call(ctx, function, *args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
  File "/usr/lib/python2.7/dist-packages/twisted/web/wsgi.py", line 332, in run
    self.reactor.callFromThread(wsgiError, self.started, *exc_info())
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/web/wsgi.py", line 315, in run
    appIterator = self.application(self.environ, self.startResponse)
  File "avenir-api/hack.py", line 19, in wrapper
    return f(_RequestDict(environ), start_response)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1518, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1506, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1194, in handle_exception
    self.log_exception((exc_type, exc_value, tb))
  File "/usr/local/lib/python2.7/dist-packages/diesel/web.py", line 61, in log_exception
    self._logger.trace().error('Exception on {0} [{1}]',
    exceptions.AttributeError: 'NoneType' object has no attribute 'trace

Not really sure whether this is an issue with Diesel or Twisted, though.

I'm not surprised that you're running into trouble here. Twisted and diesel are two different answers to the same problem, and probably aren't compatible.

I don't see any diesel code in your first traceback. Not sure what's going on there.

The second traceback is happening because DieselFlask instances are meant to run within diesel, by calling either the .run(...) or .make_service(...) methods. You can try to workaround that particular traceback by calling make_logger(...) on the DieselFlask instance (app in your examples) but WebSockets aren't going to work - they depend on diesel owning the event loop, and you are using Twisted's event loop.