My Universe Logo

My Universe Blog » Entries Tagged as code

Python für Webanwendungen 4: Lieferservice

Posted by Jesco Freund at Oct. 29, 2010 9:56 p.m.

Seit dem vorherigen Teil hat es nun ein paar Tage gedauert - aber hier ist er nun, der versprochene letzte Part. Eine funktionierende Webanwendung ist nun vorhanden, aber noch läuft sie nur im Entwicklungsmodus, mit dem CherryPy-eigenen Webserver. Für eine Produktivumgebung ist definitiv ein gestandener Webserver vorzuziehen, der den statischen Content besser und schneller ausliefert, robust und praxiserprobt ist und sich über entsprechende Startmechanismen automatisch als Dienst starten lässt.

Mit WSGI-Anwendungen und FastCGI hatte ich bisher kein Glück – zu instabil lief dieses Konstrukt, wenn es überhaupt ans Laufen kam. Richtig schnell und robust hingegen laufen bei mir diverse WSGI-Anwendungen in Kombination mit dem Apache Webserver und mod_wsgi. Unter FreeBSD beispielsweise lassen sich beide Pakete bequem aus den Ports installieren, auch Debian bringt ein entsprechendes Paket mit.

continue reading Python für Webanwendungen 4: Lieferservice

1 comment | Defined tags for this entry: Apache, CherryPy, code, development, python

Python für Webanwendungen 3: Türsteher

Posted by Jesco Freund at Oct. 26, 2010 11:29 a.m.

Mit ein wenig Phantasie kann die Anwendung aus Teil 2 hübsche HTML-Seiten rendern und verschiedene Themes verwenden. Doch was, wenn eine dieser Ansichten vor unbefugten Augen geschützt werden soll? Sicher, CherryPy bringt eine rudimentäre Authentifizierung über HTTP-Auth mit – keine sehr elegante Lösung. Von einer modernen Webanwendung erwartet man eher ein Login-Formular.

Das wäre mit einem hübschen Template und einer Controller-Methode ja noch fix erledigt. Doch wogegen authentifiziert eine Anwendung eigentlich? Viele Webanwendungen haben die in meinen Augen sehr schlechte Angewohnheit, auf eine eigene Userverwaltung zu bauen. Als Backend kommen häufig Datenbank-Tabellen in einem RDBMS zum Einsatz. Ich finde das äußerst nervig, denn i. d. R. lassen sich diese User-Informationen nicht oder nur mit aufwändigen Hacks von anderen Anwendungen nutzen – mit der Konsequenz, dass User für jede Applikation ein eigenes Login-Handle benötigen.

Sowohl für User als auch Admin ist es viel angenehmer, wenn jedem User (z. B. in einem Unternehmen, einer Organisation, einer Community, …) genau ein Handle zugeordnet ist. Nur ein Passwort, das vergessen werden kann, Änderungen von Attributen (Nickname, Name, Mail-Adresse, …) schlagen in allen Anwendungen durch, und als Admin muss man sich nur mit einer Userdatenbank herumschlagen. LDAP bietet genau diese Möglichkeit: Alle User-Handles an einem Ort zu speichern, und diese mit zusätzlichen Attributen auszustaffieren - je nachdem, was die verschiedenen Anwendungen so benötigen. Auch der Zugriff auf einzelne Anwendungen oder Anwendungsbereiche lässt sich über LDAP-Gruppen regeln.

In diesem Teil geht es daher um die Implementierung eines eigenen Authentifizierungs-Moduls für unsere Webanwendung, das gegen einen LDAP-Verzeichnisdienst authentifiziert. Um das Beispiel erst mal nicht zu komplex gestalten, soll das Modul nur die übergebenen Credentials prüfen, um den Zugang zur Applikation zu erlauben oder zu verwehren. Komplexere Prüfungen (etwa die Zugehörigkeit zu einer bestimmten LDAP-Gruppe) lassen sich dann später leicht einbauen.

continue reading Python für Webanwendungen 3: Türsteher

No comments | Defined tags for this entry: CherryPy, code, development, Genshi, python

Python für Webanwendungen 2: Genshifizierung

Posted by Jesco Freund at Oct. 25, 2010 4:34 p.m.

