Постоянный подпроцесс python - PullRequest
10 голосов
/ 24 января 2012

Есть ли способ сделать вызов подпроцесса в python "постоянным"? Я вызываю программу, которая загружается несколько раз. Поэтому было бы здорово, если бы я мог оставить эту программу открытой и общаться с ней, не убивая ее.

Мультипликационная версия моего скрипта на python выглядит так:

for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myoutputtext, err = myprocess.communicate(input=text)

Мне нужно обрабатывать каждый текст отдельно, поэтому объединять его в один большой текстовый файл и обрабатывать его один раз невозможно.

Предпочтительно, если есть такая опция

myprocess = subprocess.Popen(["myexecutable"],
            stdin = subprocess.PIPE, stdout = subprocess.PIPE,
            stderr = None)    for text in textcollection:
for text in textcollection:
    myoutputtext, err = myprocess.communicate(input=text)

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

Ответы [ 4 ]

25 голосов
/ 24 января 2012

Вы можете использовать myprocess.stdin.write() и myprocess.stdout.read() для связи с вашим подпроцессом, вам просто нужно быть осторожным, чтобы убедиться, что вы правильно обрабатываете буферизацию, чтобы предотвратить блокировку ваших вызовов.

Если вывод из вашегоПодпроцесс хорошо определен, вы должны иметь возможность надежно общаться с ним, используя буферизацию строки и myprocess.stdout.readline().

Вот пример:

>>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> p.stdin.write('hello world\n')
>>> p.stdout.readline()
'hello world\n'
>>> p.stdout.readline()        # THIS CALL WILL BLOCK

Альтернатива этому методу дляUnix должен перевести дескриптор файла в неблокирующий режим, который позволит вам вызывать такие функции, как myprocess.stdout.read() и возвращать данные, если таковые имеются, или вызывать IOError, если данных нет:

>>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> import fcntl, os
>>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
0
>>> p.stdout.read()         # raises an exception instead of blocking
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 11] Resource temporarily unavailable

Это позволит вам сделать что-то вроде этого:

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
for text in textcollection:
    myprocess.stdin.write(text + '\n')
    while True:
        myoutputtext = ''
        try:
            myoutputtext += myprocess.stdout.read()
        except IOError:
            pass
        if validate_output(myoutputtext):
            break
        time.sleep(.1)    # short sleep before attempting another read

В этом примере validate_output() - это функция, которую вам нужно написать, которая возвращает True, если данные, которые выдо сих пор получил весь результат, который вы ожидаете получить.

5 голосов
/ 24 января 2012

Это вызов communicate(), который убивает ваш подпроцесс.Согласно документации для подпроцесса метод communicate() будет:

Взаимодействовать с процессом: отправлять данные в stdin.Читайте данные из stdout и stderr, пока не будет достигнут конец файла.Дождитесь завершения процесса.

То, что вы хотите сделать, это напрямую взаимодействовать со свойствами stdin и stdout объекта POpen, чтобы напрямую взаимодействовать с подпроцессом.Тем не менее, в документации не рекомендуется использовать следующее высказывание:

Предупреждение. Используйте функцию connect () вместо .stdin.write, .stdout.read или .stderr.read, чтобы избежать взаимных блокировок из-за любой другой ОС.конвейерные буферы заполняют и блокируют дочерний процесс.

Таким образом, вам нужно либо внедрить собственные обходные пути для потенциальных тупиков, либо надеяться, что кто-то написал для вас модуль асинхронного подпроцесса .

Редактировать: Вот краткий пример того, как можно использовать модуль асинхронного подпроцесса:

import asyncsubprocess

textcollection = ['to', 'be', 'or', 'not', 'to be', 'that is the', 'question']

myprocess = asyncsubprocess.Popen(["cat"],
     stdin = asyncsubprocess.PIPE,
     stdout = asyncsubprocess.PIPE,
     stderr = None)

for text in textcollection:
    bytes_sent, myoutput, err = myprocess.listen(text)
    print text, bytes_sent, myoutput, err

Когда я запускаю это, он печатает:

to 2 to 
be 2 be 
or 2 or 
not 3 not 
to be 5 to be 
that is the 11 that is the 
question 8 question 
1 голос
/ 24 января 2012

Я думаю, что вы ищете

myprocess.stdin.write(text)

Вы можете создать список Popens, а затем вызывать общаться для каждого элемента в другом цикле. как то так

processes=[]
for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myprocess.stdin.write(text)
    processes.append(myprocess)

for proc in processes:
    myoutput, err=proc.communicate()
    #do something with the output here

таким образом, не нужно будет ждать, пока все попы не запустятся

0 голосов
/ 19 сентября 2013
if os.name == 'nt':
 startupinfo = subprocess.STARTUPINFO()
 startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
 subprocess.call(os.popen(tempFileName), shell=True)
 os.remove(tempFileName)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...