cython 中的异常处理
Exception handling in cython
我有两个 .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
我可以 return 来自 baz
的一些值并根据 return 值在 foo_baz
中引发异常,但我希望在 [= =16=].
# 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
正常方式的异常,而不是尝试手动设置异常和 return 错误值:
cdef public int baz() except -1:
raise ValueError
Cython 将为您处理异常设置和 return 值。
进一步扩展我的评论:导入模块时,Cython 做了很多工作,设置 C 全局变量,使其能够完全访问 C 内置类型(包括异常)、字符串常量和其他一些事情。在这种情况下,该行被翻译成
__Pyx_Raise(__pyx_builtin_ValueError, 0, 0, 0);
如果 __pyx_builtin_ValueError
没有初始化那么一切都会出错。
结果是 Cython 代码(甚至 public
Cython 代码)实际上并不是独立的,而是模块的一部分,并且确实依赖于调用模块初始化函数才能工作。在您的情况下,它在引发异常时失败了,但是如果您克服了这一点,就会有一系列类似的错误等待发生。
作为一般规则,我建议不要将多个模块链接到一个 .so 文件中。但是它可以工作;参见 的食谱。如您所见,它非常复杂,需要对 C API 导入过程有所了解。
其他选项(不涉及自己用 C 编写代码)将使用多个模块和 cimport
机制在它们之间共享实现,或者可能 the older "include" mechanism 通常是不推荐,但有时方便。
我有两个 .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
我可以 return 来自 baz
的一些值并根据 return 值在 foo_baz
中引发异常,但我希望在 [= =16=].
# 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
正常方式的异常,而不是尝试手动设置异常和 return 错误值:
cdef public int baz() except -1:
raise ValueError
Cython 将为您处理异常设置和 return 值。
进一步扩展我的评论:导入模块时,Cython 做了很多工作,设置 C 全局变量,使其能够完全访问 C 内置类型(包括异常)、字符串常量和其他一些事情。在这种情况下,该行被翻译成
__Pyx_Raise(__pyx_builtin_ValueError, 0, 0, 0);
如果 __pyx_builtin_ValueError
没有初始化那么一切都会出错。
结果是 Cython 代码(甚至 public
Cython 代码)实际上并不是独立的,而是模块的一部分,并且确实依赖于调用模块初始化函数才能工作。在您的情况下,它在引发异常时失败了,但是如果您克服了这一点,就会有一系列类似的错误等待发生。
作为一般规则,我建议不要将多个模块链接到一个 .so 文件中。但是它可以工作;参见
其他选项(不涉及自己用 C 编写代码)将使用多个模块和 cimport
机制在它们之间共享实现,或者可能 the older "include" mechanism 通常是不推荐,但有时方便。