Top

Tornado

Module - Templates - UI Module

Ist ein skalierbares HTTP Framework mit Non-Blocking Infrastruktur.
Es ist darauf ausgelegt mit vielen gleichzeitigen Verbindungen umzugehen. FriendFeed hat es entwickelt und Facebook hat dieses Framework 2009 unter der Apache-Lizenz 2.0 freigegeben. Tornado bringt einige nützliche Module mit, kann aber auch einfach mit weiteren Frameworks kombiniert werden. Seit der Version 5 nutzt Tornado asyncio und nutzt den gleichen Event loop.

Ein einfacher Server könnte so aussehen. Ein Request Handler wird definiert, dieser wird der Applikation übergeben, die auf einen bestimten Port gesetzt wird und als letztes startet man eine Instanz des Tornado IO Loops.

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_header("Content-Type", "text/plain")
        self.write("tornado response")

application = tornado.web.Application([(r"/", MainHandler)])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Mit dem tornado.options Modul ist es möglich Konfigurationen über die Kommandozeile zu übergeben.

import tornado.ioloop
import tornado.web
import tornado.options

from tornado.options import define, options
define("port", default=8888, help="run on given port", type=int)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_header("Content-Type", "text/plain")
        self.write("tornado response")

application = tornado.web.Application([(r"/", MainHandler)])

if __name__ == "__main__":
    tornado.options.parse_command_line()
    application.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

Einen Server startet man anschliessend auf der Konsole.

python main.py

Auf einem anderem Port mit tornado.options.

python main.py --port=8080

Module

tornado.web
Das Modul enthält die Klassen RequestHandler und Application.
Einen Request Handler definiert man als Klassen-Objekt und leitet es von der Klasse tornado.web.RequestHandler ab.

import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('response main handler')

application = tornado.web.Application([r"/", MainHandler])
...

Die Methoden eines Request Handlers entsprechen den Methoden von HTTP (GET, POST, PUT, DELETE, HEAD, OPTIONS).
Von Tornado werden zusätzlich noch zwei hilfreiche Methoden zur Verfügung gestellt, zum einen prepare() welche vor den anderen Methoden ausgeführt wird und on_finish() welche vor Ende des Requests ausgeführt wird.

Eine weitere hilfreiche Methode ist set_default_headers() diese überschreibt die HTTP Header am Anfang des Requests.

class MainHandler(tornado.web.RequestHandler):
    def set_default_headers(self):
        self.set_header('Content-Type', 'application/json')
    def get(self):
        self.write({"response": "GET", "uri": self.request.uri})

Der Klasse tornado.web.Application übergibt man dann eine Liste mit Request Handler um die Applikation zu definieren.
Diese Liste besteht aus zwei Elementen, der URL und der Request Handler der für Request auf diesen spezifizierte URL zuständig ist.
Anstatt von einfachen URL Definitionen ist es auch möglich Muster zu verwenden.

class UserHandler(tornado.web.RequestHandler):
    def get(self, name):
        self.write(name)

application = tornado.web.Application([(r"/user/(\w+)", UserHandler)])
...

Man kann die Klasse tornado.web.Application auch ableiten und nutzen, hier kann man Klassen instanzieren und damit der Applikation zur Verfügung stellen. Request Handler können dann mit self.application.ourstuff auf die jeweilige Klasse zuzugreifen.

class Application(tornado.web.Application):
    def __init__(self):        
        self.ourstuff = OurStuff()
        handlers = [(r"/", MainHandler)]
        settings = {}
        tornado.web.Application.__init__(self, handlers, **settings)

if __name__ == "__main__":
    app = Application()
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

tornado.httpclient
Mit dem Modul ist es möglich HTTP Request zu versenden. Mit HTTPClient() werden synchrone Anfragen versendet.

import tornado.httpclient

URL = "http://www.dbproductions.de/"

def http_request():
    http_client = tornado.httpclient.HTTPClient()
    try:
        response = http_client.fetch(URL)
    except Exception as e:
        print("Error: %s" % e)
    else:
        print("Status Code: {}, Request Time: {}, Body Length: {}".format(response.code,
                                                                        response.request_time,
                                                                        len(response.body)))

