Seite 1 von 1
Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Mittwoch 12. Mai 2010, 11:05
von martin.chatterjee
Moin zusammen,
ich kaempfe jetzt schon eine ganze Weile mit einem Problem - vielleicht kann mir einer von Euch dabei weiterhelfen:
Ich lasse ein Python Skript in einer Windows Shell laufen und moechte mitbekommen, wenn der User die Shell schliesst, um dann noch einiges an Cleanup auszufuehren (Datenbank-Connection schliessen, ...)
Wenn Der User
Ctrl-C drueckt, klappt das alles wunderbar, aber wenn er einfach die
Shell schliesst, indem der auf das Close-Icon clickt, beginnen meine Probleme.
Kurz mein prinzipieller Ansatz bis jetzt: ich hole mir ein Objekt meiner Klasse und rufe dann meine HauptMethode:
in
__init__() wird eine Datenbank-Connection etabliert.
in
mainLoopForever() werden alle paar Sekunden Daten mit der Datenbank abgeglichen.
Folgendes habe ich bereits ausprobiert: (klappt alles bei "Ctrl-C" und versagt alles beim Schliessen der Shell)
-
__del__ implementieren und dort meinen Cleanup ausfuehren
- einen
TRY FINALLY block in
mainLoopForever() einbauen und dort meinen Cleanup ausfuehren
-
__enter__ und
__exit__ implementieren und mein Objekt mit dem
WITH statement bauen
Bin dankbar fuer jede Hilfe und jedes Feedback...
Vielen Dank im voraus,
Martin
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Mittwoch 12. Mai 2010, 16:39
von jbs
Wenn du die shell schließt, dann beendest du Python auf eine eher unfreundliche Weise. Wie sehen denn deine Cleanups aus?
Und __del__ sollte man eher nicht
verwenden.
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Mittwoch 12. Mai 2010, 17:18
von martin.chatterjee
jbs hat geschrieben:Wenn du die shell schließt, dann beendest du Python auf eine eher unfreundliche Weise.
Ich weiss, aber solange dieses Tool in einer sichtbaren Shell laeuft, muss ich mich mit dem Szenario befassen, dass der User einfach die Shell schliesst...
jbs hat geschrieben:Wie sehen denn deine Cleanups aus?
Meine Klasse hat eine Methode
cleanup(), in der ein, zwei FileHandles geschlossen werden, der MaschinenStatus in einer verbundenen MySQL Datenbank auf "OFFLINE" gesetzt wird und schliesslich die Verbindung zur Datenbank geschlossen wird.
Unterm Strich ist mein Problem, dass diese Methode
cleanup() zuverlaessig gerufen werden sollte, auch wenn der User die Shell einfach schliesst.
Die FileHandles und die DB-Connection "regeln" sich ja irgendwie von selbst (auch wenn es unsauber ist, sich nicht explizit darum zu kuemmern), aber der Status der Maschine in der Datenbank wird momentan nicht auf OFFLINE gesetzt...
jbs hat geschrieben:Und __del__ sollte man eher nicht
verwenden.
Ja, da hast Du recht - hab ich eher der Vollstaendigkeit halber aufgelistet.
Unterm Strich "riecht" das eher danach, das ich grundsaetzlich an die Situation anders rangehen muss... ?
Martin
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Donnerstag 13. Mai 2010, 03:55
von jerch
Das von Dir gewünschte Verhalten erreichst Du, indem Du der Konsole einen Kontrollhandler mit der WinAPI-Funktion SetConsoleCtrlHandler zuweisst, einfaches Beispiel:
Code: Alles auswählen
from ctypes import CFUNCTYPE, windll
from ctypes.wintypes import BOOL, DWORD
from threading import Event
from time import sleep
cleanup_called = Event()
def cleanup():
cleanup_called.set()
print 'cleanup called'
sleep(3)
def HandlerRoutine(dwCtrlType):
'''http://msdn.microsoft.com/en-us/library/ms683242.aspx'''
if dwCtrlType == 2:
cleanup()
return 0
HANDLERFUNC = CFUNCTYPE(BOOL, DWORD)
callback = HANDLERFUNC(HandlerRoutine)
windll.kernel32.SetConsoleCtrlHandler(callback, True)
count = 0
while not cleanup_called.isSet():
print count
count += 1
sleep(1)
# do cleanup
sleep(3)
Dabei gibt es allerdings ein paar Dinge zu beachten:
Die callback-Funktion (im Bsp. HandlerRoutine) läuft in einem eigenen Thread, ergo darfst Du hier nur threadsichere Aufräumaktionen vornehmen. Ich weiß leider nicht, wie ctypes den Funktionscallback erstellt und inwieweit Pythons threading-Abstraktion (mit GIL usw.) mit der API-Funktion kollidieren könnte und würde nicht mit Locking usw. im cleanup-Handler rumspielen, sondern dem Hauptprogramm das close-Ereignis unterschieben und dort aufräumen lassen. Zumindest scheint Event() aus threading wie erwartet zu funktionieren. Dafür ist es wiederum nötig, dem Hauptprogramm genügend Zeit für den Cleanup einzuräumen, d.h. die callback-Funktion muß solange warten, bis das Hauptprogramm fertig ist (der Prozess wird ansonsten einfach von Windows gekillt und die Konsole geschlossen). Für eine echte Anwendung könntest Du z.B. ein zweites Event nach Abschluss der Aufräumaktion an den wartenden callback senden, der daraufhin beendet wird.
Der gesamte Aufräumspass sollte auch nicht zu lange dauern, sonst quittiert Windows den SchliessenButton-Klick mit einer Warnmeldung.
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Donnerstag 13. Mai 2010, 14:58
von jerch
Hier nochmal eine etwas aufgeräumtere threadsichere Version:
Code: Alles auswählen
from ctypes import CFUNCTYPE, windll
from ctypes.wintypes import BOOL, DWORD
from threading import Event
from time import sleep
def console_ctrl_handler(event):
'''Installs a general purpose console control handler, see
_http://msdn.microsoft.com/en-us/library/ms686016.aspx
event is a threading.Event() set by the handler,
when any of Windows exit events in HandlerRoutine occurs.
Use it to handle cleanup and clear the event afterwards.'''
def HandlerRoutine(dwCtrlType):
'''http://msdn.microsoft.com/en-us/library/ms683242.aspx'''
event.set()
while event.isSet():
sleep(.1)
return 0
globals()['c_ctrl_cb'] = CFUNCTYPE(BOOL, DWORD)(HandlerRoutine)
return windll.kernel32.SetConsoleCtrlHandler(globals()['c_ctrl_cb'], True)
if __name__ == '__main__':
exit = Event()
console_ctrl_handler(exit)
count = 0
while not exit.isSet():
print count
count += 1
sleep(1)
# for thread safety do cleanup here, not in the control handler
print 'running cleanup...'
sleep(3) # cleanup stuff
exit.clear()
Der Kontrollhandler behandelt alle exit-Events, also auch Ctrl-C. Falls Du das ändern willst, müsstest Du auf den Wert von `dwCtrlType` in HandlerRoutine testen.
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Freitag 14. Mai 2010, 15:14
von jerch
Noch was zu Deinem Grundproblem:
Einen zuverlässigen Aufruf Deines cleanups gibt es auch mit einem Kontrollhandler nicht. Dieser wird z.B. auch aufgerufen, wenn sich der User ausloggt, Services beendet werden etc. Zu diesem Zeitpunkt kann Windows nicht garantieren, das wichtige andere Dienste nicht schon beendet wurden und der Kontrollhandler eine degradierte Umgebung vorfindet. (Davor warnt die MSDN-Dokumentation auch ausdrücklich.)
Ebenso könnte ja auch die Netzwerkverbindung im laufenden Programm zur DB fehlschlagen und so einen ungültigen Zustand hinterlassen.
Diesem Problem lässt sich nur designtechnisch begegnen, z.B. in dem Du Transaktionen in Dein Zustandsmodell einführst und im Zweifelsfalle so eine Art Rollback auf den letzten gültigen Zustand ermöglichst.
Mit ein paar mehr Infos könnten wir Dir hier sicherlich weiterhelfen.
Grüsse jerch
Re: Python in Windows Shell - Destructor/Cleanup Problem...
Verfasst: Dienstag 18. Mai 2010, 09:28
von martin.chatterjee
jerch,
vielen Dank fuer Deine Hilfe.
War die letzten Tage auf einer anderen "Baustelle" gebunden, und kann mich jetzt wieder mit der Sache befassen.
Mein Bauchgefuehl geht mittlerweile auf jeden Fall auch in die Richtung, dass ich mein Problem durch eine grundsaetzliche Aenderung meiner Rangehensweise loesen sollte...
Auf jeden Fall haben mir Dein Feedback und Deine Kommentare schonmal sehr weitergeholfen!
GrussVomRhein,
Martin