Подтвердить что ты не робот

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() в процесс (из многопроцессорной обработки)... не работает

Любые идеи, что может произойти?

4b9b3361

Ответ 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()