Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
向异常添加属性的Python C扩展_Python_Python 3.x_Python C Api - Fatal编程技术网

向异常添加属性的Python C扩展

向异常添加属性的Python C扩展,python,python-3.x,python-c-api,Python,Python 3.x,Python C Api,我正在包装一个C库,它在失败时返回有限数量的错误代码之一。当错误发生时,我希望将错误代码添加为C异常的一个属性,以便Python代码可以检索它并将错误代码映射到人类可读的异常。这可能吗 例如,我想在Python层中执行此操作: try: call_my_library_func() except MyLibraryError as ex: print("Error code was %s" % ex.code) 我最接近的方法是使用PyErr\u SetObject PyObj

我正在包装一个C库,它在失败时返回有限数量的错误代码之一。当错误发生时,我希望将错误代码添加为C异常的一个属性,以便Python代码可以检索它并将错误代码映射到人类可读的异常。这可能吗

例如,我想在Python层中执行此操作:

try:
    call_my_library_func()
except MyLibraryError as ex:
    print("Error code was %s" % ex.code)
我最接近的方法是使用
PyErr\u SetObject

PyObject *tuple = PyTuple_New(2);
PyTuple_SetItem(tuple, 0, PyUnicode_FromString("Helpful error message"));
PyTuple_SetItem(tuple, 1, PyLong_FromLong(257));
//PyErr_SetString(MyLibraryError, "Helpful error message\n");
PyErr_SetObject(MyLibraryError, tuple);
然后我可以这样做:

try:
    call_my_library_func()
except MyLibraryError as ex:
    message, code = ex.args[0], -1
    if len(ex.args > 1):
        code = ex.args[1]

C API异常处理在很大程度上是根据其类、参数(传递给构造函数)和回溯来引发异常的,因此最好遵循该方案。将元组作为参数传递的基本方法可能是最好的选择

但是,有两个选项可以使您的异常类在Python端稍微更为用户友好:

  • 您可以在自定义的
    \uuuu init\uuu
    方法中处理参数,以在类上设置
    code
    属性
  • code
    定义为访问
    args[1]
    的异常类的属性
  • 我已经说明了选项2,但我不认为有很大的理由选择其中一个


    简要解释下面的示例代码:要使用C API定义异常,请使用
    PyErr_NewException
    ,它将可选的基类和字典作为第二个和第三个参数。所使用的函数(无论是
    \uuuuu init\uuuu>还是属性定义)都应该是字典的一部分

    为了定义属性定义,我已经用Python编写了代码,并使用了
    PyRun\u String
    ,因为用Python编写比用C更容易,而且我怀疑这段代码对性能至关重要。这些函数最终被注入到传递给
    PyRun\u String
    的全局字典中

    C代码:

    #包括
    PyObject*make_getter_code(){
    常量字符*代码=
    “def代码(自身):\n”
    “请尝试:\n”
    “返回self.args[1]\n”
    “除索引器外:\n”
    “返回-1\n”
    “代码=属性(代码)\n”
    “def消息(自身):\n”
    “请尝试:\n”
    “返回self.args[0]\n”
    “除索引器外:\n”
    返回“”\n
    “\n”;
    PyObject*d=PyDict_New();
    PyObject*dict_globals=PyDict_New();
    PyDict_SetItemString(dict_globals,“uuu内置项”,PyEval_GetBuiltins());
    PyObject*output=PyRun\u字符串(代码,Py\u文件\u输入,dict\u全局,d);
    if(输出==NULL){
    Py_DECREF(d);
    返回NULL;
    }
    Py_DECREF(输出);
    Py_DECREF(dict_globals);
    返回d;
    }
    静态PyObject*MyLibraryError;
    静态PyObject*my_library_函数(PyObject*self){
    /*出了点问题*/
    PyObject*tuple=PyTuple\u New(2);
    PyTuple_SetItem(tuple,0,PyUnicode_FromString(“有用的错误消息”);
    PyTuple_SetItem(tuple,1,PyLong_from long(257));
    PyErr_SetObject(MyLibraryError,元组);
    返回NULL;
    }
    静态PyMethodDef方法[]={
    {“我的库函数”,我的库函数,METH\u NOARGS,
    “提出错误。”},
    {NULL,NULL,0,NULL}/*哨兵*/
    };
    静态结构PyModuleDef librarymodule={
    PyModuleDef_HEAD_INIT,
    “库”、/*模块名称*/
    NULL,/*模块文档可能为NULL*/
    -1、/*模块每个解释器状态的大小,
    如果模块在全局变量中保持状态,则为-1*/
    方法
    };
    PyMODINIT_FUNC
    PyInit_库(void){
    PyObject*m;
    m=PyModule\u创建(&librarymodule);
    如果(m==NULL)
    返回NULL;
    PyObject*exc_dict=make_getter_code();
    if(exc_dict==NULL){
    返回NULL;
    }
    MyLibraryError=PyErr\u NewException(“library.MyLibraryError”,
    NULL,//用于拾取基类
    执行董事会);
    PyModule_AddObject(m,“MyLibraryError”,MyLibraryError);
    返回m;
    }
    
    作为更优雅的Python接口示例,您的Python代码更改为:

    try:
        my_library_func()
    except MyLibraryError as ex:
        message, code = ex.message, ex.code
    

    您能否定义类
    MyLibraryError
    ,以便
    MyLibraryError.code
    是一个返回
    args[1]
    的属性?@DavidW当前
    MyLibraryError
    在本例中是一个用C定义的异常。您是否建议创建一个Python异常,将该C异常作为子类,如
    class MyPyLibraryError(LibraryError)@property def code()返回getattr(self,'code',-1)
    ?我建议
    类MyPyLibraryError(LibraryError)@property def code(self):如果len(self.args)返回self.args[1]>=2 else-1
    。如果
    MyLibraryError
    是您创建的一个类,则可以在C中创建属性,而不是创建Python子类。@DavidW很好,然后在Python中我将执行
    尝试:调用\u my_library\u func(),MyLibraryError除外,例如:raise MyPylLibraryError(*ex.args)
    对——最好将其添加为装饰器,并将其应用于所有my函数?不完全正确-在C中,您会引发
    MyPyLibraryError
    (使用元组,就像您正在做的那样)。在Python中,您只需执行
    try:call\u my\u library\u func()除了MyPyLibraryError作为ex:code=ex.code#…和任何你想要的东西都很酷。我不知道
    PyRun_字符串
    ,也不知道在
    PyErr_NewException
    期间设置dict。谢谢。我添加了一个编辑,显示了如何直接执行你最初要求的操作(在当前异常上设置一个属性)因此,避免与自定义类混淆。我个人仍然会使用自定义类,但关于您的附录,我必须执行
    PyObject\u SetAttrString(type,…)
    ,而不是
    。使用
    引发了一个
    属性错误:“str”对象没有属性“err”
    。此外,对于
    PyErr\u还原
    ,有以下警告:“如果您不理解,请不要使用此函数。我警告过您。”-所以我会想