Freitag, 16. Oktober 2009

AJAX loading spinner without flickering artifacts

We were embedding a spinner to give user feedback while loading data from a server which might take a little longer (but can also be pretty quick in most cases).

Implementing the spinner itself isn't that hard, but we found that quick responses from the server caused visual artifacts flickering up because the spinner was only visible for a few milliseconds (probably roughly 30ms).

The solution we chose to implement looks like this:
  • before triggering the AJAX request, schedule the code that loads the spinner 150ms into the future
  • if the AJAX request returns and the spinner did not materialize yet, cancel the scheduled code
  • if the AJAX request returns and the spinner was materialized, schedule the code to hide the spinner 200ms into the future
We also had two smaller variations: if the replacement looks almost identical to what was shown before, don't hide it until the spinner actually shows up. If the replacement looks usually a lot different, then blank the area immediately waiting for the spinner to show up or not.

We implemented this with MochiKit and here is some sample code:

var expansion_element = $('mydivcontainingstuff');
var spinner_loader = MochiKit.Async.callLater(0.15, function() {
MochiKit.DOM.addElementClass(expansion_element, 'loading');
MochiKit.DOM.addElementClass(expansion_element, 'loading-spinner');
spinner_loader = null;
});
d = MochiKit.Async.doSimpleXMLHttpRequest(ajax_url);
d.addCallback(function(result){
$('mystuffdivcontainingstuff').innerHTML = result.responseText;

if (spinner_loader != null ) {
spinner_loader.cancel();
} else {
MochiKit.Async.callLater(0.2, function() {
MochiKit.DOM.removeElementClass(expansion_element, 'loading-spinner');
MochiKit.DOM.removeElementClass(expansion_element, 'loading');
});
}
And here's the CSS:
.loading-spinner {
width: 100px;
height: 100px;
margin-left: auto !important;
margin-right: auto !important;
background-image: url(ajax-loader.gif);
background-repeat: no-repeat;
background-position: 50% 50%;
}

.loading * {
display: none;
}

Samstag, 6. Dezember 2008

z3c.recipe.tag

Nachdem Wolfgang diese Woche auf der Suche nach Symboldefinitionen durch sein Egg-Verzeichnis gekrochen ist, haben wir z3c.recipe.tag ausprobiert und waren auch sofort begeistert: Mit diesem Rezept werden Vim- und Emacs-kompatible ctags-Dateien erstellt, die alle Symbole des genannten Eggs und seiner Abhängigkeiten haben. Damit entfällt endlich die Pfad-Sucherei und die händische Prüfung der Versionsnummern.

Dienstag, 8. Juli 2008

Python descriptor tutorial

Raymond Hettinger hat ein Tutorial gegeben, in dem er erklärt wie man mit Hilfe von Deskriptoren den Attributzugriff (lesen, schreiben, löschen) in Python für seine eigenen Zwecke anpassen kann.

Die Folien zum Vortrag sind praktisch orientiert und sollten demächst online verfügbar sein. In der Zwischenzeit kann man sein bestehendes Deskriptor-Tutorial anschauen.

Nebenbei wird erklärt, wie super() funktioniert und wie aus Funktionen Methoden werden.

Update: Die eigentlichen Folien sind inzwischen im Europython-Wiki verfügbar.

How we develop Launchpad at Canonical (and Bazaar too)

Steve Alexander hat über die Entwicklungsmethoden berichtet, die für Launchpad und Bazaar benutzt werden. Steve und ich haben lustige handgeschrieben Folien gebaut, die wir abfotografiert haben.

Steve AlexanderEins der Hauptprobleme mit denen man sich in der Entwicklung bei Canonical, speziell bei den Launchpad- und Bazaar-Teams, beschäftigt ist die Frage wie man "Corporate-Style Development" mit einem völlig verteilten Team unter einen Hut bekommt.

Der von Canonical verfolgte Ansatz lautet dabei: aus Methoden von agilen Prozessen und der Community-getriebenen Open-Source-Entwicklung ein Emulgat zu erzeugen, das sie "Community Agile" nennen.

Um diese Prozessentwicklung intern fortlaufend zu gestalten, verwendet Canonical ebenfalls Elemente des "LEAN"-Prozesses. Dieser stammt ursprünglich aus der Autoproduktion von Toyota und und dem Kaizen: der kontinuierlichen Verbesserung von Produkten, der Organisation und der Prozesse.

Werte

Die konkrete Ausgestaltung der Prozesse berücksichtigt einige feste Elemente, die Canonical als "Werte" bezeichnet. Diese Werte werden mit den Werten "klassischer" agiler Entwicklung kontrastiert dargestellt und beruhen auf den eigenen Erfahrungen:

Wissen und Motivation über Co-Lokation

Canonical möchte die besten Leute zusammenbringen und hat die Erfahrung gemacht, dass es produktiver ist die besten Leute miteinander über große Entfernungen arbeiten zu lassen, statt darauf zu bestehen, dass alle vor Ort arbeiten und dabei weniger gute Entwickler im Team zu haben.

Patches integrieren über Sprintplanung

Canonical entwickelt die meisten Produkte gezielt als Open-Source. Dadurch kommt es zu Code-Spenden, die nicht unbedingt in die eigene Planung passen. Um das Gesamtprodukt für so viele Anwender wie möglich interessant zu machen ist man bestrebt Patches so schnell wie möglich in die Hauptlinie des Codes zu überführen.

Gemeinschaft über In-House-Entwicklung

Software profitiert in verschiedenen Aspekten davon, wenn sich um Sie eine Gemeinschaft bildet, die sie aktiv benutzt, erweitert und entwickelt. Dazu ist es nicht immer notwendig, dass die eigene Software Open-Source ist - extensive Schnittstellen und Plugin-Systeme helfen einer Community ebenfalls, sich zu etablieren und zur Entwicklung einer Software beizutragen.

Im Vergleich zur agilen Entwicklung ist dies eigentlich eine konsequente Fortführung des Kunde-vor-Ort-Prinzips.

Hochwertige Builds

Ebenfalls ein aus der agilen Entwicklung fortgeschriebener Ansatz: Hochwertige Releases.

Selbst bei zusammensitzenden Teams ist defekter Code problematisch. Wenn sich bei stark verteilten Teams ein Fehler einschleicht, kann dieser aufgrund der Zeitunterschiede schnell gesamte Arbeitstage vernichten, während man auf die Fehlerbehebung wartet. Daher müssen alle Tests nach jedem Checkin laufen. Und eigentlich ist man dann sowieso in der Lage nach jedem Checkin ein hochwertiges Release.

LEAN

Eine weite Frage bei LEAN ist auch, wie viel Arbeit verschwendet wird, um vom Beginn der Implementation eines Features zum Kunden zu kommen?

Mit LEAN versucht man hohe Entwicklungsgeschwindigkeit zu erreichen, um Funktionen so schnell wie möglich zum Kunden zu bringen, denn solange eine Funktion in Arbeit ist, verursacht sie nur Kosten: die Spezifikationen können sich ändern, es erfordert Aufmerksamkeit der Entwickler, es fehlt dem Kunden. Je länger dieser Zustand dauert, desto teurer wird die Funktion am Ende.

Ebenfalls ist es Verschwendung, wenn eine Funktion einmal bereitgestellt wurde, und wieder in die Entwicklung muss. Beispielsweise aufgrund fehlender Qualitätssicherung oder zu großer Arbeitspakete.

Canonical versucht Verschwendung durch einige Methoden zu reduzieren:

Feste Release-Zyklen

Launchpad hat einen Release-Zyklus von einem Monat, bei einem Planungshorizont von 4 Monaten. Ubuntu hat einen Release-Zyklus von 6 Monaten. Dadurch wird es einfacher für die verschiedenen Unternehmensbereiche (Marketing, Testing, Entwicklung) miteinander zu kommunizieren.

Gleichmäßige Arbeitsbelastung

Gegenbeispiel: Wenn man einfach nur einen 1-Monatszyklusansetzt, passiert in der dritten Woche folgendes: Qualitätssicherung muss passieren. Dadurch versucht jeder seinen Code noch irgendwie ins Release zu bekommen, was für erhöhten Stress in dieser Woche führt (eigenen Code schreiben, Tests schreiben, anderer Leute Code reviewen, ...).

Um dies zu umgehen, hat Canonical jeden Code-Reviewer einen Tag in der Woche auf Bereitschaft (jeweils an verschiedenen Tagen). Dadurch kann jeder Programmierer den bereitstehenden Reviewer sofort ansprechen und muss nicht auf die Qualitätssicherung in der dritten Woche warten. Durch synchrone Kommunikation (IRC, Voip) wird hier ebenfalls Verschwendung vermieden, da sowohl Reviewer als auch Entwickler Code und Konzepte gerade im Kopf haben und nicht "swappen" müssen.

Weiterhin werden die Arbeitspakete bewußt kleingehalten: jeder Patch darf maximal 500 Zeilen inklusive Diff-Kontext sein. Ist er größer muss er in mehrere kleinere Einheiten zerteilt werden.

Zum Schluss werden Änderungen so schnell wie möglich auf den Trunk gespielt um erneute Konflikte zu und damit Verschwendung zu vermeiden.

Weiterführendes

Steve hat außerdem auf ein Paper von Ian Clatworthy hingewiesen, in dem die Gedanken zur Community-getriebenen Entwicklung noch einmal vertieft dargestellt werden.

Montag, 7. Juli 2008

Discouraging the use of Python (or: How to keep the snakes off your plane)

Harald Armin Massa hat typische FUD-Argumente gegen Python in einem unterhaltsamen Vortrag erfasst und deren Gegenargumente aufgezeigt.

Sneaking in Python

Python schleicht sich ja wirklich überall ein: durch Mock-Anwendungen (die nie ersetzt werden), durch kleine Scripting-Tasks, durch Datenbanktransfers und andere Programmiertätigkeiten.

Der geneigte Pointy-Haired-Boss (PHB) weiß, dass er jetzt vorsichtig sein muss, um zu verhindern, dass Python in seiner Abteilung um sich greift ...

Python is dangerous

... weil weniger Code geschrieben werden muss.

Dadurch braucht man weniger Programmierer, die eigene Abteilung wird kleiner, man bekommt ein kleineres Budget, kleineres Büro und einen schlechteren Parkplatz. Man verliert Macht!

Aber ein anständiger PHB weiß: Python ist nur für kleine Anwendungen geeignet. Wir schreiben aber große Anwendungen. Python kann hier nicht benutzt werden!

... weil Python-Code lesbarer ist.

Dadurch können Nicht-Programmierer (oder Programmierer anderer Teams) verstehen was wir tun. Andere können Fehler in unserem Code finden! Die
Urheber der Spezifikationen können unseren Code auf die Spezifikation prüfen!

Für einen PHB ist das natürlich kein Problem: geistige Eigentumsrechte und Geschäftsgeheimnisse müssen gewahrt werden. Code kann nicht einfach weitergegeben werden!

... weil selbst große Unternehmen Python einsetzen.

Früher konnte man als PHB gegen Python argumentieren, indem man auf eine große Firma setzt: Wir sind eine Microsoft-Firma. Wir nehmen nur Software von großen Firmen (MS, Sun, Oracle, IBM, ...). Aber das geht heute leider nicht mehr. Selbst Sun und Microsoft setzen Python ein. Sogar in der Microsoft Knowledgebase wird Beispielcode in Python veröffentlicht.

Aber zum Glück ist Python keine Sprache erster Klasse. Jeder weiß, dass die richtigen Sprachen für .NET nicht IronIrgendwas heißen. Wenn Python eine Sprache erster Klasse wäre, müßte es ja Python# genannt werden!

... weil Python frei ist.

Und zwar nicht nur frei (wie in Freiheit) sondern sogar frei (wie in Freibier). Dadurch hat die eigene Abteilung weniger Ausgaben, ein kleineres Budget und (siehe oben) der eigene Parkplatz ist gefährdet!

Ein erfahrener PHB weiß jedoch, wie wichtig es ist im Team zu arbeiten. Um in einem Team zu arbeiten, braucht man gemeinsame Ziele. Unser Ziel ist: Profit. Das Ziel eines kommerziellen Softwaretool-Herstellers ist: Profit. Das Ziel der Python-Hersteller ist aber nicht Profit. Daher können wir nur mit Werkzeugen eines kommerziellen Herstellers arbeiten.

... weil Python's Entwicklungsprozess sich an der Leistung der Mitglieder orientiert.

Das ist natürlich Diskriminierung und verstößt gegen viele viele Gesetze, denn Personen, die keine Leistung erbringen haben nichts zu sagen!

Merke: Ein guter Pointy-Haired-Boss weiß, wie man Python vermeidet!

ZEORaid-Talk

Ich habe auf der EuroPython einen Talk über den aktuellen Entwicklungsstand von ZEORaid, unserer Replikationsslösung für die ZODB, gegeben.

Der Vortrag lief recht gut, mit etwa 50 anwesenden Zuhörern. Es gabe einige interessante Nachfragen, speziell zum Problem ZEORaid selbst hochverfügbar zu machen, ohne komplizierte Lösungen (heartbeat et al) hinzuzuziehen.

Die Folien sind ebenfalls verfügbar.

py.test: rapid testing with minimal effort

Holger Krekel hat ein 1-stündiges Tutorial gegeben um die Verwendung von py.test zu erläutern.

Übersicht
  • py.test ist projektübergreifend und hält den Test-Tool-Code in einer eigenen Bibliothek
  • sammelt Python-Tests automatisch auf und führt sie aus
  • versucht die Boiler-Plate innerhalb eines Projekts klein zu halten
  • kann projektspezifisch erweitert werden
  • läuft auf Linux, Windows, OSX und unter Python 2.3-2.5
Grundlagen
  • Installation als Egg, stellt "py.test" als Executable zur Verfügung
  • Der "py"-Namensraum hat einen automatischen Lazy-Importer, der alles unterhalb von "py.*" importiert, so dass man nur "py" importieren braucht, der Rest kommt automatisch mit.
  • Tests werden über Konventionen automatisch gefunden (Klasse mit Namen "Test*" und Methoden mit dem Namen test_*) automatisch gefunden.
  • Automatisches Auffinden kann über "--collectonly" nachverfolgt werden
    um zu sehen wie py.test vorgeht und was es tatsächlich sieht und findet.
  • Assertions werden über Python's "assert"-Statement formuliert. Exceptions können über eine Helferfunktion "py.test.raises" mit dem "assert"-Statement kombiniert werden.
  • Fehlerhafte Werte bei einem Fehler werden getraced um zu zeigen wie sie berechnet wurden.
  • Generative Tests: wenn eine Testmethode ein Generator ist, kann sie Callables und deren Parameter yielden, die dann als eigentlicher Test ausgeführt werden.
  • stdout/stderr werden umgeleitet und nur im Fehlerfall ausgegeben
  • --pdb lädt einen pdb nach einem Fehler
  • -x beendet den Testlauf nach dem ersten Fehler
  • --looponfailing: führt die fehlgeschlagenen Tests automatisch neu aus, wenn Dateien, die zu diesen Tests gehören verändert werden
  • setup/teardown: setup_module, setup_class, setup_method, teardown_*
  • setup_method bekommt das Methodenobjekt reingereicht, setup_module erhält das Modul-Objekt, setup_class erhält die Klasse
  • doctests werden anhand der Extension ".txt" automatisch gefunden
Erweiterbarkeit
  • py.test sucht nach "conftest.py"-Dateien, die die Konfiguration von py.test beeinflussen.
  • Mehrere conftest.py-Dateien können über Verzeichnisebenen miteinander kombiniert werden.
  • Diverse Elemente (Session, Collection, Module, ...) können beerbt werden, um eigene Funktionalität unterzubringen.
  • Dazu werden via "conftest.py" dynamisch die konkreten Klassen für Session, Collection, Module, ... nachgeschlagen, die unterhalb eines Verzeichnisses verwendet werden sollen.
  • Fehlerberichte können in HTMl generiert werden. Dies kann ältere Testläufe beinhalten, so dass man den historischen Verlauf einsehen kann.
  • Weitere Plugins für Prolog-Tests, JavaScript-Tests (via SpiderMonkey), verteilte Tests um Tests auf anderen Maschinen (mit ggfs. anderen Plattformen) auszuführen, Integration mit klassischen UnitTest
  • Reporting kann man momentan nicht (einfach) anpassen
Fragen
  • Ist die py-Import-Magic kompatibel mit py2exe? Antwort: Unbekannt.
  • Wie schwer wird es sein py.test auf Python 3k auszuführen? Antwort: Unbekannt
  • Parallele Ausführung: über verteilte Tests. Können sowohl lokal als auch remote verteilt werden inkl. Multi-CPU-Support.
  • Gruppierung von tests nach Setup ala Layer: Gibt es momentan nichts.