Time-Limited Input? [duplicate]
Asked Answered
I

2

40

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.

Imogene answered 20/3, 2013 at 16:12 Comment(6)
@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.Imogene
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.Bump
Keyboard input with timeout in PythonDisorganize
related: raw_input and timeout /3471461Ibbie
related: How to set time limit on input /2933399Ibbie
this worked for me https://mcmap.net/q/204593/-keyboard-input-with-timeoutSelfanalysis
N
14

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: ")
Naples answered 20/3, 2013 at 17:28 Comment(6)
you could use threading.Timer instead of Thread + time.sleep. There is no raw_input in Python 3.Disorganize
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".Arita
This (a) isn't Python 3, and (b) doesn't work, for the reason @Arita pointed out.Beetroot
Why was this accepted?Eleanoraeleanore
This should not have been accepted, it doesn't terminate.Leadwort
Yes, it doesn't terminate. However, if you phrase the print statement as follows: print("Time is up! Press enter to exit ...") then the answer is still useful.Trim
D
33

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
Disorganize answered 20/3, 2013 at 19:55 Comment(6)
First answer did print after a timeout, but the input was still available.Postulate
@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().Disorganize
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.Subir
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)Crossways
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.Pazit
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.Steamer
N
14

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: ")
Naples answered 20/3, 2013 at 17:28 Comment(6)
you could use threading.Timer instead of Thread + time.sleep. There is no raw_input in Python 3.Disorganize
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".Arita
This (a) isn't Python 3, and (b) doesn't work, for the reason @Arita pointed out.Beetroot
Why was this accepted?Eleanoraeleanore
This should not have been accepted, it doesn't terminate.Leadwort
Yes, it doesn't terminate. However, if you phrase the print statement as follows: print("Time is up! Press enter to exit ...") then the answer is still useful.Trim

© 2022 - 2024 — McMap. All rights reserved.