Entrada de teclado con timeout en Python

¿Cómo le pediría al usuario alguna entrada pero el tiempo de espera después de N segundos?

Google apunta a un hilo de correo al respecto en http://mail.python.org/pipermail/python-list/2006-January/533215.html pero parece que no funciona. La statement en la que se produce el tiempo de espera, no importa si es un sys.input.readline o timer.sleep (), siempre recibo:

: entrada [raw_] esperada como máximo 1 argumentos, obtuve 2

que de alguna manera la excepción no logra atrapar.

El ejemplo al que se ha vinculado es incorrecto y la excepción se produce realmente cuando se llama al controlador de alarmas en lugar de cuando se bloquea la lectura. Mejor prueba esto:

import signal TIMEOUT = 5 # number of seconds your want for timeout def interrupted(signum, frame): "called when read times out" print 'interrupted!' signal.signal(signal.SIGALRM, interrupted) def input(): try: print 'You have 5 seconds to type in your stuff...' foo = raw_input() return foo except: # timeout return # set alarm signal.alarm(TIMEOUT) s = input() # disable the alarm after success signal.alarm(0) print 'You typed', s 

Usar una llamada de selección es más corto y debería ser mucho más portátil

 import sys, select print "You have ten seconds to answer!" i, o, e = select.select( [sys.stdin], [], [], 10 ) if (i): print "You said", sys.stdin.readline().strip() else: print "You said nothing!" 

No es una solución de Python, pero …

Me encontré con este problema con un script que se ejecuta bajo CentOS (Linux), y lo que funcionó para mi situación fue simplemente ejecutar el comando Bash “read -t” en un subproceso. Truco brutal y asqueroso, lo sé, pero me siento lo suficientemente culpable por lo bien que funcionó que quería compartirlo con todos los presentes.

 import subprocess subprocess.call('read -t 30', shell=True) 

Todo lo que necesitaba era algo que esperara 30 segundos a menos que se presionara la tecla ENTER. Esto funcionó muy bien.

Y aquí hay uno que funciona en Windows.

No he podido obtener ninguno de estos ejemplos para trabajar en Windows, así que he combinado algunas respuestas diferentes de StackOverflow para obtener lo siguiente:

 import threading, msvcrt import sys def readInput(caption, default, timeout = 5): class KeyboardThread(threading.Thread): def run(self): self.timedout = False self.input = '' while True: if msvcrt.kbhit(): chr = msvcrt.getche() if ord(chr) == 13: break elif ord(chr) >= 32: self.input += chr if len(self.input) == 0 and self.timedout: break sys.stdout.write('%s(%s):'%(caption, default)); result = default it = KeyboardThread() it.start() it.join(timeout) it.timedout = True if len(it.input) > 0: # wait for rest of input it.join() result = it.input print '' # needed to move to next line return result # and some examples of usage ans = readInput('Please type a name', 'john') print 'The name is %s' % ans ans = readInput('Please enter a number', 10 ) print 'The number is %s' % ans 

La respuesta de Pablo no funcionó del todo. Código modificado a continuación que funciona para mí en

  • windows 7 x64

  • shell CMD de vainilla (por ejemplo, no git-bash u otro shell que no sea de M $)

    – nada msvcrt funciona en git-bash aparece.

  • python 3.6

