34
votes

What I would like to be able to do is ask a user a question using input. For example:

print('some scenario')
prompt = input("You have 10 seconds to choose the correct answer...\n")

and then if the time elapses print something like

print('Sorry, times up.')

Any help pointing me in the right direction would be greatly appreciated.

2
@interjay I already read this posting before posting my question. First of all, I'm on a windows platform not Unix. The accepted answer says it is Unix only, and I believe the person who answered it later said it didn't even work. Also I'm working with Python 3. I need to use input not raw_input. - cloud311
There are multiple answers on that question and on the one posted by Francesco Frassinelli, many of which are not unix-only. And you can simply change raw_input to input. BTW When you ask questions you should specify relevant information such as running on Windows, and solutions you have tried but didn't work so that people don't waste their time rewriting the old answers. - interjay

2 Answers

16
votes

Interesting problem, this seems to work:

import time
from threading import Thread

answer = None

def check():
    time.sleep(2)
    if answer != None:
        return
    print("Too Slow")

Thread(target = check).start()

answer = input("Input something: ")
29
votes

If it is acceptable to block the main thread when user haven't provided an answer:

from threading import Timer

timeout = 10
t = Timer(timeout, print, ['Sorry, times up'])
t.start()
prompt = "You have %d seconds to choose the correct answer...\n" % timeout
answer = input(prompt)
t.cancel()

Otherwise, you could use @Alex Martelli's answer (modified for Python 3) on Windows (not tested):

import msvcrt
import time

class TimeoutExpired(Exception):
    pass

def input_with_timeout(prompt, timeout, timer=time.monotonic):
    sys.stdout.write(prompt)
    sys.stdout.flush()
    endtime = timer() + timeout
    result = []
    while timer() < endtime:
        if msvcrt.kbhit():
            result.append(msvcrt.getwche()) #XXX can it block on multibyte characters?
            if result[-1] == '\r':
                return ''.join(result[:-1])
        time.sleep(0.04) # just to yield to other processes/threads
    raise TimeoutExpired

Usage:

try:
    answer = input_with_timeout(prompt, 10)
except TimeoutExpired:
    print('Sorry, times up')
else:
    print('Got %r' % answer)

On Unix you could try:

import select
import sys

def input_with_timeout(prompt, timeout):
    sys.stdout.write(prompt)
    sys.stdout.flush()
    ready, _, _ = select.select([sys.stdin], [],[], timeout)
    if ready:
        return sys.stdin.readline().rstrip('\n') # expect stdin to be line-buffered
    raise TimeoutExpired

Or:

import signal

def alarm_handler(signum, frame):
    raise TimeoutExpired

def input_with_timeout(prompt, timeout):
    # set signal handler
    signal.signal(signal.SIGALRM, alarm_handler)
    signal.alarm(timeout) # produce SIGALRM in `timeout` seconds

    try:
        return input(prompt)
    finally:
        signal.alarm(0) # cancel alarm