如果某些参数无效,我想在C++类构造函数中引发异常,例如,PyErr_SetString(PyExc_ValueError, "Error occurred")
。不幸的是,它不能正确传播,我得到了SystemError: <class 'testlib.foo.Foo'> returned a result with an error set
。甚至有可能使其在构造函数中工作?
几个问题:
.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
可以通过将throw std::invalid_argument("Some error")
与Foo() except +
中的c_foo.pxd
一起执行来解决前两个问题,但不适用于任意python异常。
最佳答案
首先,请注意,这样做有可能犯错。 Python范围规则与C++范围规则不同,Cython在很大程度上遵循Python范围规则。考虑
# distutils: language=c++
cdef extern from "something.hpp":
cdef cppclass C:
C()
def f(x):
cdef C c
if x>0:
c = C()
c
在任何分支上都必须可用,因此默认情况下在函数开始时进行了初始化:CYTHON_UNUSED C __pyx_v_c;
此默认初始化不会具有标准的异常处理(原则上,这种情况下可能会发生Python异常,但是在构造函数中处理C++异常并制作在C++中编译的代码非常困难) 。因此,如果您有该类的任何堆栈分配变量,则可能要冒使Cython进入设置了异常的状态的风险,但它认为不应这样做。
话虽如此,我认为做到这一点的方法可能是使用静态工厂功能。 Cython确实了解这些方面的异常规范(它不了解C++构造函数上的
except *
可能是一个小错误……)# distutils: language=c++
cdef extern from *:
"""
class Foo {
public:
Foo() {
PyErr_SetString(PyExc_ValueError, "Error occurred");
}
~Foo() {}
static Foo getFoo() {
return Foo();
}
};
"""
cdef cppclass Foo:
Foo()
@staticmethod
Foo getFoo() except *
def f():
Foo.getFoo()
此代码从Cython生成以下C++代码:Foo::getFoo(); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 23, __pyx_L1_error)
从而实现您的期望-它检查C++异常并按预期方式处理它。我仍然认为这是一个坏主意。
关于python - 使用cython从C++构造函数传播异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64773494/