Timeout in the Python input function

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

How do I do if the user takes more than a certain time interval to take some action. Eg: write on the screen 'You took too long'.

    
asked by anonymous 10.05.2018 / 02:01

2 answers

3
  

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, 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 is 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 the alarm time of our signal to 5 seconds, that is, from this moment run for 5 seconds, the timeout function will be executed; we try to read the user name and, if successful, 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 therefore being caught by try , waxing the program.

The complete 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 at Repl.it | GitHub GIST

Obviously you can make everything much more beautiful 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 define 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 works also in Windows is to use the multiprocessing module, defining a distinct process to execute the task. Similar 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 at Repl.it | GitHub GIST

Note that since the function will be executed in another process, the exception that is thrown at the time expires does not influence the original process, so to get around this, I returned the instance of the exception itself and checked the type before treat the value of the variable.

    
10.05.2018 / 02:37
1

I had this doubt a while back. The way I found it was this.

import time

print('Pausa de 5 segundos (Aperte Ctrl-C para inserir algo)')
try:
    for i in range(0,5):
        time.sleep(1)    
except KeyboardInterrupt:
    input("Entrada: ")
    
10.05.2018 / 17:59