Voting Tornado
James Alexander
Our Story Begins

Async Framework
- Originally Developed by Friend Feed
- Scale to Thousands of connections
- Designed for Long Polling and Web Sockets

Four Major Parts
- Web Framework - RequestHandler
- Client / Server Http Implementations (HTTPServer, AsyncHTTPClient)
- IOLoop and IOStream - Building Blocks for HTTP and other protocols
- Coroutine Library - tornado.gen
Hello World
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
Blocking
Occurs when waiting for something to happen. Typically network i/o, disk i/o, mutexes, etc.
Asynchronous
- Returns before it is finished
- Callback argument
- Return a placeholder (Future, Promise, Deferred)
- Deliver to a queue
- Callback registry (e.g. POSIX signals)
Blocking
from tornado.httpclient import HTTPClient
def synchronous_fetch(url):
http_client = HTTPClient()
response = http_client.fetch(url)
return response.body
Call Back
from tornado.httpclient import AsyncHTTPClient
def asynchronous_fetch(url, callback):
http_client = AsyncHTTPClient()
def handle_response(response):
callback(response.body)
http_client.fetch(url, callback=handle_response)
Future
from tornado.concurrent import Future
def async_fetch_future(url):
http_client = AsyncHTTPClient()
my_future = Future()
fetch_future = http_client.fetch(url)
fetch_future.add_done_callback(
lambda f: my_future.set_result(f.result()))
return my_future
Coroutine
from tornado import gen
@gen.coroutine
def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
# In Python versions prior to 3.3, returning a value from
# a generator is not allowed and you must use
# raise gen.Return(response.body)
# instead. The coroutine catches the 'Return'
# exception and treats it like a return value
return response.body
Python 3.5
async def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = await http_client.fetch(url)
return response.body
Web App Structure
- One or more RequestHandler Objects
- A main() method initializing an Application Object
The Application Object holds global configuration, and maps routes to RequestHandlers
Subclassing RequestHandler
class MyFormHandler(tornado.web.RequestHandler):
def get(self):
self.write('<html><body><form action="/myform" method="POST">'
'<input type="text" name="message">'
'<input type="submit" value="Submit">'
'</form></body></html>')
def post(self):
self.set_header("Content-Type", "text/plain")
self.write("You wrote " + self.get_body_argument("message"))
Error Handling
- Tornado will call RequestHandler.write_error to allow you to generate a custom error page.
- Most likely implement this in a BaseRequestHandler to handle for all RequestHandler classes
Templates
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<ul>
{% for item in items %}
<li>{{ escape(item) }}</li>
{% end %}
</ul>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
def get(self):
items = ["Item 1", "Item 2", "Item 3"]
self.render("template.html", title="My title", items=items)
UI Modules
class Entry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", entry=entry, show_comments=show_comments)
from . import uimodules
settings = {
"ui_modules": uimodules,
}
application = tornado.web.Application([
(r"/", HomeHandler),
(r"/entry/([0-9]+)", EntryHandler),
], **settings)
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}
Internationalization
<html>
<head>
<title>FriendFeed - {{ _("Sign in") }}</title>
</head>
<body>
<form action="{{ request.path }}" method="post">
<div>{{ _("Username") }} <input type="text" name="username"/></div>
<div>{{ _("Password") }} <input type="password" name="password"/></div>
<div><input type="submit" value="{{ _("Sign in") }}"/></div>
{% module xsrf_form_html() %}
</form>
</body>
</html>
_("A person liked this", "%(num)d people liked this",
len(people)) % {"num": len(people)}
Authentication
class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
tornado.auth.GoogleOAuth2Mixin):
@tornado.gen.coroutine
def get(self):
if self.get_argument('code', False):
user = yield self.get_authenticated_user(
redirect_uri='http://your.site.com/auth/google',
code=self.get_argument('code'))
# Save the user with e.g. set_secure_cookie
else:
yield self.authorize_redirect(
redirect_uri='http://your.site.com/auth/google',
client_id=self.settings['google_oauth']['key'],
scope=['profile', 'email'],
response_type='code',
extra_params={'approval_prompt': 'auto'})
class MainHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
name = tornado.escape.xhtml_escape(self.current_user)
self.write("Hello, " + name)
Voting Tornado





Demo
Benefits of Tornado
- Designed for Async, long-lived connections
- Native Coroutines with Python 3.5 simplify the call-back style of programming
- RequestHandler hierarchy for common request needs
FIN
- Tornado Documentation: http://www.tornadoweb.org/
- Voting Tornado app available on github: https://github.com/indypy
- Slides available at: slides.com/yanigisawa/voting-tornado
- IO Loop Talk: http://pyvideo.org/pycon-us-2012/more-than-just-a-pretty-web-framework-the-tornad.html
Contact
- jamesralexander.com
- @yanigisawa
- github.com/yanigisawa
Voting Tornado
By James Alexander
Voting Tornado
- 1,478