tornado.wsgi.WSGIApplication issue: __call__ takes

2019-03-03 19:50发布

问题:

As part of a project, I've been trying to port a Tornado server to work on the Google App Engine. Since the App Engine doesn't implement the asynchronous functions of normal Tornado, I've been trying to convert the main Application to a WSGIApplication. The normal main code works fine (forgive the imports and formatting, it's a mess from trying to follow other examples):

import wsgiref
import tornado.wsgi
import tornado.web
import tornado.httpserver
import os
import Handlers
from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

def application():
    handlers=[(r"/", Handlers.MainHandler),
            (r"/Login", Handlers.LoginHandler),
            (r"/ViewHistory",Handlers.ViewHistoryHandler),
            (r"/UploadFile", Handlers.UploadHandler),
            (r"/Index", Handlers.IndexHandler),
            (r"/About", Handlers.AboutHandler),
            (r"/Profile", Handlers.ProfileHandler)]

    settings=dict(template_path=os.path.join(os.path.dirname(__file__), "templates"),
                static_path=os.path.join(os.path.dirname(__file__), "static"),
                debug=True)

    return tornado.web.Application(handlers, **settings)

def main():
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(application())
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

I can access the web pages, browse the site, works great. If I change line 24 to return a tornado.wsgi.WSGIApplication, like thus:

import wsgiref
import tornado.wsgi
import tornado.web
import tornado.httpserver
import os
import Handlers
from tornado.options import define, options

define("port", default=8000, help="run on the given port", type=int)

def application():
    handlers=[(r"/", Handlers.MainHandler),
            (r"/Login", Handlers.LoginHandler),
            (r"/ViewHistory",Handlers.ViewHistoryHandler),
            (r"/UploadFile", Handlers.UploadHandler),
            (r"/Index", Handlers.IndexHandler),
            (r"/About", Handlers.AboutHandler),
            (r"/Profile", Handlers.ProfileHandler)]

    settings=dict(template_path=os.path.join(os.path.dirname(__file__), "templates"),
                static_path=os.path.join(os.path.dirname(__file__), "static"),
                debug=True)

    return tornado.wsgi.WSGIApplication(handlers, **settings)

def main():
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(application())
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()

it also runs just fine. However, when I try to access any of the web pages, it gives me the following error:

[E 140131 10:02:18 iostream:357] Uncaught exception, closing connection.
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/iostream.py", line 354, in wrapper
        callback(*args)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 331, in wrapped
        raise_exc_info(exc)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 302, in wrapped
        ret = fn(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/httpserver.py", line 328, in _on_headers
        self.request_callback(self._request)
    TypeError: __call__() takes exactly 3 arguments (2 given)
[E 140131 10:02:18 ioloop:491] Exception in callback <functools.partial object at 0xf6e3ecfc>
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/ioloop.py", line 477, in _run_callback
        callback()
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 331, in wrapped
        raise_exc_info(exc)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 302, in wrapped
        ret = fn(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/iostream.py", line 354, in wrapper
        callback(*args)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 331, in wrapped
        raise_exc_info(exc)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/stack_context.py", line 302, in wrapped
        ret = fn(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/tornado-3.2-py2.7-linux-x86_64.egg/tornado/httpserver.py", line 328, in _on_headers
        self.request_callback(self._request)
    TypeError: __call__() takes exactly 3 arguments (2 given)

Which I can't make heads or tails of, and Google hasn't turned up anyone with the same problem (except for the guy who recommended mucking around in the Tornado files, which won't help on the App Engine AFAIK). Has anyone else seen this error before, or can spot why the WSGIApplication crashes when the default Application does not?

回答1:

The name "application" is unfortunately overloaded here - tornado.web.Application and WSGIApplication differ in their interface to the server. tornado.web.Application can be used by a tornado HTTPServer, but WSGIApplication must be run in a WSGI container. In app engine deployment, you'd just mention your WSGIApplication instance directly in the config file, with no mention of tornado.httpserver. To use adapt a WSGIApplication to tornado.httpserver, use a tornado.wsgi.WSGIContainer: HTTPServer(WSGIContainer(application()))