Die im vorhergehenden Teil vorgestellte Anwendung ist in ihrem Können bisher eher sparsam – keine großartige Anwendungslogik, keine komplexe Steuerung, keine hübsche Darstellung. Genau diesem letzten Askpekt möchte ich mich im zweiten Teil der HowTo-Serie zu Python und Webanwendungen widmen. Für Webanwendungen (fast) unumgänglich ist die Darstellung der Oberfläche mittels (X)HTML. Die finsteren Zeiten, in denen man sich in CGI-Anwendungen das HTML-Dokument noch mittels print() zusammengebaut hat, sind glücklicherweise längst vorbei.

Heute benutzt man dafür Template-Systeme. Grundsätzlich lassen sich Template-Systeme in drei Kategorien einteilen:

  1. Als Platzhalter-Templates bezeichne ich Template-Systeme, die darauf beruhen, in einem (wie auch immer gearteten) Template-Dokument auf bestimmte Weise gekennzeichnete Platzhalter auszutauschen. Die Verarbeitung dieser Templates basiert meist auf Regulären Ausdrücken oder auf einfachen Suchen/Ersetzen-Operationen.
  2. XML-Templates würde ich Templates nennen, die einen eigenen XML-Namensraum definieren und dadurch dem xhtml-Namensraum im selben Dokument nicht in die Quere kommen. Die Verarbeitung erfolgt über einen XML-Parser, Xpath und ggf. XSLT-Transformationen.
  3. Kompilierte Templates kenne ich eigentlich nur aus einem Bereich, und hier stellen sie für mich einen Sonderfall der XML-Templates dar. Das Template wird beim Start der Applikation geladen und in eine Controller-Funktion umgewandelt, die aus den im Template eingebetteten Code-Schnipseln und print()-Anweisungen für die HTML-Dekoration besteht.

Für Python steht eine Reihe von fertigen Template-Systemen aus den Kategorien Platzhalter- und XML-Templates zur Verfügung. Gerade bei arbeitsteiliger Entwicklung bietet sich der Einsatz eines XML-Template Systems an. Auch wenn man sich dadurch zunächst etwas mehr Komplexität einhandelt – die höhere Flexibilität macht das in großen Teilen wieder wett. Aus dieser Überlegung heraus kommt in der Beispielanwendung das Template-System Genshi zum Einsatz.

continue reading Python für Webanwendungen 2: Genshifizierung

No comments | Defined tags for this entry: CherryPy, code, development, Genshi, python

Python für Webanwendungen 1: Projektstruktur

Posted by Jesco Freund at Oct. 25, 2010 1:03 p.m.

Hier nun wie angedroht der erste Teil, in dem ich einmal vorstellen möchte, wie man sich selbst basierend auf CherryPy und Genshi das Grundgerüst für eine Webanwendung aufbaut. Dabei geht es mir jedoch nicht um's Wiederkäuen der Dokumentation (wie etwa des Genshi Tutorials) – die darf jeder schön selber lesen ;-). Vielmehr geht es mir darum, ein Grundgerüst zu beschreiben, das eine belastbare Basis auch für komplexere Anwendungen darstellt. Das kommt IMHO in der angebotenen Dokumentation einfach zu kurz – ein „Hello, World“ mit dem gesamten Code in einer einzigen Python-Datei ist sicherlich kein Best Practice für die Entwicklung von Webanwendungen…

Zunächst einmal möchte ich kurz darauf eingehen, warum ich CherryPy und Genshi gewählt habe. Bei Genshi liegt der Fall relativ einfach: In meinen Augen ist es die derzeit mächtigste und professionellste Framework-unabhängige Template Engine, die für Python zu haben ist. Anders als bei vielen anderen Template-Engines üblich verwendet Genshi einen eigenen XML-Namensraum, so dass Templates während der Entwicklungsphase als ganz normale XHTML-Seiten behandelt werden können – für Designer und HTML-Bastler ein großes Entgegenkommen.

