'异常被忽略:'cython 扩展类及其`cdef` 方法

问题描述

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

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

的 Node 类
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

解决方法

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

因此,只需从 voidremove_child 中的 remove_parent.pyx 函数中删除 .pxd 将使其行为如预期,错误消息可以正常咳嗽。

更多信息here