Функция glib.spawn_async позволяет подключить три обратных вызова, вызываемых в событии на stdout
, stderr
, и в процессе завершение.
Как я могу имитировать те же функции с помощью subprocess с нитями или асинхронными вызовами?
Меня больше интересуют функциональность, а не threading/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 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()
и убедитесь, что по окончании команды вы закончите чтение всех строк, которые вы ранее не читали.