Aus Linux-Magazin 08/2006

Web.py - ein schlankes Web-Framework für Python

Nach aufgeblähten Frameworks, Monsterbibliotheken und riesigen Webplattformen ist es Zeit für eine Schlankheitskur. Das Python-Framework Web.py setzt dabei neue Maßstäbe: Es besteht aus nur einer Datei. Mit seiner Hilfe lassen sich im Handumdrehen dynamische Websites erstellen.

Es gibt sie noch, die Wunderkinder. Eines von ihnen ist Aaron Swartz [1], der Programmierer des Python-Webtoolkits Web.py. Bereits mit 13 Jahren gewann er einen Preis der Webfirma Arsdigita, mit 14 arbeitete er am RSS-Standard mit. Heute ist er 20 Jahre alt und mit einer eigenen Startup-Firma bereits ein alter Hase im Webgeschäft.

Das Web zurückerobern

Als Teil der Web-2.0-Bewegung versucht Web.py das Internet mit Schlichtheit neu zu beleben. Wenn Ruby on Rails [2] die schlimmsten Auswüchse von J2EE und Ähnlichem eindämmt, dann verwandelt Web.py die Webentwicklung in ein wahres Kinderspiel. Denn damit lässt sich in weniger als 10 Sekunden eine dynamische Webseite erstellen. Mit Web.py hat Aaron Swartz ein überraschend leistungsfähiges und gleichzeitig einfaches Werkzeug entwickelt.

Derzeit liegt Web.py in Version 0.138 vor und ist gerade mal 74 KBytes groß, aber man sollte sich nicht von der Größe täuschen lassen. Beim Programmieren hat Swartz trickreiche Python-Techniken angewandt, zum Beispiel Metaprogrammierung: Web.py erzeugt bei Bedarf dynamisch Methoden und Klassen. Das Skript vereinfacht das Zusammenspiel zwischen Datenbanken und Website, ohne dass der Programmierer Verbindung und Abfrage von Hand programmieren muss.

All das realisiert Web.py über Klassen und Methoden, deren Namen so gewählt sind, dass sie möglichst kurz und damit leicht zu merken sind. Umfangreiche Dokumentation wird damit überflüssig. Die Projekt-Homepage selbst bezeichnet Web.py sogar als Anti-Framework.

Mitspieler

Web.py lässt sich von [3] herunterladen. Zwei weitere Python-Bibliotheken helfen ihm bei der Arbeit: Cheetah [4], um Templates zu erstellen, und Psycopg [5] für den Zugriff auf PostgreSQL-Datenbanken. Die relationale Datenbank PostgreSQL selbst darf natürlich nicht fehlen. Cheetah wie auch Psycopg sind optional, denn Web.py ist sehr modular aufgebaut. Deshalb arbeitet es auch mit anderen Templates-Systemen sowie anderen Datenbanken zusammen, zum Beispiel mit MySQL. Web.py lässt sich mit dem eingebauten Webserver betreiben, Tipps für den Einsatz mit Apache gibt der Kasten “Web.py und Apache”.

Web.py und Apache

Zwar enthält das Skript Web.py einen eigenen Webserver. Wer aber bereits einen Apache-Server betreibt oder seine Site bei einem Provider hostet, wird Web.py wohl lieber in die bereits bestehende Umgebung integrieren. Die Web.py-Homepage empfiehlt für den Produktiveinsatz das heute fast vergessene FCGI und gibt Tipps zur Konfiguration.

Alternativ lässt sich Web.py auch mit dem SCGI-Modul [6] an Apache anbinden, das ähnlich wie FCGI arbeitet. Dabei läuft im Hintergrund ein Serverprozess, zu dem Apache als Client auftritt. Damit kann er Webskripte ausführen, ohne für jeden Request einen neuen Prozess zu starten.

Das SCGI-Paket enthält den Modul-Quellcode für Apache 1 und 2 sowie Python-Code für den SCGI-Server, den Web.py aber nicht verwendet. Zur Installation genügt es normalerweise, in das jeweilige Apache-Verzeichnis zu wechseln und »make« auszuführen. Unter Fedora Core 5 ist es nötig, im Makefile die Namen der Tools »apxs« und »apachectl« anzupassen – im Makefile steht in beiden Namen eine überflüssige 2. Bleibt nur noch die Apache-Konfiguration mit den folgenden Zeilen:

