cython 中的异常处理

Posted

技术标签:

【中文标题】cython 中的异常处理【英文标题】:Exception handling in cython 【发布时间】:2021-02-05 07:24:38 【问题描述】:

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

【问题讨论】:

这是因为您从不导入 baz 模块。 Cython 在模块导入期间为异常设置全局变量。由于这个全局变量从未被初始化,所以一切都出错了。解决方案可能不是尝试将多个模块强制放入单个 .so 文件:它不是为它设计的,而且要正确执行非常复杂。 似乎合法,然后用 C 重写 baz.pyx。谢谢! 【参考方案1】:

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

__Pyx_Raise(__pyx_builtin_ValueError, 0, 0, 0);

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

结果是 Cython 代码(甚至是 public Cython 代码)实际上并不是独立的,而是模块的一部分,并且确实依赖于调用模块 init 函数才能工作。在您的情况下,它在引发异常时失败了,但是如果您通过了,就会有一系列类似的错误等待发生。


作为一般规则,我建议不要将多个模块链接到一个 .so 文件中。但是它可以工作;请参阅https://***.com/a/52714500/4657412 获取食谱。正如您所看到的,它涉及很多,并且需要对 C API 导入过程有所了解。


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

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

如果我完全这样改变它,我会得到Exception clause not allowed for function returning Python object。如果我删除类型等,我最终会得到带有分段错误的 Variant 1。 @Tzoiker:这应该只发生在实际声明为返回 Python 对象的函数中,cdef public int ... 不是。你确定你在测试时有int吗? 我的错,对不起。仔细检查它,现在可以编译,但我得到 segmentation fault python 与变体 1 一样。它可以与我如何对扩展程序进行 cythonize 连接吗?

以上是关于cython 中的异常处理的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中的异常处理机制的原理

Java中的异常处理机制

C++中的异常处理

Java中的异常处理机制

Java中的全局异常处理

web开发中的异常处理