Quelltextanalysen helfen Entwicklern schon im Vorfeld Fehler zu vermeiden. Wer zudem Formatierungsregeln einhält, macht seinen Code lesbarer. Verschiedene Tools nehmen dem Python-Entwickler die Arbeit ab.
Dank einer dynamischen Typverwaltung muss ein Entwickler Python-Programme nicht explizit kompilieren. Allerdings finden Compiler frühzeitig Fehler, die bei Python oft erst zur Laufzeit überraschen. Verschiedene Werkzeuge helfen dabei, die Python-Programme mit Hilfe statischer Quelltextanalyse systematisch auf Fehler abzuklopfen. Einige davon untersuchen auch die Formatierung und machen, basierend auf den offiziellen Formatierungsregeln, Vorschläge für besser lesbaren Quelltext.
Stilvoll
Guter Stil ist in Python vorgeschrieben – oder zumindest empfohlen. Wie ein Entwickler seine Quelltexte optimal formatiert, beschreibt detailliert PEP8 (Python Enhancement Proposal, [1]). Ob er nun nach einem Komma ein Leerzeichen setzt oder einen Variablennamen groß oder klein schreibt, mag manchem Entwickler unwichtig erscheinen, bringt aber Konsistenz in den Quellcode. Das erleichtert es anderen Programmierern, den Code zu lesen und zu verstehen. Anstatt mit der exotischen Formatierung zu hadern, können sie sich voll auf die inhaltliche Arbeit stürzen.
Python hat einige Werkzeuge im Gepäck, die Programme auf ihre Konformität zu PEP8 abklopfen und diese teilweise sogar automatisch korrigieren (Tabelle 1). Das dafür wohl am häufigsten genutzte heißt Pep8 [2]. Es setzt PEP8 zum größten Teil um und ist konfigurierbar. Andere Werkzeuge wie Flake8 [3] und Autopep8 [4] verwenden Pep8 als Unterbau. Auch Pylint [5] testet Quelltexte gegen viele der PEP8-Regeln, benutzt dazu aber einen eigenen Ansatz.
Tabelle 1
Überblick der Werkzeuge
|
Werkzeug |
Python 2.7 |
Pypi |
Python 3.4 |
Pypi |
Abhängigkeiten |
Version |
Lizenz |
Versionskontrolle |
Dokumentationsqualität |
|---|---|---|---|---|---|---|---|---|---|
|
Pylint |
x |
x |
x |
x |
»logilab-common« , »astroid« |
1.3.1 |
GPL |
Mercurial, Bitbucket |
10/10 |
|
Pyflakes |
x |
x |
x |
x |
– |
0.8.1 |
MIT |
Git, Launchpad |
0/10 |
|
Frosted |
x |
x |
x |
x |
»pies« , (nur Python 2: »pies2overrides« , »enum34« , »ipaddress« ) |
1.4.1 |
MIT |
Git, Github |
3/10 |
|
Pychecker |
x |
– |
– |
– |
– |
0.8.18 |
BSD |
CVS, Sourceforge |
5/10 |
|
Pep8 |
x |
x |
x |
x |
– |
1.5.7 |
MIT |
Git, Github |
8/10 |
|
Flake8 |
x |
x |
x |
x |
»pep8« , »mccabe« , »setuptools« |
2.2.2 |
MIT |
Mercurial, Bitbucket |
6/10 |
|
McCabe |
x |
x |
x |
x |
– |
0.2.1 |
MIT |
Git, Github |
3/10 |
|
Vulture |
x |
x |
x |
x |
– |
1.0.3 |
GPL |
Mercurial, Bitbucket |
3/10 |
|
Autopep8 |
x |
x |
x |
x |
»pep8« |
0.2.1 |
MIT |
Git, Github |
5/10 |
Höchst verdächtig
Während er Python-Programme in Bytecode verwandelt, erkennt der Python-Interpreter, abgesehen von Syntaxfehlern, nur relativ wenige potenzielle Bugs im Programm.
Verschiedene Werkzeuge helfen dem Entwickler jedoch, indem sie Hinweise auf offensichtliche oder mögliche Fehler liefern, bevor er das Programm ausführt (Tabelle 2). Das wohl älteste davon heißt Pychecker [6]. Es kann sehr viel, muss aber die zu kontrollierenden Module importieren und ist deshalb kein statischer Quelltext-Analyst. Das Werkzeug Pylint geht darum über Pychecker hinaus, weil es tatsächlich statisch arbeitet, also ohne Modulimporte.
Tabelle 2
Ausgewählte Funktionen
|
Funktion |
Pep8 |
Autopep8 |
Pylint |
Pyflakes |
Frosted |
Pychecker |
Flake8 |
McCabe |
Vulture |
|---|---|---|---|---|---|---|---|---|---|
|
PEP8 |
x |
x |
(x) |
– |
– |
– |
x |
– |
– |
|
Nicht genutzte Variablen |
– |
– |
x |
x |
x |
– |
– |
– |
x |
|
Nicht erreichbarer Code |
– |
– |
x |
– |
– |
– |
– |
– |
– |
|
Codemetriken |
– |
– |
x |
– |
– |
– |
– |
– |
– |
|
Re-Definition von eingebauten Funktionen |
– |
– |
x |
– |
– |
x |
– |
– |
– |
|
Doc-Strings |
– |
– |
x |
– |
– |
– |
– |
– |
– |
|
Zu lange Bezeichner |
– |
– |
x |
– |
– |
– |
– |
– |
– |
|
Zu viele Verzweigungen |
– |
– |
x |
– |
– |
– |
– |
x |
– |
Wesentlich leichtgewichtiger, allerdings auch mit entsprechend weniger Funktionen ausgestattet, kommt Pyflakes [7] daher. Ein erweiterter Nachfolger heißt Frosted [8] und erlaubt es zum Beispiel, das Überprüfen spezieller Fehlerarten zu deaktivieren. McCabe [9] misst einzig die zyklomatische Komplexität [10], Vulture [11] findet nicht genutzte Programmteile.
Werkzeuge aus Werkzeugen
Wer sowohl die Formatierung untersuchen als auch Fehler aufspüren möchte, kombiniert in der Regel verschiedene Werkzeuge miteinander. Entwickler greifen gern zu Flake8, weil es zum Beispiel Pyflakes, Pep8 und McCabe unter einen Dach vereint. Seine Funktionalität entspricht in etwa der von Pylint, wobei das letztgenannte Tool zwar zusätzliche Fehlerarten abdeckt, dafür aber die PEP8-Regeln weniger stark im Auge behält. Abbildung 1 versucht, die bislang aufgezählten Werkzeuge nach ihrer Funktionalität zu ordnen und dabei zugleich in eine grobe Systematik einzubetten.
Die Testumgebungen
Im Test liefen die vorgestellten Werkzeuge in virtuellen Umgebungen für Python 2.7 und 3.4. Dabei waren keine anderen als die Testpakete im Spiel. Das länger nicht aktualisierte Pychecker erwies sich als einziges Tool, das es nur für Python 2 gibt, die anderen liegen in Python 3 vor. Über »pip« [12] ließen sie sich komfortabel installieren.
Stilsicher mit Pep8 & Co.
Das auf Github gehostete Pep8 ist gut dokumentiert und lässt sich über Kommandozeilen-Schalter oder eine Konfigurationsdatei weitreichend einrichten. Einzelne Formatierungsregeln schaltet der Programmierer nach Bedarf ein oder aus. Die Dokumentation listet die dafür nötigen Fehlercodes auf. Die Option »–show-pep« zeigt zum Beispiel den passenden Text aus PEP8 und die Option »–show-source« die zu beanstandende Stelle im Quelltext an.
In der minimalen Testdatei von Listing 1 fehlt nach dem Komma das von PEP8 geforderte Leerzeichen. Listing 2 zeigt, wie Pep8 dank unterschiedlicher Kommandozeilen-Optionen reagiert.
Listing 1
whitespace.py
01 """Kein Leerzeichen nach Komma. 02 """ 03 04 a,b = 10, 25
Listing 2
Pep8 in Aktion
01 $ pep8 whitespace.py
02 whitespace.py:4:2: E231 missing whitespace after ','
03
04 $ pep8 --show-source whitespace.py
05 whitespace.py:4:2: E231 missing whitespace after ','
06 a,b = 10, 25
07 ^
08 $ pep8 --show-pep8 whitespace.py
09 whitespace.py:4:2: E231 missing whitespace after ','
10 Each comma, semicolon or colon should be followed by whitespace.
11
12 Okay: [a, b]
13 Okay: (3,)
14 Okay: a[1:4]
15 Okay: a[:4]
16 Okay: a[1:]
17 Okay: a[1:4:2]
18 E231: ['a','b']
19 E231: foo(bar,baz)
20 E231: [{'a':'b'}]
Pep8 untersucht sowohl einzelne Dateien als auch ganze Verzeichnisbäume. Wer eine größere Anzahl von Bibliotheken durchforstet, greift am besten zur Option »-qq« , um die Ausgabe einzelner Fehlermeldungen zu unterdrücken. Ein »–statistics« listet die Anzahl der gefundenen Formatierungsempfehlungen auf. Abbildung 2 zeigt das Ergebnis für das Verzeichnis »site-packages« in der für diesen Artikel genutzten virtuellen Umgebung von Python 3.4.
Es dauert oft recht lange, größere Quelltextdateien von Hand zu formatieren. Der Entwickler fügt zum Beispiel Leerzeichen nach Kommas ein, bricht lange Zeilen um – kurzum, die Arbeitsschritte wiederholen sich und sind nicht sehr anspruchsvoll. Hier macht sich Autopep8 nützlich. Die Dokumentation ist ordentlich, viele Kommandozeilen-Optionen und eine Konfigurationsdatei zügeln die Autokorrektur.
Gibt der Entwickler die Option »–select« an, korrigiert Autopep8 nur die angegebenen Formatierungsprobleme. Hierbei hilft eine kommaseparierte Liste, welche die Problem-Kurzbezeichnungen enthält. Die Option »–agressive« darf verstärkend auch mehrfach auftauchen. Sie steuert, wie tief Autopep8 in den Code eingreift. Wer Quelltext automatisch modifizieren lässt, geht jedoch das Risiko ein, dass die Prüfsoftware den Code an einigen Stellen unbeabsichtigt ändert – eine gute Testabdeckung steuert dagegen.
Pychecker
Als dienstältestes Werkzeug im Test leidet Pychecker an Alterungserscheinungen, unterstützt Python 3 noch nicht und lässt sich nicht mit Pip installieren. Sourceforge hostet das Projekt in einem CVS, was ebenfalls nicht für dessen Modernität spricht. Der Funktionsumfang ist zwar groß, deckt sich aber überwiegend mit dem von Pylint. Letzteres funktioniert auch mit Python 3, bietet zusätzliche Funktionalität und eine wesentlich bessere Dokumentation.
Pylint
Das Werkzeug mit den meisten Fähigkeiten ist von Hause aus sehr gesprächig. Für das kleine Testprogramm aus Listing 3 erzeugt Pylint die Ausgabe aus Abbildung 3. Es beschwert sich, dass der Name »unused« zwar auf den Wert »5« zeigt, dieser aber nicht mehr auftaucht. Das ergibt in den meisten Fällen keinen Sinn und könnte auch auf einem Tippfehler beruhen.
Listing 3
unused.py
01 """Nicht genutzte Variable. 02 """ 03 04 05 def add(arg1, arg2): 06 """Add two objects. 07 """ 08 unused = 5 09 return arg1 + arg2
Ausnahmen davon wären etwa Zeitmessungen mit einem Zeitstempel vor und nach einer Anweisung. Im diesem Fall hätte Pylint ein False Positive erzeugt, die Meldung ließe sich aber leicht unterdrücken.
Die Ausgabe in Abbildung 3 listet also den gefundenen Fehler auf und gibt ein sehr ausführliches Feedback. Das umfasst einen Bericht über die Anzahl der analysierten Anweisungen, die Benachrichtigungen – sortiert nach Kategorie und Häufigkeit –, eine Bewertung mit maximal zehn Punkten sowie Statistiken und Codemetriken. Das Ganze lässt sich mit »–reports=n« ausschalten, wie Listing 4 zeigt.
Listing 4
unused.py ohne Feedback
01 $ pylint --reports=n unused.py 02 ************* Module unused 03 W: 8, 4: Unused variable 'unused' (unused-variable)
Pylint bringt zudem eine einfache grafische Benutzeroberfläche mit. Die lange Ausgabe aus Abbildung 3 lässt sich damit etwas komfortabler betrachten, etwa gefiltert nach Rubriken.
Wie die ausführliche Hilfe zeigt, kann ein Entwickler nahezu jeden Aspekt der Analyse und Ausgabe konfigurieren. Pylint unterstützt viele Kommandozeilen-Argumente, zieht aber auch eine Konfigurationsdatei im ».ini« -Format heran. Jedes Projekt erhält bei Bedarf eine eigene Konfiguration, Teams können gegen diese testen.
Was Pylint prüfen soll, stellt der Entwickler auch Wunsch auch innerhalb einer Datei sehr fein ein. So schaltet »#pylint: disable=unused-variable« die Fehlermeldung aus Listing 4 ab. Das kann nur für eine Zeile, für eine Funktion oder Methode, für eine Klasse oder den Rest des Moduls gelten. Der Kommentar »#pylint: enable=unused-variable« aktiviert die Regel wieder.
Daneben kennt Pylint reguläre Ausdrücke für alle Arten von Namen für Klassen, Funktionen, Methoden sowie lokale und globale Variablen. Nicht erreichbare Stellen, zum Beispiel nach einer »return« -Anweisung, fallen Pylint genauso auf wie Neudefinitionen von eingebauten Funktionen, etwa »sum()« oder »len()« . Auch komplexe Bereiche mit vielen verschachtelten Schleifen oder »if« -Anweisungen mag es nicht. Das nervt Entwickler womöglich, hilft aber dabei, Fehler in Programmen aufzuzeigen.
Pyflakes
Im Kontrast zu den vielen Konfigurationsmöglichkeiten von Pylint bietet Pyflakes so gut wie keine Einstellungen an, folglich fehlt auch eine Dokumentation. Die zu untersuchenden Fehler sind fest verdrahtet, Pyflakes eignet sich besonders zum Einbetten in Editoren oder IDEs.
Frosted
Frosted ist eine verbesserte Version von Pyflakes. Entwickler dürfen auf der Kommandozeile oder in einer Datei festlegen, welche Fehler das Werkzeug untersucht. Die Dokumentation ist spartanisch, aber ausreichend, um das Werkzeug an eigene Bedürfnisse anzupassen. Listing 5 zeigt, wie Frosted arbeitet, die Option »-vb« entlockt dem Tool etwas mehr Details. Mit Python 2 ist Frosted deutlich langsamer als mit Python 3. Das liegt offensichtlich an der »pies« -Abhängigkeit [13], die es erlaubt, den gleichen Quelltext für Python 2 und 3 zu verwenden.
Listing 5
Analyse mit Frosted
01 $ frosted unused.py 02 unused.py:8: local variable 'unused' is assigned to but never used 03 $ frosted -vb unused.py 04 unused.py:8:4:E307:unused:local variable 'unused' is assigned to but never used
Flake8
Wer nicht ständig Pyflakes, Pep8 und McCabe ausführen möchte, sollte sich Flake8 anschauen, das diese drei Tools zusammenführt. Es ordnet die Ausgabe nach Dateien und ignoriert sämtliche Zeilen, die ein »# flake8: noqa« enthalten. Wie bei Pylint erledigen Erweiterungen bei Bedarf zusätzliche Aufgaben. Standardmäßig ist McCabe ausgeschaltet und lässt sich über die Option »max-complexity« aktivieren. Das funktioniert allerdings nur mit Version 2.2.2, die allerneuste Version 2.2.3 erkennt diese Option leider nicht mehr.
Vulture
Vulture beschränkt sich auf die Suche nach nicht genutzten Programmteilen. Dabei beschwert es sich leider über jede definierte, aber nicht aufgerufene Funktion. Das ist unsinnig und erzeugt viele falsche Positive. Zurzeit erweist es sich daher als nicht sehr nützlich.
Startschuss
Für kleinere Entwicklungsaufgaben eignen sich in der Regel entweder Flake8 oder Pylint. Die automatische Korrektur mit Autopep8 ist für Einsteiger praktisch. Wer aber häufiger die Kombination aus Pylint und anschließend Pep8 einsetzt, dürfte eine Autokorrektur kaum mehr brauchen, weil er sich aufgrund des häufigen Feedbacks automatisch einen guten Programmierstil angewöhnt.
Bei der Durchsicht größerer Dateien, der Analyse ganzer Pakete oder der automatischen Kontrolle im Editor-Hintergrund spielt auch die Laufzeit eine Rolle. Pylint analysiert seinen eigenen Quelltext in etwa 22 Sekunden. Flake8 benötigt mit den Standardeinstellungen zirka 6 Sekunden. Aktiviert der Entwickler die Option »max-complexity« , verdoppelt sich die Laufzeit auf etwa 12 Sekunden.
Letztlich ist es eine Geschmacksfrage, ob ein Entwickler lieber das gesprächige Pylint einsetzt und falsche Positive explizit ausschaltet oder ob er mit Flake8 etwas schneller ans Ziel kommt, dafür aber den ein oder anderen potenziellen Fehler ignoriert. Eines der beiden Tools sollten Python-Programmierer auf jeden Fall verwenden, das Aufwand-Nutzen-Verhältnis rechtfertigt den Einsatz allemal.
Infos
- PEP8: http://legacy.python.org/dev/peps/pep-0001/
- Pep8: http://pep8.readthedocs.org/en/latest/
- Flake8: https://bitbucket.org/tarek/flake8/wiki/Home
- Autopep8: https://github.com/hhatto/autopep8
- Pylint: http://www.pylint.org
- Pychecker: http://pychecker.sourceforge.net
- Pyflakes: https://launchpad.net/pyflakes
- Frosted: https://github.com/timothycrosley/frosted
- McCabe: https://github.com/flintwork/mccabe
- Zyklomatische Komplexität: http://de.wikipedia.org/wiki/McCabe-Metrik
- Vulture: https://pypi.python.org/pypi/vulture/
- »pip« : https://github.com/pypa/pip
- »pies« : https://github.com/timothycrosley/pies