LoadModule scgi_module modules/mod_scgi.so
SCGIMount /webpy 127.0.0.1:4000

Web.py selbst greift für die SCGI-Anbindung auf die Flup-Bibliothek [7] zurück, die deshalb installiert sein muss. Mit der passenden Startoption läuft Web.py dann im Servermodus: »python web.py scgi«.

Website in 10 Sekunden

Der Code in Listing 1 zeigt ein sehr einfaches Beispiel. Das Skript importiert Web.py, das sich deshalb im gleichen Verzeichnis befinden muss oder an einer Stelle, wo der Python-Interpreter es finden kann. Um den Server zu starten, genügt der Befehl »python helloworld.py«. Im Apache-Fall (siehe Kasten) folgt noch das Schlüsselwort »scgi«.

Listing 1:
»helloworld.py«

01 import web
02 pfade = (
03      '/', 'hello'
04 )
05 
06 class hello:
07    def GET(self):
08       print "<html><head>"
09       print "<title>hello<title><head>"
10       print "<body><h1>Hello world!</h1>"
11       print "</body></html>"
12 
13 web.internalerror = web.debugerror
14 
15 if __name__ == '__main__':
16    web.run(pfade, web.reloader)

Das Skript importiert zuerst von Web.py das Modul »web«, um Zugriff auf alle Funktionen zu gewährleisten. Danach definiert es eine Liste mit dem Namen »pfade«, die Paare von Pfaden und Python-Klassen enthält. Jedes Mal, wenn ein Browser das Skript aufruft, verwendet es das zum Pfad passende Objekt. In den folgenden Zeilen definiert das Skript eine Klasse mit dem Namen »hello«. In Web.py sind es die Klassenmethoden, die auf Anfragen antworten. Dafür definiert man eine Methode »GET« oder »POST« – je nach Typ der Anfrage, die zu bedienen ist. Die Standardfunktion »print« erzeugt die Antwort. Schließlich übergibt Zeile 16 dem Webserver die Liste mit den eingestellten Pfaden.

Der eingebaute Web.py-Server startet per Default auf dem Port 8080, wozu keine Root-Rechte nötigt sind. Ruft man die Seite auf, erscheint im Browserfenster »Hello world!«. Über die Kommandozeile lässt sich der Port ändern, auf dem der Server startet, zum Beispiel »python helloworld.py 8003«.

Der einfachste Weg, Informationen auszugeben, führt über die Python-Funktion »print«, die im obigen Beispiel einfach HTML-Code ausgibt. Schwierig wird es, wenn die HTML-Dateien komplizierter sind und sich Darstellung (HTML) und Steuerung (Python) vermischen. Deshalb arbeitet Web.py mit Templates.

Mehr Dynamik

Das folgende Beispiel ist etwas umfangreicher als das vorangegangene Hello World und demonstriert die Verwendung von Templates. Ein Verzeichnis mit dem Namen »templates« innerhalb des Ordners, in dem »helloworld.py« gespeichert ist, soll die Templates aufnehmen. In dieses Verzeichnis kommt eine Datei namens »hello.html« mit dem Inhalt aus Listing 2. Der zugehörige Code steht in Listing 3. Der Inhalt der Methode »GET« der Klasse »hello« besitzt nun zusätzlich den Parameter »name«.

Listing 2:
»hello.html«

01 <html>
02 <head><title>Hello</title></head>
03 <body><h1>
04 #if $name
05   Hello $name
06 #else
07   Hello World!
08 #end if
09 </h1><body></html>

Listing 3:
»helloworld2.py« (Ausschnitt)

01 pfade = (
02     '/(.*)', 'hello'
03     )
04 
05 class hello:
06     def GET(self,name):
07         web.render ('hello.html')

