博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
扩展Python模块系列(五)----异常和错误处理
阅读量:6432 次
发布时间:2019-06-23

本文共 5820 字,大约阅读时间需要 19 分钟。

在上一节中,讨论了在用C语言扩展Python模块时,应该如何处理无处不在的引用计数问题。重点关注的是在实现一个C Python的函数时,对于一个PyObject对象,何时调用Py_INCREF和Py_DECREF。在编写C语言代码时,需要了解Python提供的C/C++ API的实现细节,特别是有的API内部实现会调用Py_INCREF,这时自己编写的函数可能需要调用Py_DECREF,而有的API内部实现只是borrowed reference,此时一般不应该调用Py_DECREF。

本节讨论在C扩展Python时,如何对异常和错误进行处理。Python解释器的实现中有一个重要的约定:当一个函数失败,它应该设置一个exception condition并返回一个错误值(通常是NULl指针)。异常是存放在解释器的一个全局变量中,如果这个变量是NULL,那么没有异常发生。另外一个全局变量存放的是跟异常相关的值,还有一个变量包含了stack traceback,记录了产生错误时的Python Code。这三个变量对应Python的sys模块的三个变量,sys.exc_type, sys.exc_value, sys.exc_traceback。在1.5版本之后,这三个变量用exc_info()代替了。

这三个全局的变量在C Python 源码中是存放在PyThreadState *_PyThreadState_Current这个结构体中的, 而_PyThreadState_Current是在pythonrun.c的Py_InitializeEx中初始化的。

 PyThreadState结构体定义:

红色框中的三个变量就是error indicator。

常见的Python设置异常的接口

Python API定义了一系列的function来设置不同类型的异常,定义在Python源码中的Python/error.c中。

PyErr_SetString:

最常用的莫过于PyErr_SetString,函数的原型为:

 函数的作用是设置Python解释器的全局error indicator。 

参数分别为一个exception对象和一个描述异常的字符串。exception object通常是一个已经定义好的object, 不需要增加它的引用计数,在PyErr_SetString源码中已经调用了Py_XINCREF(exception)。

/* Predefined exceptions */PyAPI_DATA(PyObject *) PyExc_BaseException;PyAPI_DATA(PyObject *) PyExc_Exception;PyAPI_DATA(PyObject *) PyExc_StopIteration;PyAPI_DATA(PyObject *) PyExc_GeneratorExit;PyAPI_DATA(PyObject *) PyExc_StandardError;PyAPI_DATA(PyObject *) PyExc_ArithmeticError;PyAPI_DATA(PyObject *) PyExc_LookupError;PyAPI_DATA(PyObject *) PyExc_AssertionError;PyAPI_DATA(PyObject *) PyExc_AttributeError;PyAPI_DATA(PyObject *) PyExc_EOFError;PyAPI_DATA(PyObject *) PyExc_FloatingPointError;PyAPI_DATA(PyObject *) PyExc_EnvironmentError;PyAPI_DATA(PyObject *) PyExc_IOError;PyAPI_DATA(PyObject *) PyExc_OSError;PyAPI_DATA(PyObject *) PyExc_ImportError;PyAPI_DATA(PyObject *) PyExc_IndexError;PyAPI_DATA(PyObject *) PyExc_KeyError;PyAPI_DATA(PyObject *) PyExc_KeyboardInterrupt;PyAPI_DATA(PyObject *) PyExc_MemoryError;PyAPI_DATA(PyObject *) PyExc_NameError;PyAPI_DATA(PyObject *) PyExc_OverflowError;PyAPI_DATA(PyObject *) PyExc_RuntimeError;PyAPI_DATA(PyObject *) PyExc_NotImplementedError;PyAPI_DATA(PyObject *) PyExc_SyntaxError;PyAPI_DATA(PyObject *) PyExc_IndentationError;PyAPI_DATA(PyObject *) PyExc_TabError;PyAPI_DATA(PyObject *) PyExc_ReferenceError;PyAPI_DATA(PyObject *) PyExc_SystemError;PyAPI_DATA(PyObject *) PyExc_SystemExit;PyAPI_DATA(PyObject *) PyExc_TypeError;PyAPI_DATA(PyObject *) PyExc_UnboundLocalError;PyAPI_DATA(PyObject *) PyExc_UnicodeError;PyAPI_DATA(PyObject *) PyExc_UnicodeEncodeError;PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError;PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError;PyAPI_DATA(PyObject *) PyExc_ValueError;PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError;

PyErr_SetObject:

从PyErr_SetString实现的源码中可以看到,内部调用了PyErr_SetObject, PyErr_SetObject内部又调用了PyErr_Restore。

PyErr_SetObject函数原型是:

PyObject* PyErr_Occurred():

函数返回当前是否有异常发生,如果有返回current exception object【borrowed reference】,否则返回NULL

int PyErr_ExceptionMatches(PyObject* exc)

int PyErr_GivenExceptionMatches(PyObject* given, PyObject* exc):

判断给定的异常对象是否符合exc类型。

