'Exception ignored in:' cython 扩展 类 及其 `cdef` 方法

'Exception ignored in:' cython extension classes and their `cdef` methods

我有一些 cython classes,在他们的 cdef 方法中,我有 assert 语句。但是,例如,我没有得到我可以捕获的 AssertionError,而是得到了 Exception ignored in: 但我无法捕获它(pytest 也将此报告为警告) 此外,不会引发和忽略其他发生的错误(assert 错误除外)。

作为一个简单的例子,假设我有一个名为 Node.pyx

的节点 class
cdef class Node:
    def __init__(self, ident):
        self.identifier = ident
        self.children = set()
        self.parents = set()

    cdef void add_child(self, Node child):
        self.children.add(child.identifier)
        child.parents.add(self.identifier)

    cdef void add_parent(self, Node parent):
        self.parent.add(parent.identifier)
        parent.children.add(self.identifier)

    cdef void remove_child(self, Node child):
        assert child.identifier in self.children

        self.children.remove(child.identifier)
        child.parents.remove(self.identifier)

    cdef void remove_parent(self, Node parent):
        assert parent.identifier in self.parents

        self.parents.remove(parent.identifier)
        parent.children.remove(self.identifier)

及其对应的Node.pxd文件

cdef class Node:
    cdef int identifier
    cdef set children
    cdef set parents

    cdef void add_child(self, Node child)
    cdef void add_parent(self, Node parent)
    cdef void remove_child(self, Node child)
    cdef void remove_parent(self, Node parent)

因为这就是全部 cdef,它只能被另一个 pyx 脚本或函数使用。所以让我们用另一个脚本来测试节点,node_test.pyx

from Node cimport Node

def test_error_raising():
        cdef Node node1 = Node(1)
        cdef Node node2 = Node(2)

        node1.remove_parent(node2)

现在,如果我用 cythonize -i *pyx 或 simpel 安装脚本编译所有这些,所有编译都很好。然而,运行这个test_node.py

from node_test import test_error_raising

try:
    test_error_raising()
except AssertionError:
    print("You have assertion error!")

我明白了

AssertionError
Exception ignored in: 'Node.Node.remove_parent'
Traceback (most recent call last):
  File "test_node.py", line 5, in <module>
    test_error_raising()
AssertionError:

系统和版本:

Distributor ID: Ubuntu
Description:    Ubuntu 20.04.2 LTS

Cython version 0.29.21

Python 3.8.5

所以,这个问题还是比较容易解决的。应该从 cdef 函数中删除 void 以便它 return 正确的错误消息,而不是忽略错误。 原因是,当函数定义为 void return 时,将创建、报告、销毁错误消息,并且函数将从异常点 return 和 return 语句永远不会被执行。

因此,只需从 remove_childremove_parent 函数中删除 .pyx.pxd 中的 void 即可使其按预期运行,并且错误信息可以正常咳嗽。

更多信息here

参见:

https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#error-return-values

除了上面的,有用的:

cdef <some_type> some_func() except? -1:

cdef <some_type> some_func() except *:

cdef <some_type> some_func() except +:

描述了表格。