cython中的异常处理 setup.py中的扩展名如何测试变种1 变种2

问题描述

我有两个.pyx文件-bar.pyxbaz.pyx。我想将它们合并为一个.so文件

baz.pyx中,我有一个函数baz,该函数应该进行一些检查,并在出现问题时引发异常。在bar.pyx中,我想调用baz(),并期望在打印回溯信息时引发异常。

不幸的是,无论我尝试什么,都会遇到其他一些运行时错误

setup.py中的扩展名

[
    Extension(
        'testlib.baz',['src/testlib/baz.pyx'],),Extension(
        'testlib.foo',['src/testlib/bar.pyx','src/testlib/baz.c'],]

如何测试

import testlib.foo
testlib.foo.foo_baz()

变种1

# baz.pyx

cdef public baz():
    raise ValueError

# bar.pyx

cdef extern baz()

def foo_baz():
    baz()   # Segmentation fault

变种2

# baz.pyx

cdef public int baz() except -1:
    PyErr_SetNone(ValueError)
    return -1

# bar.pyx

cdef extern int baz() except -1

def foo_baz():
    baz()   # SystemError: <built-in function foo_baz> returned NULL without setting an error

我可以从baz返回一些值,并根据返回值在foo_baz中引发异常,但是我希望作为最小逻辑出现在bar.pyx中。

# baz.pyx

cdef public int baz():
    return -1

# bar.pyx

cdef extern int baz()

def foo_baz():
    if baz() == -1:
        raise ValueError    # OK

解决方法

您的except -1变体应以正常方式raise引发异常,而不是尝试手动设置异常并返回错误值:

cdef public int baz() except -1:
    raise ValueError

Cython将为您处理异常设置和返回值。

,

为了进一步说明我的观点:导入模块时,Cython做了很多工作,设置了C全局变量,以使其可以很方便地访问诸如C内置类型(包括异常),字符串常量之类的思想。其他事情。在这种情况下,该行被翻译为

__Pyx_Raise(__pyx_builtin_ValueError,0);

如果__pyx_builtin_ValueError未初始化,那么一切都会出错。

结果是Cython代码(甚至public Cython代码)实际上并不是独立的,而是模块的一部分,并且确实依赖于调用它的模块初始化函数。就您而言,它在引发异常时失败,但是如果您超出此范围,还会发生一系列类似的错误。


一般来说,我建议不要将多个模块链接到一个.so文件中。但是,它可以工作;有关食谱,请参见https://stackoverflow.com/a/52714500/4657412。如您所见,它非常复杂,需要对C API导入过程有所了解。


其他选项(不涉及用C自己编写代码)是使用多个模块和cimport机制在它们之间共享实现,或者可能是the older "include" mechanism,通常不建议这样做但有时很方便。