sys.stdinとかネットワーク(ソケット)からのreadline()やreadlines()は、入力が来るまで
ブロックしてしまいますが、場合によってはタイムアウトを付けられると便利です。
そのために書いたタイムアウト付きreadline()です。
import sys,select
def readline_timeout(fd, timeout = 1.0):
(r, w, e) = select.select([fd], [], [], timeout)
if len(r) == 0: return "TIMEOUT!\n"
elif len(r) == 1: return r[0].readline()
else : assert False
print readline_timeout(sys.stdin, 3.0),
標準入力から一行読みます。3秒以内に入力がないと、TIMEOUT!と表示して終了します。
今回は"TIMEOUT!\n"というメッセージを返していますが、実際はNoneを返して
エラー分岐させるなり、Exceptionをraiseするなりしましょう。
Selectは入力があったファイルディスクリプタを返してくれるから、これを適当にread()する。
少し注意が必要なのは、通信相手のファイルディスクリプタがブロッキングモードでかつ
データがまだ全部届いていないと、select()後の処理でブロックしてしまう可能性があること。
これを防ぐためには、前もってファイルディスクリプタをノンブロッキングモードにしておく。
import fcntl
flag = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flag)
(因みにfcntlはエフ・コントロールと読みます)
これを用いて、任意のfdに対しタイムアウト付き通信をするのが次のコードです。
SelectTest()のコンストラクタに好きなファイルディスクリプタを入れます。
import os,sys,fcntl,socket,select
class SelectTest:
def __init__(self, fd):
self.buf = ""
self.fd = fd
flag = fcntl.fcntl(fd, fcntl.F_GETFL)
new_flag = flag | os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, new_flag)
def handle_msg(self, msg):
ms = msg.split("\n")
if self.buf != "":
ms[0] = self.buf + ms[0]
self.buf = ""
m = ms.pop()
if m != "":
self.buf = m
for m in ms:
print >> sys.stderr, "Received line: <%s>"%m
def async_recv(self):
# Read from FDs with timeouts
r,w,e = select.select([self.fd], [], [], 3.0) # Timeout
print >> sys.stderr, "Selected: [%d, %d, %d]"%(len(r), len(w), len(e))
if len(r) == 0:
print >> sys.stderr, "Timeout!"
else:
for fd in r:
assert fd == self.fd
m = fd.read(1024)
self.handle_msg(m)
st = SelectTest(sys.stdin)
while True:
st.async_recv()
例のようにstdinなんかを入れても、(コンソールからの入力は改行単位でしか送信されないので)
あまり面白くないのですが、ネットワークの入力なんかを入れると、self.bufという変数に
改行が来る前の(不完全な)データがバッファされて、完全な1行単位でデータが出力されるのがポイントです。
[an error occurred while processing this directive]