タイムアウト付きのinput
概要
タイム付きのinputが欲しくなったので自分で作ってPyPIに公開してみた。(1回公開したけどバグってたので削除済み)
ググって参考にしたもの
Python 3 Timed Input - Stack Overflow
困ったこと
PythonのselectはWindowsのファイルオブジェクトを受理できないのでWindowsとUnix系で実装を分ける必要があった。
判別方法
>>> import platform
>>> platform.system()
'Windows' or 'Darwin' or 'Linux' # など
Windows の場合
34.2. msvcrt — MS VC++実行時システムの有用なルーチン群 — Python 3.6.1 ドキュメント
ドキュメントの最初にgetpassモジュールに使われていると書いてあったので参考にした。
cpython/getpass.py at 3.6 · python/cpython
import msvcrt
import time
DEFAULT_TIMEOUT = 30.0
def win_input_with_timeout(prompt='', timeout=DEFAULT_TIMEOUT):
begin = time.monotonic()
end = begin + timeout
for c in prompt:
msvcrt.putwch(c)
line = ''
is_timeout = True
while time.monotonic() < end:
if msvcrt.kbhit():
c = msvcrt.getwch()
msvcrt.putwch(c)
if c == '\r' or c == '\n':
is_timeout = False
break
if c == '\003':
raise KeyboardInterrupt
if c == '\b':
line = line[:-1]
else:
line = line + c
time.sleep(0.05)
msvcrt.putwch('\r')
msvcrt.putwch('\n')
if is_timeout:
raise TimeoutOccurred
return line
msvcrt.kbhit()の呼び出しが重たいみたいのでtime.sleep(0.05)を入れないとCPU使用率が上がるみたい。
Unix系の場合
signalとかthreadとかselectとか実現できそうな方法はいろいろあるけどselectで実装
import sys
import select
def unix_input_with_timeout(prompt='', timeout=DEFAULT_TIMEOUT):
sys.stdout.write(prompt)
sys.stdout.flush()
(ready, _, _) = select.select([sys.stdin], [], [], timeout)
if ready:
return sys.stdin.readline().rstrip('\n')
else:
raise TimeoutOccurred
インストール
$ pip install inputimeout
使い方
from inputimeout import inputimeout, TimeoutOccurred
try:
something = inputimeout(prompt='>>', timeout=5)
except TimeoutOccurred:
print('Timeout!')
something = 'something'