Bei CherryPy liegen die Pro's und Con's dichter beieinander. Auf der einen Seite bringt CherryPy schon Funktionalität mit, die ich eigentlich gar nicht haben will. Auf der anderen Seite ist CherryPy ein aktiv entwickeltes und gepflegtes Projekt, und genau die Dinge, die ich benötige, bringt es ebenfalls mit: WSGI-Fähigkeit, eine einfache Schnittstelle zum Request, URL-Mapping und Sessions. Als zusätzlicher Leckerbissen bringt CherryPy auch noch eine eigene Konfigurations-Engine mit und bietet mit der Tools-Schnittstelle ein zugleich einfaches und mächtiges Werkzeug für eigene Erweiterungen. Eine Alternative wäre gewesen, Flup als WSGI-Basis zu verwenden und URL-Mapping, Sessions und Request-Handling selbst zu implementieren. Die Wahrscheinlichkeit, dabei Bugs und ggf. eine Angriffsfläche gleich mit einzubauen, halte ich jedoch für relativ hoch – daher meine Entscheidung für CherryPy.

continue reading Python für Webanwendungen 1: Projektstruktur

No comments | Defined tags for this entry: CherryPy, code, development, Genshi, python

Python für Webanwendungen: Einführung

Posted by Jesco Freund at Oct. 25, 2010 12:49 p.m.

Django ist derzeit eines der mächtigsten RAD-Frameworks für's Web. Genau wie seine Konkurrenten (Ruby on Rails, Pylons, TurboGears & Co.) ist Django jedoch sehr stark auf Anwendungsfälle fixiert, in denen Datensätze in bzw. aus einem RDBMS angelegt, bearbeitet, angezeigt und gelöscht werden. In anderen Anwendungsfällen dominiert heute Java – wirklich gerechtfertigt ist das aber nicht.

Gerade Python macht es einem Entwickler dank WSGI und einer Vielzahl nützlicher Bibliotheken sehr leicht, sein eigenes Framework zusammenzusetzen. Dabei beziehe ich mich mit „Framework“ auf eine bestimmte Strukturierung der Anwendung, durch die Standard-Aufgaben (wie etwa das Rendern von HTML-Seiten) extrem einfach zu implementieren sind. Da ein ganzes Framework zu viel für einen einzelnen Blog-Eintrag ist, habe ich dieses HowTo in vier Stücke zerhackt:

  1. Aufbau einer Projektstruktur
  2. Integration von Genshi
  3. Authentifizierung gegen LDAP
  4. Deployment in einer Produktivumgebung

Zu guter Letzt noch ein kleiner Hinweis: Meine Code-Beispiele beziehen sich auf Python 2.7 – daher ist bei älteren Versionen (insbesondere <2.6) Vorsicht geboten. Gerade wenn Exceptions ins Spiel kommen, halte ich mich an die neue as-Notation gemäß PEP 3110.

No comments | Defined tags for this entry: CherryPy, code, development, Django, Genshi, python, TurboGears

MVC ohne Java, Python, Ruby & Co

Posted by Jesco Freund at Sept. 15, 2010 10:23 p.m.

MVC kann man auch jenseits der klassischen objektorientierten Programmiersprachen umsetzen. Derzeit bastle ich an einer Implementierung in C – teils als Proof of Concept, teils für einen bestimmten Einsatzzweck – auf jeden Fall aber aus Spaß an der Freude.

Die Idee hinter Stormrose (so habe ich das Projekt getauft) ist relativ einfach: Ein kleiner, schlanker HTTPd nimmt Requests entgegen, ermittelt (ähnlich wie bei Django) anhand der URI den zuständigen Controller, übergibt diesem den Request und lässt vom Controller dann eine Antwort generieren, die zurück an den anfragenden Client übertragen wird.

Controller sind dabei einfache C-Funktionen, die einem bestimmten Schema entsprechen. Zu Demonstrationszwecken habe ich einen „Hello World“ Controller schon mal eingebaut, der – wie nicht anders zu erwarten – nur ein einfaches „Hello, World“ an den Client sendet. Ein Controller für statische Inhalte soll noch hinzukommen, so dass Stormrose theoretisch auch als „normaler“ Webserver einsetzbar wäre. Weitere Controller werden dann im Zuge der Entwicklung einer Stormrose-Applikation erstellt.

Auf Seiten der Views habe ich noch keine Aktiv-Posten zu verbuchen – hier muss ich mir noch etwas sinnvolles ausdenken. Einen (schwergewichtigen) XML-Parser möchte ich aber tunlichst vermeiden – sonst kann man ja gleich JSPs verwenden…

