Блоки подпроцесса Python во встроенной среде

Мой скрипт принимает на вход большое количество файлов, вызывая внешние программы с помощью subprocess.call(). Он отлично работает на обычных консолях ОС, но зависает после обработки примерно 10 файлов при работе во встроенной среде с использованием встроенного Python 2.7.x.

Я ссылался на различные подобные вопросы и не смог найти ни одного, работающего на меня:

Python: subprocess.Popen и subprocess.call зависают

Подпроцесс Python зависает

Вызов подпроцесса Python зависает

и это подробное обсуждение: https://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

Все они подразумевают уязвимый буферизованный PIPE и предлагают использовать файловые объекты для stdout и stderr. Поэтому я также добавил временный текстовый файл, открыл его и скормил stdout и stderr из subprocess.call(). Это тоже не сработало.

Мой старый код довольно прост:

# script1.py

folder = "/path/to/my/folder"
for root, dirs, files in os.walk(folder):
    for file in files:
        path = join(root, file)
        try:
            cmd = ['dir', path, '1>&2']
            _logger.debug(' '.join(cmd))
            completed = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as err:
            _logger.debug(err)
        else:
            _logger.debug('returncode: {}'.format(completed))
print('all done!!')

основной скрипт:

try:
    cmd = ['python', 'script1.py', '1>&2']
    ue.log(' '.join(cmd))
    completed = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
    _logger.error(err)

Я смешал и сопоставил пару решений stdout/stderr. в том числе добавление:

with open(join(_script_dir, 'tmp.txt'), 'w') as tmp:
    #
    # old code
    #
     completed = subprocess.call(cmd, shell=True, stdout=tmp, stderr=tmp)

Ни один не работал.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
165
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В итоге я выбросил все каналы и stdout/stderr и использовал аргументы subprocess.Popen() Python 3.7.2 по умолчанию, то есть None для stdout/stderr, и удалил 1>&2. Так что в основном просто не утруждайте себя получением вывода другой программы в реальном времени, когда программа работает с большим объемом ввода и потенциально генерирует огромную волну вывода через узкий и скрытый канал. Я перешел к использованию только лог-файлов, написанных для каждого отдельного скрипта.

Я гораздо более доволен этим решением, чем слепо пробовать всевозможные трюки UNIX для решения проблемы канала в Windows.

P.S., Некоторые ответы из моих приведенных выше ссылок предложили shell=False, я обнаружил, что это раздражает тем, что встроенная среда будет порождать множество плавающих окон консоли из каждого subprocess.call().

Есть решение проблемы с висящей трубой, которое заключается в использовании from multiprocessing import Manager Вы настраиваете очередь задач:

mgr = Manager()

task_queue = mgr.Queue()

Передайте его процессу в качестве аргумента:

gmat_args.append([gmat_arg, task_queue])
...
pool = Pool(processes=nrunp, maxtasksperchild=20)
...
results = pool.map(run_gmat, gmat_args, chunksize=ninstances)

Процесс пишет в пул:

def run_gmat(args):
    q = args[1]

    scriptname = os.path.basename(args[0])

    proc = sp.Popen(['gmat', '-m', '-ns', '-x', '-r', str(args[0])])

    (outs, errors) = proc.communicate(timeout=cpto)

    outs = outs.decode('UTF-8')

    q.put(filter_outs(outs, scriptname))

Затем, вернувшись в основной процесс, вы читаете очередь и регистрируете ее:

while 1:

    qout = task_queue.get(cpto)   

    logging.info(qout)

    if task_queue.qsize() < 1:

        break

Я не могу сказать, что это идеально, я выполняю более 4000 заданий и получаю 2-3 тайм-аута, которые, похоже, связаны с файловым вводом-выводом (задание записывает массивный файл отчета по завершении). Чтобы предотвратить зависание, я ловлю исключение тайм-аута, записываю последний stdout, stderr в очередь и убиваю задание. Я теряю файлы отчетов, но я вижу тайм-аут в журнале из очереди, так что это просто вопрос повторного запуска этих 2-3 заданий.

Другие вопросы по теме