KeyboardInterrupt abfangen und an Nachfrage weiterleiten

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
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

Hallo,

ich habe eine kurze Frage. Ich habe ein Programm das über

Code: Alles auswählen

while True:
quasi permant läuft. Per STRG+C kann man das ja beenden nun möchte ich aber vorher eine Art Sicherheitsabfrage einbauen à la: "Wollen Sie wirklich beenden? (N/j)"

Ich würde also einen

Code: Alles auswählen

try:
    while True:
        meinProgram...
except KeyboardInterrupt:

Block anlegen und dort die Frage einbauen nur wie würde ich bei "nein" einfach in while fortfahren?

Vielen Dank für eure Ideen oder Hilfen!

Viele Grüße
Daniel
BlackJack

Falsche Schachtelung. Wenn die Ausnahme ausserhalb der Schleife behandelt wird, führt kein Weg mehr in die Schleife hinein. Du musst innerhalb der Schleife behandeln und wenn der Benutzer beenden will die Schleife zum Beispiel mit ``break`` abbrechen.
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

hi,

seltsam, kann das einer erklären?


dieser code funktioniert (KeyboardInterrupt wird abgefangen und MyKeyboardInterrupt geworfen)

Code: Alles auswählen

while True:
  try:
    print "hello"
  except KeyboardInterrupt:
    raise Exception, "MyKeyboardInterrupt"
steht aber nur ein "pass" in der schleife wird KeyboardInterrupt nicht abgefangen:

Code: Alles auswählen

while True:
  try:
    pass
  except KeyboardInterrupt:
    raise Exception, "MyKeyboardInterrupt"
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Nein, aber bei mir tritt das auch auf (Python 2.4 und 2.5 unter Debian Testing).
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

es wird noch spassiger:

bei diesem code scheint es von der gerade in der abarbeitung befindlichen codezeile abzuhängen was passiert.

Code: Alles auswählen

def raiser():
    if False:
        raise Exception
        
def no_raise():
    if False:
        pass

while True:
  try:
    #raiser()
    no_raise()
  except KeyboardInterrupt:
    raise Exception, "MyKeyboardInterrupt" 
nichts geändert am code während den aufrufen:

Code: Alles auswählen

C:\working_copy\source\test>python py_keybint.py
Traceback (most recent call last):
  File "py_keybint.py", line 20, in <module>
    raise Exception, "MyKeyboardInterrupt"
Exception: MyKeyboardInterrupt

C:\working_copy\source\test>python py_keybint.py
Traceback (most recent call last):
  File "py_keybint.py", line 14, in <module>
    while True:
KeyboardInterrupt
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

Hmm, irgendwie stehe ich ein wenig auf dem Schlauch... Bis jetzt:

Code: Alles auswählen

import string

while True:
    try:
        print "Laufe durch und tue dies"
        print "Laufe durch und tue und das"
        print "Laufe durch und tue und jenes"
        print "Laufe durch und tue noch viel mehr"
    except KeyboardInterrupt:
        while True:
            confirm = raw_input('Enter "yes" to cancel or "no" to keep running [yes/no]:').strip().lower()
            if confirm == 'yes':
                print "Cancel!"
                break
            elif confirm == 'no':
                print "Keep runnning!" 
                pass
            else:
                print ('Sorry, no valid answer...')
            pass
print "Draussen"
Das klappt aber noch nicht so richtig. Was mir gerade dazu eingefallen ist, nach Auslösen des KeyboardInterruptes und Beantwortung mit "Nein, soll weiterlaufen" wird dann an der Stelle weitergemacht wo das Programm vorher stand? Oder fängt die Schleife dann wieder komplett neu an?

Ich möchte also einfach sicherstellen das der Benutzer nicht aus Versehen das Programm stoppt - im Fall des Falles (doch mal STRG-C gedrückt) soll es aber einfach weitermachen. Geht das dann überhaupt? Oder brauche ich einen ganz anderen Ansatz. Das ganze ist ein Task, der halt die ganze Zeit quasi im Hintergrund laufen sollte aber ab und an sind die Konsolenausgaben wichtig.
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

wird der interrupt ausgelöst, so wird aus dem try-block rausgesprungen.
hier würde dann also (falls der nutzer das so will) ein neuer schelifendurchlauf gestartet werden.

du musst dann also im except block (oder in finally) sicherstellen, dass das programm in einem definierten (vernünftigen) zustand ist.

zu deinem code: überleg mal was bei dem break bei cancel passiert ...
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

Mhmm, ok. Dann hilft mir dieser Ansatz nicht unbedingt weiter. Gibt es keine Möglichkeit die Unterbrechung vorher abzufangen? Also bevor "unterbrochen" wird?
zu deinem code: überleg mal was bei dem break bei cancel passiert ...
Ok, das break springt zurück in die while Schleife. Das will ich natürlich nicht. Also dann besser das Programm mit exit() beenden?
CrackPod
User
Beiträge: 205
Registriert: Freitag 30. Juni 2006, 12:56