PyErr_Clear():

清除Python解释器的error indicator。Python解释器不会检测到有异常发生。

PyErr_Fetch(PyObject** ptype, PyObject** pvalue, PyObject** ptraceback)

获得error indicator的三个变量,如果error indicator没有设置,ptype, pvalue, ptraceback都被设置为NULL。error indicator会被置空,将三个变量的地址赋给ptype, pvalue, ptraceback。

PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback)

使用给定的三个变量设置error indicator。

 

代码中使用异常接口的规则

 如果函数f调用函数g,其中g函数失败,应该怎样设置异常呢?通常的做法是在g中调用设置异常的各个接口,比如PyErr_SetString,通知Python解释器有异常发生了,函数g然后返回一个NULL给函数f,而f不用再处理异常,因为函数g已经上报过了。比如我们自己写的函数调用了PyArg_ParseTuple(),这个函数出现错误时返回NULL,但是我们不用自己上报异常,异常的上报由PyArg_ParseTuple本身处理。一旦通过PyErr_SetString设置了异常,那么Python解释器在主循环中检测到error indicator被设置,会暂停执行当前的Python Code,会试图寻找exception handler来处理异常。

 

Python源码中使用异常处理接口的例子

PyTuple_GetItem:

PyObject *PyTuple_GetItem(register PyObject *op, register Py_ssize_t i){    if (!PyTuple_Check(op)) {        PyErr_BadInternalCall();        return NULL;    }    if (i < 0 || i >= Py_SIZE(op)) {        // 如果索引有错误,设置异常        PyErr_SetString(PyExc_IndexError, "tuple index out of range");        return NULL;    }    return ((PyTupleObject *)op) -> ob_item[i];}

PyErr_SetString实现:

voidPyErr_SetString(PyObject *exception, const char *string){    PyObject *value = PyString_FromString(string);    PyErr_SetObject(exception, value);    Py_XDECREF(value);}

PyErr_SetObject实现:

voidPyErr_SetObject(PyObject *exception, PyObject *value){    Py_XINCREF(exception);  // 增加引用计数    Py_XINCREF(value);   // 增加引用计数    PyErr_Restore(exception, value, (PyObject *)NULL);}

PyErr_Restore实现:

voidPyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback){    PyThreadState *tstate = PyThreadState_GET();    PyObject *oldtype, *oldvalue, *oldtraceback;    if (traceback != NULL && !PyTraceBack_Check(traceback)) {        /* XXX Should never happen -- fatal error instead? */        /* Well, it could be None. */        Py_DECREF(traceback);        traceback = NULL;    }    /* Save these in locals to safeguard against recursive       invocation through Py_XDECREF */    oldtype = tstate->curexc_type;    oldvalue = tstate->curexc_value;    oldtraceback = tstate->curexc_traceback;    // 设置当前的异常    tstate->curexc_type = type;    tstate->curexc_value = value;    tstate->curexc_traceback = traceback;        // 旧的异常变量需要减少引用计数    Py_XDECREF(oldtype);    Py_XDECREF(oldvalue);    Py_XDECREF(oldtraceback);}

 

自定义异常

 除了使用Python已经定义好的异常对象之外,我们可以自定义异常类型,主要是通过PyErr_NewException创建一个异常对象。

1. 在文件头部定义一个static 变量:

static PyObject *MyError;

2. 在模块初始化时传入我们自定义的异常对象

PyMODINIT_FUNCinittest(void){    PyObject *m;    m = Py_InitModule("test", TestMethods);    if (m == NULL)        return;    MyError = PyErr_NewException("test.error", NULL, NULL);    Py_INCREF(MyError);    PyModule_AddObject(m, "error",MyError);}

3. 在通过PyErr_SetString设置异常时,第一个参数传入MyError即可。

 

转载于:https://www.cnblogs.com/jianmu/p/7469146.html

你可能感兴趣的文章
控制台下的字符图像界面
查看>>
c++ 数组形参
查看>>
Memcache的安全
查看>>
KVM/Xen and libvirt: currentMemory, memory and ballooning
查看>>
metasploit 笔记
查看>>
hdu 2845(最大不连续子序列)
查看>>
J2me的异常处理和多线程
查看>>
选择、生成-EA与数据库的交互-by小雨
查看>>
客户网页WIZnet无线解决方案 之 太阳能逆变器
查看>>
CCRepeatForever和CCDelayTime
查看>>
android jni aotf 错误
查看>>
Azkaban的功能特点(二)
查看>>
[RxJS] Add debug method to Observable in TypeScript
查看>>
1、金融之关于BIAS
查看>>
[转]ASP.NET Core基本原理(11)-管理应用程序状态
查看>>
VS Code搭建.NetCore开发环境(一)
查看>>
01字典树贪心查询+建立+删除(个人模版)
查看>>
java-信息安全(十一)-非对称加密算法ECC以及ECDSA签名
查看>>
(转)Flex的编译过程--ActionScript字节码(ABC)
查看>>
Directory Listing Denied
查看>>