Как сделать модуль Python с функцией для параллельного запуска инструментов командной строки (без использования if __name__ == '__main__': так что его можно импортировать)? - PullRequest
0 голосов
/ 31 декабря 2018

Я хотел создать модуль python с удобной функцией для параллельного запуска команд с использованием Python 3.7 в Windows.(для команд az cli)

Я хотел создать функцию, которая бы:

  • была простой в использовании: просто передайте список команд в виде строк, и они будут выполняться параллельно.
  • Позвольте мне посмотреть вывод, сгенерированный командами.
  • Используемая сборка в библиотеках python
  • Работает одинаково хорошо в Windows и Linux (многопроцессорная обработка Python использует fork (), иВ Windows нет функции fork (), поэтому иногда многопроцессорный код будет работать в Linux, но не в Windows.)
  • Может быть преобразован в импортируемый модуль для большего удобства.

Это было удивительно сложно, я думаю, возможно, раньше это было невозможно в старых версиях python?(Я видел несколько вопросов по 2-8 лет, в которых говорилось, что вы должны использовать if __name__==__main__: для запуска параллельной обработки, но я обнаружил, что это не сработало предсказуемо, когда дело дошло до создания импортируемого модуля.

def removeExtraLinesFromString(inputstring):
    stringtoreturn = ""
    for line in inputstring.split("\n"):
        if len(line.strip()) > 0: #Only add non empty lines to the stringtoreturn
            stringtoreturn = stringtoreturn + line
    return stringtoreturn


def runCmd(cmd): #string of a command passed in here
    from subprocess import run, PIPE
    stringtoreturn = str( run(cmd, shell=True, stdout=PIPE).stdout.decode('utf-8') )
    stringtoreturn = removeExtraLinesFromString(stringtoreturn)
    return stringtoreturn


def exampleOfParrallelCommands():
    if __name__ == '__main__': #I don't like this method, because it doesn't work when imported, refractoring attempts lead to infinite loops and unexpected behavior.
        from multiprocessing import Pool
        cmd = "python -c \"import time;time.sleep(5);print('5 seconds have passed')\""
        cmds = []
        for i in range(12):  #If this were running in series it'd take at least a minute to sleep 5 seconds 12 times
            cmds.append(cmd)
        with Pool(processes=len(cmds)) as pool:
            results = pool.map(runCmd, cmds) #results is a list of cmd output
        print(results[0])
        print(results[1])
        return results

Когда я попытался импортировать это как модуль, он не работал (делает так из-за оператора if), поэтому я попытался переписать код, чтобы переместить оператор if, я думаю, что однажды удалил его, что вызваломой компьютер должен зацикливаться до тех пор, пока я не закрою программу. В другой раз я смог импортировать модуль в другую программу на python, но чтобы это работало, мне пришлось добавить __name__ == '__main__', и это очень интуитивно понятно.

Я почти сдался, но после 2 дней поиска по тоннам веб-сайтов и публикаций на Python я наконец-то понял, как это сделать, увидев код пользователя jfs в этом вопросе-ответе ( Python: параллельный запуск подпроцесса cat * )Я изменил его код, чтобы он лучше подходил к ответу на мой вопрос.

1 Ответ

0 голосов
/ 31 декабря 2018

toolbox.py

def removeExtraLinesFromString(inputstring):
    stringtoreturn = ""
    for line in inputstring.split("\n"):
        if len(line.strip()) > 0: #Only add non empty lines to the stringtoreturn
            stringtoreturn = stringtoreturn + line
    return stringtoreturn


def runCmd(cmd): #string of a command passed in here
    from subprocess import run, PIPE
    stringtoreturn = str( run(cmd, shell=True, stdout=PIPE).stdout.decode('utf-8') )
    stringtoreturn = removeExtraLinesFromString(stringtoreturn)
    return stringtoreturn


def runParallelCmds(listofcommands): 
    from multiprocessing.dummy import Pool #thread pool
    from subprocess import Popen, PIPE, STDOUT
    listofprocesses = [Popen(listofcommands[i], shell=True,stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) for i in range(len(listofcommands))] 
    #Python calls this list comprehension, it's a way of making a list
    def get_outputs(process): #MultiProcess Thread Pooling require you to map to a function, thus defining a function.
        return process.communicate()[0] #process is object of type subprocess.Popen
    outputs = Pool(len(listofcommands)).map(get_outputs, listofprocesses) #outputs is a list of bytes (which is a type of string)
    listofoutputstrings = []
    for i in range( len(listofcommands) ):
        outputasstring = removeExtraLinesFromString(  outputs[i].decode('utf-8')  ) #.decode('utf-8') converts bytes to string
        listofoutputstrings.append( outputasstring )
    return listofoutputstrings

main.py

from toolbox import runCmd #(cmd)
from toolbox import runParallelCmds #(listofcommands)

listofcommands = []
cmd = "ping -n 2 localhost"
listofcommands.append(cmd)
cmd = "python -c \"import time;time.sleep(5);print('5 seconds have passed')\""
for i in range(12):
    listofcommands.append(cmd) # If 12 processes each sleep 5 seconds, this taking less than 1 minute proves parrallel processing

outputs = runParallelCmds(listofcommands)
print(outputs[0])
print(outputs[1])

вывод:

Pinging neokylesPC [:: 1] с 32 байтами данных: Ответ от :: 1: время <1 мс. Ответ от :: 1: время <1 мс. Статистика пинга для :: 1: Пакеты: Отправлено = 2, Получено = 2, Потерянно = 0 (потеря 0%), Приблизительное время приема-передачи вмиллисекунды: минимум = 0 мс, максимум = 0 мс, среднее значение = 0 мс </p>

5 секунд прошло

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...