谷歌指向一个邮件线程(http://mail.python.org/pipermail/python-list/2006-January/533215.html),但它似乎不起作用。无论是
sys.input.readline
还是timer.sleep()
发生超时的语句,我总是得到:<type 'exceptions.TypeError'>: [raw_]input expected at most 1 arguments, got 2
某种情况下,异常未被捕获。
sys.input.readline
还是timer.sleep()
发生超时的语句,我总是得到:<type 'exceptions.TypeError'>: [raw_]input expected at most 1 arguments, got 2
某种情况下,异常未被捕获。
我曾经遇到过同样的问题,并通过键盘和kthread解决了它。一旦您按下回车键,输入字段就会消失。这对我来说是最重要的事情,但我无法使用其他方法使其正常工作。
如果您想的话,可以使用pip进行安装:
pip install input-timeout
from input_timeout import InputTimeout
i = InputTimeout(
timeout=20,
input_message=" >> ",
timeout_message="'Sorry, you were not fast enough'",
defaultvalue="slow",
cancelbutton="esc",
show_special_characters_warning='If you want to use special characters, you have to use alt+\\d\\d\\d\\d\nPress "ctrl" to see a complete list of all combinations!',
).finalvalue
print(f"\n\nYour input was {i}")
i = InputTimeout(
timeout=5,
input_message=" >> ",
timeout_message="Sorry, you were not fast enough: ",
defaultvalue="slow",
cancelbutton="esc",
show_special_characters_warning='If you want to use special characters, you have to use alt+\\d\\d\\d\\d\nPress "ctrl" to see a complete list of all combinations!',
).finalvalue
print(f"\n\nYour input was {i}")
i = InputTimeout(
timeout=10,
input_message=" >> ",
timeout_message="Sorry, you were not fast enough",
defaultvalue="Wake up!",
cancelbutton=None,
show_special_characters_warning=None,
).finalvalue
print(f"\n\nYour input was {i}")
i = InputTimeout(
timeout=10,
input_message=" >> ",
timeout_message="Sorry, you were not fast enough",
defaultvalue="Are you sleeping?",
cancelbutton="esc",
show_special_characters_warning=None,
).finalvalue
print(f"\n\nYour input was {i}")
i = InputTimeout(
timeout=10,
input_message=" >>",
timeout_message="Sorry, you were not fast enough",
defaultvalue="you are so slow",
cancelbutton=None,
show_special_characters_warning='If you want to use special characters, you have to use alt+\\d\\d\\d\\d\nPress "ctrl" to see a complete list of all combinations!',
).finalvalue
print(f"\n\nYour input was {i}")
#output
If you want to use special characters, you have to use alt+\d\d\d\d
Press "ctrl" to see a complete list of all combinations!
>> babba
Your input was babba
If you want to use special characters, you have to use alt+\d\d\d\d
Press "ctrl" to see a complete list of all combinations!
alt+0192 -> À alt+0193 -> Á alt+0196 -> Ä alt+0194 -> Â
alt+0195 -> Ã alt+0197 -> Å alt+0198 -> Æ alt+0228 -> ä
alt+0224 -> à alt+0225 -> á alt+0226 -> â alt+0227 -> ã
alt+0229 -> å alt+0230 -> æ alt+0199 -> Ç alt+0231 -> ç
alt+0208 -> Ð alt+0240 -> ð alt+0203 -> Ë alt+0200 -> È
alt+0201 -> É alt+0202 -> Ê alt+0235 -> ë alt+0232 -> è
alt+0233 -> é alt+0234 -> ê alt+0207 -> Ï alt+0204 -> Ì
alt+0205 -> Í alt+0206 -> Î alt+0239 -> ï alt+0236 -> ì
alt+0237 -> í alt+0238 -> î alt+0209 -> Ñ alt+0241 -> ñ
alt+0214 -> Ö alt+0210 -> Ò alt+0211 -> Ó alt+0212 -> Ô
alt+0213 -> Õ alt+0216 -> Ø alt+0140 -> Œ alt+0246 -> ö
alt+0242 -> ò alt+0243 -> ó alt+0244 -> ô alt+0245 -> õ
alt+0248 -> ø alt+0156 -> œ alt+0138 -> Š alt+0223 -> ß
alt+0154 -> š alt+0222 -> Þ alt+0254 -> þ alt+0220 -> Ü
alt+0217 -> Ù alt+0218 -> Ú alt+0219 -> Û alt+0252 -> ü
alt+0249 -> ù alt+0250 -> ú alt+0251 -> û alt+0159 -> Ÿ
alt+0221 -> Ý alt+0255 -> ÿ alt+0253 -> ý alt+0168 -> ¨
alt+0136 -> ˆ alt+0180 -> ´ alt+0175 -> ¯ alt+0184 -> ¸
alt+0192 -> À alt+0193 -> Á alt+0196 -> Ä alt+0194 -> Â
alt+0195 -> Ã alt+0197 -> Å alt+0198 -> Æ alt+0228 -> ä
alt+0224 -> à alt+0225 -> á alt+0226 -> â alt+0227 -> ã
alt+0229 -> å alt+0230 -> æ alt+0199 -> Ç alt+0231 -> ç
alt+0208 -> Ð alt+0240 -> ð alt+0203 -> Ë alt+0200 -> È
alt+0201 -> É alt+0202 -> Ê alt+0235 -> ë alt+0232 -> è
alt+0233 -> é alt+0234 -> ê alt+0207 -> Ï alt+0204 -> Ì
alt+0205 -> Í alt+0206 -> Î alt+0239 -> ï alt+0236 -> ì
alt+0237 -> í alt+0238 -> î alt+0209 -> Ñ alt+0241 -> ñ
alt+0214 -> Ö alt+0210 -> Ò alt+0211 -> Ó alt+0212 -> Ô
alt+0213 -> Õ alt+0216 -> Ø alt+0140 -> Œ alt+0246 -> ö
alt+0242 -> ò alt+0243 -> ó alt+0244 -> ô alt+0245 -> õ
alt+0248 -> ø alt+0156 -> œ alt+0138 -> Š alt+0223 -> ß
alt+0154 -> š alt+0222 -> Þ alt+0254 -> þ alt+0220 -> Ü
alt+0217 -> Ù alt+0218 -> Ú alt+0219 -> Û alt+0252 -> ü
alt+0249 -> ù alt+0250 -> ú alt+0251 -> û alt+0159 -> Ÿ
alt+0221 -> Ý alt+0255 -> ÿ alt+0253 -> ý alt+0168 -> ¨
alt+0136 -> ˆ alt+0180 -> ´ alt+0175 -> ¯ alt+0184 -> ¸
Sorry, you were not fast enough:
Your input was slow
>> super
Your input was super
>> adasa
Your input was adasa
If you want to use special characters, you have to use alt+\d\d\d\d
Press "ctrl" to see a complete list of all combinations!
Sorry, you were not fast enough
Your input was you are so slow
灵感来自于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))
有些答案需要在超时发生时按下Enter
键才能继续运行代码。其他答案似乎很复杂,而且在超时后仍然需要按下Enter
键。
我在另一个线程中找到了答案,它非常有效,但我发现了一个警告。我决定将我的代码放在一个class
中以实现可移植性。
我不得不使用keyboard
来注入Enter
键的按下,因为我的代码中还有另一个input()
语句。由于某种原因,除非我按下Enter
键,否则后续的input()
语句不会出现。
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)
这是使用Python 3.8.3在Windows 10上制作的。
这里还有一个关于在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()
这是一个适用于 Python 3.8+(虽然可以适应 Python 3.6+)的跨平台方法,仅使用 threading
(因此不使用 multiprocessing
或调用 shell 实用程序)。它旨在从命令行运行脚本,不太适合动态使用。
您可以按如下方式包装内置的 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
中定义了设置,以隐藏闭包中的input
的“静态”变量,以避免污染全局命名空间。
这里的想法是创建一个单独的线程来处理对builtins.input
的任何调用,并使input
包装器管理超时。由于对builtins.input
的调用总会阻塞,直到有输入为止,当超时结束时,特殊线程仍在等待输入,但input
包装器将返回(带有None
)。在下一次调用时,如果上一次调用超时,则不需要再次调用builtins.input
(因为输入线程已经在等待输入),它只是打印提示,然后像往常一样等待该线程返回一些输入。
定义以上内容后,请尝试运行以下脚本:
import time
if __name__ == '__main__':
timeout = 2
start_t = time.monotonic()
if (inp := input(f"Enter something (you have {timeout} seconds): ", timeout)) is not None:
print("Received some input:", repr(inp))
else:
end_t = time.monotonic()
print(f"Timed out after {end_t - start_t} seconds")
inp = input("Enter something else (I'll wait this time): ")
print("Received some input:", repr(inp))
input(f"Last chance to say something (you have {timeout} seconds): ", timeout)
pytimedinput
。它在我的Windows 10系统上运行良好。您可以像这样使用pip
安装它:C:\Users\User> pip install pytimedinput
from pytimedinput import timedInput
userText, timedOut = timedInput("Enter something: ", timeout=5)
if(timedOut):
print("Timed out when waiting for input.")
print(f"User-input so far: '{userText}'")
else:
print(f"User-input: '{userText}'")
这是我写的代码。使用多进程,我们可以设置输入超时。
from multiprocessing import Queue, Process
from queue import Empty
class ProcessTimedOutException(Exception):
def __init__(self, message: str):
self.message: str = message
class Terminal:
@staticmethod
def input_with_timeout(message: str = '', timeout: int = 60) -> Tuple[Optional[str], Optional[Exception]]:
queue = Queue()
err: Optional[Exception] = None
user_input: Optional[str] = None
input_thread = Process(target=Terminal._input_async, args=(queue, message), daemon=True)
input_thread.start()
try:
user_input = queue.get(timeout=timeout)
except Empty:
input_thread.terminate()
err = ProcessTimedOutException(f'process timed out')
return user_input, err
@staticmethod
def _input_async(queue, message: str = ''):
sys.stdin = open(0)
user_input = input(message).strip()
queue.put(user_input)
if __name__ == '__main__':
input_message: str = 'enter anything'
user_input, err = Terminal.input_with_timeout(message=input_message,timeout=60)
if err is not None:
raise err
print(user_input)
使用 asyncio 可以实现这一点。基本上,将 https://dev59.com/rlsW5IYBdhLWcg3wO1KN#65909044 和 https://dev59.com/qbHma4cB1Zd3GeqPT_2Z#54787498 组合在一起就会得到类似于以下内容的东西:
import asyncio
import sys
async def main():
reader = asyncio.StreamReader()
pipe = sys.stdin
loop = asyncio.get_event_loop()
await loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(reader), pipe)
got_input = None
async def get_input():
nonlocal got_input
inp = await anext(aiter(reader))
got_input = inp.decode()
tasks = [asyncio.create_task(asyncio.sleep(5)), asyncio.create_task(get_input())]
await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
if got_input is None:
print("Time up!")
else:
print("Input: ", got_input)
if __name__ == "__main__":
asyncio.run(main())
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.')
我知道这不是同样的情况,但许多现实生活中的问题可以通过这种方式解决。(当我希望某些东西继续运行而用户此时不在场时,我通常需要设置超时来等待用户输入。)
希望这至少在某种程度上有所帮助。(如果有人读到的话 :) )