views:

935

answers:

3

How would you prompt the user for some input but timing out after N seconds?

Google is pointing to a mail thread about it at http://mail.python.org/pipermail/python-list/2006-January/533215.html but it seems not to work. The statement in which the timeout happens, no matter whether it is a sys.input.readline or timer.sleep(), I always get:

<type 'exceptions.TypeError'>: [raw_]input expected at most 1 arguments, got 2

which somehow the except fails to catch.

+3  A: 

The example you have linked to is wrong and the exception is actually occuring when calling alarm handler instead of when read blocks. Better try this:

import signal
TIMEOUT = 5 # number of seconds your want for timeout

def interrupted(signum, frame):
    "called when read times out"
    print 'interrupted!'
signal.signal(signal.SIGALRM, interrupted)

def input():
    try:
            print 'You have 5 seconds to type in your stuff...'
            foo = raw_input()
            return foo
    except:
            # timeout
            return

# set alarm
signal.alarm(TIMEOUT)
s = input()
# disable the alarm after success
signal.alarm(0)
print 'You typed', s
Nice solution, this only works on Linux though.
Nadia Alramli
+1  A: 

Using a select call is shorter, and should be much more portable

import sys, select

print "You have ten seconds to answer!"

i, o, e = select.select( [sys.stdin], [], [], 10 )

if (i):
  print "You said", sys.stdin.readline().strip()
else:
  print "You said nothing!"
Pontus
+1 for cross platform solution.
Great Turtle
I just tested and this does NOT work for windows. Select is available, but on windows the input to select can only be a socket - sys.stdin and file descriptors are unix. I'll be sure to test first next time.
Great Turtle
Darn. Well, what self respecting programmer uses windows anyway? ;)For simple user input I guess it could be done with a loop around "kbhit", which detects keyboard presses, and "getch" with "time.sleep" to break after a timeout. But it will be ugly.
Pontus
A: 

And here's one that works on Windows

I haven't been able to get any of these examples to work on Windows so I've merged some different StackOverflow answers to get the following:


import threading, msvcrt
import sys

def readInput(caption, default, timeout = 5):
    class KeyboardThread(threading.Thread):
        def run(self):
            self.timedout = False
            self.input = ''
            while True:
                if msvcrt.kbhit():
                    chr = msvcrt.getche()
                    if ord(chr) == 13:
                        break
                    elif ord(chr) >= 32:
                        self.input += chr
                if len(self.input) == 0 and self.timedout:
                    break    


    sys.stdout.write('%s(%s):'%(caption, default));
    result = default
    it = KeyboardThread()
    it.start()
    it.join(timeout)
    it.timedout = True
    if len(it.input) > 0:
        # wait for rest of input
        it.join()
        result = it.input
    print ''  # needed to move to next line
    return result

# and some examples of usage
ans = readInput('Please type a name', 'john') 
print 'The name is %s' % ans
ans = readInput('Please enter a number', 10 ) 
print 'The number is %s' % ans 
Paul
I just realised I didn't need to use a thread. See the same code but without a thread at http://stackoverflow.com/questions/3471461/raw-input-and-timeout/3911560#3911560
Paul