# coding: utf-8
import pprint
import tornado
import tornado.escape
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.websocket
from six import integer_types
pp = pprint.PrettyPrinter(indent=4)
[docs]class TornadoWrapper:
"""
Wrapper for Tornado application and server handling.
It let you access to Tornado app, handlers and settings everywhere in your code (it's really
useful when you run ``runtornado`` management command and WebSockets management).
"""
tornado_app = None
tornado_server = None
# Default values for user configuration
tornado_port = 0 # will never be 0
handlers = []
@classmethod
[docs] def start_app(cls, tornado_handlers=None, tornado_settings=None):
"""
Initialize the Tornado web application with given handlers and settings.
:param tornado_handlers: Handlers (routes) for Tornado
:param tornado_settings: Settings for Tornado
:type tornado_handlers: list
:type tornado_settings: dict
:return: None
"""
if not tornado_handlers:
tornado_handlers = []
if not tornado_settings:
tornado_settings = {}
if not isinstance(tornado_handlers, list):
raise TypeError('Expected a list for Tornado handlers.')
if not isinstance(tornado_settings, dict):
raise TypeError('Expected a dictionary for Tornado settings.')
# Not `tornado_handlers += cls.handlers` because wildcard handler should be the last value in handlers list
# http://www.tornadoweb.org/en/stable/_modules/tornado/web.html#Application.add_handlers
tornado_handlers = cls.handlers + tornado_handlers
cls.tornado_app = tornado.web.Application(tornado_handlers, **tornado_settings)
@classmethod
[docs] def listen(cls, tornado_port):
"""
Start the Tornado HTTP server on given port.
:param tornado_port: Port to listen
:type tornado_port: int
:return: None
.. todo:: Add support for HTTPS server.
"""
if not isinstance(tornado_port, integer_types):
raise TypeError('Expected an integer for Tornado port.')
if not cls.tornado_app:
raise TypeError('Tornado application was not instantiated, call TornadoWrapper.start_app method.')
cls.tornado_port = tornado_port
cls.tornado_server = tornado.httpserver.HTTPServer(cls.tornado_app)
cls.tornado_server.listen(cls.tornado_port)
@classmethod
[docs] def loop(cls):
"""
Run Tornado main loop and display configuration about Tornado handlers and settings.
:return: None
"""
# print('== Using port %s' % cls.tornado_port)
# print('== Using handlers:')
# pp.pprint(cls.tornado_app.handlers)
# print('== Using settings:')
# pp.pprint(cls.tornado_app.settings)
tornado.ioloop.IOLoop.instance().start()
@classmethod
[docs] def add_handlers(cls, handlers):
"""
Add an handler to Tornado app if it's defined, otherwise it add this handler to the
TornadoWrapper.tornado_handlers list.
:param handlers: Handler(s) to add
:type handlers: list|tuple
:return: Tornado application handlers
:rtype: list
"""
if isinstance(handlers, tuple):
handlers = [handlers]
elif not isinstance(handlers, list):
raise TypeError('Expected a list or a tuple for handlers.')
if not TornadoWrapper.tornado_app:
# print('== Prepare new handlers for Tornado application:')
# pp.pprint(handlers)
# ``cls.handlers = handlers + cls.handlers`` and not ``cls.handlers += handlers``,
# see `TornadoWrapper.start_app` source to know why.
cls.handlers = handlers + cls.handlers
return cls.handlers
# print('== Adding handlers to already running Tornado application. New handlers are:')
# pp.pprint(cls.tornado_app.handlers)
TornadoWrapper.tornado_app.add_handlers('.*', handlers)
return cls.tornado_app.handlers