Hallo,

wie wärs, wenn du einfach das "Programm" also die Ausgabe in eine Funktion kapselst?

Code: Alles auswählen

def a():
  print a
def main():
  try:
    a()
  except KeyboardInterrupt:
    confirm = raw_input('Enter "yes" to cancel or "no" to keep running [yes/no]:').strip().lower()
    if confirm == 'yes':
      break
    else:
      continue
if __name__=='__main__':
  main()
LG
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

CrackPod hat geschrieben:wie wärs, wenn du einfach das "Programm" also die Ausgabe in eine Funktion kapselst?
Das aendert nichts am Problem.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
BlackJack

@der_dan: Du solltest vielleicht an die Quelle gehen und verhindern dass das entsprechende Signal vom Betriebssystem an den Prozess nicht ausgewertet wird. Geht vielleicht mit dem `signal`-Modul.

Grundsätzlich halte ich das für keine gute Idee. Programme die sich nicht mit SIGINT abbrechen lassen und mich zwingen `ps` und `kill` oder `killall` zu bemühen, finde ich jedenfalls ziemlich nervig.
Jona
User
Beiträge: 94
Registriert: Sonntag 23. September 2007, 23:25

blackjack, hast du eine idee bzgl des seltsamen verhaltens das python hier an den tag legt?
BlackJack

Tja, da treffen wohl asynchrone Signalbehandlung in C und ein Interpreter unglücklich aufeinander. Wahrscheinlich wird auf dieses Signal nur bei bestimmten Bytecode-Befehlen geprüft um es in eine Ausnahme umzuwandeln und bei so grundlegende Sachen wie Sprungbefehlen wird nicht geprüft um Schleifen nicht auszubremsen. Und wenn in der Schleife "nichts" gemacht wird, wird halt auch nicht auf das Signal geprüft.

Wenn es stört, sollte man vielleicht einen Bug-Report absetzen.
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

BlackJack hat geschrieben:@der_dan: Du solltest vielleicht an die Quelle gehen und verhindern dass das entsprechende Signal vom Betriebssystem an den Prozess nicht ausgewertet wird.
Danke für den Tipp! Das werde ich mir mal ansehen. Wie du schon geschrieben hast ist das natürlich ziemlich brachial ;-)

Ansonsten werde ich wohl den Code entsprechend umstellen und das ganze irgendwie als Hintergrundprozess realsieren und den Rest irgendwie über eine Art "logging" abbilden.
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

Ich habe mit "signal" experimentiert und bin dank BlackJack und diesem Posting zu einer Lösung gekommen. Sieht so aus:

Code: Alles auswählen

import string
import signal
import sys
import itertools
from time import sleep

def onexit(signum, handler):
    print('STRG+C pressed! (Signal: %s)' % (signum,))
    while True:
        confirm = raw_input('Enter "yes" to cancel programm now or "no" to keep running [yes/no]:').strip().lower()
        if confirm == 'yes':
            print "Cancel!"
            sys.exit()
        elif confirm == 'no':
            print "Keep runnning!" 
            break
        else:
            print ('Sorry, no valid answer...')
        pass

signal.signal(signal.SIGINT, onexit)
counter = itertools.count()

# Angenommen hier wäre mein eigentliches Programm
while True:
    print counter.next()
    sleep(2)
Ist scheinbar das, was ich gesucht habe. Zumindest läuft der "Simulations-Counter", der mein Programm darstellen soll an der Stelle weiter wo ich ihn erwarte. Vielleicht übersehe ich auf Grund meiner leider noch begrenzten Python Kenntnisse allerdings etwas? Wie sieht es mit Threads aus? Kann ich das so machen? Was meint ihr?

Danke!
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

Eine Verständnis-Frage dazu habe ich noch. Der Zähler läuft zwar weiter, aber wie verhält es sich mit Threads, die noch im Hintergrund laufen?

Was passiert jetzt bei STRG+C genau? Wird das komplette Programm "angehalten", "eingefroren" und nach dem break:

Code: Alles auswählen

        elif confirm == 'no':
            print "Keep runnning!"
            break
gehts halt einfach weiter?

Danke euch!
der_dan
User
Beiträge: 7
Registriert: Montag 29. Oktober 2007, 09:58

sorry, wenn ich den nochmal rauskrame aber ich bin diesbezüglich leider noch nicht weiter...

Mir gehts darum: Wird die Ausführung des Programms inkl. aller Threads nach dem SIGINT / STRG+C "angehalten/eingefroren" oder laufen die Threads im Hintergrund einfach weiter und sobald ich dann den Abbruch bestätige bin ich "irgendwo" im Code?

Danke!
Antworten