【发布时间】:2021-02-22 15:53:47
【问题描述】:
如果某些参数无效,我想在 C++ 类构造函数中引发异常,例如,执行PyErr_SetString(PyExc_ValueError, "Error occurred")。不幸的是,它没有正确传播,我得到了SystemError: <class 'testlib.foo.Foo'> returned a result with an error set。甚至有可能让它在构造函数中工作吗?
几个问题:
- 所有逻辑都应该在 C++ 类中,而不是分散到
.pyx文件中。 - 应该在构造函数内部进行验证,这样就不需要在对象创建后调用单独的
init()方法。 - 应该不仅可以提出一些标准例外,还可以提出任何自定义。
setup.py
from Cython.Build import cythonize
from Cython.Distutils import Extension
from setuptools import find_packages, setup
setup(
name='testlib',
python_requires='~=3.7.0',
packages=find_packages('src'),
package_dir={'': 'src'},
ext_modules=cythonize(
[
Extension(
'testlib.foo',
['src/testlib/foo.pyx', 'src/testlib/c_foo.cpp'],
extra_compile_args=['-std=c++11'], language='c++',
)
], language_level='3'
)
)
src/testlib/c_foo.h
#ifndef FOO_H
#define FOO_H
#define PY_SSIZE_T_CLEAN
#include <Python.h>
namespace foo {
class Foo {
public:
Foo();
~Foo();
};
}
#endif
src/testlib/c_foo.cpp
#include "c_foo.h"
namespace foo {
Foo::Foo() {
PyErr_SetString(PyExc_ValueError, "Error occurred");
}
Foo::~Foo() {}
}
src/testlib/c_foo.pxd
cdef extern from "c_foo.h" namespace "foo":
cdef cppclass Foo:
Foo()
src/testlib/foo.pyx
from .c_foo cimport Foo as CFoo
cdef class Foo:
cdef CFoo *c_foo
def __cinit__(self):
self.c_foo = new CFoo()
def __dealloc__(self):
del self.c_foo
测试/test_foo.py
import pytest
from testlib.foo import Foo
def test_foo():
with pytest.raises(ValueError):
Foo() # SystemError: <class 'testlib.foo.Foo'> returned a result with an error set
前两个问题可以通过在c_foo.pxd 中执行throw std::invalid_argument("Some error") 和Foo() except + 来解决,但它不适用于任意python 异常。
【问题讨论】:
-
你试过
except *吗(这意味着可以设置任何Python异常,无法从返回类型中判断)?不过,空的 c++ 构造函数可能存在特定问题。 -
docs.cython.org/en/latest/src/userguide/… 中描述的可能方法是否不够(通过选择适当的 C++ 异常,或尝试分配“异常转换”函数或将所有异常转换为 Py 端的某个特定异常) .
-
@DavidW 是的,我试过了,它给了
Exception clause not allowed for function returning Python object -
这听起来像是一个错误。复杂之处在于,任何堆栈分配的 C++ 对象都在 Cython 函数的开头进行初始化,在该函数中实际上不可能添加异常检查,因此可能很难用它做很多有用的事情。
-
你可以用
except *而不是构造函数来查看静态工厂函数吗?
标签: python c++ exception cython