Die Methode »web.render()« lädt das übergebene Template »hello.html« und interpretiert es im Kontext des Objekts. Durch die Regular Expression der »pfade« in Zeile 2 erhält die Variable »name« automatisch die Zeichenkette »Joe« zugewiesen, wenn der Browser die URL »http://Server/Joe« abruft.

Bei Listing 2 fällt vielleicht die etwas merkwürdige Syntax auf: Die Zeilen, die mit »#« beginnen, stammen von Cheetah, einem verbreiteten Template-System für Python. Dessen oberstes Ziel ist Einfachheit, weshalb es »#« für die Codezeilen und » für die Variablen verwendet statt der berühmt-berüchtigten zweigeteilten Kennzeichner »<%« und »%>« von PHP oder Java Server Pages. Web.py benutzt Cheetah, um Templates zu erzeugen, die möglichst einfach zu lesen und zu ändern sind. Der Vorteil der Templates ist, dass die Änderungen sich auswirken, ohne den Server erst neu starten zu müssen.

Philosophie

Wem beim Schreiben des Templates oder der Datei ein Fehler unterlaufen ist, der hat bereits bemerkt, wie informativ Web.py bei Fehlermeldungen werden kann. Es ist ein Teil der Enwicklungsphilosophie von Web.py, Fehler möglichst ausführlich anzuzeigen, damit Probleme früh zu erkennen sind (Abbildung 1). Schließlich beruht eine schnelle Programmentwicklung auf zwei Faktoren: Möglichst wenig schreiben und Fehler möglichst schnell korrigieren.

Abbildung 1: Web.py liefert ausführliche Fehlermeldungen. Durch Anklicken der kleinen Dreiecke lassen sich lokale Variablen einblenden.

Abbildung 1: Web.py liefert ausführliche Fehlermeldungen. Durch Anklicken der kleinen Dreiecke lassen sich lokale Variablen einblenden.

Aaron Swartz hat bei Web.py beide Punkte beachtet, der Code ist sehr kompakt geschrieben und leicht zu lesen, die Namen der Funktionen sind kurz und es gibt viele Konventionen statt unnötiger Flexibilität. Solche Namensübereinkünfte bestimmen auch das Design von Ruby on Rails, das aber wesentlich umfangreicher als Web.py ist.

Vor einiger Zeit hat der Python-Erfinder Guido van Rossum einen Artikel veröffentlicht, in dem er schrieb, dass sein neuer Job bei Google das Erzeugen einer Webseite für das Intranet beinhaltet. Dazu hat er verschiedene Frameworks angeschaut, doch alle schienen ihm zu komplex. Rossum ist kein Entwickler von Webanwendungen, sondern von Programmiersprachen, er bevorzugt daher einfache Lösungen. Nach seiner Meinung ist Web.py eins der wenigen Frameworks, die die Philosophie von Python übernommen haben.

Intelligente Pfade

Listing 4 erweitert die Regular Expression der Variablen »pfade« um einen neuen Unterausdruck, der vom ersten durch ein »/« getrennt ist. Entsprechend besitzt die Get-Methode eine weitere Variable »nachname«, die Web.py beim Aufruf durch den Browser mit der zweiten Pfadkomponente auffüllt. Eine passende HTML-Template-Datei, die beide Variablen einsetzt, liegt auf der Linux-Magazin-Website [9].

Listing 4:
»helloworld2b.py« (Ausschnitt)

01 pfade = (
02     '/(.*)/(.*)', 'hello'
03     )
04 
05 class hello:
06     def GET(self,name,nachname):
07         web.render ('hello2.html')

Nach Eingabe des Standardpfads »http://localhost:8080« erscheint wieder die Nachricht »Hello world!«, weil das Template überprüft, ob die beiden Variablen »name« und »nachname« vorhanden sind. Sind keine zu finden, wird das »else« nach dem »if« ausgeführt. Mit dieser Methode lässt sich ein beliebiges Schema mit Pfaden erzeugen, als ein Beispiel: »http://localhost:8080/datum/nachricht-id«.

Web.py unterstützt MVC

