当Python进程被终止时运行atexit()函数

24

我有一个在后台运行的Python进程,我希望它只在脚本终止时生成一些输出。

def handle_exit():
    print('\nAll files saved in ' + directory)
    generate_output()

atexit.register(handle_exit)

调用引发KeyboardInterupt异常和sys.exit()正确地调用handle_exit(),但如果我从终端执行kill {PID}则会在不调用handle_exit()的情况下终止脚本。

有没有一种方法可以终止在后台运行的进程,并在终止之前运行handle_exit()


1
仅使用atexit是不可能的。因为文档说明:“通过此模块注册的函数在程序被未被Python处理的信号杀死、检测到Python致命内部错误或调用os._exit()时不会被调用。” [在这里] (https://docs.python.org/2/library/atexit.html) - Alexander Ejbekov
4个回答

36

尝试使用signal.signal。它可以捕获任何系统信号:

import signal

def handle_exit():
    print('\nAll files saved in ' + directory)
    generate_output()

atexit.register(handle_exit)
signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)

现在你可以 kill {pid},执行handle_exit


6
请注意,您的处理程序函数应提供两个位置参数 - 信号编号和堆栈帧,例如handle_exit(signum, frame)handle_exit(*args) - 因为这是通过signal.signal(…)调用这些函数的方式。如果您的函数需要显式参数,则可以设置atexit.register(…)的值以传递自己的调用,例如atexit.register(handle_exit, None, None)或类似方法。 - fish2000
3
通过任务管理器或PyCharm的停止按钮关闭时,在Windows上无法正常工作。 - IlyaP
这将执行 handle_exit 两次 - 一次来自 handle_exit,然后再次来自 atexit.register - KamilCuk

1

在Windows上使用PyCharm进行调试时启用信号:

  1. 在PyCharm中按下Ctrl + Shift + A以打开“查找操作...”菜单
  2. 搜索“注册表”并按Enter键
  3. 找到键kill.windows.processes.softly并启用它(您可以开始输入“kill”,它将搜索该键)
  4. 重新启动PyCharm

0

如果您想自己处理信号,在Python 文档中指出,处理函数需要两个参数。

如果您要处理SIGINT信号,则必须在处理函数中调用 sys.exit(0);否则,按下 Ctrl + C 后程序将不会终止。

正如KamiCuk所提到的,如果与atexit注册的函数与信号处理函数相同,则该函数将被调用两次。

因此,我更喜欢为信号处理和atexit注册使用单独的函数。这里是一个示例:

import atexit
import signal
import sys

def on_exit():
    print('real clean code here')

def handle_exit(signum, frame):
    sys.exit(0)

atexit.register(on_exit)
signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)

正确处理这些信号可能会很棘手,特别是对于跨平台应用程序而言。

为了解决这个问题,我创建了一个名为safe-exit的软件包。

文档可以在这里找到:https://safe-exit.readthedocs.io/en/latest/


0

检查您的系统并查看调用哪个信号:

import signal
import time


def handle_signal(sig_id, frame):
    sig = {x.value: x for x in signal.valid_signals()}.get(sig_id)
    print(f'{sig.name}, {sig_id=}, {frame=}')
    exit(-1)


for sig in signal.valid_signals():
    print(f'{sig.value}: signal.{sig.name},')
    signal.signal(sig, handle_signal)

time.sleep(30)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接