Aus Linux-Magazin 11/2014

Statische Quelltextanalyse für Python

© Aleksandr Markin, 123RF

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.

Abbildung 1: Pythons Codechecker verfolgen etwas unterschiedliche Ziele.

Abbildung 1: Pythons Codechecker verfolgen etwas unterschiedliche Ziele.

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.

Abbildung 2: Dank »qq« zeigt Pep8 die Fehler mehrerer Bibliotheken an.

Abbildung 2: Dank »qq« zeigt Pep8 die Fehler mehrerer Bibliotheken an.

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
Abbildung 3: Pylint geizt nicht mit Informationen.

Abbildung 3: Pylint geizt nicht mit Informationen.

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.

Der Autor

Dr.-Ing. Mike Müller ist Geschäftsführer der Python Academy http://www.python-academy.de und ein erfahrener Python-Trainer. Er ist erster Vorstandsvorsitzender des Python Software Verband e.V. und war Chairman der Euro-Python 2014 in Berlin. Auch die Euro-Sci-Py 2008 und 2009 sowie die Pycon DE 2011 und 2012 hat er geleitet.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben