https://docs.python.org/3/library/subprocess.html

こういうやつ


      +-------+
      | child |
      |process|
      +-------+
   stdin ^ | stdout
         | v
      +-------+
      |process|
      +-------+
-> stdin     stdout ->

subprocess モジュール

subprocess は、

os.system
os.spawn*

の置き換え。

subprocess.run

中でPopenして結果を集めて CompletedProcess として返す。

# 抜粋
def run(...):
    with Popen(*popenargs, **kwargs) as process:
        try:
            stdout, stderr = process.communicate(input, timeout=timeout) 

    return CompletedProcess(process.args, retcode, stdout, stderr)

実行して結果の文字列を得て終わりというタイプの用途向け。 旧 os.system の代替になると思う。

subprocess.Popen

標準入力、標準出力を制御するのはこっち。

subprocess.Popen(['cmd_name', 'arg0', 'arg1'...],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    cwd=None, env=None,
    text=False
    )

Readループが一個しかない時はこれでいいんでないかな。

今回のテーマ


      +-------+
      | child |
      |process|
      +-------+
   stdin ^ | @stdout
         | v
      +-------+
      |process| ここでロギングして通信内容を確認したい
      +-------+
  @stdin ^ | stdout
         | v
      +-------+
      | parent|
      |process|
      +-------+
  • @のところを常時読み込みにしたい(2つのReadループ)

  • @stdin をReadするとブロックして固まるのでつらい

つらいのだ。

asyncio

2つのReaderを非同期で制御しようということで 。

asyncio の基本

loop を露出させる。

loop は暗黙。 基本的にこちらでよいと思う。 必要に応じて取得する。

asyncio.create_task で新しいスタックを開始する

新しいスタックなのでエラーハンドリングが無いことに注意。

StreamReaderProtocol と StreamWriterProtocol

コールバックと Stream を結び付ける。

Windowsの標準入出力はIOCPできない

IOCPできるハンドルは決まっていて、

python3.7 で asyncio.create_subprocess ができた

child process 側はこれで助かった。

なんか、たまに socket.exception が出るので、 tcpのlocalhost接続にリダイレクトするとか謎の技使っているのかもしれぬ。

おかげで、子プロセスの標準入出力から StreamReaderStreamWriter を楽に取得できる。

重い NativeCoroutine は、ThreadPoolExecutorに逃がす

標準入力側

GILを回避して、別スレッドで待てるのではないか。

どんな処理が、NativeCoroutine なのか。