Time-Limited Input?

56,079

Solution 1

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: ")

Solution 2

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
Share:
56,079
cloud311
Author by

cloud311

I am new to programming, and am tring to learn Python as my first language. I'm not sure if I want to pursue a career in programming yet, but I do enjoy problem solving. I currently have a job that will allow me to experiment with writing some text processing scripts. I joined stack overflow so I could talk to and learn from people in the community that don't mind helping beginning programmers.

Updated on October 04, 2021

Comments

  • cloud311
    cloud311 over 2 years

    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.

  • jfs
    jfs about 11 years
    you could use threading.Timer instead of Thread + time.sleep. There is no raw_input in Python 3.
  • Eliezer Miron
    Eliezer Miron almost 8 years
    First answer did print after a timeout, but the input was still available.
  • DevPlayer
    DevPlayer over 7 years
    How does raw_input() terminate in the mainthread? I see how thread check() ends and pushes "Too slow" to stdout. But not how raw_input() gets its stdin buffer filled or "completes".
  • jfs
    jfs over 7 years
    @EliezerMiron: yes, the input() call is not interrupted in the first example that is why there is: "If it is acceptable to block the main thread" before the example. If you need to interrtupt the input, use the following examples with input_with_timeout().
  • Pro Q
    Pro Q about 7 years
    I tried using the import signal one, and its sense of timing appears to be way off. I'm using Cloud9 IDE. If I give it a .5 timeout, it will wait about 3 seconds before timing out.
  • abarnert
    abarnert almost 6 years
    This (a) isn't Python 3, and (b) doesn't work, for the reason @DevPlayer pointed out.
  • Akaisteph7
    Akaisteph7 almost 5 years
    Why was this accepted?
  • Dmitri Nesteruk
    Dmitri Nesteruk over 3 years
    This should not have been accepted, it doesn't terminate.
  • DannyTalent
    DannyTalent about 3 years
    For the windows option, you may want to replace if result[-1] == '\n': for if result[-1] == '\r': (tested on vscode, pwshell, cmd and terminal app)
  • Julian
    Julian almost 3 years
    Couple of things on the Windows version. You may want to print() before you return. Also, if you want to (kind-of) support backspace, you want another branch: if ord(result[-1]) == 8: result = result[:-2]. Tested in PyCharm console.
  • stucash
    stucash about 2 years
    the *nix version does not work for me ; one thing I was asked to pause the process to use input prompt when I was in debug mode in Pycharm; another thing was the timer simply ignored my input even if I entered a valid input before time was up.