programmierung - 22.11.2005 - 2.1.2006

Taste for the Web - nette Karrikatur von Paul Graham Artikeln. Ja, sein manchmal recht onkelhafter Stil mit ständigen Plugs von Yahoo Stores kann einem ab und an ganz schön auf den Keks gehen.

Re: Web application design: the REST of the story - eine sehr interessante Diskussion von zwei derzeit dominierenden Architekturstilen für Webanwendungen: REST und Continuations.

LGT: Lightweight Game Toolkit for Python - besonders interessant die NanoThreads (Coroutinen für Python), das EventNet (erweiterte Eventprogrammierung) und Gherkin (eine Alternative zu Pickle/Marshal). Zu NanoThreads und EventNet gibt es mit FibraNet jetzt auch einen erweiterten Nachfolger (der von LGT losgelöst ist).

Webstemmer - HTML-Grabber der aufgrund des Layouts den eigentlichen Kerntext von Websites extrahiert.

simple_json 1.0 - Alternative zu json.py mit weniger Macken (dafür einem Soziopathen als Programmierer - aber man kann eben nicht alles haben. In diesem Fall ist funktionierender Code jedenfalls wichtiger als freundlicher Umgangston).

CSS2/DOM - Styling an input type="file" - wilde Hacks um File-Upload-Buttons mit CSS oder JavaScript zu stylen.

Rechenspielchen

Wie ich gerade festgestellt habe, ist es sinnvoller 90*24*60*60 zu rechnen, statt 90*24*60 - jedenfalls wenn man 90 Tage in Sekunden ausdrücken will. Jetzt sollten Kommentarcookies auch länger als 1.5 Tage halten

StickBlog » Blog Archive » Upload multiple files with a single file element - nette Methode um mehrere Files hochzuladen ohne einen Wald von Browse-Buttons haben zu müssen.

Weblogs - Variation auf den vorigen Link, hier JavaScript und CSS zusammen.

Dejavu - Trac - ein weiterer ORM für Python. Dieser zeichnet sich durch absurde Klassennamen aus (Arena, Sandbox, Units ...)

Download DrScheme v300 - eine neue Version des besten Scheme-Systems der Welt ist raus. Greift es euch, so lange es frisch ist. Jetzt mit Unicode!

appscript - Python als Alternative zu AppleScript. Also Applikations-Steuerung über die AppleScript Schnittstellen direkt aus Python Programmen heraus.

Generische Funktionen mit Python

PEAK bietet ja seit geraumer Zeit generische Funktionen ala CLOS für Python an. Ich wollte immer mal damit rumspielen, aber lange Zeit war es ja einfach nur Bestandteil von PyProtocols, und die Installation etwas haarig. Seit September diesen Jahres ist es aber ausgekoppelt und sehr viel einfacher zu installieren. Also hab ich mich mal draufgestürzt.

Und ich muss sagen: wow. Was Phillip J. Eby da geleistet hat ist wirklich fantastisch. Die Integration in Python (funktioniert ab Python 2.3 - er hat einfach eine eigene Implementierung von Dekoratoren für Python 2.3 erfunden) ist super, auch wenn natürlich das eine oder andere etwas gewöhnungsbedürftig ist.

Ein kleines Beispiel:

import dispatch

[dispatch.generic()]
def anton(a,b):
 "handle two objects"

[anton.when('isinstance(a,int) and isinstance(b,int)')]
def anton(a,b):
 return a+b

[anton.when('isinstance(a,str) and isinstance(b,str)')]
def anton(a,b):
 return a+b

[anton.when('isinstance(a,str) and isinstance(b,int)')]
def anton(a,b):
 return a*b

[anton.when('isinstance(a,int) and isinstance(b,str)')]
def anton(a,b):
 return b*a

[anton.before('True')]
def anton(a,b):
 print type(a), type(b)

