Fehlerbehandlung beim Modul "cmd"

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hallo!

Ich schreibe gerade an einer Art Mini-Wrapper, der Methoden aus der ftplib durch typische Unix-Befehle zugänglich machen soll. Das ganze läuft auf einer eigenen Shell und klappt in seiner bisherigen Form auch.

Jetzt möchte ich mich aber an die Fehlerbehandlung machen. Das ist mir schon allein für den persönlichen Gebrauch wichtig. Denn sobald ich irgendeinen Vertipper mache, werde ich komplett aus der Shell geworfen.

An dem gleich folgenden Skript kann man sehen, dass ich bisher den "Vertipper-Fehler" abfange und die Begründung ausgebe. Natürlich könnte ich jetzt direkt in allen vorhandenen (und möglicherweise folgenden) do_*-Funktionen den selben Fehler abfangen - dann würde er mich nicht rausschmeissen. Aber gibt es nichts eleganteres als die ständige Wiederholung des try-except-Blocks? Mag auch sein, dass ich mich gerade etwas dumm anstelle. Helft mir bitte auf die Sprünge. :)

http://paste.pocoo.org/show/104011/
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Evtl mit nem decorator?

Code: Alles auswählen

def catch_ftp_errors(func):
    def catch(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except ftplib....: pass
    return catch

...
    @catch_ftp_errors
    def do_...
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Wenns bissi Metaklassen Magie sein soll (ja, ich selbst zweifle auch die Sinnhaftigkeit des Folgenden an).

Code: Alles auswählen

def dec(f):
    def exc(*args, **kwargs):
        # Hier dein error handling rein.
        print 'foo'
        return f(*args, **kwargs)
    return exc


def metacls(name, bases, dict_):
    for key, value in dict_.iteritems():
        if hasattr(value, '__call__'):
            dict_[key] = dec(value)
    return type(name, bases, dict_)


class Foo(object):
    __metaclass__ = metacls
    def x(self):
        pass


foo = Foo()
foo.x()
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Besten Dank. Mit Dekoratoren war ich noch nicht so vertraut, aber jetzt funktioniert es. :)

http://paste.pocoo.org/show/104053/
name hat geschrieben:Wenns bissi Metaklassen Magie sein soll (ja, ich selbst zweifle auch die Sinnhaftigkeit des Folgenden an).
Oha. Sofern das nicht irgendwelche phänomenalen Vorteile in meinem Fall bringt, würde ich doch eher Dekoratoren bevorzugen. ;)
Zuletzt geändert von snafu am Sonntag 15. Februar 2009, 00:47, insgesamt 2-mal geändert.
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Warum machst du nicht statt

Code: Alles auswählen

if hasattr(value, '__call__'):
das hier:

Code: Alles auswählen

if callable(value):
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Und für Metaklassen sollte man auch wirklich eine Klasse verwenden und nicht eine Funktion ;-) Es wäre in diesem Fall nur eine Zeile länger, aber sieht etwas besser was passiert.

Die Dekorator-Lösung halte ich hier auch für die richtige Wahl. Man kann natürlich auf den Dekoratoren noch mit Metaklassen rumspielen :)
Zuletzt geändert von EyDu am Sonntag 15. Februar 2009, 00:25, insgesamt 2-mal geändert.
Das Leben ist wie ein Tennisball.
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

derdon hat geschrieben:Warum machst du nicht statt

Code: Alles auswählen

if hasattr(value, '__call__'):
das hier:

Code: Alles auswählen

if callable(value):
Weil das mit 3.0 gehen muss.
EyDu hat geschrieben:Und für Metaklassen sollte man auch wirklich eine Klasse verwenden und nicht eine Funktion ;-) Es wäre in diesem Fall nur eine Zeile länger, aber sieht etwas besser was passiert.

Die Dekorator-Lösung halte ich hier auch für die richtige Wahl. Man kann natürlich auf den Dekoratoren noch mit Metaklassen rumspielen :)
Ich zitiere:
__metaclass__

This variable can be any callable accepting arguments for name, bases, and dict. Upon class creation, the callable is used instead of the built-in type().
Also wozu eine Klasse wenn man sie nicht braucht (Das ist hier ja kein Java)? Die Metaklasse ist dann halt immer noch type.
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

name hat geschrieben:
derdon hat geschrieben:Warum machst du nicht statt

Code: Alles auswählen

if hasattr(value, '__call__'):
das hier:

Code: Alles auswählen

if callable(value):
Weil das mit 3.0 gehen muss.
Steht wo? callable sollte automatisch nach 3.0 konvertiert werden können, andererseits hat nicht jedes callable unter 2.5/(6?) __call__. Also muss man callable verwenden.
Benutzeravatar
name
User
Beiträge: 254
Registriert: Dienstag 5. September 2006, 16:35
Wohnort: Wien
Kontaktdaten:

