问题描述
我有以下程序,其中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()
。不过,这对我来说似乎是一个糟糕的设计。