Dieses kleine Beispiel liefert einfach eine Funktion namens 'anton', welche auf Basis der Parametertypen unterschiedlichen Code ausführt. Das Beispiel ist natürlich völlig sinnfrei, zeigt aber einige wichtige Eigenschaften von generischen Funktionen:

  • generische Funktionen sind - anders als klassische Objekt/Klassenmethoden - nicht an irgendwelche Klassen oder Objekte fest gebunden. Statt dessen werden sie über ihre Parametertypen ausgewählt.
  • Parametertypen müssen demnach definiert werden - im Regelfall passiert das über eine Minisprache, mit der die Bedingungen der Auswahl formuliert werden. Das ist auch der einzige syntaktische Teil der mir nicht so gut gefällt: die Bedingungen werden als String abgelegt. Allerdings ist die Integration sehr gut, man erhält saubere Syntaxfehler schon beim Laden.
  • eine generische Funktion kann mit beliebigen Bedingungen überladen werden - nicht nur der erste Parameter ist entscheidend. Bedingungen können übrigens auch auf Basis von Werten entscheidungen treffen - jeder beliebige Python-Ausdruck kann dort verwendet werden.
  • mit Methodenkombinationen (Methoden sind hier die konkreten Ausprägungen einer generischen Funktion) kann man eine Methode vor oder nach ihrem Aufruf modifizieren, ohne an den Code selber ranzukommen. Das Beispiel benutzt eine before-Methode die immer (deshalb das 'True') herangezogen wird, um Debugging-Output zu erzeugen. Natürlich kann man bei before/after Methoden jederzeit auch Bedingungen benutzen, um sich auf spezifische Ausprägungen des Aufrufs der generischen Funktion zu hängen - womit generische Funktionen ein vollwertiges Event-System sind.

Ein recht guter Artikel über RuleDispatch (das generische Funktionen Paket) gibt es bei Developerworks.

Das Beispiel zeigt übrigens die Python 2.3 Syntax für Dekoratoren. Mit Python 2.4 kann natürlich auch die @-Syntax benutzt werden. Ein Nachteil soll nicht verschwiegen werden: die Definition von generischen Funktionen und ihren Methoden ist nicht interaktiv möglich - jedenfalls nicht mit der Python 2.3 Syntax. Leider muss man da generell mit externen Definitionen in Dateien arbeiten.

RuleDispatch wird definitiv einen Platz in meinem Werkzeugkasten finden - die Syntax ist einfach genug, die Möglichkeiten hingegen sind gigantisch. Als Eventsystem schlägt es an Flexibilität jedes andere System und als generelle Möglichkeit der Strukturierung des Codes kommt es sehr nah an CLOS heran. Schade, das Django sich vermutlich auf PyDispatch ausrichten wird - RuleDispatch würde meiner Meinung nach wesentlich besser passen (da viele Aspekte in Django als Dispatch auf mehrere Parametertypen geschrieben werden könnten).

LTK - The Lisp Toolkit - wenns mal nur einfach ein bischen GUI sein soll, aber nicht unbedingt die grosse Keule nötig ist - LTK bietet einfache Bindings für TK in Common Lisp. Arbeitet auch hervorragend mit OpenMCL zusammen, sogar CLISP mag damit.

Sams Teach Yourself Shell Programming in 24 Hours - Ein ganzes Buch über Shell-Programmierung. Und damit natürlich auch eine recht gute Einführung in die diversen Tools, die Unix-Systeme zur Verfügung stellen. Sicherlich jedem zu empfehlen, der sich z.B. einen Root-Server zugelegt hat und da jetzt mehr mit machen will - aber eben sonst Linux eher vom GUI her kennt.

[GOODIE] Headless Squeak for OS X (Re: Mac VM 3.2.X) - wie man unter OS X ein headless Squeak (Smalltalk-Umgebung ohne GUI-Anteil) für Serverdienste hinbekommt. Besonders interessant für den Einsatz von Seaside.

Hyper Estraier: a full-text search system for communities - Volltextdatenbank mit Attributsuche und einigen anderen netten Eigenschaften - sowie Bindings für verschiedene Programmiersprachen

The Xapian Project - noch ein Volltext-Indexer, dieser mit diversen weitergehenden Features wie z.B. Stemming für verschiedene Sprachen.

Inets 2.5.5 - Webserver in Erlang

Is Rails a DSL? What is a DSL, and is it possible in Python? - Domain Specific Languages - eine recht brauchbare Beschreibung und Betrachtung der Situation in Python und Ruby.

Linux Daemon Writing HOWTO - wie man unter Linux einen Daemon schreiben sollte (allgemeine Infos)

Yaws - noch ein Webserver in Erlang - dieser ist allerdings HTTP 1.1 kompatibel und enthält Ansätze für Webentwicklung

