Я использую модуль подпроцесса, чтобы запустить подпроцесс и подключиться к его выходному потоку (stdout). Я хочу иметь возможность выполнять неблокирующие чтения на своем стандартном выводе. Есть ли способ сделать .readline неблокирующим или проверить, есть ли данные в потоке, прежде чем я вызову .readline
? Я хотел бы, чтобы это было переносимо или, по крайней мере, работало под Windows и Linux.
вот как я делаю это сейчас (это блокирует, .readline
если нет доступных данных):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Матье Паге
источник
источник
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Ответы:
fcntl
,select
,asyncproc
Не поможет в этом случае.Надежный способ чтения потока без блокировки независимо от операционной системы заключается в использовании
Queue.get_nowait()
:источник
out.readline
блокировка потока и основного потока, и мне приходится ждать, пока readline вернется, прежде чем все остальное продолжится. Есть ли простой способ обойти это? (Я читаю несколько строк из моего процесса, который также является еще одним .py файлом, который выполняет DB и все такое)shelljob
pypi.python.org/pypi/shelljobУ меня часто была похожая проблема; Программы на Python, которые я пишу часто, должны иметь возможность выполнять некоторые основные функции, одновременно принимая пользовательский ввод из командной строки (stdin). Простое помещение функции обработки пользовательского ввода в другой поток не решает проблему, поскольку
readline()
блокирует и не имеет времени ожидания. Если основная функциональность завершена и больше нет необходимости ждать дальнейшего ввода данных пользователем, я обычно хочу, чтобы моя программа завершала работу, но это не может быть, потому чтоreadline()
все еще блокируется в другом потоке, ожидая строки. Решение, которое я нашел для этой проблемы, состоит в том, чтобы сделать stdin неблокирующим файлом с помощью модуля fcntl:На мой взгляд, это немного чище, чем использование модулей select или signal для решения этой проблемы, но опять же, это работает только в UNIX ...
источник
buffer_size
определяется как?Python 3.4 представляет новый временный API для асинхронного IO-
asyncio
модуля .Подход похож на
twisted
ответ на основе @Bryan Ward - определите протокол, и его методы вызываются, как только данные готовы:См. «Подпроцесс» в документации .
Существует высокоуровневый интерфейс,
asyncio.create_subprocess_exec()
который возвращаетProcess
объекты, которые позволяют асинхронно читать строки, используяStreamReader.readline()
сопрограмму (с синтаксисомasync
/await
Python 3.5+ ):readline_and_kill()
выполняет следующие задачи:Каждый шаг может быть ограничен тайм-аутом секунд, если это необходимо.
источник
print(text, flush=True)
так, чтобы напечатанный текст был немедленно доступен для вызова наблюдателяreadline
. Когда я тестировал его с помощью исполняемого файла на Фортране, я на самом деле хочу обернуть / посмотреть, он не буферизует вывод, поэтому он ведет себя как ожидалось.readline_and_kill
во втором сценарии работает очень похожеsubprocess.comunicate
на то, что завершает процесс после одной операции чтения / записи. Я также вижу, что вы используете один каналstdout
, который подпроцесс обрабатывает как неблокирующий. Пытаясь использовать оба,stdout
иstderr
я обнаружил, что в конечном итоге блокирует .Попробуйте модуль asyncproc . Например:
Модуль заботится обо всех потоках, как это было предложено S.Lott.
источник
Вы можете сделать это очень легко в Twisted . В зависимости от существующей кодовой базы это может быть не так просто использовать, но если вы создаете скрученное приложение, такие вещи становятся почти тривиальными. Вы создаете
ProcessProtocol
класс и переопределяетеoutReceived()
метод. Twisted (в зависимости от используемого реактора) обычно представляет собой большойselect()
цикл с обратными вызовами, установленными для обработки данных из различных файловых дескрипторов (часто сетевых сокетов). Таким образом,outReceived()
метод просто устанавливает обратный вызов для обработки данных, поступающих изSTDOUT
. Простой пример, демонстрирующий это поведение:В документации Twisted есть хорошая информация об этом.
Если вы строите все свое приложение на Twisted, оно делает асинхронную связь с другими процессами, локальными или удаленными, действительно элегантной. С другой стороны, если ваша программа построена не на Twisted, это не очень полезно. Надеемся, что это может быть полезно для других читателей, даже если это не применимо для вашего конкретного приложения.
источник
select
не должен работать на окнах с файловыми дескрипторами, в соответствии с документамиselect()
он имеет в виду то же, что и ты. Я предполагаю это, потому чтоTwisted
работает на окнах ...asyncio
из stdlib .select()
них является наиболее переносимым для юниксов и юникс-лайков, но для Windows также доступны два реактора: twistedmatrix.com/documents/current/core/howto/…Используйте select & read (1).
Для readline () - как:
источник
select
не должен работать на окнах с файловыми дескрипторами, в соответствии с документамиproc.stdout.read()
независимо от того, насколько мал аргумент блокирующий вызов.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Одним из решений является создание другого процесса для выполнения чтения процесса или создание потока процесса с таймаутом.
Вот многопоточная версия функции тайм-аута:
http://code.activestate.com/recipes/473878/
Тем не менее, вам нужно прочитать стандартный вывод по мере его поступления? Другим решением может быть выгрузка вывода в файл и ожидание завершения процесса с помощью p.wait () .
источник
Отказ от ответственности: это работает только для торнадо
Вы можете сделать это, установив fd как неблокирующее, а затем использовать ioloop для регистрации обратных вызовов. Я упаковал это в яйцо под названием tornado_subprocess, и вы можете установить его через PyPI:
Теперь вы можете сделать что-то вроде этого:
Вы также можете использовать его с RequestHandler
источник
threading.Thread
для создания новых неблокирующих процессов? Я использовал его вon_message
экземпляре Tornado Websocket, и он отлично справился со своей задачей.select
с файловыми дескрипторами этого не происходит )select
вызов. Я не пробовал это под Windows, но вы, вероятно, столкнетесь с проблемами, так как библиотека используетfcntl
модуль. Итак, вкратце: нет, это, вероятно, не будет работать под Windows.Существующие решения не работают для меня (подробности ниже). Наконец, работало, чтобы реализовать readline с использованием read (1) (основываясь на этом ответе ). Последний не блокирует:
Почему существующие решения не работают:
источник
q.get_nowait()
от моего ответа никогда не должны блокировать, вот в чем смысл его использования. 2. Поток, который выполняет readline (enqueue_output()
функцию ), завершается в EOF, например, включая случай, когда процесс, производящий вывод, завершается. Если вы верите, что это не так; Пожалуйста, предоставьте полный пример минимального кода, который показывает иначе (возможно, как новый вопрос ).dcmpid = myprocess
.Вот мой код, используемый для перехвата всех выходных данных подпроцесса ASAP, включая частичные строки. Он качает одновременно и stdout и stderr в почти правильном порядке.
Протестировано и правильно работает на Python 2.7 Linux & Windows.
источник
Я добавляю эту проблему для чтения некоторого подпроцесса. Откройте стандартный вывод. Вот мое не блокирующее решение для чтения:
источник
msvcrt.kbhit()
вместоЭта версия неблокируемого чтения не требует специальных модулей и будет работать "из коробки" на большинстве дистрибутивов Linux.
источник
Вот простое решение на основе потоков, которое:
select
).stdout
иstderr
асинхронно.asyncio
(что может конфликтовать с другими библиотеками).printer.py
reader.py
источник
Добавление этого ответа здесь, поскольку он предоставляет возможность устанавливать неблокирующие каналы в Windows и Unix.
Все
ctypes
детали благодаря ответу @ techtonik .Существует немного измененная версия, которая будет использоваться как в системах Unix, так и в Windows.
Таким образом, вы можете использовать ту же функцию и исключение для кода Unix и Windows.
Чтобы избежать чтения неполных данных, я написал собственный генератор readline (который возвращает строку байтов для каждой строки).
Это генератор, так что вы можете, например ...
источник
readline()
не работает с неблокирующими каналами (такими как set usingfcntl
) на Python 2 - как вы думаете, это больше не правильно? (мой ответ содержит ссылку (fcntl
), которая предоставляет ту же информацию, но теперь кажется, что она удалена). (2) Посмотрите, какmultiprocessing.connection.Pipe
используетсяSetNamedPipeHandleState
У меня проблема с оригинальным вопросником, но я не хотел вызывать темы. Я смешал решение Джесси с прямым read () из канала и своим собственным обработчиком буфера для чтения строк (однако, мой подпроцесс - ping - всегда записывал полные строки <размер системной страницы). Я избегаю ожидания, занятого только чтением в часах, зарегистрированных в gobject. В эти дни я обычно запускаю код внутри главного цикла gobject, чтобы избежать потоков.
Наблюдатель
И основная программа устанавливает пинг, а затем вызывает gobject mail loop.
Любая другая работа привязана к обратным вызовам в gobject.
источник
В современном Python дела обстоят намного лучше.
Вот простая дочерняя программа "hello.py":
И программа для взаимодействия с ним:
Это распечатывает:
Обратите внимание, что фактический шаблон, который также содержится почти во всех предыдущих ответах, как здесь, так и в связанных вопросах, состоит в том, чтобы установить дескриптор файла дочернего элемента stdout на неблокирующее, а затем опрашивать его в каком-то цикле выбора. В эти дни, конечно, этот цикл предоставляется asyncio.
источник
выберите помогает определить, где находится следующий полезный ввод.
Тем не менее, вы почти всегда счастливее с отдельными темами. Один выполняет блокировку чтения стандартного ввода, другой - везде, где вы не хотите блокировать.
источник
зачем беспокоить поток и очередь? в отличие от readline (), BufferedReader.read1 () не блокирует ожидание \ r \ n, он возвращает как можно скорее, если есть какие-либо выходные данные.
источник
read1
будет блокировать, если первые нижележащие блоки чтения, что происходит, когда канал еще открыт, но вход недоступен.В моем случае мне понадобился модуль журналирования, который ловит выходные данные фоновых приложений и увеличивает их (добавляя метки времени, цвета и т. Д.).
Я закончил с фоновым потоком, который делает фактический ввод / вывод. Следующий код предназначен только для платформ POSIX. Я снял ненужные детали.
Если кто-то собирается использовать этого зверя на долгое время, подумайте об управлении открытыми дескрипторами. В моем случае это не было большой проблемой.
источник
Моя проблема немного отличается, так как я хотел собрать и stdout, и stderr из запущенного процесса, но, в конечном счете, то же самое, поскольку я хотел визуализировать вывод в виджете как сгенерированный.
Я не хотел прибегать ко многим из предложенных обходных путей с использованием очередей или дополнительных потоков, поскольку они не должны быть необходимы для выполнения такой распространенной задачи, как запуск другого сценария и сбор его выходных данных.
Прочитав предложенные решения и документы по Python, я решил свою проблему с помощью реализации ниже. Да, это работает только для POSIX, так как я использую
select
вызов функции.Я согласен с тем, что документы сбивают с толку, и реализация такой распространенной задачи сценариев неудобна. Я полагаю, что более старые версии python имеют разные значения по умолчанию
Popen
и разные объяснения, что создавало много путаницы. Кажется, это хорошо работает как для Python 2.7.12, так и для 3.5.2.Ключ должен был установить
bufsize=1
буферизацию строки, а затемuniversal_newlines=True
обработать как текстовый файл вместо двоичного файла, который, по-видимому, становится настройкой по умолчаниюbufsize=1
.ERROR, DEBUG и VERBOSE - это просто макросы, которые печатают вывод на терминал.
Это решение ИМХО эффективно на 99,99%, так как оно все еще использует
readline
функцию блокировки , поэтому мы предполагаем, что подпроцесс хорош и выводит полные строки.Я приветствую отзывы, чтобы улучшить решение, так как я все еще новичок в Python.
источник
Я создал библиотеку на основе решения Дж. Ф. Себастьяна . Вы можете использовать это.
https://github.com/cenkalti/what
источник
Опираясь на ответ Дж. Ф. Себастьяна и несколько других источников, я собрал простой менеджер подпроцессов. Он обеспечивает запрос неблокируемого чтения, а также запускает несколько процессов параллельно. Он не использует какой-либо специфичный для ОС вызов (который я знаю) и поэтому должен работать где угодно.
Это доступно от pypi, так просто
pip install shelljob
. Обратитесь к странице проекта для примеров и полных документов.источник
РЕДАКТИРОВАТЬ: эта реализация по-прежнему блокирует. Вместо этого используйте ответ JFSebastian .
Я попробовал лучший ответ , но дополнительный риск и обслуживание кода потока вызывали беспокойство.Просматривая модуль io (и ограничиваясь 2.6), я нашел BufferedReader. Это моё безрезультатное, неблокирующее решение.источник
for line in iter(p.stdout.readline, ""): # do stuff with the line
? Он без потоков (однопоточный) и блокирует, когда ваш код блокируется.Недавно я наткнулся на ту же проблему, мне нужно прочитать одну строку за раз из потока (хвостовой запуск в подпроцессе) в неблокирующем режиме. Я хотел избежать следующих проблем: не записывать процессор, не читать поток одним байтом ( как readline сделал) и т. д.
Вот моя реализация https://gist.github.com/grubberr/5501e1a9760c3eab5e0a, она не поддерживает Windows (опрос), не обрабатывает EOF, но она хорошо работает для меня
источник
timeout
как в вашем решении) и.readline()
читает более чем один байт в то время (bufsize=1
средства от прямой линии -buffered (подходит только для записи)). Какие еще проблемы вы нашли? Ответы только на ссылки не очень полезны.Это пример запуска интерактивной команды в подпроцессе, и стандартный вывод является интерактивным с использованием псевдотерминала. Вы можете обратиться к: https://stackoverflow.com/a/43012138/3555925
источник
Это решение использует
select
модуль для «чтения любых доступных данных» из потока ввода-вывода. Эта функция первоначально блокируется до тех пор, пока данные не станут доступны, но затем считывает только те данные, которые доступны и не блокируются в дальнейшем.Учитывая тот факт, что он использует
select
модуль, это работает только на Unix.Код полностью PEP8-совместимый.
источник
Я также столкнулся с проблемой, описанной Джесси, и решил ее, используя «select», как это сделали Брэдли , Энди и другие, но в режиме блокировки, чтобы избежать петли занятости. Он использует фиктивную трубу как поддельный стандарт. Блоки выбора и ждут, когда будет готов либо стандартный ввод, либо канал. Когда клавиша нажата, stdin разблокирует выбор, и значение клавиши можно получить с помощью read (1). Когда другой поток записывает в канал, канал разблокирует выбор, и это может быть воспринято как указание на то, что потребность в stdin закончена. Вот некоторый ссылочный код:
источник
Попробуйте wexpect , который является альтернативой pexpect для Windows .
источник
В Unix-подобных системах и Python 3.5+ есть
os.set_blocking
именно то, что написано.Это выводит:
С
os.set_blocking
комментариями это:источник
Вот модуль, который поддерживает неблокирующие чтения и фоновые записи в python:
https://pypi.python.org/pypi/python-nonblock
Обеспечивает функцию,
nonblock_read, который будет считывать данные из потока, если он доступен, в противном случае возвращает пустую строку (или None, если поток закрыт с другой стороны и все возможные данные были прочитаны)
Вы также можете рассмотреть модуль python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
который добавляет в модуль подпроцесса. Поэтому к объекту, возвращаемому из «subprocess.Popen», добавляется дополнительный метод runInBackground. Это запускает поток и возвращает объект, который будет автоматически заполняться, когда материал записывается в stdout / stderr, без блокировки вашего основного потока.
Наслаждайтесь!
источник