PyObject segfault函数调用 [英] PyObject segfault on function call
问题描述
我正在尝试使用Python打开对话框以接受输入到我的C ++应用程序中.
I'm trying to use Python to open a dialog to accept input into my C++ application.
以下是我要执行的操作的最小表示:
Here is a very minimal representation of what I am trying to do:
#include <iostream>
#include <Python.h>
int main()
{
/* Begin Python Ititialization - only needs to be done once. */
PyObject *ip_module_name = NULL;
PyObject *ip_module = NULL;
PyObject *ip_module_contents = NULL;
PyObject *ip_module_getip_func = NULL;
Py_Initialize();
PyEval_InitThreads();
ip_module_name = PyString_FromString( "get_ip" );
ip_module = PyImport_Import( ip_module_name );
ip_module_contents = PyModule_GetDict( ip_module );
ip_module_getip_func = PyDict_GetItemString( ip_module_contents, "get_ip_address" );
/* End Initialization */
PyGILState_STATE state = PyGILState_Ensure();
PyObject *result = PyObject_CallObject( ip_module_getip_func, NULL );
if( result == Py_None )
printf( "None\n" );
else
printf( "%s\n", PyString_AsString( result ) );
PyGILState_Release( state );
/* This is called when the progam exits. */
Py_Finalize();
}
但是,当我使用PyObject_CallObject调用函数时,应用程序出现段错误.我猜这是因为我正在使用Tk库.我尝试将我的应用程序链接到_tkinter.lib,tk85.lib,tcl85.lib,tkstub85.lib,tclstub85.lib,但这些都无济于事.我很沮丧...
However, when I call the function with PyObject_CallObject, the app segfaults. I'm guessing that it's because I'm using the Tk library. I've tried linking my app against _tkinter.lib, tk85.lib, tcl85.lib, tkstub85.lib, tclstub85.lib and none of that helps. I'm pretty stumped...
这是脚本:
import Tkinter as tk
from tkSimpleDialog import askstring
from tkMessageBox import showerror
def get_ip_address():
root = tk.Tk()
root.withdraw()
ip = askstring( 'Server Address', 'Enter IP:' )
if ip is None:
return None
ip = ip.strip()
if ip is '':
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
if len(ip.split(".")) is not 4:
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
for octlet in ip.split("."):
x = 0
if octlet.isdigit():
x = int(octlet)
else:
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
if not ( x < 256 and x >= 0 ):
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
return ip
添加了我的线程设置
推荐答案
添加PySys_SetArgv(argc, argv)
(以及main
的int argc, char **argv
参数),您的代码即可使用.
Add PySys_SetArgv(argc, argv)
(along with int argc, char **argv
parameters to main
), and your code will work.
tk.Tk()
访问sys.argv
,除非已调用PySys_SetArgv
,否则该目录不存在.这将导致一个异常,该异常将从get_ip
传播出去,并通过返回NULL
的PyObject_CallObject
报告给Python/C. NULL
被存储到result
并传递给PyString_AsString
,这是观察到的崩溃的直接原因.
tk.Tk()
accesses sys.argv
, which doesn't exist unless PySys_SetArgv
has been called. This causes an exception which gets propagated out of get_ip
and reported to Python/C by PyObject_CallObject
returning NULL
. The NULL
gets stored to result
and passed to PyString_AsString
, which is the immediate cause of the observed crash.
代码上的几点说明:
-
花费了很多精力进行调试,因为代码不会进行任何错误检查,它会盲目地向前推动,直到由于传递NULL指针而导致崩溃为止.至少可以做的是写类似的东西:
It took effort to debug this because the code does no error checking whatsoever, it blindly presses forward until it crashes due to passing NULL pointers around. The least one can do is write something like:
if (!ip_module_name) {
PyErr_Print();
exit(1);
}
// and so on for every PyObject* that you get from a Python API call
在真实代码中,您不会exit()
,但是会进行一些清理并返回NULL
(或引发C ++级异常,或任何适当的操作).
In real code you wouldn't exit()
, but do some cleanup and return NULL
(or raise a C++-level exception, or whatever is appropriate).
无需在您已经知道拥有GIL的线程中调用PyGILState_Ensure
.由于 PyEval_InitThreads
状态的文档,它将初始化GIL并获取.从C回调调用Python时,您只需要重新获取GIL,该C回调来自与Python无关的工具包事件循环.
There is no need to call PyGILState_Ensure
in the thread that you already know holds the GIL. As the documentation of PyEval_InitThreads
states, it initializes the GIL and acquires it. You only need to re-acquire the GIL when calling into Python from a C callback that comes from, say, the toolkit event loop that has nothing to do with Python.
一旦不再需要从Python接收到的新引用,就需要进行Py_DECREF
编辑.为了简洁起见,可能在最小示例中省略了引用计数,但应始终牢记.
New references received from Python need to be Py_DECREF
'ed once no longer needed. Reference counting might be omitted from the minimal example for brevity, but it should always be minded.
这篇关于PyObject segfault函数调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!