Das Entwicklungsschema MVC (Model View Controller) ist bei der Webentwicklung in letzter Zeit ziemlich populär geworden. Dieses Schema verteilt die Entwicklung eines Schemas auf drei Komponenten:

  • Präsentation/View ist das Template, das den Code zur
    Darstellung erzeugt, also HTML oder XML.
  • Steuerung/Controller ist die Klasse »hello«, die
    Informationen aufnimmt und die Umgebung für die Darstellung
    (View) vorbereitet.
  • Modell ist die reine Information, die normalerweise in einer
    Datenbank gespeichert wird, die zudem die Integrität der Daten
    gewährleistet.

MVC teilt die Aufgaben in drei Teile, jeder unabhängig von den anderen. Die Modifizierung eines Teils bedeutet nicht, auch die anderen Teile ändern zu müssen. Dadurch wächst auch die Chance für eine Wiederverwertung von Code, denn ein Controller kann zum Beispiel für verschiedene Darstellungen verwendet werden. Eine recht häufige Anwendung ist ein Controller, der eine Liste von Artikeln für zwei unterschiedliche Ansichten erzeugt: zum einen HTML und zum anderen RSS.

Psycopg für PostgreSQL

Eine der Voraussetzungen für das folgende Beispiel ist die Bibliothek »Psycopg«, ein kleines und effizientes Interface für PostgreSQL-Datenbanken, das für den intensiven Gebrauch von Threads optimiert ist. Das gezeigte Beispielskript interagiert in keinem Moment selbst mit »Psycopg«, diese Aufgabe übernimmt hinter den Kulissen Web.py.

Der SQL-Code für die Datenbanktabelle »notizen« in Listing 5 lässt sich zum Beispiel im PostgreSQL-Interface »psql« mit dem Befehl »i notizen.sql« einlesen. Listing 6 zeigt den zugehörigen Code von »notizen.py«, der in Zeile 21 die Parameter für die Konfiguration der Datenbank setzt. Das Skript baut eine Datenbank-Verbindung nicht explizit auf, das erledigt Web.py im Hintergrund automatisch.

Den Inhalt von Tabellen wie »notizen« fragen Webprogrammierer normalerweise mit einer SQL-Anwendung »SELECT« ab. Bei Web.py können sie die entsprechende Python-Funktion einfach in die Methode »GET« integrieren (Zeile 9). So wird in der Variablen »notizen« das Ergebnis der »select«-Anfrage über die gleichnamige Tabelle gespeichert. Von diesem Moment an stehen die »notizen« dem Template »notiere.html« zur Verfügung. Den Code, der eine HTML-Liste (»UL«) erzeugt, zeigt Listing 7. Die Template-Sprache Cheetah iteriert in einer For-Schleife über die »notizen« (Zeilen 7 bis 9).

Listing 5:
»notizen.sql«

01 CREATE TABLE notizen (
02   id serial primary key,
03   titel text,
04   created timestamp default now()
05 );

Listing 6:
»notizen.py«

01 import web
02 pfade = (
03     '/', 'index',
04     '/neu', 'neueNotiz'
05     )
06 
07 class index:
08     def GET(self):
09         notizen = web.select("notizen")
10         web.render ('notiere.html')
11 
12 class neueNotiz:
13     def POST(self):
14         params = web.input()
15         n = web.insert('notizen',titel = params.titel)
16         web.seeother('./#t'+str(n))
17 
18 web.internalerror = web.debugerror
19 
20 if __name__ == '__main__':
21     web.db_parameters = dict(dbn='postgres', user='heinz', pw='geheim', db='test')
22     web.run(pfade, web.reloader)

Bequeme Webformulare

Um die Webseite interaktiv zu gestalten, wird ein Eingabeformular erstellt, in dem der Anwender neue Notizen hinzufügen kann. Die Parameter des Form-Elements sind »method=”post” action=”/neu”« (Listing 7, Zeile 11). Das Formular enthält ein Eingabefeld mit dem Namen »titel« und einen Submit-Button. Beim Drücken von »Submit« schickt der Browser den Inhalt des Formulars an den Pfad »/neu«. Der entsprechende Eintrag in »pfade« ordnet dem Pfad die Klasse »neueNotiz« zu (Listing 6, Zeile 4). Deren Methode »POST« führt Web.py beim Abschicken des Formulars aus.

