Tornado

A Python Web Framework
  • Asynchronous Networking
  • Scalable
  • Uses non-blocking network I/O
  • Fast (faster than Django, web.py, CherryPy)
  • Great for creating RESTful services

INstallation


  • Available in PyPI
  • Install with pip or easy_install
  • Source on GitHub
  • Runs on python 2.6+ and 3.2+
  • Supports Unix-Like environments
  • Not suggested for running on Mac OS X
  • (But who would host off of OS X anyways?)

INFORMATION

  • Created by FriendFeed
  • FF acquired by Facebook, tornado opensourced afterwards
  • Docs/Examples available at www.tornadoweb.org
  • In use here at Synacor!

The Basics

Tornado Application
Request Handlers
Arguments
Routing

Tornado Application

tornado.web.Application
  • Holds a mapping of URLs to Handlers
  • Used in conjunction with httpserver.HTTPServer
  • Can register static files as well
  • Even can handle virtual hosts!
  • Configuration also registered with Application

REQUEST HANDLERS

  • Accept a request
  • Main methods are HTTP verbs
    • GET
    • POST
    • PUT
    • DELETE
    • HEAD
    • OPTIONS

Request Handlers

  • Able to access request parameters
  • Does the communication heavy lifting
  • self.write() to send data to client
  • self.set_header() set appropriate headers
  • self.set_status() to modify HTTP status codes
  • self.redirect() to send user elsewhere

REQUEST HANDLERS

  • Can also use templating engine
    • Python embedded in templates
    • Sounds familiar
  • Localization is a breeze
    • If you use their templates, at least

REQUEST HANDLERS

import tornado.ioloopimport tornado.web
class BaseHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")application = tornado.web.Application([ (r"/", BaseHandler),])if __name__ == "__main__": application.listen(8080) tornado.ioloop.IOLoop.instance.start()
Start a server on 8080
Respond to GET requests with "Hello, world"
Available on the url "/"

Arguments

(I'm right, you're wrong!)
  • Easy access to request arguments (GET and POST!)
  • self.get_argument('name')
    • ?name=Matt
    • arg 'name' required
    • only returns last defined argument
  • self.get_argument('name', default='John Doe', strip=True)
    • arg 'name' defaults to 'John Doe', strips whitespace
  • self.get_arguments('name') -- return list of arguments 'name'
    • returns list of argument 'name'
    • ?name[]=Matt&name[]=John

ARGUMENTS

File Uploads

  • Access via self.request
  • (also where headers and request body are found)
  • self.request.files['name'][0]
  • where 'name' is the field name
  • provides 'filename', and binary data in 'body'


Note: The file sits in RAM

the use of nginx's upload module is suggested

Arguments

class ArgumentHandler(tornado.RequestHandler):   def get(self):      #name is required      name = self.get_argument('name')      names = self.get_arguments('names') # empty list if not present      self.write('Hello, {0}<br />'.format(name))      if len(names) > 0:           self.write('Your list of names are: {0}'.format(                      ', '.join(names)))      else:           self.write("Provide a list of names in the 'names' argument!")

Routing

  • Registered within Application
  • Pairs a regular expression with a handler
  • Regex mapped to handler
  • Matches provided to handler as parameters
  • Can map as many Regex to a single Handler as you want

Routing

application = tornado.web.Application([
    (r"/", MainHandler),    (r"/hello/world/", HelloWorldHandler),    (r"/hello/([^/]+)/?"), HelloAnythingHandler),])
class HelloWorldHandler(tornado.web.RequestHandler):    def get(self):        self.write("Hello, World")
class HelloAnythingHandler(tornado.web.RequestHandler):    def get(self, anything):        self.write("Hello, {0}".format(anything))
Yes, I could drop the hello world handler and have the same functionality.

Some things to consider


@tornado.asynchronous decorator
means request is asynchronous
does not make code asynchronous
use a mongodb driver such as motor
motor allows for asynchronous mongo operations
if using decorator, must use self.finish
request will hang until client timesout otherwise

Demo Time


Let's see a working demo of a service!


Source code available at:

https://github.com/mattgen88/tornado-demo

Tornado

By Matthew General