Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Built-in Keyboard Signal/Interrupts

I currently need to provide multiple keyboard interrupts for a program. Is there an easy way to do this with the signal class? I currently use the SIGINT/Ctrl+C but I can't find any other keyboard mappings.

Would be nice to have more than 2 signals. How can I either define more signals or is there a better way to capture an "interrupt from a user"?

Here is a highlevel view of the current code:

 def shutdown(signal, frame):
       if(signal==2): #sigint
          print 'do something'
       elif signal==XX:
          print 'do something else'
       # continued...

 signal.signal(signal.SIGINT, shutdown)
 signal.signal(signal.SOMEOTHERTYPE, shutdown)


 print 'start'
 t = Thread(target=run)
 t.setDaemon(True)
 t.start()

 print 'Done, press ctrl c, or ctrl ? '
 signal.pause()
like image 506
Nix Avatar asked Mar 29 '11 16:03

Nix


Video Answer


1 Answers

The Ctrl+\ that has been mentioned is interpreted by your terminal software, and the key binding is configured through stty. Unless you have some way of customizing your terminal software you'll only be able to use the few signals that are already built in.

Depending on how much functionality you need or how far you want to take it, another option is to write your own simple "process execution terminal". This would be a script that executes an app for you and places your terminal in raw mode so that it can process keystrokes which perform custom actions.

Below is an oversimplified example showing what I mean. You could also do something similar via curses or urwid if you like.

To handle process output you'd need to capture the stdout/stderr of and display it nicely to the screen, using ANSI escape sequences if you are manipulating the terminal manually, or using an urwid widget to display the output in a scrolling window, etc. The same idea would also extend to other GUI systems (wx, tkinter, etc) but terminal control was mentioned.

Here is term.py which implements a basic raw terminal interpreter:

import os, signal, subprocess, sys, tty, termios

sigmap = {
    '\x15': signal.SIGUSR1,     # ctrl-u
    '\x1c': signal.SIGQUIT,     # ctrl-\
    '\x08': signal.SIGHUP,      # ctrl-h
    '\x09': signal.SIGINT,      # ctrl-i
    }
# setup tty
fd = sys.stdin.fileno()
old_tc = termios.tcgetattr(fd)
tty.setraw(fd)
# spawn command as a child proc
cmd = sys.argv[1:]
proc = subprocess.Popen(cmd)
while 1:
    try:
        ch = sys.stdin.read(1)
        # example of ansi escape to move cursor down and to column 0
        print '\033[1Eyou entered', repr(ch)
        if ch == 'q':
            break
        signum = sigmap.get(ch)
        if signum:
            os.kill(proc.pid, signum)
    finally:
        pass
termios.tcsetattr(fd, termios.TCSANOW, old_tc)
sys.exit()

Here is a simple target.py script to spin and print the signals it receives:

import signal, sys, time

def handler(num, _):
    print 'got:', sigmap.get(num, '<other>')
    if num == signal.SIGINT:
        sys.exit(1)
    return 1

signames = ['SIGINT','SIGHUP','SIGQUIT','SIGUSR1']
sigmap = dict((getattr(signal, k), k) for k in signames)
for name in signames:
    signal.signal(getattr(signal, name), handler)
while 1:
    time.sleep(1)

Usage example:

% python term.py python target.py
you entered 'h'
you entered 'i'
you entered '\x1c'
                  got: SIGQUIT
you entered '\x15'
                  got: SIGUSR1
you entered '\x08'
                  got: SIGHUP
you entered '\t'
                got: SIGINT
you entered 'q'
like image 87
samplebias Avatar answered Oct 27 '22 16:10

samplebias