Python Cheese Shop : python-fastcgi 1.0 - FastCGI Implementation die auf der OpenMarket FastCGI C Bibliothek aufbaut und daher deutlich schneller als reine Python-Lösungen ist.

Python OpenID 1.0.1 Released — OpenID Enabled - OpenID Client und Server in Python. Müsste ich mir mal angucken, könnte ja recht interessant für Kommentarfunktionen sein.

Hacking the jProject - The Daily WTF - autsch. Ein Bestellsystem, bei dem jede Bestellung in ihrer eigenen Tabelle im SQL Server gespeichert wird. Grandiose Idee.

How-To Guide for Descriptors - eine sehr gute Erklärung, wie in Python die Properties funktionieren und was es mit den magic methods get , set und del auf sich hat (und wie getattribute da mitspielt).

Nur so eine Überlegung

Was wäre eigentlich, wenn die Gnome-Entwickler in die Linux Kernel Mainlingliste gingen und dort verkünden, das sie Usern empfehlen FreeBSD zu benutzen, weil ja das chroot Modell von Linux armselig ist, und die Kernel APIs sowieso ein Chaos sind und Linux bis heute keine richtig guten Filesystem Notifikationen hat und die Entwicklung von Linux einfach viel zu wenig auf GUI-Anforderungen eingeht. Deshalb würden sie eben Usern den Einsatz von FreeBSD vorschlagen, denn die Kernelprogrammierer von Linux sind ja eh alles Idioten.

Wie würde Linus' Reaktion dann aussehen?

Und wir machen alle Fehler erneut

Es gibt im Moment einiges an Aktivität im Bereich der Microformats - die Idee dahinter: Informationsblöcke nicht in XML kodiert abzulegen, sondern in vordefiniertem HTML. Dazu werden dann die CSS Klassen benutzt, um zu definieren was ein einzelnes Format ist. Logischerweise gibt es Probleme mit kollidierenden Stylings - was für ein Wunder. Ich selber bin jedenfalls immer wieder erstaunt, wie viel Energie Entwickler auf saublöde Ideen verwenden können.

Wir hatten mal ein HTML, das sich nicht nur mit Semantik beschäftigte, sondern auch mit Layout. Und das produzierte die allseits beliebten FONT-Tag-Orgien in HTML-Seiten. Im Laufe der Zeit hat sich bei den meisten die Idee durchgesetzt, das Trennung von Semantik und Layout sinnvoll ist - Semantik als Auszeichnungsgrundlage für den Inhalt, Layout in die CSS Files und als Verbindung zwischen diesen die IDs und Klassen an Tags. Zusätzlich mit DIV und SPAN "anonyme" Tags ohne vordefinierte Semantik (ausser "das ist ein Block von Text" und "das ist eine Inline-Strecke von Text" - wobei diese Bedeutung problemlos überladen werden kann), für die Sachen die mit der normalen Semantischen Auszeichnung nicht gehen (was hauptsächlich an der selten dämlichen Idee von HTML liegt, das es zwar Auszeichnungen für Überschriften gibt, aber keine Auszeichnungen für Abschnitte von Texten, zu denen diese Überschriften gehören würden).

Was machen Microformate jetzt? Nunja, die gleiche blöde Idee, etwas zu missbrauchen - nämlich in diesem Fall die oben angesprochenen Verbindungsstücke zwischen der Semantik und dem Layout. Microformats geben diesen eine Bedeutung - z.B. wäre eine DIV mit einer Klasse 'description' dann die Beschreibung eines Reviews - lest euch die Details in der hReview Referenz durch. Sorry, aber sowas muss einfach zu Konflikten führen - haben die Pappnase noch nie was von Namespaces gehört? Die Microformats addressieren explizit XHTML - und das hat genau für den Zweck der Einbettung eben Namespaces. Und wenn man meint, man müsse schon eine so blöde Idee wie diese umsetzen - könnte man dann nicht wenigstens schlau genug sein, den Teilen etwas kryptischere, aber dafür eindeutigere Klassen zu geben?

Wie gesagt, erstaunlich wie viel Energie in solche dämlichen Ideen gehen, die dazu verdammt sind mehr Probleme als Lösungen zu schaffen.

Deadlock - interessanter Artikel über Deadlocks in Systemen und über Zombiprozesse, Signalbearbeitung etc.

