你会如何提示用户输入一些信息但是在N秒后超时?
谷歌在http://mail.python.org/pipermail/python-list/2006-January/533215.html指向一个关于它的邮件线程,但似乎没有用。超时发生的语句,无论是sys.input.readline还是timer.sleep(),我总是得到:
< type'exceptions.TypeError'>:[raw_]输入最多需要1个参数,得到2
以某种方式除了没有抓住。
答案 0 :(得分:79)
使用选择呼叫的时间更短,而且应该更便携
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!"
答案 1 :(得分:26)
您链接到的示例是错误的,并且在调用警报处理程序而不是读取块时实际发生异常。最好试试这个:
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
答案 2 :(得分:10)
不是Python解决方案,但是......
我使用在CentOS(Linux)下运行的脚本遇到了这个问题,对我的情况有用的只是在子进程中运行Bash“read -t”命令。我知道,野蛮恶心的黑客,但我对它的运作情况感到内疚,我想与大家分享。
import subprocess
subprocess.call('read -t 30', shell=True)
除非按下ENTER键,否则我需要的是等待30秒的东西。这很有效。
答案 3 :(得分:5)
这是适用于Windows的一个
我无法让这些示例中的任何一个在Windows上运行,因此我合并了一些不同的StackOverflow答案以获得以下内容:
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
答案 4 :(得分:5)
保罗的回答并不奏效。修改后的代码对我有用
windows 7 x64
vanilla CMD shell(例如,不 git-bash或其他非M $ shell)
- 没有任何msvcrt
在git-bash中有效。
python 3.6
(我发布了一个新的答案,因为直接编辑Paul的答案会改变它来自python 2.x - > 3.x,这对编辑来说似乎太多了(py2仍在使用中)
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)
答案 5 :(得分:4)
如果你不在乎它是如何工作的,就
pip install inputimeout
和
from inputimeout import inputimeout, TimeoutOccurred
if __name__ == "__main__":
try:
c = inputimeout(prompt='hello\n', timeout=3)
except TimeoutOccurred:
c = 'timeout'
print(c)
答案 6 :(得分:3)
我花了大约二十分钟左右的时间,所以我认为值得一试将它放在这里。不过,它直接建立在用户137673的答案之上。我发现做这样的事情最有用:
#! /usr/bin/env python
import signal
timeout = None
def main():
inp = stdinWait("You have 5 seconds to type text and press <Enter>... ", "[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()
答案 7 :(得分:3)
以下代码为我工作。
我使用两个线程来获取raw_Input而另一个线程等待特定时间。 如果任何线程退出,则线程终止并返回。
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()
答案 8 :(得分:2)
类似于Locane的Windows:
import subprocess
subprocess.call('timeout /T 30')
答案 9 :(得分:1)
您可以在 Python >= 3.4 中使用 inputimeout 库。 麻省理工学院许可证。
$ pip install inputimeout
from inputimeout import inputimeout, TimeoutOccurred
try:
something = inputimeout(prompt='>>', timeout=5)
except TimeoutOccurred:
something = 'something'
print(something)
答案 10 :(得分:1)
我正在使用外部工具 inputimeout 。源代码可在 github 获得。我知道它是一个外部工具,但它很简单也很方便。 安装该工具后使用此代码:
from inputimeout import inputimeout, TimeoutOccurred
try:
something = inputimeout(prompt='>>', timeout=5)
except TimeoutOccurred:
something = 'No input.'
print(something)
答案 11 :(得分:1)
from threading import Thread
import time
def get_input():
while True:
print(input('> '))
t1 = Thread(target=get_input)
t1.setDaemon(True)
t1.start()
time.sleep(3)
print('program exceeds')
只需设置一个新的Daemon线程,然后将睡眠时间设置为您想要的超时时间即可。我认为这很容易赶上XD
答案 12 :(得分:1)
已经有好几年了,但是如果万一有人像我最近尝试解决这种问题那样碰到这个问题,可以使用func-timeout
包来一种简便快捷的方法来实现。
对于大多数IDE,必须先安装它。您可以通过pip
安装它。
上面的链接是不言自明的,但是我将举例说明如何实现它。
from func_timeout import FunctionTimedOut, func_timeout
try:
ans = func_timeout(5, lambda: int(input('What is the sum of 2 and 3?\n')))
print(ans)
except FunctionTimedOut:
print(5)
func_timeout
在其参数中返回方法的值,在这种情况下为question()
函数。它还允许函数需要其他参数(请参见文档)。
如果经过了设置的时间(此处为5秒),它将引发TimedOutException
并在except
块中运行代码。
答案 13 :(得分:1)
这里是使用线程的可移植且简单的Python 3解决方案。 这是跨平台时唯一为我工作的人。
我尝试过的其他所有方法都有问题:
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
答案 14 :(得分:0)
对于Linux,我希望使用@Pontus的[root@allselenium ~]# docker-compose version
docker-compose version 1.18.0, build 8dd22a9
docker-py version: 2.6.1
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.0.2k-fips 26 Jan 2017
版本。在这里,只有python3函数像select
在外壳中一样工作:
read
运行
import sys, select
def timeout_input(prompt, timeout=3, default=""):
print(prompt, end=': ', flush=True)
inputs, outputs, errors = select.select([sys.stdin], [], [], timeout)
print()
return (0, sys.stdin.readline().strip()) if inputs else (-1, default)
还有一个In [29]: timeout_input("Continue? (Y/n)", 3, "y")
Continue? (Y/n):
Out[29]: (-1, 'y')
In [30]: timeout_input("Continue? (Y/n)", 3, "y")
Continue? (Y/n): n
Out[30]: (0, 'n')
函数
yes_or_no
答案 15 :(得分:0)
受iperov回答启发的解决方案,希望它会更清洁一些:
import multiprocessing
import sys
def input_with_timeout(prompt, timeout=None):
"""Requests the user to enter a code at the command line."""
queue = multiprocessing.Queue()
process = multiprocessing.Process(
_input_with_timeout_process, args=(sys.stdin.fileno(), queue, prompt),
)
process.start()
try:
process.join(timeout)
if process.is_alive():
raise ValueError("Timed out waiting for input.")
return queue.get()
finally:
process.terminate()
def _input_with_timeout_process(stdin_file_descriptor, queue, prompt):
sys.stdin = os.fdopen(stdin_file_descriptor)
queue.put(input(prompt))
答案 16 :(得分:0)
这是我解决此问题的方法。我尚未对其进行彻底的测试,并且不确定它是否存在一些重要的问题,但是考虑到其他解决方案也远非完美,我决定分享一下:
import sys
import subprocess
def switch():
if len(sys.argv) == 1:
main()
elif sys.argv[1] == "inp":
print(input(''))
else:
print("Wrong arguments:", sys.argv[1:])
def main():
passw = input_timed('You have 10 seconds to enter password:', timeout=10)
if passw is None:
print("Time's out! You explode!")
elif passw == "PasswordShmashword":
print("H-h-how did you know you h-h-hacker")
else:
print("I spare your life because you at least tried")
def input_timed(*args, timeout, **kwargs):
"""
Print a message and await user input - return None if timedout
:param args: positional arguments passed to print()
:param timeout: number of seconds to wait before returning None
:param kwargs: keyword arguments passed to print()
:return: user input or None if timed out
"""
print(*args, **kwargs)
try:
out: bytes = subprocess.run(["python", sys.argv[0], "inp"], capture_output=True, timeout=timeout).stdout
except subprocess.TimeoutExpired:
return None
return out.decode('utf8').splitlines()[0]
switch()
答案 17 :(得分:0)
如果名称 =='主要'添加:#在Windows上通过多处理处理
导入sys,os,多处理,时间
def input_process(stdin_fd, sq, sstr):
sys.stdin = os.fdopen(stdin_fd)
try:
inp = input(sstr)
sq.put(True)
except:
sq.put(False)
def input_in_time(sstr, max_time_sec):
sq = multiprocessing.Queue()
p = multiprocessing.Process(target=input_process, args=( sys.stdin.fileno(), sq, sstr))
p.start()
t = time.time()
inp = False
while True:
if not sq.empty():
inp = sq.get()
break
if time.time() - t > max_time_sec:
break
tleft=int( (t+max_time_sec)-time.time())
if tleft<max_time_sec-1 and tleft>0:
print('\n ...time left '+str(tleft)+'s\ncommand:')
time.sleep(2)
p.terminate()
sys.stdin = os.fdopen( sys.stdin.fileno() )
return inp
if __name__=='__main__':
input_in_time("command:", 17)
答案 18 :(得分:0)
这是Python 3.8+(尽管可以适应Python 3.6 +)跨平台方法,仅使用threading
(因此,{ {1}}或对Shell实用程序的调用)。它专门用于从命令行运行脚本,并不适合动态使用。
您可以如下包装内置的multiprocessing
函数。在这种情况下,我将内置名称input
重新定义为包装器,因为此实现要求对input
的所有调用都必须通过此路由。 (免责声明:这就是为什么它不是一个好主意,只是一个有趣的想法而已。)
input
(为了避免污染全局名称空间,我已经在仅使用一次的import atexit
import builtins
import queue
import threading
def _make_input_func():
prompt_queue = queue.Queue(maxsize=1)
input_queue = queue.Queue(maxsize=1)
def get_input():
while (prompt := prompt_queue.get()) != GeneratorExit:
inp = builtins.input(prompt)
input_queue.put(inp)
prompt_queue.task_done()
input_thread = threading.Thread(target=get_input, daemon=True)
last_call_timed_out = False
def input_func(prompt=None, timeout=None):
"""Mimics :function:`builtins.input`, with an optional timeout
:param prompt: string to pass to builtins.input
:param timeout: how long to wait for input in seconds; None means indefinitely
:return: the received input if not timed out, otherwise None
"""
nonlocal last_call_timed_out
if not last_call_timed_out:
prompt_queue.put(prompt, block=False)
else:
print(prompt, end='', flush=True)
try:
result = input_queue.get(timeout=timeout)
last_call_timed_out = False
return result
except queue.Empty:
print(flush=True) # optional: end prompt line if no input received
last_call_timed_out = True
return None
input_thread.start()
return input_func
input = _make_input_func()
del _make_input_func
中定义了设置,以将_make_input_func
的“静态”变量隐藏在其闭包中。) / p>
这里的想法是创建一个单独的线程来处理对input
的所有调用,并使builtins.input
包装器管理超时。由于对input
的调用始终阻塞,直到有输入为止,所以在超时结束时,特殊线程仍在等待输入,但是builtins.input
包装器返回(带有input
)。在下一次调用时,如果最后一次调用超时,则不需要再次调用None
(因为输入线程已经在等待输入),它仅显示提示,然后等待该线程像往常一样返回一些输入。
已定义以上内容,请尝试运行以下脚本:
builtins.input
答案 19 :(得分:0)
某些答案需要在发生超时时按Enter
键才能继续运行您的代码。其他人似乎很困惑,要引导,仍然需要在超时后按Enter
键。
我找到了answer in another thread,效果很好,但是我发现了一个警告。我决定将代码放在class
中以实现可移植性。
由于我的代码中还有另一个keyboard
语句,因此我不得不使用Enter
来注入input()
键。出于某种原因,除非我按下input()
键,否则随后的Enter
语句不会出现。
import threading
import keyboard # https://github.com/boppreh/keyboard
class Utilities:
# Class variable
response = None
@classmethod
def user_input(cls, timeout):
def question():
cls.response = input("Enter something: ")
t = threading.Thread(target=question)
# Daemon property allows the target function to terminate after timeout
t.daemon = True
t.start()
t.join(timeout)
if cls.response:
# Do something
else:
# Do something else
# Optional. Use if you have other input() statements in your code
keyboard.send("enter")
Utilities.user_input(3)
这是在Windows 10上使用Python 3.8.3制作的。
答案 20 :(得分:0)
这里是linux上的python 3.8+的另一个版本,其中包含yes_no答案,默认返回超时值
import signal
def alarm_handler(signum, frame):
raise TimeoutError
def input_with_timeout(prompt, timeout=30):
""" get input with timeout
:param prompt: the prompt to print
:param timeout: timeout in seconds, or None to disable
:returns: the input
:raises: TimeoutError if times out
"""
# set signal handler
if timeout is not None:
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(timeout) # produce SIGALRM in `timeout` seconds
try:
return input(prompt)
except TimeoutError as to:
raise to
finally:
if timeout is not None:
signal.alarm(0) # cancel alarm
def yes_or_no(question, default='y', timeout=None):
""" Get y/n answer with default choice and optional timeout
:param question: prompt
:param default: the default choice, i.e. 'y' or 'n'
:param timeout: the timeout in seconds, default is None
:returns: True or False
"""
if default is not None and (default!='y' and default!='n'):
log.error(f'bad option for default: {default}')
quit(1)
y='Y' if default=='y' else 'y'
n='N' if default=='n' else 'n'
while "the answer is invalid":
try:
to_str='' if timeout is None else f'(Timeout {default} in {timeout}s)'
reply = str(input_with_timeout(f'{question} {to_str} ({y}/{n}): ',timeout=timeout)).lower().strip()
except TimeoutError:
log.warning(f'timeout expired, returning default={default} answer')
reply=''
if len(reply)==0:
return True if default=='y' else False
elif reply[0] == 'y':
return True
if reply[0] == 'n':
return False
在代码中使用的示例
if yes_or_no(f'model {latest_model_folder} exists, start from it?', timeout=TIMEOUT):
log.info(f'initializing model from {latest_model_folder}')
model = load_model(latest_model_folder)
else:
log.info('creating new empty model')
model = create_model()
答案 21 :(得分:0)
我的跨平台解决方案
def input_process(stdin_fd, sq, str):
sys.stdin = os.fdopen(stdin_fd)
try:
inp = input (str)
sq.put (True)
except:
sq.put (False)
def input_in_time (str, max_time_sec):
sq = multiprocessing.Queue()
p = multiprocessing.Process(target=input_process, args=( sys.stdin.fileno(), sq, str))
p.start()
t = time.time()
inp = False
while True:
if not sq.empty():
inp = sq.get()
break
if time.time() - t > max_time_sec:
break
p.terminate()
sys.stdin = os.fdopen( sys.stdin.fileno() )
return inp
答案 22 :(得分:0)
答案 23 :(得分:-4)
迟到的答案:)
我会做这样的事情:
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.')
我知道这不一样,但许多现实生活中的问题都可以通过这种方式解决。 (如果用户现在不想在那里继续运行,我通常需要超时用户输入。)
希望这至少部分有帮助。 (如果有人再读它:))