Posted at

timeout付きinputを作った

More than 1 year has passed since last update.


タイムアウト付きの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'


リポジトリ

johejo/inputimeout: Standard input with timeout.