Неблокирование чтения на подпроцессе.PIPE в python

Я использую модуль подпроцесса, чтобы запустить подпроцесс и подключиться к его выходному потоку (stdout). Я хочу, чтобы иметь возможность выполнять неблокирующие чтения на его stdout. Есть ли способ сделать .readline non-blocking или проверить, есть ли данные в потоке, прежде чем я буду ссылаться на .readline ? Я бы хотел, чтобы это было портативным или, по крайней мере, работало под Windows и Linux.

вот как я это делаю сейчас (он блокирует .readline если нет данных):

 p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE) output_str = p.stdout.readline() 

  • Django + Apache + Windows WSGIDaemonProcess Alternative
  • Python, Popen и select - ожидание завершения процесса или тайм-аута
  • Ошибка потоковой передачи Python Hadoop «ERROR streaming.StreamJob: Job not Successful!» И трассировка стека: ExitCodeException exitCode = 134
  • Подпроцесс Python: дождитесь окончания команды до начала следующего?
  • Python: как убить дочерний процесс (ы), когда родитель умирает?
  • Интерактивный ввод / вывод с использованием python
  • Чтение / запись подпроцесса Popen ()
  • Как получить PID через subprocess.Popen с пользовательской переменной среды?
  • 23 Solutions collect form web for “Неблокирование чтения на подпроцессе.PIPE в python”

    fcntl , select , asyncproc в этом случае не поможет.

    Надежным способом чтения потока без блокировки вне зависимости от операционной системы является использование Queue.get_nowait() :

     import sys from subprocess import PIPE, Popen from threading import Thread try: from Queue import Queue, Empty except ImportError: from queue import Queue, Empty # python 3.x ON_POSIX = 'posix' in sys.builtin_module_names def enqueue_output(out, queue): for line in iter(out.readline, b''): queue.put(line) out.close() p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX) q = Queue() t = Thread(target=enqueue_output, args=(p.stdout, q)) t.daemon = True # thread dies with the program t.start() # ... do other things here # read line without blocking try: line = q.get_nowait() # or q.get(timeout=.1) except Empty: print('no output yet') else: # got line # ... do something with line 

    У меня часто была аналогичная проблема; Программы Python, которые я пишу, часто должны иметь возможность выполнять некоторые основные функции, одновременно принимая пользовательский ввод из командной строки (stdin). Простое использование функций обработки ввода пользователем в другом потоке не решает проблему, потому что readline() блокирует и не имеет таймаута. Если основная функциональность завершена и больше не нужно ждать ввода нового пользователя, я обычно хочу, чтобы моя программа выходила, но это невозможно, потому что readline() по-прежнему блокирует другой поток, ожидающий строки. Решение, которое я нашел для этой проблемы, заключается в том, чтобы сделать stdin неблокирующим файлом с помощью модуля fcntl:

     import fcntl import os import sys # make stdin a non-blocking file fd = sys.stdin.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # user input handling thread while mainThreadIsRunning: try: input = sys.stdin.readline() except: continue handleInput(input) 

    По-моему, это немного чище, чем использование выбранных или сигнальных модулей для решения этой проблемы, но затем он работает только в UNIX …

    Python 3.4 представляет новый предварительный API для асинхронного модуля ввода-вывода asyncioasyncio .

    Этот подход похож на критический ответ от @Bryan Ward – определяет протокол, и его методы вызывается, как только данные готовы:

     #!/usr/bin/env python3 import asyncio import os class SubprocessProtocol(asyncio.SubprocessProtocol): def pipe_data_received(self, fd, data): if fd == 1: # got stdout data (bytes) print(data) def connection_lost(self, exc): loop.stop() # end loop.run_forever() if os.name == 'nt': loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() try: loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, "myprogram.exe", "arg1", "arg2")) loop.run_forever() finally: loop.close() 

    См. «Подпроцесс» в документах .

    Существует интерфейс высокого уровня asyncio.create_subprocess_exec() который возвращает объекты Process которые позволяют читать строку асинхронно с помощью StreamReader.readline() coroutine (с синтаксисом aync / await Python 3.5+ ):

     #!/usr/bin/env python3.5 import asyncio import locale import sys from asyncio.subprocess import PIPE from contextlib import closing async def readline_and_kill(*args): # start child process process = await asyncio.create_subprocess_exec(*args, stdout=PIPE) # read line (sequence of bytes ending with b'\n') asynchronously async for line in process.stdout: print("got line:", line.decode(locale.getpreferredencoding(False))) break process.kill() return await process.wait() # wait for the child process to exit if sys.platform == "win32": loop = asyncio.ProactorEventLoop() asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() with closing(loop): sys.exit(loop.run_until_complete(readline_and_kill( "myprogram.exe", "arg1", "arg2"))) 

    readline_and_kill() выполняет следующие задачи:

    • начать подпроцесс, перенаправить его вывод на канал
    • читать асинхронно строку из подпроцесса 'stdout
    • убить подпроцесс
    • дождитесь его выхода

    Каждый шаг может быть ограничен тайм-аутами секунд, если это необходимо.

    Попробуйте модуль asyncproc . Например:

     import os from asyncproc import Process myProc = Process("myprogram.app") while True: # check to see if process has ended poll = myProc.wait(os.WNOHANG) if poll != None: break # print any new output out = myProc.read() if out != "": print out 

    Модуль заботится обо всех потоках, как это предложил С.Лотт.

    Вы можете сделать это очень легко в Twisted . В зависимости от существующей базы кода это может быть не так просто, но если вы создаете скрученное приложение, то подобные вещи становятся почти тривиальными. Вы создаете класс ProcessProtocol и переопределяете метод outReceived() . Twisted (в зависимости от используемого реактора) обычно представляет собой просто большой цикл select() с обратными вызовами, установленными для обработки данных из разных файловых дескрипторов (часто сетевых сокетов). Таким образом, метод outReceived() просто устанавливает обратный вызов для обработки данных, поступающих из STDOUT . Простой пример, демонстрирующий это поведение, выглядит следующим образом:

     from twisted.internet import protocol, reactor class MyProcessProtocol(protocol.ProcessProtocol): def outReceived(self, data): print data proc = MyProcessProtocol() reactor.spawnProcess(proc, './myprogram', ['./myprogram', 'arg1', 'arg2', 'arg3']) reactor.run() 

    В документации Twisted есть хорошая информация об этом.

    Если вы создаете все приложение вокруг Twisted, он делает асинхронную связь с другими процессами, локальными или удаленными, действительно элегантными. С другой стороны, если ваша программа не построена поверх Twisted, на самом деле это не будет полезно. Надеемся, это может быть полезно для других читателей, даже если оно не применимо для вашего конкретного приложения.

    Используйте select & read (1).

     import subprocess #no new requirements def readAllSoFar(proc, retVal=''): while (select.select([proc.stdout],[],[],0)[0]!=[]): retVal+=proc.stdout.read(1) return retVal p = subprocess.Popen(['/bin/ls'], stdout=subprocess.PIPE) while not p.poll(): print (readAllSoFar(p)) 

    Для readline () – например:

     lines = [''] while not p.poll(): lines = readAllSoFar(p, lines[-1]).split('\n') for a in range(len(lines)-1): print a lines = readAllSoFar(p, lines[-1]).split('\n') for a in range(len(lines)-1): print a 

    Одним из решений является сделать другой процесс для выполнения вашего чтения процесса или сделать поток процесса с таймаутом.

    Вот потоковая версия функции таймаута:

    http://code.activestate.com/recipes/473878/

    Тем не менее, вам нужно прочитать stdout, поскольку он входит? Другим решением может быть сброс вывода в файл и ожидание завершения процесса с использованием p.wait () .

     f = open('myprogram_output.txt','w') p = subprocess.Popen('myprogram.exe', stdout=f) p.wait() f.close() str = open('myprogram_output.txt','r').read() 

    Отказ от ответственности: это работает только для торнадо

    Вы можете сделать это, установив fd для неблокирования, а затем используйте ioloop для регистрации обратных вызовов. Я упаковал это в яйце под названием tornado_subprocess, и вы можете установить его через PyPI:

     easy_install tornado_subprocess 

    теперь вы можете сделать что-то вроде этого:

     import tornado_subprocess import tornado.ioloop def print_res( status, stdout, stderr ) : print status, stdout, stderr if status == 0: print "OK:" print stdout else: print "ERROR:" print stderr t = tornado_subprocess.Subprocess( print_res, timeout=30, args=[ "cat", "/etc/passwd" ] ) t.start() tornado.ioloop.IOLoop.instance().start() 

    вы также можете использовать его с RequestHandler

     class MyHandler(tornado.web.RequestHandler): def on_done(self, status, stdout, stderr): self.write( stdout ) self.finish() @tornado.web.asynchronous def get(self): t = tornado_subprocess.Subprocess( self.on_done, timeout=30, args=[ "cat", "/etc/passwd" ] ) t.start() 

    Существующие решения для меня не работали (подробности ниже). Что в конечном итоге было сделано для реализации readline, используя read (1) (на основе этого ответа ). Последнее не блокирует:

     from subprocess import Popen, PIPE from threading import Thread def process_output(myprocess): #output-consuming thread nextline = None buf = '' while True: #--- extract line using read(1) out = myprocess.stdout.read(1) if out == '' and myprocess.poll() != None: break if out != '': buf += out if out == '\n': nextline = buf buf = '' if not nextline: continue line = nextline nextline = None #--- do whatever you want with line here print 'Line is:', line myprocess.stdout.close() myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread p1.daemon = True p1.start() #--- do whatever here and then kill process and thread if needed if myprocess.poll() == None: #kill process; will automatically stop thread myprocess.kill() myprocess.wait() if p1 and p1.is_alive(): #wait for thread to finish p1.join() 

    Почему существующие решения не работают:

    1. Решения, требующие чтения (включая Очередь), всегда блокируются. Трудно (невозможно?) Убить поток, который выполняет readline. Он только убивается, когда процесс, который его создал, заканчивается, но не тогда, когда процесс производства продукции уничтожается.
    2. Смешение низкоуровневого fcntl с высокоуровневыми вызовами readline может не работать должным образом, как указывал anonnn.
    3. Использование select.poll () является аккуратным, но не работает в Windows в соответствии с документами python.
    4. Использование сторонних библиотек кажется излишним для этой задачи и добавляет дополнительные зависимости.

    Я добавляю эту проблему, чтобы прочитать некоторый subprocess.Popen stdout. Вот мое неблокирующее решение для чтения:

     import fcntl def non_block_read(output): fd = output.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) try: return output.read() except: return "" # Use example from subprocess import * sb = Popen("echo test && sleep 1000", shell=True, stdout=PIPE) sb.kill() # sb.stdout.read() # <-- This will block non_block_read(sb.stdout) 'test\n' 

    Вот мой код, используемый для вывода каждого вывода из подпроцесса ASAP, включая частичные линии. Он нагнетает в то же время и stdout и stderr в почти правильном порядке.

    Протестировано и правильно работает на Linux и Windows Python 2.7.

     #!/usr/bin/python # # Runner with stdout/stderr catcher # from sys import argv from subprocess import Popen, PIPE import os, io from threading import Thread import Queue def __main__(): if (len(argv) > 1) and (argv[-1] == "-sub-"): import time, sys print "Application runned!" time.sleep(2) print "Slept 2 second" time.sleep(1) print "Slept 1 additional second", time.sleep(2) sys.stderr.write("Stderr output after 5 seconds") print "Eol on stdin" sys.stderr.write("Eol on stderr\n") time.sleep(1) print "Wow, we have end of work!", else: os.environ["PYTHONUNBUFFERED"]="1" try: p = Popen( argv + ["-sub-"], bufsize=0, # line-buffered stdin=PIPE, stdout=PIPE, stderr=PIPE ) except WindowsError, W: if W.winerror==193: p = Popen( argv + ["-sub-"], shell=True, # Try to run via shell bufsize=0, # line-buffered stdin=PIPE, stdout=PIPE, stderr=PIPE ) else: raise inp = Queue.Queue() sout = io.open(p.stdout.fileno(), 'rb', closefd=False) serr = io.open(p.stderr.fileno(), 'rb', closefd=False) def Pump(stream, category): queue = Queue.Queue() def rdr(): while True: buf = stream.read1(8192) if len(buf)>0: queue.put( buf ) else: queue.put( None ) return def clct(): active = True while active: r = queue.get() try: while True: r1 = queue.get(timeout=0.005) if r1 is None: active = False break else: r += r1 except Queue.Empty: pass inp.put( (category, r) ) for tgt in [rdr, clct]: th = Thread(target=tgt) th.setDaemon(True) th.start() Pump(sout, 'stdout') Pump(serr, 'stderr') while p.poll() is None: # App still working try: chan,line = inp.get(timeout = 1.0) if chan=='stdout': print "STDOUT>>", line, "<?<" elif chan=='stderr': print " ERROR==", line, "=?=" except Queue.Empty: pass print "Finish" if __name__ == '__main__': __main__() 

    Эта версия неблокирующего чтения не требует специальных модулей и будет работать из коробки на большинстве дистрибутивов Linux.

     import os import sys import time import fcntl import subprocess def async_read(fd): # set non-blocking flag while preserving old flags fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) # read char until EOF hit while True: try: ch = os.read(fd.fileno(), 1) # EOF if not ch: break sys.stdout.write(ch) except OSError: # waiting for data be available on fd pass def shell(args, async=True): # merge stderr and stdout proc = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if async: async_read(proc.stdout) sout, serr = proc.communicate() return (sout, serr) if __name__ == '__main__': cmd = 'ping 8.8.8.8' sout, serr = shell(cmd.split()) 

    Модуль выбора помогает определить, где находится следующий полезный ввод.

    Тем не менее, вы почти всегда счастливы с отдельными потоками. Один блокирует чтение stdin, другой делает, где бы вы его не хотели заблокировать.

    Здесь добавляется этот ответ, поскольку он обеспечивает возможность установки неблокирующих каналов в Windows и Unix.

    Все детали ctypes благодаря ответу @ techtonik .

    Существует небольшая версия, которая будет использоваться как в Unix, так и в Windows.

    • Совместимость с Python3 (требуется только небольшое изменение) .
    • Включает posix-версию и определяет исключение для использования для обоих.

    Таким образом, вы можете использовать ту же функцию и исключение для кода Unix и Windows.

     # pipe_non_blocking.py (module) """ Example use: p = subprocess.Popen( command, stdout=subprocess.PIPE, ) pipe_non_blocking_set(p.stdout.fileno()) try: data = os.read(p.stdout.fileno(), 1) except PortableBlockingIOError as ex: if not pipe_non_blocking_is_error_blocking(ex): raise ex """ __all__ = ( "pipe_non_blocking_set", "pipe_non_blocking_is_error_blocking", "PortableBlockingIOError", ) import os if os.name == "nt": def pipe_non_blocking_set(fd): # Constant could define globally but avoid polluting the name-space # thanks to: https://stackoverflow.com/questions/34504970 import msvcrt from ctypes import windll, byref, wintypes, WinError, POINTER from ctypes.wintypes import HANDLE, DWORD, BOOL LPDWORD = POINTER(DWORD) PIPE_NOWAIT = wintypes.DWORD(0x00000001) def pipe_no_wait(pipefd): SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD] SetNamedPipeHandleState.restype = BOOL h = msvcrt.get_osfhandle(pipefd) res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None) if res == 0: print(WinError()) return False return True return pipe_no_wait(fd) def pipe_non_blocking_is_error_blocking(ex): if not isinstance(ex, PortableBlockingIOError): return False from ctypes import GetLastError ERROR_NO_DATA = 232 return (GetLastError() == ERROR_NO_DATA) PortableBlockingIOError = OSError else: def pipe_non_blocking_set(fd): import fcntl fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) return True def pipe_non_blocking_is_error_blocking(ex): if not isinstance(ex, PortableBlockingIOError): return False return True PortableBlockingIOError = BlockingIOError 

    Чтобы избежать чтения неполных данных, я закончил писать собственный генератор readline (который возвращает строку байтов для каждой строки).

    Его генератор, чтобы вы могли, например …

     def non_blocking_readlines(f, chunk=1024): """ Iterate over lines, yielding b'' when nothings left or when new data is not yet available. stdout_iter = iter(non_blocking_readlines(process.stdout)) line = next(stdout_iter) # will be a line or b''. """ import os from .pipe_non_blocking import ( pipe_non_blocking_set, pipe_non_blocking_is_error_blocking, PortableBlockingIOError, ) fd = f.fileno() pipe_non_blocking_set(fd) blocks = [] while True: try: data = os.read(fd, chunk) if not data: # case were reading finishes with no trailing newline yield b''.join(blocks) blocks.clear() except PortableBlockingIOError as ex: if not pipe_non_blocking_is_error_blocking(ex): raise ex yield b'' continue while True: n = data.find(b'\n') if n == -1: break yield b''.join(blocks) + data[:n + 1] data = data[n + 1:] blocks.clear() blocks.append(data) 

    Я создал библиотеку на основе решения JF Sebastian . Вы можете использовать его.

    https://github.com/cenkalti/what

    Работая с ответом Дж. Ф. Себастьяна и несколькими другими источниками, я собрал простой менеджер подпроцессов. Он обеспечивает неблокирующее чтение запроса, а также выполняет несколько процессов параллельно. Он не использует какой-либо OS-специфический вызов (который я знаю) и, следовательно, должен работать в любом месте.

    Это доступно от pypi, так что просто pip install shelljob . См. Страницу проекта для примеров и полных документов.

    EDIT: эта реализация по-прежнему блокируется. Вместо этого используйте ответ JFSebastian.

    Я попробовал верхний ответ , но дополнительный риск и обслуживание кода потока были тревожными.

    Просматривая модуль io (и ограничиваясь 2.6), я нашел BufferedReader. Это мое безболезненное, неблокирующее решение.

     import io from subprocess import PIPE, Popen p = Popen(['myprogram.exe'], stdout=PIPE) SLEEP_DELAY = 0.001 # Create an io.BufferedReader on the file descriptor for stdout with io.open(p.stdout.fileno(), 'rb', closefd=False) as buffer: while p.poll() == None: time.sleep(SLEEP_DELAY) while '\n' in bufferedStdout.peek(bufferedStdout.buffer_size): line = buffer.readline() # do stuff with the line # Handle any remaining output after the process has ended while buffer.peek(): line = buffer.readline() # do stuff with the line 

    Я недавно наткнулся на ту же проблему, что мне нужно прочитать одну строку во времени из потока (хвост в подпроцессе) в неблокирующем режиме. Я хотел избежать следующих проблем: не записывать процессор, не читать поток на один байт ( например, readline) и т. д.

    Вот моя реализация https://gist.github.com/grubberr/5501e1a9760c3eab5e0a, она не поддерживает окна (опрос), не обрабатывает EOF, но она работает для меня хорошо

    зачем беспокоить поток и очередь? в отличие от readline (), BufferedReader.read1 () не блокирует ожидание \ r \ n, он возвращает ASAP, если есть выход.

     #!/usr/bin/python from subprocess import Popen, PIPE, STDOUT import io def __main__(): try: p = Popen( ["ping", "-n", "3", "127.0.0.1"], stdin=PIPE, stdout=PIPE, stderr=STDOUT ) except: print("Popen failed"); quit() sout = io.open(p.stdout.fileno(), 'rb', closefd=False) while True: buf = sout.read1(1024) if len(buf) == 0: break print buf, if __name__ == '__main__': __main__() 

    In my case I needed a logging module that catches the output from the background applications and augments it(adding time-stamps, colors, etc.).

    I ended up with a background thread that does the actual I/O. Following code is only for POSIX platforms. I stripped non-essential parts.

    If someone is going to use this beast for long runs consider managing open descriptors. In my case it was not a big problem.

     # -*- python -*- import fcntl import threading import sys, os, errno import subprocess class Logger(threading.Thread): def __init__(self, *modules): threading.Thread.__init__(self) try: from select import epoll, EPOLLIN self.__poll = epoll() self.__evt = EPOLLIN self.__to = -1 except: from select import poll, POLLIN print 'epoll is not available' self.__poll = poll() self.__evt = POLLIN self.__to = 100 self.__fds = {} self.daemon = True self.start() def run(self): while True: events = self.__poll.poll(self.__to) for fd, ev in events: if (ev&self.__evt) != self.__evt: continue try: self.__fds[fd].run() except Exception, e: print e def add(self, fd, log): assert not self.__fds.has_key(fd) self.__fds[fd] = log self.__poll.register(fd, self.__evt) class log: logger = Logger() def __init__(self, name): self.__name = name self.__piped = False def fileno(self): if self.__piped: return self.write self.read, self.write = os.pipe() fl = fcntl.fcntl(self.read, fcntl.F_GETFL) fcntl.fcntl(self.read, fcntl.F_SETFL, fl | os.O_NONBLOCK) self.fdRead = os.fdopen(self.read) self.logger.add(self.read, self) self.__piped = True return self.write def __run(self, line): self.chat(line, nl=False) def run(self): while True: try: line = self.fdRead.readline() except IOError, exc: if exc.errno == errno.EAGAIN: return raise self.__run(line) def chat(self, line, nl=True): if nl: nl = '\n' else: nl = '' sys.stdout.write('[%s] %s%s' % (self.__name, line, nl)) def system(command, param=[], cwd=None, env=None, input=None, output=None): args = [command] + param p = subprocess.Popen(args, cwd=cwd, stdout=output, stderr=output, stdin=input, env=env, bufsize=0) p.wait() ls = log('ls') ls.chat('go') system("ls", ['-l', '/'], output=ls) date = log('date') date.chat('go') system("date", output=date) 

    This is a example to run interactive command in subprocess, and the stdout is interactive by using pseudo terminal. You can refer to: https://stackoverflow.com/a/43012138/3555925

     #!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import select import termios import tty import pty from subprocess import Popen command = 'bash' # command = 'docker run -it --rm centos /bin/bash'.split() # save original tty setting then set it to raw mode old_tty = termios.tcgetattr(sys.stdin) tty.setraw(sys.stdin.fileno()) # open pseudo-terminal to interact with subprocess master_fd, slave_fd = pty.openpty() # use os.setsid() make it run in a new process group, or bash job control will not be enabled p = Popen(command, preexec_fn=os.setsid, stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, universal_newlines=True) while p.poll() is None: r, w, e = select.select([sys.stdin, master_fd], [], []) if sys.stdin in r: d = os.read(sys.stdin.fileno(), 10240) os.write(master_fd, d) elif master_fd in r: o = os.read(master_fd, 10240) if o: os.write(sys.stdout.fileno(), o) # restore tty settings back termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) 

    My problem is a bit different as I wanted to collect both stdout and stderr from a running process, but ultimately the same since I wanted to render the output in a widget as its generated.

    I did not want to resort to many of the proposed workarounds using Queues or additional Threads as they should not be necessary to perform such a common task as running another script and collecting its output.

    After reading the proposed solutions and python docs I resolved my issue with the implementation below. Yes it only works for POSIX as I'm using the select function call.

    I agree that the docs are confusing and the implementation is awkward for such a common scripting task. I believe that older versions of python have different defaults for Popen and different explanations so that created a lot of confusion. This seems to work well for both Python 2.7.12 and 3.5.2.

    The key was to set bufsize=1 for line buffering and then universal_newlines=True to process as a text file instead of a binary which seems to become the default when setting bufsize=1 .

     class workerThread(QThread): def __init__(self, cmd): QThread.__init__(self) self.cmd = cmd self.result = None ## return code self.error = None ## flag indicates an error self.errorstr = "" ## info message about the error def __del__(self): self.wait() DEBUG("Thread removed") def run(self): cmd_list = self.cmd.split(" ") try: cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None , universal_newlines=True , stderr=subprocess.PIPE , stdout=subprocess.PIPE) except OSError: self.error = 1 self.errorstr = "Failed to execute " + self.cmd ERROR(self.errorstr) finally: VERBOSE("task started...") import select while True: try: r,w,x = select.select([cmd.stdout, cmd.stderr],[],[]) if cmd.stderr in r: line = cmd.stderr.readline() if line != "": line = line.strip() self.emit(SIGNAL("update_error(QString)"), line) if cmd.stdout in r: line = cmd.stdout.readline() if line == "": break line = line.strip() self.emit(SIGNAL("update_output(QString)"), line) except IOError: pass cmd.wait() self.result = cmd.returncode if self.result < 0: self.error = 1 self.errorstr = "Task terminated by signal " + str(self.result) ERROR(self.errorstr) return if self.result: self.error = 1 self.errorstr = "exit code " + str(self.result) ERROR(self.errorstr) return return 

    ERROR, DEBUG and VERBOSE are simply macros that print output to the terminal.

    This solution is IMHO 99.99% effective as it still uses the blocking readline function, so we assume the sub process is nice and outputs complete lines.

    I welcome feedback to improve the solution as I am still new to Python.

    Here is a module that supports non-blocking reads and background writes in python:

    https://pypi.python.org/pypi/python-nonblock

    Provides a function,

    nonblock_read which will read data from the stream, if available, otherwise return an empty string (or None if the stream is closed on the other side and all possible data has been read)

    You may also consider the python-subprocess2 module,

    https://pypi.python.org/pypi/python-subprocess2

    which adds to the subprocess module. So on the object returned from "subprocess.Popen" is added an additional method, runInBackground. This starts a thread and returns an object which will automatically be populated as stuff is written to stdout/stderr, without blocking your main thread.

    Наслаждайтесь!

    Python - лучший язык программирования в мире.