Mit HTTPAsyncClient() werden asynchrone Anfragen versendet.

import tornado.ioloop
import tornado.httpclient

async def async_http_request():
    http_client = tornado.httpclient.AsyncHTTPClient()
    try:
        response = await http_client.fetch(URL)
    except Exception as e:
        print("Error: %s" % e)
    else:
        print("Status Code: {}, Request Time: {}, Body Length: {}".format(response.code,
                                                                        response.request_time,
                                                                        len(response.body)))

tornado.ioloop.IOLoop.current().run_sync(async_http_request)

tornado.escape
Das Modul stellt Funktionen zum maskiere und manipulieren von Strings zur Verfügung.

tornado.options
Stellt die Möglichkeit zur Verfügung Konfigurationen über die Kommandozeile zu übergeben.

Templates

Tornado bringt ein einfaches Template System mit, was HTML mit Python kombiniert.
Es ist möglich Templates zu generieren oder sie aus einem Verzeichnis zu laden.
Zusätzlich können auch Funktionen an Templates übergeben werden.
Seit der Version 2.0 werden Templates automatisch escaped, dies kann mit autoescape=None unterbunden werden.

Mit render() wird ein bestimmtes Template an den Browser gesendet.

self.render('index.html')

Man kann Templates auch dynamisch erstellen und an den Browser senden.

t = template.Template("<html><body>{{ myvalue }}</body></html>")
print(t.generate(myvalue="content"))

Der Loader bietet sich an Templates aus einem bestimmten Verzeichnis zu laden.

loader = template.Loader("/home/tornado")
print(loader.load("index.html").generate(myvalue="content"))

Templates haben eine einfache HTML Struktur mit Platzhaltern, Selektionen oder Iterationen.

<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        <ul>
        {% for item in items %}
            <li>{{ escape(item) }}</li>
        {% end %}
        </ul>
    </body>
</html>

Entsprechende Werte für die Templates werden beim Aufruf von render() einfach mit Komma getrennt übergeben.

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        items = ["Item 1", "Item 2", "Item 3"]
        self.render("template.html", title="My title", items=items)

Der Methode render() kann auch ein Pfad zu den Templates übergeben werden oder man definiert einen Pfad und übergibt ihn beim erstellen der Applikation.

template_path = os.path.join(os.path.dirname(__file__), "templates")
application = tornado.web.Application([(r".*", MainHandler)], template_path=template_path)

Um Dateien wie CSS oder JavaScript mit Templates zu nutzen kann ein Verzeicnis für statische Dateien angelegt werden.

static_path = os.path.join(os.path.dirname(__file__), "static")
application = tornado.web.Application([(r".*", MainHandler)], static_path=static_path)

Ist der Pfad definiert kann man innerhalb von Templates auf den Pfad über die Funktion static_url() zugreifen.

{{ static_url("style.css") }}

Genau auf die gleiche Art kann man auch selbst definierte Funktionen innerhalb von Templates nutzen, dazu übergibt man diese als Wert.

Templates können auch vererbt werden. Man kann Blöcke definieren die von anderen Templates ersetzt werden.

{% extends "main.html" %}
{% block header %}{% end %}

UI Module

Tornado bietet zusätzlich noch die Möglichkeit mit Modulen zu arbeiten.

class HeaderModule(tornado.web.UIModule):
    def render(self):
        return '<h1>Static Header</h1>'

application = tornado.web.Application([(r".*", MainHandler)], ui_modules={'Header': HeaderModule})

Hat man sich solche UI Module definiert kann man sie innerhalb eines Temaplates mit Namen ansprechen.

{% module Header %}

Für Module kann man sich auch seperates Markup definieren, diese Dateien liegen in einem eigenen Verzeichnis unterhalb des Template Verzeichnis.

class HeaderModule(tornado.web.UIModule):
    def render(self, header):
        return self.render_string('modules/header.html', header=header)

Parameter übergibt man einfach beim Aufruf.

{% module Header("Welcome") %}