- Класс Process() модуля multiprocessing в Python.
- Создание отдельного процесса, запуск задачи и получение результата выполнения.
- Синтаксис:
- Параметры:
- Возвращаемое значение:
- Описание:
- Методы объекта Process .
- Process.run() :
- Process.start() :
- Process.join([timeout]) :
- Process.name :
- Process.is_alive() :
- Process.daemon :
- Process.pid :
- Process.exitcode :
- Process.authkey :
- Process.sentinel :
- Process.terminate() :
- Как запускать внешние процессы, используя Python и модуль subprocess
- Функция «run»
- Захват вывода команды: stdout и stderr
- Управление сбоями процесса
- Выполнение процесса в оболочке
- Ограничение времени работы процесса
- Функции call, check_output и check_call
- Работа на более низком уровне с классом Popen
Класс Process() модуля multiprocessing в Python.
Создание отдельного процесса, запуск задачи и получение результата выполнения.
Синтаксис:
Параметры:
Конструктор всегда следует вызывать с ключевыми аргументами.
- group=None — не используется модулем,
- target=None — вызываемый объект (функция),
- name=None — имя процесса,
- args=() — аргументы для target ,
- kwargs=<> — ключевые аргументы для target ,
- daemon=None — флаг для демонизации процесса.
Возвращаемое значение:
Описание:
Класс Process() модуля multiprocessing запускает вызываемый объект target на исполнение, который будет выполняется в отдельном процессе/ядре процессора.
Есть два способа запустить какое либо действие:
- Передать вызываемый объект (функцию) target в конструктор.
- Переопределить метод Process.run() в подклассе.
Внимание! Никакие другие методы не должны переопределяться в подклассе (кроме конструктора). Другими словами, можно переопределять только методы __init__() и Process.run() этого класса.
Объект Process имеет эквиваленты всех методов объекта Thread() .
Аргумент group всегда должна быть None ; он существует исключительно для совместимости с классом threading.Thread() .
Аргумент target — это вызываемый объект, который будет вызываться методом Process.run() . По умолчанию это None , что означает, что ничего не вызывается. По умолчанию в вызываемый объект target не передаются его аргументы.
Аргумент name — это имя процесса, подробнее смотрите атрибут Process.name .
Аргумент args — это кортеж аргументов для целевого вызова target . Аргумент kwargs — это словарь ключевых аргументов для целевого вызова target .
Если предоставлен ключевой аргумент daemon , то он устанавливает флаг демона процесса значение True или False . Если daemon=None (по умолчанию), этот флаг будет унаследован от процесса создателя.
Если подкласс переопределяет конструктор класса multiprocessing.Process() , то необходимо вызывать конструктор базового класса Process.__init__() , прежде чем делать что-либо еще с процессом.
Обратите внимание, что методы Process.start() , Process.join() , Process.is_alive() , Process.terminate() и Process.exitcode должны вызываться только процессом, создавшим этот объект процесса.
Методы объекта Process .
Объекты Process представляют собой действия, которые выполняются в отдельном процессе.
Process.run() :
Метод Process.run() представляет активность процесса.
Можно переопределить этот метод в своем подклассе. Стандартный метод Process.run() вызывает вызываемый объект, переданный конструктору в качестве целевого аргумента target , с позиционными и ключевыми аргументами, взятыми из аргументов args и kwargs соответственно.
Метод Thread.run() можно переопределить в пользовательском подклассе. Например:
Process.start() :
Метод Process.start() запускает экземпляр Process в отдельном процессе/ядре процессора.
Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он организует вызов метода Process.run() объекта в отдельном процессе/ядре процессора.
Дополнительно, смотрите описание этого метода у объекта Thread , примеры кода аналогичны.
Process.join([timeout]) :
Метод Process.join() ждет, пока не завершится процесс.
Если необязательный аргумент timeout=None (по умолчанию), то процесс, из которого вызван этот метод, блокируется до тех пор, пока не завершится процесс, у которого вызван этот метод. Проще говоря, если вызвать метод Process.join() для создаваемых процессов из основного процесса программы, то дальнейшее выполнение программы будет заблокировано, пока созданные процессы не завершаться.
Если тайм-аут timeout является положительным числом, то процесс блокируется не более чем на величину секунд, указанную в timeout . Обратите внимание, что метод возвращает None , если его процесс завершается или если время ожидания метода истекает. Проверьте код выхода процесса Process.exitcode , чтобы определить, завершился ли он.
К процессу можно присоединяться много раз.
Процесс не может присоединиться к самому себе, так как это вызовет взаимоблокировку. Попытка присоединиться к процессу до его запуска является ошибкой.
Дополнительно, смотрите описание этого метода у объекта Thread , примеры кода аналогичны.
Process.name :
Атрибут Process.name это имя процесса и представляет собой строку, которая используется только для идентификации. У имени нет семантики.
Несколько процессов могут иметь одно и то же имя (можно присваивать).
Начальное имя задается конструктором класса. Если в конструктор не передан аргумент name , то имена порожденных процессов будет выглядеть как «Process-N1:N2. Nk», где каждый Nk является N-м потомком своего родителя.
Дополнительно, смотрите описание этого метода у объекта Thread , примеры кода аналогичны.
Process.is_alive() :
Метод Process.is_alive() проверяет, является ли процесс живым.
Грубо говоря, объект процесса является живым с момента вызова метода Process.start() и до завершения порожденного дочернего процесса.
Process.daemon :
Атрибут Process.daemon флаг демона процесса, логическое значение. Этот атрибут должен быть установлен перед вызовом метода Process.start() .
Начальное значение наследуется от процесса создателя.
Когда процесс завершается, он пытается завершить все свои демонические дочерние процессы.
Обратите внимание, что демоническому процессу не разрешается создавать дочерние процессы. Так как если демонический процесс будет прерван выходом из родительского процесса, то демонический процесс оставил бы свои дочерние процессы «сиротами«. Кроме того, это не демоны или службы Unix, это обычные процессы, которые будут завершены (но не присоединены), если завершились не-демонические процессы.
Process.pid :
Атрибут Process.pid возвращает идентификатор процесса. До того, как процесс будет порожден, это будет None .
Process.exitcode :
Атрибут Process.exitcode возвращает код выхода порожденного процесса. Значение будет None , если процесс еще не завершен.
Отрицательное значение -N указывает, что дочерний элемент был прерван сигналом N .
Process.authkey :
Атрибут Process.authkey ключ аутентификации процесса, представляет из себя байтовую строку.
При инициализации модуля multiprocessing , основному процессу присваивается случайная строка с помощью os.urandom() .
Когда создается объект процесса, то он наследует ключ аутентификации своего родительского процесса, хотя это может быть изменено путем установки атрибута authkey в другую байтовую строку.
Process.sentinel :
Атрибут Process.sentinel числовой дескриптор системного объекта, который станет «готовым» по завершении процесса.
Можно использовать это значение, если необходимо дождаться нескольких событий одновременно, используя multiprocessing.connection.wait() . Вообще это проще сделать вызвав метод Process.join() у каждого из создаваемых процессов, результатов которых надо дождаться.
В Windows это дескриптор ОС, который можно использовать с семейством вызовов API WaitForSingleObject и WaitForMultipleObjects . В Unix это файловый дескриптор, который можно использовать с примитивами из модуля select .
Process.terminate() :
Метод Process.terminate() завершает процесс. В Unix это делается с помощью сигнала SIGTERM , в Windows используется TerminateProcess() .
Обратите внимание, что обработчики выхода, предложения tru/finally и т. д. не будут выполняться.
Источник
Как запускать внешние процессы, используя Python и модуль subprocess
В скриптах, написанных для автоматизации определенных задач, нам часто требуется запускать внешние программы и контролировать их выполнение. При работе с Python мы можем использовать модуль subprocess для создания подобных скриптов. Этот модуль является частью стандартной библиотеки языка. В данном руководстве мы кратко рассмотрим subprocess и изучим основы его использования.
Прочитав статью, вы узнаете как:
- Использовать функцию run для запуска внешнего процесса.
- Получить стандартный вывод процесса и информацию об ошибках.
- Проверить код возврата процесса и вызвать исключение в случае сбоя.
- Запустить процесс, используя оболочку в качестве посредника.
- Установить время ожидания завершения процесса.
- Использовать класс Popen напрямую для создания конвейера ( pipe ) между двумя процессами.
Так как модуль subprocess почти всегда используют с Linux все примеры будут касаться Ubuntu. Для пользователей Windows советую скачать терминал Ubuntu 18.04 LTS.
Функция «run»
Функция run была добавлена в модуль subprocess только в относительно последних версиях Python (3.5). Теперь ее использование является рекомендуемым способом создания процессов и должно решать наиболее распространенные задачи. Прежде всего, давайте посмотрим на простейший случай применения функции run .
Предположим, мы хотим запустить команду ls -al ; для этого в оболочке Python нам нужно ввести следующие инструкции:
Вывод внешней команды ls отображается на экране:
Здесь мы просто использовали первый обязательный аргумент функции run , который может быть последовательностью, «описывающей» команду и ее аргументы (как в примере), или строкой, которая должна использоваться при запуске с аргументом shell=True (мы рассмотрим последний случай позже).
Захват вывода команды: stdout и stderr
Что, если мы не хотим, чтобы вывод процесса отображался на экране. Вместо этого, нужно чтобы он сохранялся: на него можно было ссылаться после выхода из процесса? В этом случае нам стоит установить для аргумента функции capture_output значение True :
Как мы можем впоследствии получить вывод ( stdout и stderr ) процесса? Если вы посмотрите на приведенные выше примеры, то увидите, что мы использовали переменную process для ссылки на объект CompletedProcess , возвращаемый функцией run . Этот объект представляет процесс, запущенный функцией, и имеет много полезных свойств. Помимо прочих, stdout и stderr используются для «хранения» соответствующих дескрипторов команды, если, как уже было сказано, для аргумента capture_output установлено значение True . В этом случае, чтобы получить stdout , мы должны использовать:
По умолчанию stdout и stderr представляют собой последовательности байтов. Если мы хотим, чтобы они хранились в виде строк, мы должны установить для аргумента text функции run значение True .
Управление сбоями процесса
Команда, которую мы запускали в предыдущих примерах, была выполнена без ошибок. Однако при написании программы следует принимать во внимание все случаи. Так, что случится, если порожденный процесс даст сбой? По умолчанию ничего «особенного» не происходит. Давайте посмотрим на примере: мы снова запускаем команду ls , пытаясь вывести список содержимого каталога /root, который не доступен для чтения обычным пользователям:
Мы можем узнать, не завершился ли запущенный процесс ошибкой, проверив его код возврата, который хранится в свойстве returncode объекта CompletedProcess :
Видите? В этом случае returncode равен 2 , подтверждая, что процесс столкнулся с ошибкой, связанной с недостаточными правами доступа, и не был успешно завершен. Мы могли бы проверять выходные данные процесса таким образом чтобы при возникновении сбоя возникало исключение. Используйте аргумент check функции run : если для него установлено значение True , то в случае, когда внешний процесс завершается ошибкой, возникает исключение CalledProcessError :
Обработка исключений в Python довольно проста. Поэтому для управления сбоями процесса мы могли бы написать что-то вроде:
Исключение CalledProcessError , как мы уже сказали, возникает, когда код возврата процесса не является 0 . У данного объекта есть такие свойства, как returncode , cmd , stdout , stderr ; то, что они представляют, довольно очевидно. Например, в приведенном выше примере мы просто использовали свойство cmd , чтобы отобразить последовательность, которая использовалась для запуска команды при возникновении исключения.
Выполнение процесса в оболочке
Процессы, запущенные с помощью функции run , выполняются «напрямую», это означает, что для их запуска не используется оболочка: поэтому для процесса не доступны никакие переменные среды и не выполняются раскрытие и подстановка выражений. Давайте посмотрим на пример, который включает использование переменной $HOME :
Как видите, переменная $HOME не была заменена на соответствующее значение. Такой способ выполнения процессов является рекомендованным, так как позволяет избежать потенциальные угрозы безопасности. Однако, в некоторых случаях, когда нам нужно вызвать оболочку в качестве промежуточного процесса, достаточно установить для параметра shell функции run значение True . В таких случаях желательно указать команду и ее аргументы в виде строки:
Все переменные, существующие в пользовательской среде, могут использоваться при вызове оболочки в качестве промежуточного процесса. Хотя это может показаться удобным, такой подход является источником проблем. Особенно при работе с потенциально опасным вводом, который может привести к внедрению вредоносного shell-кода. Поэтому запуск процесса с shell=True не рекомендуется и должен использоваться только в безопасных случаях.
Ограничение времени работы процесса
Обычно мы не хотим, чтобы некорректно работающие процессы бесконечно исполнялись в нашей системе после их запуска. Если мы используем параметр timeout функции run , то можем указать количество времени в секундах, в течение которого процесс должен завершиться. Если он не будет завершен за это время, процесс будет остановлен сигналом SIGKILL. Который, как мы знаем, не может быть перехвачен. Давайте продемонстрируем это, запустив длительный процесс и предоставив timeout в секундах:
В приведенном выше примере мы запустили команду ping без указания фиксированного числа пакетов ECHO REQUEST, поэтому она потенциально может работать вечно. Мы также установили время ожидания в 5 секунд с помощью параметра timeout . Как мы видим, ping была запущена, а по истечении 5 секунд возникло исключение TimeoutExpired и процесс был остановлен.
Функции call, check_output и check_call
Как мы уже говорили ранее, функция run является рекомендуемым способом запуска внешнего процесса. Она должна использоваться в большинстве случаев. До того, как она была представлена в Python 3.5, тремя основными функциями API высокого уровня, применяемыми для создания процессов, были call , check_output и check_call ; давайте взглянем на них вкратце.
Прежде всего, функция call : она используется для выполнения команды, описанной параметром args; она ожидает завершения команды; ее результатом является соответствующий код возврата. Это примерно соответствует базовому использованию функции run.
Поведение функции check_call практически не отличается от run , когда для параметра check задано значение True : она запускает указанную команду и ожидает ее завершения. Если код возврата не равен 0 , возникает исключение CalledProcessError .
Наконец, функция check_output . Она работает аналогично check_call , но возвращает вывод запущенной программы, то есть он не отображается при выполнении функции.
Работа на более низком уровне с классом Popen
До сих пор мы изучали функции API высокого уровня в модуле subprocess, особенно run . Все они под капотом используют класс Popen . Из-за этого в подавляющем большинстве случаев нам не нужно взаимодействовать с ним напрямую. Однако, когда требуется большая гибкость, без создания объектов Popen не обойтись.
Предположим, например, что мы хотим соединить два процесса, воссоздав поведение конвейера (pipe) оболочки. Как мы знаем, когда передаем две команды в оболочку, стандартный вывод той, что находится слева от пайпа «|», используется как стандартный ввод той, которая находится справа. В приведенном ниже примере результат выполнения двух связанных конвейером команд сохраняется в переменной:
Чтобы воссоздать подобное поведение с помощью модуля subprocess без установки параметра shell в значение True , как мы видели ранее, мы должны напрямую использовать класс Popen :
Источник