Benutzerkennwörter in der Administration festlegen

Ein eher hässlicher, aber dennoch nützlicher Monkeypatch:

# monkey-patch für auth.users
from django.models.auth import User

def user_pre_save(self):
    if not self.password.startswith('sha1$'):
        self.set_password(self.password)

User._pre_save = user_pre_save

Fügen Sie dies in Ihre Modelldatei (oder an einen anderen Ort, der früh geladen wird) ein, und Sie können Passwörter im Admin-Bereich durch Eingabe von Klartext-Passwörtern festlegen. Wenn das Passwort mit 'sha1$' beginnt, wird es als bereits verschlüsselt angesehen und es passiert nichts. Wenn es nicht mit dieser Zeichenfolge beginnt, wird es mit der Standard-Django-Funktion zur Passwortverschlüsselung umgewandelt.

Nein, dies ist nichts, das in den Kern gehört - es ist viel zu hässlich dafür. Aber zumindest ermöglicht es Ihnen, Passwörter über den Admin-Bereich festzulegen, ohne dass der Benutzer den tatsächlichen Passwort-Hash berechnen muss.

SystemExit und exception handlers

Immer wieder gerne genommen: SystemExit. Eine Python-Exception, die viele nicht kennen. Das besondere an dieser Exception: sie ist kein Fehler. Sie tritt auch nicht unerwartet auf. Sie wird nämlich einfach von sys.exit ausgelöst. Die Idee dahinter ist, das man so im dynamischen Ablauf eine Ende-Bearbeitung einhängen kann (z.B. irgendwelche Dateibereinigungen), ohne sich in globale Exitbearbeitung einzuklinken (mit all den Problemen die das hat).

Das Problem ist jetzt, das viele Programme und Bibliotheken einen globalen Exception-Handler installieren. Einen, der jeden Fehler abfängt und hübsch formatiert per Mail verschickt, irgendwo logged oder ähnliches. Mache ich auch ständig. Geht auch klasse - ausser wenn man in seinem Programm tatsächlich mal explizit ein vorzeitiges Ende einleiten will. Dann klappt da garnix mehr - denn man erhält entsprechende Fehler für einen Nicht-Fehler.

Besonders kritisch wird das ganze im Zusammenhang mit mehreren Prozessen. Wenn man nämlich im Laufenden Betrieb einen Prozess anstartet, will man diesen auch beenden, ohne das eventueller nachgelagerter Code ausgeführt wird. Am besten sieht man das an einem Beispielprogramm:

import signal
import os

try:
 pid = os.fork()
 if pid:
 print "Elternprozess", os.getpid()
 else:
 print "Kindprozess", os.getpid()
 sys.exit(0)
except:
 print 'Fehler aufgetreten in Prozess', os.getpid()

print "Das darf nur der Elternprozess ausführen", os.getpid()

Dieser Code hat einfach einen globalen Fehlerbehandler, der Fehler recht unspezifisch abfängt. Innerhalb des Codes wird ein paralleler Prozess mit fork gestartet. Dadurch, das SystemExit wie alle anderen Exceptions behandelt wird, wird aber der Kindprozess nicht korrekt beendet - ein Prozess kopiert den gesamten Zustand des Elternprozesses, inklusive Rücksprungadressen, offene Fehlerbehandlungen, Dateien, Datenbankverbindungen und so weiter.

Das ist natürlich fatal - denn hier wird ja sys.exit abgefangen. Es gibt also eine Fehlermeldung für den ganz normalen sys.exit(0) Aufruf. Und noch schlimmer: da SystemExit nicht extra behandelt wird, gehts danach normal weiter - und der Kindprozess rennt in Code für den Elternprozess rein. Code läuft also doppelt, was unter Umständen kritische Ergebnisse haben kann.

Wenn man den ganzen Stack an Software voll kontrollieren kann, ist die Lösung einfach:

import signal
import os

try:
 pid = os.fork()
 if pid:
 print "Elternprozess", os.getpid()
 else:
 print "Kindprozess", os.getpid()
 sys.exit(0)
except SystemExit:
 raise
except:
 print 'Fehler aufgetreten in Prozess', os.getpid()

print "Das darf nur der Elternprozess ausführen", os.getpid()

