2012-01-23 6 views
10

Есть ли способ сделать вызов подпроцесса в 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) 

, где я могу оставить этот процесс открытым, я действительно ценю это.

ответ

24

Вы можете используйте 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, если данные, которые вы получили до сих пор, - это весь вывод, который вы ожидаете получить.

+1

Спасибо! Мне нравится ваше решение лучше всего, так как оно не требует загрузки сторонних производителей. К сожалению, это не работает для меня. После нескольких попыток, я уверен, что это проблема с программой java, которую я вызываю, а не с вашим решением, поэтому ваше решение хорошо. – JasonMond

+0

Почему голос? –

+0

Это было по ошибке. Мой upvote неактивен, пока ничего не будет отредактировано, но я не вижу ничего, чтобы улучшить или не повредить. Отличный ответ. – hynekcer

1

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

myprocess.stdin.write(text) 

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

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

К сожалению, это не сработает для меня, потому что это java-программа, которая ест около 3 ГБ памяти при каждом запуске. Вот почему так долго загружаться. У меня не может быть 5000 экземпляров 3G-процесса. – JasonMond

+0

Думаю, я понимаю. После ввода входного текста он выводит что-то, а затем выходит? или он ждет, пока вы не введете что-нибудь еще. –

+0

Он выдает затем выходы. – JasonMond

5

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

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

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

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

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

Edit: Вот quick'n'dirty пример того, как асинхронный модуль подпроцесс может быть использован:

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 
-2
if os.name == 'nt': 
startupinfo = subprocess.STARTUPINFO() 
startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW 
subprocess.call(os.popen(tempFileName), shell=True) 
os.remove(tempFileName) 
Смежные вопросы