Keyboard input with timeout?

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.


Solution 1:

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!"

Solution 2:

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

Solution 3:

Not a Python solution, but...

I ran in to this problem with a script running under CentOS (Linux), and what worked for my situation was just running the Bash "read -t" command in a subprocess. Brutal disgusting hack, I know, but I feel guilty enough about how well it worked that I wanted to share it with everyone here.

import subprocess
subprocess.call('read -t 30', shell=True)

All I needed was something that waited for 30 seconds unless the ENTER key was pressed. This worked great.

Solution 4:

if you dont care how it works, just
pip install inputimeout
and

from inputimeout import inputimeout, TimeoutOccurred

if __name__ == "__main__":
    try:
        c = inputimeout(prompt='hello\n', timeout=3)
    except TimeoutOccurred:
        c = 'timeout'
    print(c)

so easy
https://pypi.org/project/inputimeout/

Solution 5:

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