gpt4 book ai didi

python - 在 C 中嵌入 Python : Error when attempting to call Python code in a C callback called by Python code

转载 作者:太空狗 更新时间:2023-10-29 23:55:10 27 4
gpt4 key购买 nike

我有一些调用 Python 函数的 C 代码。此 Python 函数接受地址并使用 WINFUNCTYPE 最终将其转换为 Python 可以调用的函数。作为参数发送给 Python 函数的 C 函数最终会调用另一个 Python 函数。正是在这最后一步导致崩溃。所以简而言之,我从 C -> Python -> C -> Python 开始。最后一个 C -> Python 导致崩溃。我一直试图理解这个问题,但一直无法理解。

有人能指出我的问题吗?

使用 Visual Studio 2010 编译并使用参数“c:\...\crash.py”和“func1”运行的 C 代码:

#include <stdlib.h>
#include <stdio.h>

#include <Python.h>

PyObject* py_lib_mod_dict; //borrowed

void __stdcall cfunc1()
{
PyObject* py_func;
PyObject* py_ret;
int size;
PyGILState_STATE gil_state;

gil_state = PyGILState_Ensure();
printf("Hello from cfunc1!\n");

size = PyDict_Size(py_lib_mod_dict);
printf("The dictionary has %d items!\n", size);
printf("Calling with GetItemString\n");
py_func = PyDict_GetItemString(py_lib_mod_dict, "func2"); //fails here when cfunc1 is called via callback... will not even go to the next line!
printf("Done with GetItemString\n");
py_ret = PyObject_CallFunction(py_func, 0);

if (py_ret)
{
printf("PyObject_CallFunction from cfunc1 was successful!\n");
Py_DECREF(py_ret);
}
else
printf("PyObject_CallFunction from cfunc1 failed!\n");

printf("Goodbye from cfunc1!\n");
PyGILState_Release(gil_state);
}

int wmain(int argc, wchar_t** argv)
{
PyObject* py_imp_str;
PyObject* py_imp_handle;
PyObject* py_imp_dict; //borrowed
PyObject* py_imp_load_source; //borrowed
PyObject* py_dir; //stolen
PyObject* py_lib_name; //stolen
PyObject* py_args_tuple;
PyObject* py_lib_mod;
PyObject* py_func;
PyObject* py_ret;

Py_Initialize();

//import our python script
py_dir = PyUnicode_FromWideChar(argv[1], wcslen(argv[1]));
py_imp_str = PyString_FromString("imp");
py_imp_handle = PyImport_Import(py_imp_str);
py_imp_dict = PyModule_GetDict(py_imp_handle); //borrowed
py_imp_load_source = PyDict_GetItemString(py_imp_dict, "load_source"); //borrowed
py_lib_name = PyUnicode_FromWideChar(argv[2], wcslen(argv[2]));

py_args_tuple = PyTuple_New(2);
PyTuple_SetItem(py_args_tuple, 0, py_lib_name); //stolen
PyTuple_SetItem(py_args_tuple, 1, py_dir); //stolen

py_lib_mod = PyObject_CallObject(py_imp_load_source, py_args_tuple);
py_lib_mod_dict = PyModule_GetDict(py_lib_mod); //borrowed

printf("Calling cfunc1 from main!\n");
cfunc1();

py_func = PyDict_GetItem(py_lib_mod_dict, py_lib_name);
py_ret = PyObject_CallFunction(py_func, "(I)", &cfunc1);

if (py_ret)
{
printf("PyObject_CallFunction from wmain was successful!\n");
Py_DECREF(py_ret);
}
else
printf("PyObject_CallFunction from wmain failed!\n");

Py_DECREF(py_imp_str);
Py_DECREF(py_imp_handle);
Py_DECREF(py_args_tuple);
Py_DECREF(py_lib_mod);

Py_Finalize();

fflush(stderr);
fflush(stdout);
return 0;
}

Python代码:

from ctypes import *

def func1(cb):
print "Hello from func1!"
cb_proto = WINFUNCTYPE(None)
print "C callback: " + hex(cb)
call_me = cb_proto(cb)
print "Calling callback from func1."
call_me()
print "Goodbye from func1!"

def func2():
print "Hello and goodbye from func2!"

输出:

Calling cfunc1 from main!
Hello from cfunc1!
The dictionary has 88 items!
Calling with GetItemString
Done with GetItemString
Hello and goodbye from func2!
PyObject_CallFunction from cfunc1 was successful!
Goodbye from cfunc1!
Hello from func1!
C callback: 0x1051000
Calling callback from func1.
Hello from cfunc1!
The dictionary has 88 items!
Calling with GetItemString
PyObject_CallFunction from wmain failed!

我在最后添加了一个 PyErr_Print(),结果是这样的:

Traceback (most recent call last):
File "C:\Programming\crash.py", line 9, in func1
call_me()
WindowsError: exception: access violation writing 0x0000000C

编辑:修复了 abarnert 指出的错误。输出不受影响。
编辑:添加到解决错误的代码中(获取 cfunc1 中的 GIL 锁)。再次感谢 abarnert。

最佳答案

问题是这段代码:

py_func = PyDict_GetItemString(py_lib_mod_dict, "func2"); //fails here when cfunc1 is called via callback... will not even go to the next line!
printf("Done with GetItemString\n");
py_ret = PyObject_CallFunction(py_func, 0);

Py_DECREF(py_func);

作为the docs比如说,PyDict_GetItemString返回借用的引用。所以,你第一次在这里调用时,你借用了引用,并 decref 它,导致它被销毁。下次调用时,返回垃圾,并尝试调用它。

因此,要修复它,只需删除 Py_DECREF(py_func) (或在 Py_INCREF(py_func) 行之后添加 pyfunc =)。

实际上,你通常会得到一个特殊的“死”对象,所以你可以很容易地测试它:把一个PyObject_Print(py_func, stdout)py_func =之后行和 Py_DECREF 之后行,你可能会看到类似 <function func2 at 0x10b9f1230> 的内容第一次,<refcnt 0 at 0x10b9f1230>第二次和第三次(你不会看到第四次,因为它会在你到达那里之前崩溃)。

我手边没有 Windows 框,但正在更改 wmain , wchar_t , PyUnicode_FromWideChar , WINFUNCTYPE等到main , char , PyString_FromString , CFUNCTYPE等,我能够构建并运行您的代码,但我在同一个地方崩溃了……修复成功了。

还有……你不应该把 GIL 放在 cfunc1 里面吗? ?我不经常写这样的代码,所以也许我错了。而且我不会按原样使用代码崩溃。显然,产生一个线程来运行 cfunc1 确实崩溃,并且PyGILState_Ensure/Release解决了那个崩溃……但这并不能证明在单线程情况下你需要任何东西。所以也许这无关紧要……但如果您在修复第一个崩溃后再次崩溃(在线程情况下,我的看起来像 Fatal Python error: PyEval_SaveThread: NULL tstate ),请调查一下。

顺便说一句,如果您是 Python 扩展和嵌入的新手:大量无法解释的崩溃,就像这一次,是由手动引用计数错误引起的。这就是 boost::python 之类的原因等存在。并不是说用普通的 C API 就不可能把它弄对,只是它很容易出错,而且你将不得不习惯于调试这样的问题。

关于python - 在 C 中嵌入 Python : Error when attempting to call Python code in a C callback called by Python code,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14677190/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com