Dadurch wird einfach der SystemExit neu geworfen - also neu ausgelöst - ohne eine Meldung zu machen. Im Regelfall wird dann die Standardbehandlung von Python zuschlagen und den SystemExit in eine normale Beendigung umsetzen.

Was aber machen, wenn man mehrere gestapelte Varianten des falschen Fehlerhandlings hat? Ich hab sowas zum Beispiel bei Django und FLUP (dem FCGI/SCGI Server für Python). In Django hab ich es geändert, dann hat der Fehler im FLUP zugeschlagen. Was macht man dann?

Die Lösung ist ein wenig brutaler:

import signal
import os

try:
 pid = os.fork()
 if pid:
 print "Elternprozess", os.getpid()
 else:
 print "Kindprozess", os.getpid()
 os.kill(os.getpid(), signal.SIGTERM)
except:
 print 'Fehler aufgetreten in Prozess', os.getpid()

print "Das darf nur der Elternprozess ausführen", os.getpid()

Letzten Endes begeht der Prozess einfach Selbstmord - er schickt sich selber ein SIGTERM, also ein Beendigungssignal. Das gleich, das man normalerweise von der Shell schicken würde. Allerdings muss man dann sicherstellen, das alle eventuell nötigen Nachbereinigungen entweder schon gemacht sind, oder dann in einer SIGKILL Behandlungsroutine laufen - sonst hat man unter Umständen Problemen (z.B. sollten Datenbanktransaktionen schon commited sein).

Auch bei dieser Lösung muss man aufpassen, das nicht irgendwelche offenen Resourcen den Prozess blockieren - sonst produziert man unter Umständen Zombiprozesse. Oftmals ist es daher besser für solches Multiprozessing einen Verwaltungsprozess sehr viel früher im System zu starten - ausserhalb der Fehlerbehandlungskette - und diesen dann zu benutzen um Bearbeitungsprozesse zu starten. Allerdings hat das dann den Nachteil, das diese solcherart gestarteten Prozesse nicht die Umgebung des Elternprozess erben. Man muss daher dann in der Regel mehr Vorbereitungen treffen, um die gewünschten Aktionen auszuführen. Einen ähnlichen Ansatz verfolgt übrigens Apache - dort werden die Prozesse aus einem sehr frühen Basiszustand heraus erzeugt, so das sie möglichst Resourcenfrei daherkommen.

Vampire - Erweiterung von mod_python, mit dem es etwas Entwicklerfreundlicher wird. Zum Beispiel kann es dann auch automatischen Code-Reload.

Learning Seaside - coole Demo was mit Seaside (Smalltalk-Webframework) und AJAX gemacht werden kann. Im Prinzip eine Datenbankoberfläche mit frei konfigurierbarem Datenbankmodell - sowas wie Google Base, nur in Cool.

AJAX ist meistens schlecht (Jakob Nielsens Alertbox Dezember 2005) - warum Jakob Nielsen manchmal recht hat.

Commentary - Postit-Kommentare für Webseiten, ausgeführt als WSGI Middleware. Sehr interessant, könnte vor allem für Sourceviews oder ähnliches interessant sein, oder für längere Texte.

pyinotify - sehr schön, endlich ein brauchbarer Wrapper für die notify-Funktion in Linux. Damit können Python-Programme sich über Änderungen an Dateisystemen informieren lassen - ideal für Verzeichnismonitoring.

Paj's Home: Cryptography: JavaScript MD5: sha1.js - JavaScript-Implementation von SHA1 - praktisch, wenn man Klartextpasswörter in Webformularen vermeiden will. Natürlich sollte man immer einen Fallback haben, denn nicht jeder hat JavaScript verfügbar oder aktiviert. Auf der Site sind auch MD5 und MD4 Implementationen und ein paar andere Schnipsel zu dem Thema.

akismet.py - Python-Interface für den (zentralen) Akismet Spam Scanner.

Development « Akismet - das Akismet API

Louie - ein neues Event-Dispatching-Modul für Python. Baut auf PyDispatcher auf.

SQLAlchemy README - ein weiterer ORM für Python, orientiert sich stark an SQL und bietet einiges an magischer Syntax. Faszinierend, wie gerade in diesem Bereich die Programmierer jedes Sprachfeature versuchen zu missbrauchen nur um nicht SQL schreiben zu müssen ...

