Input function with timeout

I suggest to add aditional parameter ‘timeout’ to built-in function input. This will be time (in seconds) in which user should put any input for script. When for specified time user won’t provide any input, then input function should throw an exception (i.e. MissingInputError). Default value for this parameter shoul be -1 (or None), and this will turn off timout, so user should have unlimited amount of time to provide input

Something that I have wanted many times and that I have seen many others want is just a simple cross-platform way to set a timeout on an operation. If such a thing were available then you could use it with input:

try:
    with timeout(seconds=10)
        result = input("Enter something:")
except TimeOut:
    result = "whatever"

Maybe there are reasons why it is hard to make something like this work in general.

6 Likes

With complete generality? This is quite hard. The best I can think of is something based on SIGALRM, but that won’t work on Windows. So there would probably have to be multiple implementations for different systems, with unique quirks.

I tried writing a Timeout context manager with tkinter root.after function raise an exception. But input() is a blocking function, so the tcl event loop never runs enough to fire the after function.

Someone made a timeout decorator - using multiprocessing to also make it work on Windows, but with support for using signals on other platforms. It can also be used with input if you make a little convenience function. It doesn’t work as context manager, but I suppose it would be possible to add a context manager variant. See: GitHub - bitranox/wrapt_timeout_decorator: Python Powerful Timeout Decorator that can be used safely on classes, methods, class methods

It’s interesting to see all the hoops the developer has to jump through to make it work on Windows.
(I’ve never used this package, so don’t know what in general its code quality/performance is. Just wanted to give a pointer to it.)

The reasons a generalized “timeout” isn’t feasible, are the same reasons that “killing a thread” isn’t:

  1. What exactly happens if time runs out and the computation is incomplete? Is there actually any sense to be made out of the partial results? How should the work be cleaned up and whatever results reported?

  2. What happens if, in the exact instant time runs out, the timed-out action is the middle of some critical step? At exactly what moments is it “safe” to stop the process?

Presumably it raises an exception, so with and finally blocks are handled correctly.

1 Like

If we throw the idea of safety out, you can try to use func-timeout. Here is an example:

from func_timeout import func_timeout, FunctionTimedOut

def input_timeout(timeout, *args):
    try:
        return func_timeout(timeout, input, args=args)
    except FunctionTimedOut:
        pass

Sometimes it works… sometimes, I get:

Traceback (most recent call last):
  File "C:\Python3\Lib\site-packages\pyreadline3\console\console.py", line 804, in hook_wrapper_23
    res = ensure_str(readline_hook(prompt))
                     ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python3\Lib\site-packages\pyreadline3\rlmain.py", line 589, in readline
    self._readline_from_keyboard()
  File "C:\Python3\Lib\site-packages\pyreadline3\rlmain.py", line 553, in _readline_from_keyboard
    if self._readline_from_keyboard_poll():
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python3\Lib\site-packages\pyreadline3\rlmain.py", line 570, in _readline_from_keyboard_poll
    event = c.getkeypress()
            ^^^^^^^^^^^^^^^
  File "C:\Python3\Lib\site-packages\pyreadline3\console\console.py", line 534, in getkeypress
    e = self.get()
        ^^^^^^^^^^
  File "C:\Python3\Lib\site-packages\pyreadline3\console\console.py", line 525, in get
    status = self.ReadConsoleInputW(self.hin,
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So… yeah: probably don’t do this in production code.

1 Like