cython异常被忽略,而返回类型为void *无效指针

问题描述

我有以下程序,其中c代码通过cython调用python函数,如果python返回ctypes.Structure以外的任何内容,我希望引发/捕获异常。

C代码

$ cat main.c
#include <Python.h>
#include "caller.h"
#include <stdio.h>

typedef struct {
    uint8_t type;
    uint8_t subtype;
    uint8_t action;
} __attribute__ ((packed)) c_data;

int
main()
{
    PyImport_AppendInittab("caller",initcaller);
    Py_Initialize();
    PyImport_ImportModule("caller");

        const char* str = get_data_frm_python(2);
        printf("Printing in C returing str - %s\n",str);

        c_data* reqP = (c_data*)get_data_frm_python(1);
        if (reqP)
                printf("Printing in C returing struct - %u,%u,%u\n",reqP->type,reqP->subtype,reqP->action);

    return 0;
}

cython界面

$ cat caller.pyx
import sys
sys.path.insert(0,'')

from ctypes import addressof,Structure
from libc.stdint cimport uintptr_t
from c_struct import get_message

cdef public const void* get_data_frm_python(int typ) except *:
    data = get_message(typ)

    if not isinstance(data,Structure):
        print "Raising an exception"
        raise ValueError("get_message MUST return object of ctypes.Structure")

    cdef uintptr_t ptr = <uintptr_t>(addressof(data))
    return <const void*>(ptr)

Python代码

$ cat c_struct.py
#!/usr/bin/env python2.7

from ctypes import *

class Request(Structure):
    _pack_ = 1
    _fields_ = [
            ('type',c_ubyte),('subtype',('action',]

req_msg = Request()
some_string = "String object From Python"
def get_message(typ):
    if typ == 1:
        req_msg.type = 12
        req_msg.subtype = 2
        req_msg.action = 3
        return req_msg
    else:
        return "String object From Python"

Makefile供参考

$ cat Makefile

target = main
cy_interface = caller

CY := cython  --gdb
PYTHONINC := $(shell python-config --includes)
CFLAGS := -Wall $(PYTHONINC) -fPIC -O0 -ggdb3
LDFLAGS := $(shell python-config --ldflags)

CC=gcc

all: $(target)

.PRECIOUS: caller.c

%.c: %.pyx
        $(CY) $+

%.o: %.c
        $(CC) -fPIC $(CFLAGS) -c $+

$(target): $(cy_interface).o $(target).o
        $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

clean:
        rm -rf caller.o caller.c caller.h $(target).o $(target)
        rm -rf __pycache__ *.pyc

最后输出

$ ./main
Raising an exception
Printing in C returing str - (null)
Printing in C returing struct - 12,2,3

如果我从cython界面中删除了except *,则会得到以下输出结果

cdef public const void* get_data_frm_python(int typ):
    data = get_message(typ)
...

$ ./main
Raising an exception
ValueError: get_message MUST return object of ctypes.Structure
Exception ValueError: ValueError('get_message MUST return object of ctypes.Structure',) in 'caller.get_data_frm_python' ignored
Printing in C returing str - (null)
Printing in C returing struct - 12,3

有人引发异常时,有人可以帮助我终止c程序吗? 在cython界面中

解决方法

我不认为这是严格要求的,但是通常最好告诉Cython“错误返回值”应该是什么(例如public static class ControllerExtensions { public static async Task RenderViewAsync(this Controller controller,string viewName,TModel model,bool partial = false) { if (string.IsNullOrEmpty(viewName)) { viewName = controller.ControllerContext.ActionDescriptor.ActionName; } controller.ViewData.Model = model; using (var writer = new StringWriter()) { IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine; ViewEngineResult viewResult = viewEngine.FindView(controller.ControllerContext,viewName,!partial); if (viewResult.Success == false) { return $"A view with the name {viewName} could not be found"; } ViewContext viewContext = new ViewContext( controller.ControllerContext,viewResult.View,controller.ViewData,controller.TempData,writer,new HtmlHelperOptions() ); await viewResult.View.RenderAsync(viewContext); return writer.GetStringBuilder().ToString(); } )。如果有时还可以将except NULL作为有效值返回,请使用NULL

如果从Cython调用了该函数,则它将在函数完成时自动检查错误。但是,您是从C调用它的,因此您必须自己这样做。 Python documentation gives useful functions for probing the error state

将其放到一个简单的最小示例中(即从您相当广泛且不完整的示例中删减)即可:

except NULL?

# caller.pyx

cdef int i = 1;

cdef public const void* get_data_frm_python(int typ) except *:
    if typ:
        raise ValueError("get_message MUST return object of ctypes.Structure")

    return &i

如果您真的想让Cython在错误时退出(而不是传播异常),那么仅使用Python内置的// main.c #include <Python.h> #include "caller.h" #include <stdio.h> int main() { PyImport_AppendInittab("caller",PyInit_caller); // initcaller in Python 2 Py_Initialize(); PyImport_ImportModule("caller"); const void* reqP = get_data_frm_python(1); if (reqP) { printf("Printing in C returing struct - %p\n",reqP); } else { if (PyErr_Occurred()) { PyErr_Print(); } } return 0; } 函数就可以了。 显然,在这种情况下,您不会引发Python异常-您只需打印一条错误消息并调用exit()。不过,这对我来说似乎是一个糟糕的设计。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...