Seite 1 von 1

Fehlerbehandlung beim Modul "cmd"

Verfasst: Samstag 14. Februar 2009, 20:55
von snafu
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/

Verfasst: Samstag 14. Februar 2009, 22:16
von Darii
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_...

Verfasst: Samstag 14. Februar 2009, 23:16
von name
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()

Verfasst: Sonntag 15. Februar 2009, 00:08
von snafu
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. ;)

Verfasst: Sonntag 15. Februar 2009, 00:16
von derdon
Warum machst du nicht statt

Code: Alles auswählen

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

Code: Alles auswählen

if callable(value):

Verfasst: Sonntag 15. Februar 2009, 00:22
von EyDu
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 :)

Verfasst: Sonntag 15. Februar 2009, 00:24
von name
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.

Verfasst: Sonntag 15. Februar 2009, 00:49
von Darii
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.

Verfasst: Sonntag 15. Februar 2009, 01:25
von name
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

Verfasst: Sonntag 15. Februar 2009, 03:48
von DasIch
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? ;)

Verfasst: Sonntag 15. Februar 2009, 11:16
von Darii
name hat geschrieben:Welche Funktion hat es denn nicht? Und hier brauchen wir ja eigtl. nur Funktionen. o_O
Klassen z.B.

Verfasst: Sonntag 15. Februar 2009, 12:49
von 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>

Verfasst: Sonntag 15. Februar 2009, 18:43
von str1442
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.)

Verfasst: Sonntag 15. Februar 2009, 19:20
von Trundle
Klassendekoratoren gibt es bereits in Python 2.6. Könnte dann beispielsweise so aussehen.

Verfasst: Sonntag 15. Februar 2009, 19:53
von str1442
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.

Verfasst: Sonntag 15. Februar 2009, 20:28
von fred.reichbier
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

Verfasst: Sonntag 15. Februar 2009, 20:55
von snafu
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.