Pythonでサブプロセスと対話する

標準入力に1行入れると、標準出力で1行返してくるようなプログラムがあって、起動のたびに7分かかってめんどうだから、プロセスを起動しっぱなしにしておいてHTTPサーバで包んでやろうと思ったんですよ。

サブプロセスの挙動としてはcatだと思っていい。それで

p = Popen(["cat"], stdin=PIPE, stdout=PIPE)

ってやってから

p.stdin.write("%s\n" % query)
result = p.stdout.read()

ってやるとブロックされてしまった。ブロックを避けるために生のread/writeではなくp.communicateを使えとマニュアルには書いてあるが、それを使うと1回やり取りした時点でfdを閉じてしまうから2回目で「ValueError: I/O operation on closed file」になる。p.stdin.flush()は関係なし。p.stdout.read(1)ならブロックせずに読める。つまりはPythonのfile#read()がread(1024)みたいな挙動をして、しかも指定された量のデータがない場合にブロッキングすることが原因。POSIXのreadは

It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now...

という挙動をするので、POSIX環境でコードを書いているならos.readを使えばよい。

>>> p.stdin.write("hoge\n")
>>> os.read(p.stdout.fileno(), 1024)
'hoge\n'

thanks id:moriyoshi, id:mopemope

追記: 1行単位で返ってくることが既知ならreadlineでもいいと言う指摘が多数ありました。