Timeout in Python input function

Question:

teste = input('Olá qual o seu nome?')

How can I run something if the user takes more than a certain amount of time to respond to input?

Example:

  • Writing on screen Você demorou demais .

Answer:

The signal.alarm function is only available for Unix environment.

You can use the signal module. First, we define a function that will handle the case that the user is inactive and their time expires. For simplicity's sake, I'll just throw an exception:

def timeout():
    raise Exception('Seu tempo acabou!')

Finally, the magic. With the signal module, we define a time signal in which the timeout function will be responsible for processing. We do this simply with:

signal.signal(signal.SIGALRM, timeout)

And then we do:

try:
    signal.alarm(5)
    name = input('Qual é o seu nome? ')
    signal.alarm(0)
    print('Seja bem-vindo,', name)
except Exception as e:
    print(e)

That is, we set our signal's alarm time to 5 seconds, that is, from this moment on, after 5 seconds, the timeout function will be executed; we try to read the username and, if successful, we cancel our signal by setting the alarm time to 0. If the user is inactive, the signal will not be canceled and after 5 seconds, the timeout function will be executed, triggering the exception and, consequently, being caught by the try , terminating the program.

The full code would look like:

import signal

def timeout(signum, frame):
    raise Exception('Seu tempo acabou!')

signal.signal(signal.SIGALRM, timeout)
signal.alarm(5)

try:
    signal.alarm(5)
    name = input('Qual é o seu nome? ')
    signal.alarm(0)
    print('Seja bem-vindo,', name)
except Exception as e:
    print(e)

See working on Repl.it | GitHub GIST

Of course, you can make everything much prettier by implementing a decorator in Python. For example:

import signal

def timeout(seconds):
    def decorator(function):
        def wrapper(*args, **kwargs):
            def handler(signum, frame):
                raise Exception(f'Timeout of {function.__name__} function')
            signal.signal(signal.SIGALRM, handler)
            signal.alarm(seconds)
            result = function(*args, **kwargs)
            signal.alarm(0)
            return result
        return wrapper
    return decorator

So, to set the timeout of any function, just do:

@timeout(seconds=5)
def read_user_name():
    name = input('Qual é o seu nome? ')
    print('Seja bem-vindo,', name)

And use it with:

try:
    read_user_name()
except Exception as e:
    print(e)

You can even use it in other situations, such as downloading a file with the requests module:

@timeout(seconds=30)
def download_awesome_image(save):
    with open(save, 'wb') as stream:
        response = requests.get('http://url.to/awesome_image.jpg')
        stream.write(response.content)

try:
    download_awesome_image(save='awesome_image.jpg')
except Exception as e:
    print('Desculpe-me, mas demorou muito e eu não quis esperar')

Windows and others

An alternative that also works on Windows is to use the multiprocessing module, defining a different process to execute the task. Similarly to the previous one, we can define a decorator:

from multiprocessing import TimeoutError
from multiprocessing.pool import ThreadPool

def timeout(seconds):
    def decorator(function):
        def wrapper(*args, **kwargs):
            pool = ThreadPool(processes=1)
            result = pool.apply_async(function, args=args, kwds=kwargs)
            try:
                return result.get(timeout=seconds)
            except TimeoutError as e:
                return e
        return wrapper
    return decorator

@timeout(5)
def read_user_name():
    return input('Nome? ')

name = read_user_name()

if isinstance(name, TimeoutError):
    print('Demorou demais, parsa!')
else:
    print('Olá', name)

See working on Repl.it | GitHub GIST

Note that, as the function will be executed in another process, the exception that is thrown when the time expires does not influence the original process, so, to work around this, I returned the exception instance itself and did the type checking before handling the value of the variable.

Scroll to Top