解决pytest-xdist下libpython崩溃导致pytest挂起的问题
解决pytest-xdist下C代码调用abort()导致挂起的问题
针对你遇到的C代码调用abort()后,pytest-xdist多进程模式挂起的问题,给你几个可行的解决方案:
1. 在C绑定层捕获SIGABRT,转为Python异常
这是最彻底的解决方式,从根源避免进程崩溃导致的挂起。在你的Python C扩展代码里注册信号处理函数,把abort()触发的SIGABRT信号转换成Python异常抛出,让pytest能正常捕获并标记测试失败:
#include <signal.h> #include <Python.h> #include <setjmp.h> static jmp_buf abort_jmp_buf; static void handle_abort(int sig) { // 恢复默认信号处理,防止递归触发 signal(sig, SIG_DFL); // 跳转到之前设置的点,抛出Python异常 longjmp(abort_jmp_buf, 1); } // 封装调用C代码的函数 static PyObject* your_c_function(PyObject* self, PyObject* args) { // 设置跳转点 if (setjmp(abort_jmp_buf) == 0) { // 注册信号处理 signal(SIGABRT, handle_abort); // 调用你的C代码,可能触发abort() your_actual_c_code(); Py_RETURN_NONE; } else { // 抛出Python异常 PyErr_SetString(PyExc_RuntimeError, "C code triggered abort()"); return NULL; } } PyMODINIT_FUNC PyInit_your_module(void) { static PyMethodDef methods[] = { {"your_c_function", your_c_function, METH_VARARGS, "Call C code"}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "your_module", NULL, -1, methods }; return PyModule_Create(&module); }
这样修改后,当C代码调用abort()时,会触发信号处理函数,转而抛出Python异常,不管单进程还是多进程模式,pytest都能正常捕获并标记测试失败,不会挂起。
2. 自定义pytest钩子监控worker进程状态
如果无法修改C代码,可以通过pytest的钩子函数监控xdist worker的退出状态,当检测到异常退出时强制终止主进程:
在项目根目录创建conftest.py,添加以下代码:
import pytest from pytest_xdist.remote import RemoteTestSession def pytest_testnodedown(node, error): # 检查worker节点是否异常退出 if isinstance(node, RemoteTestSession): # 134是abort()对应的退出码,也可以扩展到其他崩溃码 if node.exitstatus == 134 or node.exitstatus < 0: pytest.exit(f"Worker {node.gateway.id} crashed with exit code {node.exitstatus}", returncode=1)
这个钩子会在worker节点下线时检查其退出码,如果是abort()对应的134或者负数值(表示被信号终止),就让pytest主进程直接退出并返回失败状态。
3. 用pytest-timeout强制终止超时测试
如果上面的方法都不生效,可以给测试添加超时限制,用pytest-timeout插件强制终止挂起的测试:
- 安装插件:
pip install pytest-timeout
- 运行测试时添加超时参数:
pytest -n auto --timeout=5
或者在测试函数上直接加装饰器:
import pytest @pytest.mark.timeout(5) def test_c_abort_scenario(): # 调用你的C绑定代码 your_module.your_c_function()
这样即使测试因为abort()挂起,超时后会被自动终止,主进程不会一直无响应。
4. 开启xdist严格模式
结合你试过的--max-worker-restart 0,再加--xdist-strict参数,让主进程在worker崩溃时直接终止测试会话:
pytest -n auto --max-worker-restart 0 --xdist-strict
这个选项会禁用worker重试,一旦有worker崩溃,主进程就会立即退出并标记测试失败。
内容的提问来源于stack exchange,提问作者firmament
相关产品推荐
相关产品推荐