Für das Model gilt im Prinzip ähnliches – hier möchte ich allerdings dem Anwendungsentwickler wirklich komplett freie Hand lassen. Einen ORM-Mechanismus wird es nicht geben – Frameworks mit diesem Dreiklang (ORM, Templates, Glue) gibt es schon zu Hauf. Stormrose ist eher für Applikationen gedacht, die eben nicht hauptsächlich Datensätze aus einem RDBMS bearbeiten.

Damit wird es sicherlich eher ein Nieschendasein führen – wer es sich trotzdem mal ansehen möchte, kann sich den Code jederzeit per Subversion auschecken (https://svn.my-universe.com/stormrose/trunk). Derzeit ist der Code aber noch sehr unreif und befindet sich in ständiger Entwicklung. Momentan taugt er also bloß zum Experimentieren. Sobald die Controller-API stabil ist, werde ich dann auch ein kleines How-To zur Entwicklung einer kleinen Stormrose-Applikation veröffentlichen.

No comments | Defined tags for this entry: C, code, development, programming, Stormrose

cdeploy Has Entered FreeBSD Ports

Posted by Jesco Freund at May 9, 2010 5:32 p.m.

The utility cdeploy (developed by me within the scope of the root-tools project) has been committed to the FreeBSD Ports. From now on, installing cdeploy on FreeBSD is as simple as:

cd /usr/ports/sysutils/cdeploy
make install clean

Originally, cdeploy has been developed to deploy centrally (e. g. in a scm) maintained configuration files onto a target system. A sample configuration for this purpose is described in the RootForum wiki (German). Of course, you can abuse cdeploy for any other kind of files… ;-)

No comments | Defined tags for this entry: code, development, FreeBSD, ports, root-tools, RootUtils

The Optimal Archive Format for Backtory

Posted by Jesco Freund at Feb. 13, 2010 9:11 p.m.

During the last few days, I've been thinking about what archive format to use with Backtory. First, everything seemed clear: I intended to use tar (resp. USTar) – a wide-spread standard, meaning backup data would have been accessible with any standard-compliant tar implementation as present among any Unix system I know. However, looking closer at the specification of the tar file format, it shows some weaknesses which turns it to be a bad choice for a differential backup tool. The worst of them are:

  • Tar archives have no index or table of content. This means the whole file has to be scanned to find out about its content, and extracting only one particular file means the same.
  • Tar only reserves 100 bytes for file names. Working with longer path names is possible, yet painful
  • Tar header information is encoded in ASCII, making it difficult working with international character sets
  • Tar archives do not handle arbitrary meta data, meaning
    • it is not possible to encode files in any way (encryption, compression, …) before adding them to an archive
    • it is impossible to store extended information like ACLs for a file without resorting to ugly workarounds (like creating .meta files for each file)
  • When it comes to compression, tar files can only be post-processed with a compressor, meaning the entire archive has to be decompressed before it can be scanned

This rant may give you some more reasons why tar is really a bad choice, but to sum it up: I consider it too painful downloading a complete backup archive to the local harddisk, decompress it there and then scan through the whole file just to restore one single file. Wouldn't it be much smarter if Backtory just had to download the archive header and then check which part of the archive actually has to be downloaded and post-processed (e. g. decompressed)? Even with stupid old FTP this would be possible – just by aborting RETR after x received bytes and using REST plus ABOR again to fetch a specific part of a file.

So how would the optimal archive format for Backtory look like? I think this can be best described by a list of requirements:

  • The archive must be indexed. Minimum requirement for an index would be a table providing (relative) path names and offsets to meta data and data section of the file. The index table must be located at a predictable or easily determinable position within the archive file.
  • For each file, a bunch of standard meta data must be stored (virtually the information provided by lstat)
  • The archive format must allow arbitrary meta data for each file. Some of them could be standardized (e. g. encoding or compression method), others may vary from application to application (e. g. ACL data, MAC labels, encryption method, …)
  • It must be easy and cheap to extend an archive. To be more precise, I would consider it inacceptable if something had to be inserted at the beginning of an archive file, entrailing a shift of all successive bytes, meaning all offset data have to be reacalculated and nearly all data in the file reorganized on the file system. “Easy” and “cheap” would be perfectly achieved if an archive could be extended even via FTP (but I consider this very unlikely to become true without breaking the other requirements).
  • Recoverability in case of a damaged index: By doing a single pass scan over the meta data and content data blocks, it should be possible to regain all information necessary to rebuild the archive index.

