[Python-de] Fehlerausgabe bei eigenen Modulen/Bibliotheken

Stefan Schwarzer sschwarzer at sschwarzer.net
Do Aug 5 09:08:14 CEST 2010


Hallo Johannes,

On 2010-08-05 00:11, Johannes Markert wrote:
> Grüße an die Python-Gemeinschaft,

Grüße zurück! ;-)

> ich entwickle zur Zeit ein kleines Modul, vorerst für den privaten 
> Gebrauch; wenn es ausgereift ist,
> habe ich vor dieses eventuell auch zu veröffentlichen.
> 
> Mich stellt sich aber momentan eine Frage.
> Wie kann ich in Python professionell eine Fehlerausgabe implementieren.
> D.h. wenn ein Programmierer in seinem Projekt dieses Modul einbindet und 
> dabei zum Beispiel falsche Parameter
> an Modulfunktionen übergibt.
> [...]

Man unterscheidet üblicherweise zwischen unvorhersehbaren
Fehlern, zum Beispiel einer fehlenden Datei, und
Programmierfehlern. Zu letzteren würde auch ein falscher
Aufruf deiner Modulfunktion zählen.

Die unvorhersehbaren Fehler werden üblicherweise behandelt,
indem man eine "High-Level"-Ausnahme wirft. Was man eher
nicht machen sollte, ist, einen IndexError oder KeyError
einfach "durchzulassen". Das hätte den Nachteil, dass der
Aufrufer, wenn du mal einen Container in der Implementierung
deines Moduls von einer Liste auf ein Dictionary änderst,
plötzlich eine andere Ausnahme bekommt.

Nun zu deiner eigentlichen Frage. :-) Programmierfehler
werden im Allgemeinen in Python gar nicht behandelt, da man
davon ausgeht, dass ein Benutzer deiner Bibliothek ohnehin
testen muss, auch um logische Fehler zu finden. Wenn eine
Funktion zum Beispiel sinus heißt, aber den Cosinus des
Arguments zurückgibt, nützt der korrekte Aufruf mit einem
Float-Parameter auch nichts. ;-)

Python als Sprache verzichtet bewusst auf Typprüfungen,
deshalb ist es auch nicht sinnvoll, das nachzubilden. Du
kannst aber, quasi als "ausführbare Dokumentation",
assert-Anweisungen verwenden. Das würde ich aber nur tun,
wenn du bestimmte Fehler regelrecht erwartest; routinemäßig
alle denkbaren falschen Aufrufe mit assert absichern würde
ich nicht.

Ganz konkrete Typen zu verlangen, schränkt außerdem die
Aufrufmöglichkeiten oft unnötig ein. Die allermeisten
Funktionen, die ein Tupel vertragen, laufen zum Beispiel
auch mit einer Liste. _Falls_ du eine Typprüfung vornehmen
willst, verwende isinstance statt mit einem ganz konkreten
Typ zu vergleichen, denn isinstance(obj, Class) gibt auch
einen wahren Wert zurück, wenn obj zu einer von Class
abgeleiteten Klasse gehört.

> Dann soll noch währender der Interpretierung und/oder der Ausführung 
> seines Quellcodes das Programm stoppen und eine
> Fehlermeldung ausgegeben werden.

Das wäre mit assert-Anweisungen gegeben. Aber auch die
meisten anderen Aufruf-Probleme machen sich schnell durch
AttributeError, IndexError etc. bemerkbar.

> In der Form:
> 
>      File "dateiXY", line XY, in ModulXY
>      TypeError: Funktion_mit_falschen_Parametern():
>      received integer, tupel required
> 
> Mit anderen Worten eine Typenprüfung.

Siehe oben.

> Als weiteres Beispiel fällt mir noch ein, wenn die Werteanzahl einer 
> Liste oder eines Tupel zu gering/groß ist.
> Auch hier sollte entsprechend eine Fehlermeldung erscheinen.

Auch das solltest du, falls es in die Kategorie
Programmierfehler fällt, mit assert behandeln. Ein
fehlgeschlagenes assert löst einen AssertionError aus.

Wenn du es "nicht lassen kannst" eine Typ-Prüfung zu
implementieren, kannst du eine ProgrammingError-Exception
definieren und diese im Fehlerfall mit möglichst
ausführlichen Informationen auslösen. Eine scheinbare
Parallele gibt es bei der Python-Datenbank-API, bei der ein
ProgrammingError ausgelöst werden kann. Diese Exception
bezieht sich aber eher auf Fehler, die in der Abstimmung von
Programm und Datenbank liegen, zum Beispiel wenn gegen ein
Constraint wie NOT NULL oder UNIQUE verstoßen wird. Diese
Dinge sind ebenso wie auch externe Daten nicht vorhersehbar,
fallen also wohl nicht in die gleiche Kategorie wie
"offensichtlich" fehlerhafte Aufrufe.

Um das ganze Kopfzerbrechen um falsche Aufrufe möglichst
weitgehend zu vermeiden, solltest du von vornherein auf eine
für deine Modul-Funktionalität möglichst "natürliche" API
achten. Ich könnte mir vorstellen, dass du bei deiner Frage
schon bestimmte potenziell fehleranfällige Schnittstellen im
Sinn hattest. In dem Fall frag ruhig konkret nach Rat für
die Gestaltung der Aufruf-Schnittstelle.

Viele Grüße
Stefan




Mehr Informationen über die Mailingliste python-de