Dr. Maximillian Dornseif home

Struktur von AppEngine Applikationen

Ich habe nirgends überzeugende Beispiele dafür gefunden, wie man bei Applikationen für Google Appengine ein vernünftiges Verzeichnislayout gestaltet. Eine Applikation für AppEngine muss ja sicherstellen, das alle Abhängigkeiten automatisch hochgeladen werden. Da ich mit in den letzten Wochen vermehrt in der Dependency Hell wiederfand, sollten die benötigten Libraries bitte automatisch zusammengesucht werden.

In den letzten Monaten setzte ich dabei sehr auf pip, doch die “editable checkouts” von pip bringen einen ganz schnell wieder in die Dependency Hell.

Datei Layout

Die erste Entscheidung war, alle Abhängigkeiten, an denen ich aktiv mitentwickle als git submodule in lib zu packen. Das hat vielerlei Vorteile: Git Submodles referenzieren eine spezifische Revision und werden im Gegensatz zu svn:externals nicht automatisch geupdated. Das bedeutet, solange ich als Entwickler nicht aktiv eine externe Library update bin ich vor Überraschungen sicher. Es bedeutet auch, dass die Operations Abteilung problemlos genau den gleichen Software Stand für ein deployment erzeugen kann, wie ich als Entwickler auf meinem System habe.

Ein weiterer Vorteil ist, das pip mir versehentlich Änderungen im “editable Checkout” überschreibt oder ich vergesse, eine Änderung zu submitten: Bei Submodulen reicht ein git status im Toplevel-Projekt um auch für die Submodule mitzubekommen, ob dort was uncomittetes liegt.

$ git status
#	modified:   lib/DeadTrees (new commits)
#	modified:   lib/EDIlib (modified content, untracked content)
#	modified:   lib/huSoftM (modified content)

Der Nachteil ist, dass man abhängigkeiten für diese Subprojekte selber verwalten muss. Das ist aber nicht ganz so schlimm, denn man ist jetzt vor überraschenden Updates sicher. Ich denke momentan nach, ob ich für grösere Software-Projekte nicht lieber von dem jeweiligen Subprojekt einen Branch anlege, um noch sicherer vor überraschenden Änderungen zu sein.

Nun gibt es die eine oder andere Library, an der ich eh nichts ändern werde und/oder die nicht als git repository verfügbar ist. Die sollen weiterhin per pip installiert werden. Dazu screibe ich die gewünschten Pakete in eine Datei Namens requirements.txt, die etwa so aussieht:

Jinja2
# huTools benötigt httplib2
httplib2

Per Makefile erzeuge ich ein virtualenv, in das die Pakete mit pip hineien installiert werden. Das ganze sieht dann als make dependencies target etwa so aus:

Ich bin mir sicher, das man die Erzeugung der git Submodule noch eleganter lösen kann, aber es klappt auch so.

Applikationskonfiguration

Jetzt sind alle gewünschten software Module zusammengesucht, was jetzt fehlt ist die Möglichkeit, die Module auch in unserer Applikation zu importieren.

Die Appengine unterstützt die von pip generiertn .egg-link Dateien nicht, so dass man die Pfade f¨rud iese Dateien von Hand zusammen suchen muss. Dazu kommen noch die Module, die wir in das lib verzeichnis gelegt haben. Um es spannender zu machen, kann das aktuelle Verzeichnis, aus dem unsere Skripte aufgerufen werden bei der AppEngine je nach Lust und Laune variieren. Alles in allem kommt daher ein recht komplexes Pfad-Konfigurationsskript dabei heraus.

In der Datei config.py werden neben den Pfaden auch direkt die gewünschte Django Version und das Verzeichnis für die Templates gesetzt. Im Ergebnis sieht das dann so aus:

Was sich dabei herausgestellt hat, ist dass das Setzten von Environment-Variablen (hier PYPOSTAL_PIXELLETTER_CRED) in config.py keine gute Idee ist. Ob es am Module-Caching der AppEngine liegt, oder woran auch immer, gelegentlich findet man in dem Modul, wo amn sie ausliest, die Environment Variablen leer. pyJasper ist deswegen schon auf einen alternativen Konfiguartionsmechanismus umgestiegen, pyPostal hat das noch vor sich.

Nun kann man einfach in jedem Modul der eigenen Applikation als erstes config.py importieren und man ist fast im grünen Bereich. Vermutlich bruacht man noch Sessions und dergleichen, so dass ein appengine_config.py file fällig wird, in dme auf jeden Fall auch config.py importiert werden muss.

Zu guter Letzt sollte man noch dafür sorgen, dass bei einem Deployment all das Zubehör zu den Libraries nciht mit auf den Server geladen wird. Das kann in der app.yaml dann z.B. so aussehen:

Qualitätskontrolle

Das Makefile hat auch ein check target, das die gröbsten Programmierfehler aufzuspüren hilft. Damit die Codechecker wie gewünscht funktionieren, wird erst das Appengine SDK lokal installiert. Die nötingen Pfade werden von config.py erzeugt.