Well, that's it. However, I have not found any yet-existing archive format that covers these requirements (at least none which is patent-free and has at least one open source implementation). And before anyone starts rhapsodizing about xar, I'd like to state why I see xar to be unfit for Backtory:

  • The meta data block is in XML, which means you need a fat parser to process it.
  • The heap (all file content data) is useless without the meta data ⇒ recovery of a damaged toc is anything else than easy (I doubt it's possible at all).
  • The toc (XML meta data) is located at the beginning of a xar container. When extending a xar archive, all data inside must be relocated since its toc has to be extended.

I guess I have to design my own container format for Backtory. However, if I really should do so, I would implement it as a library independent of backtory and give it its own CLI tool. I already got some idea how the archive container could look like, but there are still some details I have to work out. Stay tuned, I'll keep you informed about what I'll do and how I will implement it…

No comments | Defined tags for this entry: backtory, code, programming

Schöner Schreiben

Posted by Jesco Freund at Nov. 29, 2009 11:32 p.m.

reStructuredText und Pygments sind ein prima Gespann. Mit ihnen kann man z. B. Blog-Inhalte oder Wiki-Seiten rendern, oder gleich ein komplettes Dokumentationspaket bauen. Ich selbst verwende das Pärchen gerade für den Rewrite meiner Homepage. Dabei ist mir allerdings aufgefallen, dass der rst-Renderer leider keine typographischen Zeichen rendert, also z. B. aus ein – macht oder typographische Anführungszeichen („“ statt „“) erzeugt.

Das ließe sich eigentlich mit einfachen .replace()-Aufrufen auf dem Rendering-Ergebnis nachrüsten – allerdings geht das nicht mehr, sobald mit Pygments gehighlightete Code-Blöcke (oder andere unformatierte Blöcke) mit von der Partie sind. Aber hey, es ist doch Python! Mit ein bisschen Code funktioniert das nachträgliche Umschreiben recht gut:

from docutils.core import publish_parts

# Diese einfache Ersetzen-Funktion erstzt hier im Beispiel nur
# -- und ---, könnte aber ohne weiteres erweitert werden.
def ent_replace(text):
    text = text.replace(" -- ", " &ndash; ")
    text = text.replace(" --- ", " &mdash; ")
    return text

# Diese Funktion wird mit dem ReStructuredText-Input gefüttert
# und übernimmt das Aufrufen der docutils-Funktionen
def render(text):
    parts = publish_parts(source=text, writer_name='html')
    frags = parts['html_body'].split('<pre>')
    content = ent_replace(frags.pop(0))

    for snatch in frags:
        content += "<pre>"
        content += snatch.split('</pre>')[0]
        content += "</pre>"
        content += ent_replace(snatch.split('</pre>')[1])

    return content

Zur Erklärung: Die render()-Funktion splittet das von publish_parts() gelieferte Ergebnis anhand der <pre>-Tags auf. Das erste Element der so entstehenden Liste ist grundsätzlich kein Preformatted Text – dies wird durch den HTML Writer aus den docutils gewährleistet. Das erste Element kann daher unbesehen aus der Liste entfernt und an die Ersetzen-Funktion verfüttert werden; das Ergebnis wird als Startwert für den Rückgabewert festgehalten.

Die verbleibenden Elemente in der Liste (frags) bestehen nun immer aus einem Anfangsteil Preformatted Text, dem abschließenden </pre>-Tag und ggf. einem Rest „normalem“ Text. Jedes der verbleibenden Elemente muss nun anhand von </pre> erneut gesplittet werden. Der vordere Teil des Split-Ergebnis' muss dabei unverändert an den Rückgabewert angehängt werden (die zugehörigen Tags müssen jedoch wieder ergänzt werden, da sie durch den Split verloren gehen), während der jeweils hintere Teil vorher einmal die Ersetzen-Funktion passieren muss.

No comments | Defined tags for this entry: code, python

Portable Shell-Skripte mit absoluten Pfaden

Posted by Jesco Freund at Oct. 3, 2009 2:23 p.m.

Gute Shell-Skripte sprechen Binaries mit dem absoluten Pfad an. Dadurch wird das Skript unabhängig von der Umgebungsvariablen $PATH und funktioniert auch manierlich, wenn es beispielsweise üder die Crontab oder unter einem andere User aufgerufen wird. Soweit die Theorie, doch in der Praxis gibt es einen gemeinen Fallstrick, der den Wanderer zwischen den Unix-Welten das Leben bzw. Skripten schwer macht. Betrachten wir einmal das folgende Beispiel:

#!/bin/sh

b_uname="/bin/uname"
b_awk="/bin/awk"

name=`$b_uname -n | $b_awk -F. '{print $1}'`

echo "Das System heisst ${name}."
exit 0

Dieses kleine Skript ist ein (zugegebenermaßen etwas umständlicher) Ersatz für den Befehl hostname -s – das tut aber eigentlich nichts zur Sache. Viel spannender ist die Frage, wie portabel dieses Skript arbeitet. Unter CentOS 5.3 kann ich es fehlerfrei ausführen. Anders sieht es aus, wenn dieses Skript auf einer Maschine mit FreeBSD oder Solaris aufgerufen wird: Unter FreeBSD erhält man Fehlermeldungen („Command not found“), da uname und awk nicht wie bei CentOS und Solaris in /bin liegen, sondern in /usr/bin (bei Solaris kommt es ebenfalls zu Problemen, die aber darin begründet sind, dass die Solaris-Implementierungen von uname und awk von den GNU-Implementierungen abweichen – doch dazu später).

Unter den verschiedenen Unix-Derivaten und sogar Linux-Distributionen liegen Binaries also nicht immer am selben Ort. Müssen portable Skripte also darauf verzichten, absolute Pfade zu verwenden und sich auf $PATH verlassen? Natürlich nicht. Allerdings funktioniert der Schnellschuss „dann definiere ich Binaries eben systemabhängig“ auch nicht so ohne weiteres – man sieht ja, dass das häufig dazu herangezogene uname schon nicht sehr portabel gelagert ist. Es gibt jedoch einen eleganteren Weg, der sich nur auf Shell-interne Funktionen stützt und daher tatsächlich komplett portabel ist:

#!/bin/sh

detect_binary() {
    local BINDIRS="/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /opt/csw/bin /opt/sfw/bin /usr/openwin/bin"

    for i in $BINDIRS; do
        if [ -x "${i}/${1}" ]; then
            echo "${i}/${1}"
            break
        fi
    done
}

b_uname=$(detect_binary "uname")
b_awk=$(detect_binary "awk")

Die Funktion detect_binary probiert alle angegebenen Pfade durch, bis sie in einem auf eine ausführbare Datei mit dem gesuchten Namen stößt. Anschließend gibt sie den absoluten Pfad des gefundenen Binaries zurück. Auf diese Weise wird zum einen sichergestellt, dass nur Binaries aus den vorgegebenen Pfaden verwendet werden, und zum anderen, dass die Binaries weiterhin unabhängig von der Umgebungsvariable $PATH gefunden werden können.

Eines löst dieser Code-Schnipsel jedoch nicht: Das unterschiedliche Verhalten von gleichnamigen Binaries unter verschiedenen Unix-Derivaten. Auf vielen Nicht-Linux Systemen stehen Binaries der GNU-Implementierung zur Verfügung, oder zumindest Binaries, die das Verhalten der GNU-Implementierungen nachahmen. Unter FreeBSD beispielsweise findet man in gsed und gawk die GNU-Implementierungen, während sich hinter sed und awk die traditionellen BSD-Implementierungen verbergen. Die <programm>-Binaries findet man im übrigen auch unter Linux. Benötig man also in einem Skript explizit das GNU-Verhalten (beispielsweise beim Einsatz von sed -i, so sollte man auch die GNU-Binary-Namen verwenden.

Happy Scripting!

4 comments | Defined tags for this entry: code, FreeBSD, Linux, programming, scripting, shell, Solaris

Page 1 of 2 next page »