PyObject segfault函数调用 [英] PyObject segfault on function call

查看:66
本文介绍了PyObject segfault函数调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用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)(以及mainint 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传播出去,并通过返回NULLPyObject_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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