Обработка прерывания клавиатуры при использовании подпроцесса

У меня есть python script, называемый monitiq_install.py, который вызывает другие скрипты (или модули) с помощью модуля subprocess python. Однако, если пользователь отправляет прерывание клавиатуры (CTRL + C), он выходит, но с исключением. Я хочу, чтобы он вышел, но приятно.

Мой код:

import os
import sys
from os import listdir
from os.path import isfile, join
from subprocess import Popen, PIPE
import json

# Run a module and capture output and exit code
def runModule(module):
    try:
        # Run Module
        process = Popen(os.path.dirname(os.path.realpath(__file__)) + "/modules/" + module, shell=True, stdout=PIPE, bufsize=1)
        for line in iter(process.stdout.readline, b''):
            print line,

        process.communicate()
        exit_code = process.wait();

        return exit_code;
    except KeyboardInterrupt:
        print "Got keyboard interupt!";
        sys.exit(0);

Ошибка, которую я получаю, ниже:

python monitiq_install.py -a
Invalid module filename: create_db_user_v0_0_0.pyc
Not Running Module: '3parssh_install' as it is already installed
######################################
Running Module: 'create_db_user' Version: '0.0.3'
Choose username for Monitiq DB User [MONITIQ]
^CTraceback (most recent call last):
  File "/opt/monitiq-universal/install/modules/create_db_user-v0_0_3.py", line 132, in <module>
    inputVal = raw_input("");
Traceback (most recent call last):
  File "monitiq_install.py", line 40, in <module>
KeyboardInterrupt
    module_install.runModules();
  File "/opt/monitiq-universal/install/module_install.py", line 86, in runModules
    exit_code = runModule(module);
  File "/opt/monitiq-universal/install/module_install.py", line 19, in runModule
    for line in iter(process.stdout.readline, b''):
KeyboardInterrupt

Решение или некоторые указатели были бы полезны:)

- EDIT С try catch

Running Module: 'create_db_user' Version: '0.0.0'
Choose username for Monitiq DB User [MONITIQ]
^CGot keyboard interupt!
Traceback (most recent call last):
  File "monitiq_install.py", line 36, in <module>
    module_install.runModules();
  File "/opt/monitiq-universal/install/module_install.py", line 90, in runModules
    exit_code = runModule(module);
  File "/opt/monitiq-universal/install/module_install.py", line 29, in runModule
    sys.exit(0);
NameError: global name 'sys' is not defined
Traceback (most recent call last):
  File "/opt/monitiq-universal/install/modules/create_db_user-v0_0_0.py", line 132, in <module>
    inputVal = raw_input("");
KeyboardInterrupt

Ответ 1

Если вы нажмете Ctrl + C в терминале, то SIGINT будет отправлен всем процессам в группе процессов. См. дочерний процесс получает родительский SIGINT.

Вот почему вы видите трассировку из дочернего процесса, несмотря на try/except KeyboardInterrupt в родительском.

Вы можете подавить вывод stderr из дочернего процесса: stderr=DEVNULL. Или запустите его в новой группе процессов: start_new_session=True:

import sys
from subprocess import call

try:
    call([sys.executable, 'child.py'], start_new_session=True)
except KeyboardInterrupt:
    print('Ctrl C')
else:
    print('no exception')

Если вы удалите start_new_session=True в приведенном выше примере, тогда KeyboardInterrupt также может быть поднят в дочернем элементе, и вы можете получить трассировку.

Если subprocess.DEVNULL недоступен; вы можете использовать DEVNULL = open(os.devnull, 'r+b', 0). Если параметр start_new_session недоступен; вы можете использовать preexec_fn=os.setsid в POSIX.

Ответ 2

Вы можете сделать это, используя try и за исключением следующего:

import subprocess
try:
    proc = subprocess.Popen("dir /S", shell=True,  stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while proc.poll() is None:
        print proc.stdout.readline()
except KeyboardInterrupt:
    print "Got Keyboard interrupt"

Вы могли бы избежать shell=True в своем исполнении в качестве лучшей практики безопасности.