我有两个.pyx
文件——bar.pyx
和baz.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代码(甚至是public
Cython码(实际上并不是独立的,而是模块的一部分,它确实依赖于调用模块init函数才能工作。在您的案例中,它在引发异常时失败了,但如果您超过了这一点,还会有一系列类似的错误等待发生。
一般来说,我建议不要将多个模块链接到一个.so文件中。然而,它可以发挥作用;看见https://stackoverflow.com/a/52714500/4657412食谱。正如您所看到的,它非常复杂,需要对C API导入过程有一些了解。
其他选项(不涉及自己用C编写代码(将是使用多个模块和cimport
机制来在它们之间共享实现;包括";该机制通常不推荐使用,但有时很方便。
您的except -1
变体应该以正常方式raise
为异常,而不是尝试手动设置异常并返回错误值:
cdef public int baz() except -1:
raise ValueError
Cython将为您处理异常设置和返回值。