2016-09-07 9 views
13

関数glib.spawn_asyncは、stdoutstderr、および処理完了時にイベントで呼び出される3つのコールバックをフックできます。Popupでglib.spawn_asyncを模倣します。

subprocessと同じ機能を、スレッドまたはasyncioでどのように模倣できますか?

私はスレッド/ asynioではなく機能性に興味がありますが、両方を含む回答では賞金が得られます。ここで

は、私が何をしたいのかを示しおもちゃのプログラムです:

import glib 
import logging 
import os 
import gtk 


class MySpawn(object): 
    def __init__(self): 
     self._logger = logging.getLogger(self.__class__.__name__) 

    def execute(self, cmd, on_done, on_stdout, on_stderr): 
     self.pid, self.idin, self.idout, self.iderr = \ 
      glib.spawn_async(cmd, 
          flags=glib.SPAWN_DO_NOT_REAP_CHILD, 
          standard_output=True, 
          standard_error=True) 
     fout = os.fdopen(self.idout, "r") 
     ferr = os.fdopen(self.iderr, "r") 
     glib.child_watch_add(self.pid, on_done) 
     glib.io_add_watch(fout, glib.IO_IN, on_stdout) 
     glib.io_add_watch(ferr, glib.IO_IN, on_stderr) 
     return self.pid 


if __name__ == '__main__': 
    logging.basicConfig(format='%(thread)d %(levelname)s: %(message)s', 
         level=logging.DEBUG) 
    cmd = '/usr/bin/git ls-remote https://github.com/DiffSK/configobj'.split() 

    def on_done(pid, retval, *args): 
     logging.info("That's all folks!…") 

    def on_stdout(fobj, cond): 
     """This blocks which is fine for this toy example…""" 
     for line in fobj.readlines(): 
      logging.info(line.strip()) 
     return True 

    def on_stderr(fobj, cond): 
     """This blocks which is fine for this toy example…""" 
     for line in fobj.readlines(): 
      logging.error(line.strip()) 
     return True 

    runner = MySpawn() 
    runner.execute(cmd, on_done, on_stdout, on_stderr) 
    try: 
     gtk.main() 
    except KeyboardInterrupt: 
     print('') 

私はreadlines()以来、すべての出力をバッファリングし、一度にそれを送信します上記、ブロックしていることを追加する必要があります。これが何を望んでいないのであれば、readline()を使用し、コマンドの最後にあなたが以前に読んでいなかったすべての行を読み終えるようにしなければなりません。

答えて

4

asyncioがsubprocess_execを持って、すべてのサブプロセスモジュールを使用する必要はありません。

import asyncio 

class Handler(asyncio.SubprocessProtocol): 
    def pipe_data_received(self, fd, data): 
     # fd == 1 for stdout, and 2 for stderr 
     print("Data from /bin/ls on fd %d: %s" % (fd, data.decode())) 

    def pipe_connection_lost(self, fd, exc): 
     print("Connection lost to /bin/ls") 

    def process_exited(self): 
     print("/bin/ls is finished.") 

loop = asyncio.get_event_loop() 
coro = loop.subprocess_exec(Handler, "/bin/ls", "/") 

loop.run_until_complete(coro) 
loop.close() 

サブプロセスやスレッドでは、それは同様に簡単です。あなただけのプロセスのためにwait()にパイプごとにスレッド、および1つを起動することができます

import subprocess 
import threading 

class PopenWrapper(object): 
    def __init__(self, args): 
     self.process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL) 

     self.stdout_reader_thread = threading.Thread(target=self._reader, args=(self.process.stdout,)) 
     self.stderr_reader_thread = threading.Thread(target=self._reader, args=(self.process.stderr,)) 
     self.exit_watcher = threading.Thread(target=self._exit_watcher) 

     self.stdout_reader_thread.start() 
     self.stderr_reader_thread.start() 
     self.exit_watcher.start() 

    def _reader(self, fileobj): 
     for line in fileobj: 
      self.on_data(fileobj, line) 

    def _exit_watcher(self): 
     self.process.wait() 
     self.stdout_reader_thread.join() 
     self.stderr_reader_thread.join() 
     self.on_exit() 

    def on_data(self, fd, data): 
     return NotImplementedError 

    def on_exit(self): 
     return NotImplementedError 

    def join(self): 
     self.process.wait() 

class LsWrapper(PopenWrapper): 
    def on_data(self, fd, data): 
     print("Received on fd %r: %s" % (fd, data)) 

    def on_exit(self): 
     print("Process exited.") 


LsWrapper(["/bin/ls", "/"]).join() 

しかし、asynchroneouslyあなたのコールバックを実行するない使用スレッドを行いGLibの心。 asyncioと同様に、イベントループを使用します。プログラムの中核となるのは、何かが起きるまで待ってから、関連するコールバックを同期的に実行するというループです。あなたの場合、それは「パイプの1つでデータを読むことができるようになります」と「サブプロセスが終了しました」です。一般的には、 "X11-serverがマウスの動きを報告しました"、 "ネットワークのトラフィックが入っています"などのようなものもあります。自分のイベントループを書くことでglibの動作をエミュレートできます。 2本のパイプにselect moduleを使用してください。選択したパイプが読み取り可能であるが、readがデータを返さないという報告がある場合、このプロセスは終了している可能性があります。サブプロセスオブジェクトのpoll()メソッドを呼び出して完了したかどうかをチェックし、終了コールバックコールバックelsewise。

+0

この回答を書いていただきありがとうございます。 – Sardathrion

+1

上記はreadline()がブロックしているので 'stdout'と' stderr'の行をバッファします。更新が必要な場合は 'read()'を使用しますが、リーダースレッドが終了したらバッファを空にしてください。 – Sardathrion

関連する問題