trapping keyboard interupt

Bengt Richter bokr at oz.net
Mon Oct 7 21:44:49 EDT 2002


On Mon, 7 Oct 2002 23:32:54 +0800, "Rob Hall" <bloke at ii.net> wrote:

>Having a bit of trouble trapping the keyboard interupt.  I have something
>like this:
>
>continueFlag = 1
>
>try:
>    while continueFlag:
>        doSomeStuff
>except KeyboardInterrupt:
>    continueFlag = 0
>
>doCleanUpOperations
>
>Basically, I have a program that runs continuously.  Sometimes I need to
>terminate the program, save its state, and restart it at a later time.
>However,  In it, I have a long process that can take 5 minutes to complete,
>and once this process starts, i don't want it to stop.  But I would like it
>to know that as soon as it has finnished this process it should terminate.
>
>My intention was to trap the Keyboard Interupt, reset a flag that would tell
>the program to exit the main loop.  But I can't figure how to resume
>execution from the point I gave the Keyboard Interupt.
>
>Can someone help?
>
You want to catch the interrupt before python's default handler turns it into
an interrupt. Then you can just watch a flag in the loop and not get blasted out
of it. E.g., you may find this amusing:

----< interruptpy.py >-----------
#!/usr/bin/python
# maybe useful part
import signal
class SigHandler:
    def __init__(self):
        self.signaled = 0
        self.sn=None
    
    reset = __init__
    
    def __call__(self, sn, sf):
        self.sn = sn    # latest
        self.signaled += 1
        
def test():
    import msvcrt

    sh = SigHandler()
    old_SIGINT_Handler = signal.signal(signal.SIGINT,sh)
    old_SIGBREAK_Handler = signal.signal(signal.SIGBREAK,sh)
    signames = {
        signal.SIGINT:'SIGINT',
        signal.SIGBREAK:'SIGBREAK'
    }

    # just for this demo, but main loop might be useful
    def puts(s):    # helper
        for c in s: msvcrt.putch(c)
    CTRL_C_ECHO = '<CC>'    # same length as repr, to line up
    SIGINT_ECHO = '<Ctrl-C signal seen>'
    SIGBREAK_ECHO = '<Ctrl-Break signal seen>'

    # this plays the role of your loop body, see loop below           
    def dostuff():
        print 'Enter a line: ',
        line = c = ''
        sigEchoes=[]
        sh.reset()
        while c != '\r':    # Enter key ends input
            # we could break on signals, but we want to finish line, so
            # just echo and record them FTHOI
            if sh.signaled and sh.sn:
                sigEchoes.append((len(line),sh.sn))    # log where
                if sh.sn==signal.SIGBREAK:
                    puts(SIGBREAK_ECHO)
                elif sh.sn==signal.SIGINT:
                    puts(SIGINT_ECHO)
                sh.sn = 0   # reset latest flag
            if not msvcrt.kbhit(): continue # apparently screens ctrl-c?
            c = msvcrt.getch()      # should get ctrl-c but not ctrl-break?
            if c=='\r': break # Enter
            if c=='\b':
                if sigEchoes and len(line)==sigEchoes[-1][0]:
                    # "erase" break effect"
                    puts('\b \b'*len(
                         (SIGINT_ECHO,SIGBREAK_ECHO)[sigEchoes[-1][1]==signal.SIGBREAK]
                    ))
                    sh.signaled -= 1
                    sigEchoes = sigEchoes[:-1]
                    assert sh.signaled == len(sigEchoes)    # how fast can you type ;-)
                    continue
                elif line[-1:]=='\x03': # inc case it's getting to getch
                    puts('\b \b'*len(CTRL_C_ECHO))
                else:    
                    puts('\b \b'*(len(repr(line[-1:]))-2))
                line = line[:-1]
            elif c=='\x03':
                puts(CTRL_C_ECHO)
                line += c     
            else:
                puts(repr(c)[1:-1])
                line += c     
        print '\nIts repr was %s' % `line`
        return sigEchoes
    
    # main loop
    print 'Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.'
    while not sh.signaled:  # put this condition in your program loop
        
        result = dostuff()           # your stuff goes here
        
    print """\
    This should appear after dostuff() has done everything
    as if not interrupted by signals."""

    print   'Loop was terminated gracefully by %d signal%s\n' \
            '    %s\n' \
            'setting a polled variable (not via exception).' % (
                sh.signaled, 's'[sh.signaled==1:],
                ', '.join(map(lambda x: signames[x], [e[1] for e in result]))
    )

    # restore old signal handlers
    signal.signal(signal.SIGINT,old_SIGINT_Handler)
    signal.signal(signal.SIGBREAK,old_SIGBREAK_Handler)

if __name__ == '__main__':
    test()
---------------------------------

A log of some usage (NT4, Python 2.2):
You may find backspacing amusing ;-)

[18:38] C:\pywk>python interruptpy.py
Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.
Enter a line: 123<Ctrl-C signal seen>456<Ctrl-Break signal seen>789
Its repr was '123456789'
    This should appear after dostuff() has done everything
    as if not interrupted by signals.
Loop was terminated gracefully by 2 signals
    SIGINT, SIGBREAK
setting a polled variable (not via exception).

[18:38] C:\pywk>python interruptpy.py
Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.
Enter a line: <Ctrl-C signal seen><Ctrl-C signal seen><Ctrl-C signal seen>
Its repr was ''
    This should appear after dostuff() has done everything
    as if not interrupted by signals.
Loop was terminated gracefully by 3 signals
    SIGINT, SIGINT, SIGINT
setting a polled variable (not via exception).

[18:39] C:\pywk>python interruptpy.py
Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.
Enter a line: Now we'll show some control chars...
Its repr was "Now we'll show some control chars..."
Enter a line: Starting with ctrl-@ with spaces between
Its repr was 'Starting with ctrl-@ with spaces between'
Enter a line: \x00<CC> \x01 \x02 <Ctrl-C signal seen> \x04 \x05 \x06 \x07
Its repr was '\x00\x03 \x01 \x02  \x04 \x05 \x06 \x07'
    This should appear after dostuff() has done everything
    as if not interrupted by signals.
Loop was terminated gracefully by 1 signal
    SIGINT
setting a polled variable (not via exception).

[18:40] C:\pywk>python interruptpy.py
Type Ctrl-C(s) and/or Ctrl-Break(s) anywhere in input line.
Enter a line: Similarly F1-F5: \x00; \x00< \x00= \x00> \x00?
Its repr was 'Similarly F1-F5: \x00; \x00< \x00= \x00> \x00?'
Enter a line: <Ctrl-Break signal seen><Ctrl-Break signal seen>
Its repr was ''
    This should appear after dostuff() has done everything
    as if not interrupted by signals.
Loop was terminated gracefully by 2 signals
    SIGBREAK, SIGBREAK
setting a polled variable (not via exception).

Regards,
Bengt Richter



More information about the Python-list mailing list