cython中的异常处理



我有两个.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

进一步扩展我的评论:Cython在导入模块时做了很多工作,设置C全局变量,使其能够访问C内置类型(包括异常(、字符串常量和其他一些东西。在这种情况下,该行被转换为

__Pyx_Raise(__pyx_builtin_ValueError, 0, 0, 0);

如果CCD_ 12没有初始化,那么一切都会出错。

结果是,Cython代码(甚至是publicCython码(实际上并不是独立的,而是模块的一部分,它确实依赖于调用模块init函数才能工作。在您的案例中,它在引发异常时失败了,但如果您超过了这一点,还会有一系列类似的错误等待发生。


一般来说,我建议不要将多个模块链接到一个.so文件中。然而,它可以发挥作用;看见https://stackoverflow.com/a/52714500/4657412食谱。正如您所看到的,它非常复杂,需要对C API导入过程有一些了解。


其他选项(不涉及自己用C编写代码(将是使用多个模块和cimport机制来在它们之间共享实现;包括";该机制通常不推荐使用,但有时很方便。

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

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

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

最新更新