(Estoy publicando una nueva respuesta, porque editar la respuesta de Paul directamente la cambiaría de python 2.x -> 3.x, lo que parece demasiado para una edición (py2 todavía está en uso)

 import sys, time, msvcrt def readInput( caption, default, timeout = 5): start_time = time.time() sys.stdout.write('%s(%s):'%(caption, default)) sys.stdout.flush() input = '' while True: if msvcrt.kbhit(): byte_arr = msvcrt.getche() if ord(byte_arr) == 13: # enter_key break elif ord(byte_arr) >= 32: #space_char input += "".join(map(chr,byte_arr)) if len(input) == 0 and (time.time() - start_time) > timeout: print("timing out, using default value.") break print('') # needed to move to next line if len(input) > 0: return input else: return default # and some examples of usage ans = readInput('Please type a name', 'john') print( 'The name is %s' % ans) ans = readInput('Please enter a number', 10 ) print( 'The number is %s' % ans) 

El siguiente código funcionó para mí.

Usé dos hilos uno para obtener el raw_Input y otro para esperar un tiempo específico. Si alguno de los subprocesos se cierra, ambos se terminan y se devuelven.

 def _input(msg, q): ra = raw_input(msg) if ra: q.put(ra) else: q.put("None") return def _slp(tm, q): time.sleep(tm) q.put("Timeout") return def wait_for_input(msg="Press Enter to continue", time=10): q = Queue.Queue() th = threading.Thread(target=_input, args=(msg, q,)) tt = threading.Thread(target=_slp, args=(time, q,)) th.start() tt.start() ret = None while True: ret = q.get() if ret: th._Thread__stop() tt._Thread__stop() return ret return ret print time.ctime() t= wait_for_input() print "\nResponse :",t print time.ctime() 

Pasé unos veinte minutos más o menos en esto, así que pensé que valía la pena intentarlo aquí. Sin embargo, se está construyendo directamente a partir de la respuesta del usuario 137673. Me pareció más útil hacer algo como esto:

 #! /usr/bin/env python import signal timeout = None def main(): inp = stdinWait("You have 5 seconds to type text and press ... ", "[no text]", 5, "Aw man! You ran out of time!!") if not timeout: print "You entered", inp else: print "You didn't enter anything because I'm on a tight schedule!" def stdinWait(text, default, time, timeoutDisplay = None, **kwargs): signal.signal(signal.SIGALRM, interrupt) signal.alarm(time) # sets timeout global timeout try: inp = raw_input(text) signal.alarm(0) timeout = False except (KeyboardInterrupt): printInterrupt = kwargs.get("printInterrupt", True) if printInterrupt: print "Keyboard interrupt" timeout = True # Do this so you don't mistakenly get input when there is none inp = default except: timeout = True if not timeoutDisplay is None: print timeoutDisplay signal.alarm(0) inp = default return inp def interrupt(signum, frame): raise Exception("") if __name__ == "__main__": main() 

Análogo al de Locane para Windows:

 import subprocess subprocess.call('timeout /T 30') 

Aquí hay una solución Python 3 portátil y simple que usa hilos. Este es el único que trabajó para mí mientras estaba multiplataforma.

Otras cosas que probé todas tuvieron problemas:

  • Usando signal.SIGALRM: no funciona en Windows
  • Uso de llamada select: no funciona en Windows
  • Uso forzado de un proceso (en lugar de subproceso): stdin no se puede usar en un nuevo proceso (stdin se cierra automáticamente)
  • La redirección de stdin a StringIO y la escritura directa a stdin: aún se escribirá al stdin anterior si ya se ha llamado input () (consulte https://stackoverflow.com/a/15055639/9624704 )
  from threading import Thread class myClass: _input = None def __init__(self): get_input_thread = Thread(target=self.get_input) get_input_thread.daemon = True # Otherwise the thread won't be terminated when the main program terminates. get_input_thread.start() get_input_thread.join(timeout=20) if myClass._input is None: print("No input was given within 20 seconds") else: print("Input given was: {}".format(myClass._input)) @classmethod def get_input(cls): cls._input = input("") return 

Dado que esta pregunta parece servir como un objective duplicado, aquí está el enlace a mi respuesta aceptada en una pregunta duplicada.

Caracteristicas

  • Plataforma independiente (Unix / Windows).
  • Sólo StdLib, no hay dependencias externas.
  • Solo hilos, no subprocesos.
  • Interrupción inmediata en el tiempo de espera.
  • Limpie el apagado del indicador en el tiempo de espera.
  • Entradas ilimitadas posibles durante el lapso de tiempo.
  • Fácil clase expandible PromptManager.
  • El progtwig se puede reanudar después de un tiempo de espera, es posible realizar varias ejecuciones de instancias del indicador sin reiniciar el progtwig.

Una respuesta tardía 🙂

Haría algo como esto:

 from time import sleep print('Please provide input in 20 seconds! (Hit Ctrl-C to start)') try: for i in range(0,20): sleep(1) # could use a backward counter to be preeety :) print('No input is given.') except KeyboardInterrupt: raw_input('Input x:') print('You, you! You know something.') 

Sé que esto no es lo mismo, pero muchos problemas de la vida real podrían resolverse de esta manera. (Por lo general, necesito tiempo de espera para la entrada del usuario cuando quiero que algo continúe ejecutándose si el usuario no está allí en este momento).

Espero que esto ayude al menos parcialmente. (Si alguien lo lee de todos modos :))