如何从 C 扩展中引发 Python 异常
How to raise Python exception from a C extension
我想从 C 扩展中引发 KeyboardInterrupt。
在 C 中,我通过以下 + setup.py:
创建了一个名为 siginfo
的模块
static void siginfo_handler(int signum, siginfo_t *siginfo, void *context) {
printf("got signal from '%d'", siginfo->si_pid);
//raise(PyExc_KeyboardInterrupt) <--- i want to raise keyboard interrupt here
}
static PyObject * siginfo_register(PyObject *self, PyObject *args) {
struct sigaction act;
memset(&act, '[=11=]', sizeof(act));
act.sa_sigaction = &siginfo_handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, NULL);
PY_RETURN_NONE;
}
static PyMethodDef SiginfoMethods[] = {
//blah blah filled out
};
PyMODINIT_FUNC initsiginfo(void) {
//blah blah filled out
}
在Python中:
import siginfo
import os
siginfo.register()
print "talk to me with kill -SIGINT %d" % os.getpid()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print 'got keybaord interrupt'
C 扩展可以很好地捕获 SIGINT,但我不知道如何从中引发 Python 异常,以便我可以在 Python 代码中捕获它。
有什么想法吗?
编辑:
我发现我可以通过以下方式提高 KeyboardInterrupt:
PyErr_SetString(PyExc_KeyboardInterrupt, "SIGINT received");
但如果我在信号处理程序中引发它,它确实会出现段错误。
如果在其他函数中引发,则不会出现段错误。
为什么我不能在信号处理程序中引发它?
您只是在收到信号时未处于执行 Python 代码的状态。在引发错误之前,您需要获取 GIL:
static void siginfo_handler(int signum, siginfo_t *siginfo, void *context) {
printf("got signal from '%d'\n", siginfo->si_pid);
PyGILState_STATE gstate = PyGILState_Ensure();
PyErr_SetString(PyExc_KeyboardInterrupt, "SIGINT received");
PyGILState_Release(gstate);
}
您不能从信号处理程序内部调用任意 Python C/API 函数。信号处理程序的功能非常有限。有关详细信息,请参阅 here and here。
Python 通过一个非常简单的信号处理程序来处理它,该信号处理程序将 volatile sig_atomic_t
类型的变量设置为 1。解释器会定期检查此变量并引发 Python 异常如果设置为 1,则作为常规控制流的一部分。
主要问题是当解释器调用需要很长时间执行的 C 代码时。直到 C 代码 returns.
才会引发异常
幸运的是,这种情况不会经常发生,因为大多数系统调用在传递信号时都会中断。在下面的程序中(与您的程序非常相似),对 time.sleep()
的调用将被信号中断,并且控制将 return 转到 Python 代码,这将引发异常。
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print 'got keybaord interrupt'
我想从 C 扩展中引发 KeyboardInterrupt。
在 C 中,我通过以下 + setup.py:
创建了一个名为siginfo
的模块
static void siginfo_handler(int signum, siginfo_t *siginfo, void *context) {
printf("got signal from '%d'", siginfo->si_pid);
//raise(PyExc_KeyboardInterrupt) <--- i want to raise keyboard interrupt here
}
static PyObject * siginfo_register(PyObject *self, PyObject *args) {
struct sigaction act;
memset(&act, '[=11=]', sizeof(act));
act.sa_sigaction = &siginfo_handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, NULL);
PY_RETURN_NONE;
}
static PyMethodDef SiginfoMethods[] = {
//blah blah filled out
};
PyMODINIT_FUNC initsiginfo(void) {
//blah blah filled out
}
在Python中:
import siginfo
import os
siginfo.register()
print "talk to me with kill -SIGINT %d" % os.getpid()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print 'got keybaord interrupt'
C 扩展可以很好地捕获 SIGINT,但我不知道如何从中引发 Python 异常,以便我可以在 Python 代码中捕获它。
有什么想法吗?
编辑:
我发现我可以通过以下方式提高 KeyboardInterrupt:
PyErr_SetString(PyExc_KeyboardInterrupt, "SIGINT received");
但如果我在信号处理程序中引发它,它确实会出现段错误。
如果在其他函数中引发,则不会出现段错误。
为什么我不能在信号处理程序中引发它?
您只是在收到信号时未处于执行 Python 代码的状态。在引发错误之前,您需要获取 GIL:
static void siginfo_handler(int signum, siginfo_t *siginfo, void *context) {
printf("got signal from '%d'\n", siginfo->si_pid);
PyGILState_STATE gstate = PyGILState_Ensure();
PyErr_SetString(PyExc_KeyboardInterrupt, "SIGINT received");
PyGILState_Release(gstate);
}
您不能从信号处理程序内部调用任意 Python C/API 函数。信号处理程序的功能非常有限。有关详细信息,请参阅 here and here。
Python 通过一个非常简单的信号处理程序来处理它,该信号处理程序将 volatile sig_atomic_t
类型的变量设置为 1。解释器会定期检查此变量并引发 Python 异常如果设置为 1,则作为常规控制流的一部分。
主要问题是当解释器调用需要很长时间执行的 C 代码时。直到 C 代码 returns.
才会引发异常幸运的是,这种情况不会经常发生,因为大多数系统调用在传递信号时都会中断。在下面的程序中(与您的程序非常相似),对 time.sleep()
的调用将被信号中断,并且控制将 return 转到 Python 代码,这将引发异常。
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print 'got keybaord interrupt'