Python потерял контроль над подпроцессом?
Я использую коммерческое приложение, которое использует Python как часть его скриптового API. Одна из предоставляемых функций - это что-то вроде App.run()
. Когда эта функция вызывается, она запускает новый Java-процесс, который выполняет оставшуюся часть выполнения. (К сожалению, я не знаю, что он делает под капотом, поскольку поставляемые модули Python являются .pyc
файлами, а многие из функций Python сгенерированы SWIG).
Проблема, с которой я столкнулась, заключается в том, что я создаю вызов App.run()
в более крупное приложение Python, которое должно выполнить некоторый гарантированный код очистки (закрытие базы данных и т.д.). К сожалению, если подпроцесс прерван с помощью Ctrl + C, он прерывается и возвращается в командную строку, не возвращая управление основной программе Python. Таким образом, мой код очистки никогда не выполняется.
До сих пор я пробовал:
- Регистрация функции с atexit... не работает
- Помещение очистки в класс
__del__
destructor... не работает. (App.run()
находится внутри класса)
- Создание обработчика сигнала для Ctrl + C в основном приложении Python... не работает
- Помещение
App.run()
в поток... приводит к ошибке памяти после Ctrl + C
- Ввод
App.run()
в процесс (из многопроцессорной обработки)... не работает
Любые идеи, что может произойти?
Ответы
Ответ 1
Если try: App.run() finally: cleanup()
не работает; вы можете попробовать запустить его в подпроцессе:
import sys
from subprocess import call
rc = call([sys.executable, 'path/to/run_app.py'])
cleanup()
Или, если у вас есть код в строке, вы можете использовать опцию -c
, например:
rc = call([sys.executable, '-c', '''import sys
print(sys.argv)
'''])
Вы можете реализовать предложение @tMC, используя подпроцесс, добавив
preexec_fn=os.setsid
(примечание: no ()
), хотя я не вижу, как здесь может помочь создание группы процессов. Или вы можете попробовать аргумент shell=True
, чтобы запустить его в отдельной оболочке.
Вы можете попробовать повторить многопроцессорную обработку:
import multiprocessing as mp
if __name__=="__main__":
p = mp.Process(target=App.run)
p.start()
p.join()
cleanup()
Ответ 2
Это всего лишь контур, но что-то вроде этого?
import os
cpid = os.fork()
if not cpid:
# change stdio handles etc
os.setsid() # Probably not needed
App.run()
os._exit(0)
os.waitpid(cpid)
# clean up here
(os.fork только * nix)
Та же идея может быть реализована с помощью subprocess
в агностическом режиме OS. Идея выполняется App.run()
в дочернем процессе, а затем ждет, пока дочерний процесс завершит работу; независимо от того, как завершился детский процесс. На posix вы также можете ловушку для SIGCHLD (смерть от процесса ребенка). Я не гуру окон, поэтому, если это применимо, и subprocess
не работает, кому-то еще придется перезвонить.
После вызова App.run()
мне будет интересно, как выглядит дерево процессов. Возможно, он запустил exec
и занял пространство процесса python. Если это происходит, создание дочернего процесса - единственный способ, которым я могу думать о его улавливании.
Ответ 3
Вы можете обернуть приложение App.Run() в Try/Catch?
Что-то вроде:
try:
App.Run()
except (KeyboardInterrupt, SystemExit):
print "User requested an exit..."
cleanup()