Seite 1 von 1
Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Freitag 10. Dezember 2010, 23:08
von dkell
Hallo
Ich habe ein Programm, welches Daten über TCP senden. Hier ein Code-Ausschnitt
Code: Alles auswählen
class Sharp(object):
def __init__(self, host, port):
self.host=host
self.port=port
try:
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((self.host, self.port))
except socket.error, msg:
log.warning('Fehler bei Socket-Verbindung: %s:%d.%s ' % (self.host, self.port, msg))
else:
log.debug('Verbindung aufgebaut mit SharpTV')
def __del__(self):
self.s.close()
log.debug('Verbindung geschlossen')
def request(self, cmd):
try:
self.s.send(cmd + '\r')
sleep(0.1)
data = self.s.recv(8192)
except:
log.warning("Befehl [%s] konnte nicht gesendet werden" % (cmd))
return False
else:
return data
if __name__ == "__main__":
sharp = Sharp('192.168.1.99', 100)
sharp.request('POWER????')
sharp.request('POWER1 ')
im __init__ Teil wird die Verbindung aufgebaut und mittels Try verprobt. Falls die Verbindung nicht zustande kommt laufen die sharp.request() auf Fehler. Wie und auf welcher Stufe sollte dies am besten geprüft werden, damit der nachfolgende Code nicht stehen bleibt?
Vielen dank
Dani
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 01:12
von BlackJack
@dkell: Das Problem ist ja, dass Du die Ausnahme einfach nur loggst und dann weiter machst als wäre nichts passiert. In beiden Methoden solltest Du die Ausnahmen nicht unterdrücken, sondern in der Aufrufhierarchie weiter nach oben laufen lassen, denn das `Sharp`-Objekt ist offensichtlich nicht in der Lage die angemessen zu behandeln. Eine Methode die "Fehlercodes" zurück gibt ist nicht schön. Da passiert ganz schnell das, was Du in Deinem Quelltext ja auch machst: Du ignorierst es einfach.
Die `__del__()`-Methode ist keine gute Idee. Es ist nicht garantiert wann sie aufgerufen wird, oder ob sie *überhaupt* irgendwann aufgerufen wird. Zudem kann die Existenz so einer Methode schon dazu führen das Objekte unter bestimmten Umständen nie freigegeben werden. Das ist kein deterministischer Destruktor.
Du solltest der Klasse entweder eine `close()`-Methode spendieren und die explizit aufrufen, oder `__enter__()`/`__exit__()` für die ``with``-Anweisung implementieren.
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 08:39
von snafu
Idealerweise sogar beides. Also eine `close()`-Methode schreiben und diese in `__exit__()` aufrufen. Ich nehme ja an, dass dies von BJ ohnehin implizit so gemeint war.
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 10:19
von dkell
Ich habe jetzt den Code mal umgebaut auf __enter__ und __exit__. Irgendwas habe ich aber noch nicht rund, da s.request() in with ... aufgerufen wird auch wenn die Verbindung nicht aufgebaut werden konnte, was dann natürlich einen Fehler verursacht. Muss ich dies in def.request() abfangen?
Vielen dank
Dani
Code: Alles auswählen
class Sharp(object):
def __init__(self, host, port):
self.host=host
self.port=port
def __enter__(self):
try:
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.connect((self.host, self.port))
except socket.error, msg:
print "FEHLER VERBINDUNG"
# WAS HIER?
else:
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.s:
self.s.close()
def request(self, cmd):
try:
self.s.send(cmd + '\r')
sleep(0.1)
data = self.s.recv(8192)
except:
log.warning("Befehl [%s] konnte nicht gesendet werden" % (cmd))
return False
else:
return data
if __name__ == "__main__":
sharp = Sharp('192.168.1.99', 100)
with sharp as s:
s.request('POWER????')
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 10:25
von BlackJack
@dkell: Das mir dem `__del__()` hatte mit dem Problem nichts zu tun.
An Stelle des Kommentars ``# WAS HIER?`` und bei dem ``return False`` in der `request()` gehört ein ``raise`` hin.
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 11:12
von dkell
BlackJack hat geschrieben:@dkell: Das mir dem `__del__()` hatte mit dem Problem nichts zu tun.
An Stelle des Kommentars ``# WAS HIER?`` und bei dem ``return False`` in der `request()` gehört ein ``raise`` hin.
Und das dann im Aufruf abfangen mit
Code: Alles auswählen
if __name__ == "__main__":
try:
sharp = Sharp('192.168.1.99', 100)
with sharp as s:
s.request('POWER????')
Sorry für die vielen Fragen aber ich Blicke da noch nicht ganz durch.
Danke
Dani
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 12:53
von BlackJack
@dkell: Wo Du das abfängst kommt doch darauf an was Du an welcher Stelle mit der Information, dass eine Ausnahme aufgetreten ist, anfangen willst. Du kannst um "alles" ein ``try/except`` setzen, oder um einzelne Aufrufe -- je nach dem wie sinnvoll Du jeweils an der Stelle auf Ausnahmen reagieren kannst.
Ich persönlich würde wohl zum Beispiel nicht in `Sharp`-Objekten loggen, weil mir das nicht nach "höherer" Anwendungslogik aussieht.
Den Code zum Verbinden würde ich übrigens weiter in der `__init__()` lassen und in der `__enter__()` nur ``return self`` schreiben. Sonst ist man ja *gezwungen* ``with`` zu verwenden. Und Du musst dann nicht Namen wie `s` erfinden sondern kannst einfach sowas schreiben:
Und so ganz sauber gehst Du mit der Socketprogrammierung nicht um, denn es könnte passieren, dass der `recv()` nicht alle Daten liefert.
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 13:03
von dkell
BlackJack hat geschrieben:
Und so ganz sauber gehst Du mit der Socketprogrammierung nicht um, denn es könnte passieren, dass der `recv()` nicht alle Daten liefert.
Hi
Vielen dank schon mal für die Tipps. Was wäre denn hier besser? Schreibe noch das eine oder andere solcher Module und wäre natürlich froh, dies professionell und richtig zu haben.
Vielen dank
Dani
Re: Wie Fehler prüfen bei Klasseninitialisierung
Verfasst: Samstag 11. Dezember 2010, 13:27
von BlackJack
@dkell: Was man sauber machen muss hängt vom Protokoll ab. Wenn man weiss wiviele Bytes kommen, muss man solange lesen bis man die mindestens zusammen hat, und sich eventuelle zusätzliche Daten für den nächsten Lesevorgang merken.
Wenn es ein Endkennzeichen gibt, dann muss man solange lesen bis man das in den Eingabedaten enthalten ist, und sich eventuelle zusätzliche Daten auch hier wieder für den nächsten Lesevorgang merken.