axentric. a web designer's “tackboard”. - verallgemeinerte Version der yellow-fade-Technik von 37signals. Nett um Highlights in Seiten zu legen, die nicht dauerhaft stehen bleiben sollen.

Mal wieder was von der Bastelfront

Content-type: matter-transport/sentient-life-form - für die, die mal schnuppern wollen, wo es mit meinem Blog hingehen wird. Noch nicht ganz fertig, einige Bugs in meiner Software, ein paar Sachen warten auf Patches in Django, aber im grossen und ganzen bin ich schon ganz zufrieden.

Another OPML server...

Phil Pearson does it again - diesmal hat er den Community-Server für den OPML-Editor von Dave Winer in Python nachprogrammiert (damals hatte er ja den Radio Community Server nachgebaut, ein Projekt an dem ich auch zweitweilig beteiligt war). Jedenfalls kann man jetzt also auch seinen OPML-Editor auf die eigene Linux-Kiste veröffentlichen lassen, wenn man dort Python und SCGI zur Verfügung hat.

DragAndDrop - MochiKit - Trac - Drag und Drop mit MochiKit.

Weird Python 2.3 Bug

Also wirklich, manche Bugs die man jagt sind wirklich strange. Guckt euch einfach mal folgendes Python Script an:


 import re

r = re.compile('^', re.M)

src = '''<html> <head> <title>Logviewer</title> </head> <body> <div> <h1>Titel</h1> </div> <div> {{}}
 {% block content %}
 {% endblock %}
 </div> </body> </html> '''

for match in r.finditer(src):
 print match.start()

Sieht ja ganz harmlos aus - liefert einfach nur die Positionen der Newlines (ja, ich weiss, das macht man anders - der Source ist nicht von mir). Das Script hat unter Python 2.3 eine Endlosschleife auf dem letzten, abschließenden Newline. Nimmt man das raus (also pappt das """ direkt hinter das letzte Tag ohne Zeilenumbruch) funktioniert das Script. Unter Python 2.4 funktionieren beide Varianten. Und hinter sowas muss man dann herjagen ...

Ich brauch ja nicht extra zu betonen, das dieser kleine Schnipsel Code in einem grösseren Berg von Code versteckt war, oder?

Microsoft standardisiert Office-Formate in ECMA

Stephen Walli (Ex-Microsofter) über die zu erwartenden Fallstricke in der letzten Microsoft-Aktion:

Es wird wahrscheinlich eine gebührenfreie Lizenz sein, weil die aktuelle Patentlizenz für die proprietäre Spezifikation gebührenfrei ist. Diese Patentlizenz konnte jedoch nicht unterlizenziert werden, sodass ein Implementierer, der seine Implementierung unter der GPL lizenzieren wollte, dies nicht konnte. Tatsächlich zwangen frühere Beispiele rund um den IETF-SenderID-Standard Benutzer anderer Implementierungen dazu, eine Lizenz mit Microsoft abzuschließen, was ein eher lästiges Problem für frei lizenzierte Software ist.

Der Hinweis auf die SenderID-Geschichte ist durchaus wichtig: Dort hat Microsoft auch ständig davon geredet, dass es ein offener Standard sei, aber dabei immer verschwiegen, dass deren Verständnis von offenen Standards absolut inkompatibel mit vielen Bereichen der Open-Source-Entwicklung sind. Mit Sicherheit wird Microsoft wieder die GPL blockieren.

Abgesehen davon, ich finde es schon ziemlich armselig, wenn Microsoft sich schlicht weigert, ODF zu implementieren und meint, einen eigenen Pseudo-Standard ihres Krams machen zu müssen - zumal man ja genau weiß, wie sich Microsoft dann zu solchen Standards verhält. Die werden dann wieder an den passenden Stellen erweitert und schon ist es vorbei mit dem freien Zugriff.

Web Development Bookmarklets - diverse Bookmarklets die sehr hilfreich für Webentwicklung sind.

Closures python,scheme,ruby - eine gute Erläuterung der etwas defekten Lookups für lexikalische Variablen in Python (jedenfalls wenn eine Zuweisung in einem Inner Scope beteiligt ist).

Routes 1.0 Released - das ist die Python-Version der URL-Routen aus Ruby-on-Rails. Sehr interessant, ich muss mich irgendwann mal dransetzen und gucken ob ich das nicht in Django als alternativen URL-Dispatcher einbauen kann.