Как установить shell=True для Subprocess.run с исполнителем потоков пула concurrent.future

Я пытаюсь использовать многопоточность concurrent.future в Python с subprocess.run для запуска внешнего скрипта Python. Но у меня есть некоторые проблемы с частью shell=True subprocess.run().

Вот пример внешнего кода, назовем его test.py:

#! /usr/bin/env python3

import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-x', '--x_nb', required=True, help='the x number')
    parser.add_argument('-y', '--y_nb', required=True, help='the y number')
    args = parser.parse_args()

    print('result is {} when {} multiplied by {}'.format(int(args.x_nb) * int(args.y_nb),
                                                         args.x_nb,
                                                         args.y_nb))

В моем основном скрипте Python у меня есть:

#! /usr/bin/env python3

import subprocess
import concurrent.futures
import threading
...

args_list = []
for i in range(10):
    cmd = './test.py -x {} -y 2 '.format(i)
    args_list.append(cmd)

# just as an example, this line works fine
subprocess.run(args_list[0], shell=True)

# this multithreading is not working       
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
    executor.map(subprocess.run, args_list)

Проблема здесь в том, что я не могу передать опцию shell=True в executor.map.

Я уже пробовал безуспешно:

args_list = []
for i in range(10):
    cmd = './test.py -x {} -y 2 '.format(i)
    args_list.append((cmd, eval('shell=True'))

or

args_list = []
for i in range(10):
    cmd = './test.py -x {} -y 2 '.format(i)
    args_list.append((cmd, 'shell=True'))

Кто-нибудь знает, как решить эту проблему?


person Mesmer    schedule 05.06.2019    source источник


Ответы (1)


Я не думаю, что метод map может напрямую вызывать функцию с аргументами ключевого слова, но есть 2 простых решения вашей проблемы.

Решение 1. Используйте лямбда-выражение, чтобы установить дополнительный аргумент ключевого слова, который вы хотите

Лямбда — это, по сути, небольшая функция, которая вызывает вашу реальную функцию, передавая аргументы. Это хорошее решение, если аргументы ключевого слова фиксированы.

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
    executor.map(lambda args: subprocess.run(args, shell=True), args_list)

Решение 2. Используйте executor.submit для отправки функций исполнителю

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

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
    for args in args_list:
        executor.submit(subprocess.run, args, shell=True)
person Mark A    schedule 07.06.2019
comment
Большое спасибо Марк! Ваше решение с циклом и submit работает просто отлично. Я также попробовал функцию lambda, но в моем случае, который немного сложнее, чем в примере, при условии, что я не смог добиться желаемого результата. - person Mesmer; 08.06.2019