Darii hat geschrieben:
name hat geschrieben:
derdon hat geschrieben:Warum machst du nicht statt

Code: Alles auswählen

if hasattr(value, '__call__'):
das hier:

Code: Alles auswählen

if callable(value):
Weil das mit 3.0 gehen muss.
Steht wo? callable sollte automatisch nach 3.0 konvertiert werden können, andererseits hat nicht jedes callable unter 2.5/(6?) __call__. Also muss man callable verwenden.
Welche Funktion hat es denn nicht? Und hier brauchen wir ja eigtl. nur Funktionen. o_O
Ohloh | Mein Blog | Jabber: segfaulthunter@swissjabber.eu | asynchia – asynchrone Netzwerkbibliothek

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Darii hat geschrieben:callable sollte automatisch nach 3.0 konvertiert werden können[...]

Code: Alles auswählen

bash-3.2$ cat foo.py
callable(lambda: None)
bash-3.2$ python -3 foo.py
foo.py:1: DeprecationWarning: callable() not supported in 3.x; use hasattr(o, '__call__')
  callable(lambda: None)
Was sagt uns dass? ;)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

name hat geschrieben:Welche Funktion hat es denn nicht? Und hier brauchen wir ja eigtl. nur Funktionen. o_O
Klassen z.B.
BlackJack

Das betrifft "old-style"-Klassen. "New-style"-Klassen haben das Attribut:

Code: Alles auswählen

In [56]: class A(object): pass
   ....:

In [57]: A.__call__
Out[57]: <method-wrapper '__call__' of type object at 0x8d2b664>
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Oha. Sofern das nicht irgendwelche phänomenalen Vorteile in meinem Fall bringt, würde ich doch eher Dekoratoren bevorzugen. Wink
Im Grunde ist diese Funktion ein Dekorator für Klassen, der für jedes aufrufbare Objekt wieder eine Dekorator Funktion auf dieses Objekt anwendet. Metaklassen an sich sind deutlich mächtiger. (Mit 3.0 gibt es auch "richtige" Dekoratoren für Klassen - Mag es sein, das mit einer solchen "Metafunktion" die Funktionalität im Grunde auch implementiert werden kann? Sieht danach aus.)
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Klassendekoratoren gibt es bereits in Python 2.6. Könnte dann beispielsweise so aussehen.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Ich habe das ganze nun nochmal direkt mithilfe einer "Metafunktion" umgeschrieben, sieht dann so aus:

http://dpaste.com/120954/
(paste.pocoo.org ist down)

Da ich noch 2.5 verwende, musste ich nur unten meinen eigenen methodcaller bauen (was ich auch ohne getattr hätte machen können, aber nun ist es gepostet). Man kann also praktisch schon in 2.5 solche Pseudoklassendekoratoren benutzen, der einzige Unterschied ist, daß ein echter Dekorator die Klasse nach der Konstruktion bekommt, während ein solcher Pseudodekorator die Klasse zusammengebaut zurückliefern musst. Darum gings mir ja, man kann es praktisch 1:1 umschreiben.
fred.reichbier
User
Beiträge: 155
Registriert: Freitag 29. Dezember 2006, 18:27

Wenn ich dein Problem richtig verstehe, könntest du auch einfach die Methode `onecmd` überschreiben. Orientiere dich einfach an dem Original in cmd.py und füg das Exceptionhandling hinzu.

Gruß,

Fred
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

fred.reichbier hat geschrieben:Wenn ich dein Problem richtig verstehe, könntest du auch einfach die Methode `onecmd` überschreiben. Orientiere dich einfach an dem Original in cmd.py und füg das Exceptionhandling hinzu.
Dann würde ich den bisherigen Code der Methode übernehmen und lediglich die Zeilen für's Errorhandling einfügen. Ich finde die Abtrennung der Aufgaben mittels eigener Funktion schon besser, ehrlich gesagt. Wenn man sich den Docstring anschaut, steht da auch, dass es erstmal nur um die Interpretation der Eingabe geht. Ich will da nicht so sehr in den internen Ablauf des Moduls eingreifen, wenn es auch anders geht. Zudem wird sowas schnell unübersichtlich.

Das Problem - um es nochmal deutlich zu machen - steht im Ablauf hinter der do_*-Funktion und somit hinter der Interpretation. Es ging darum, innerhalb des Loops eine Unterbrechung durch Exception zu vermeiden, also quasi eine Art Überwachungsinstanz einzurichten, die im Fall der Fälle eingreifen kann. Das Konzept von Dekoratoren eignet sich dafür IMHO am besten.
Antworten