Listing 7:
»notiere.html«

01 <html>
02   <head>
03     <title>Meine Notizen</title>
04   </head>
05   <body>
06     <ul>
07       #for notiere in $notizen
08       <li id="t$notiere.id">$notiere.titel</li>
09       #end for
10     </ul>
11     <form method="post" action="/neu">
12       <p>
13         <input type="text" name="titel" />
14         <input type="submit" value="Neue Notiz" />
15       </p>
16     </form>
17   </body>
18 </html>

Um die Daten zu übernehmen, die der Browser schickt, stellt Web.py einige Funktionen zur Verfügung, die den Programmierern einige Arbeiten abnehmen. Die Methode »web.input()« speichert die empfangene Information in einer Variablen (Listing 6, Zeile 14). Diese Variable ist ein Python-Objekt, das ein Attribut für jeden Parameter enthält, der durch »POST« übergeben wurde. An den Titel der Notiz gelangt ein Skript also, indem es einfach »params.titel« ausliest.

Um die eingegebenen Informationen in der Datenbank zu speichern, genügt bei Web.py die Methode »web.insert()«. Sie entspricht funktional dem Kommando »INSERT« von SQL, nur muss sich der Programmierer nicht um die SQL-Syntax kümmern. Die Methode erwartet als Parameter den Namen der Tabelle und eine Gruppe von Variablen, die den Spalten der Tabelle entsprechen. Damit kann ein Entwickler die Werte anhand ihrer Namen an die Spalten übergeben und muss sich nicht um die Reihenfolge der Parameter kümmern.

Die Methode »web.insert()« gibt als Ergebnis eine »ID« zurück, die in der Datenbank der neuen Zeile zugewiesen wurde. Damit lässt sich später im eigenen Skript der Datenbankeintrag referenzieren. Mit wenigen, leicht verständlichen Zeilen Programmcode ist so eine Webseite entstanden, auf der Benutzer neue Notizen hinzufügen können.

Zusammenfassung

Mit Web.py lassen sich schnell und einfach dynamische Webseiten erzeugen. Es ist ein leistungsfähiges Werkzeug, um Prototypen zu erstellen und neue Konzepte auszuprobieren, eignet sich aber auch für Produktions-Websites, wie zum Beispiel [www.reddit.com] demonstiert. Auch [www.infogami.com], eine Website des Startup “Not A Bug” von Web.py-Entwickler Aaron Swartz, läuft damit (Abbildung 2). Die Verbreitung des Anti-Framework Web.py nimmt genauso schnell zu wie die Anzahl der Beteiligten. Alles in allem ein interessantes Projekt, dessen weitere Entwicklung zu beobachten lohnt.

Abbildung 2: Eine Website, die Websites erzeugt: Aaron Swartz' Startup-Firma "Not A Bug"setzt dazu auf das Framework Web.py.

Abbildung 2: Eine Website, die Websites erzeugt: Aaron Swartz’ Startup-Firma “Not A Bug”setzt dazu auf das Framework Web.py.

Infos

[1] Webseite von Aaron Swartz: [http://www.aaronsw.com/]

[2] A. Röhrl, “Webprogrammierung mit Ruby und Rails”: Linux-Magazin 12/04, S. 100

[3] Download von Web.py: [http://webpy.org/web.py]

[4] Cheetah Templates: [http://cheetahtemplate.org]

[5] Psycopg, Python-Adapter für PostgreSQL: [http://initd.org/projects/psycopg1]

[6] SCGI-Modul für Apache: [http://www.mems-exchange.org/software/scgi]

[7] Flup: [http://www.saddi.com/software/flup/]

[8] Listings: [https://www.linux-magazin.de/Service/Listings/2006/08/Web.py]

Der Autor

José María Ruiz arbeitet derzeit an seiner Abschlussarbeit in technischer Informatik. Seit acht Jahren verwendet und entwickelt er freie Software, seit zwei Jahren ist er spezialisiert auf FreeBSD.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben