2016-09-07 3 views
13

함수 glib.spawn_asyncstdout, stderr 및 처리 완료시 이벤트에서 호출되는 세 가지 콜백을 후크 할 수 있습니다.팝업으로 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()을 사용하고, 명령의 끝에서 이전에 읽지 않은 모든 행을 읽는 것을 끝내야합니다.

하위 프로세스 및 스레드와
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() 

, 그것뿐만 아니라 간단합니다

답변

4

asyncio이 subprocess_exec을 가지고, 전혀 서브 프로세스 모듈을 사용할 필요가 없습니다. 당신은 과정에 대한 wait()에 파이프 당 스레드 하나를 생성 할 수 있습니다

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 당신의 콜백을 실행 하지 사용 스레드를 수행 입심 마음. asyncio처럼 이벤트 루프를 사용합니다. 아이디어는 프로그램의 핵심에는 무언가가 일어날 때까지 기다린 후 관련 콜백을 동 기적으로 실행하는 루프입니다. 귀하의 경우, 그것은 "파이프 중 하나에서 데이터를 읽을 수있게됩니다", "하위 프로세스가 종료되었습니다"입니다. 일반적으로 "X11- 서버에서 마우스 움직임을보고했습니다", "들어오는 네트워크 트래픽이 있습니다"등과 같은 요소도 있습니다. 자신의 이벤트 루프를 작성하여 glib의 동작을 에뮬레이트 할 수 있습니다. 두 파이프에 select module을 사용하십시오. 파이프를 읽을 수 있다고보고하지만 read이 데이터를 반환하지 않으면 프로세스가 종료됩니다.이 경우 하위 프로세스 개체의 poll() 메서드를 호출하여 완료되었는지 확인하고 종료 콜백이 있거나 오류가있는 경우 호출합니다 콜백.

+0

시간을내어이 답변을 작성해 주셔서 감사합니다. – Sardathrion

+1

위의 코드는'readlines()'가 블로킹하는 것처럼'stdout'과'stderr'의 라인을 버퍼링합니다. 업데이트를 원한다면'read()'를 사용하고 리더 쓰레드가 끝나면 버퍼를 비운다. – Sardathrion

관련 문제