Search This Blog

Labels

Friday, December 17, 2010

《扩展和嵌入Python解释器(译稿)》1.2 Intermezzo: Errors and Exceptions

1.2 Intermezzo: Errors and Exceptions 间奏曲:错误和异常

An important convention throughout the Python interpreter is the following: when a function fails, it should set an exception condition and return an error value (usually a NULLpointer). Exceptions are stored in a static global variable inside the interpreter; if this variable isNULLno exception has occurred. A second global variable stores the “associated value” of the exception (the second argument to raise). A third variable contains the stack traceback in case the error originated in Python code. These three variables are the C equivalents of the Python variables sys.exc_type, sys.exc_valueandsys.exc_traceback(see the section on module sys in the Python Library Reference). It is important to know about them to understand how errors are passed around.

Python解释器中的一个重要的惯例是:当一个函数调用失败,它应设置一个异常条件并返回一个错误值(通常是一个空指针(NULL pointer))。异常存储在解释器的静态全局变量中;如果该变量为NULL则表示没有异常发生。第二个全局变量存储了异常的“相关值”(即raise语句的第二个参数)。第三个变量存储了堆栈追踪返回(traceback)信息,以防错误是在Python程序中发生的。这三个变量是C中的,相当于Python变量中的sys.exc_type, sys.exc_value和sys.exc_traceback(参考Python Library Reference中module的sys那一节)。了解它们对于理解错误传递是很重要的。

The Python API defines a number of functions to set various types of exceptions.

Python的API定义了一系列函数用于设置各种类型的异常。

The most common one is PyErr_SetString(). Its arguments are an exception object and a C string. The exception object is usually a predefined object like PyExc_ZeroDivisionError. The C string indicates the cause of the error and is converted to a Python string object and stored as the “associated value” of the exception.

最普通的一个就是PyErr_SetString(),其参数是一个异常对象和一个C字符串。异常对象通常是预定义的对象,例如PyExc_ZeroDivisionError。C字符串指示错误发生的原因,用来转换为Python的字符串对象,并作为异常的“相关值”存储。

Another useful function is PyErr_SetFromErrno(), which only takes an exception argument and constructs the associated value by inspection of the global variable errno. The most general function isPyErr_-SetObject(),which takes two object arguments, the exception and its associated value. You don’t need to Py_INCREF()the objects passed to any of these functions.

另一个有用的函数是PyErr_SetFromErrno(),它只接受一个异常作参数,通过检查全局变量errno来构造“相关值”(associated value)。最一般的函数是PyErr_-SetObject(),它接收两个对象参数,异常和其“相关值”。对象传入任何这些函数时,不必使用Py_INCREF()增加引用计数。

You can test non-destructively whether an exception has been set with PyErr_Occurred(). This returns the current exception object, or NULL if no exception has occurred. You normally don’t need to call PyErr_- Occurred() to see whether an error occurred in a function call, since you should be able to tell from the return value.

你可以无损地测试一个异常是否已由PyErr_Occurred()设置。这将返回当前异常对象,如果无异常发生则返回NULL。通常地,不必调用PyErr_Occurred()来查看在函数调用中是否有错误发生,因为从返回值就应该能判断出来。

When a function f that calls another function g detects that the latter fails, f should itself return an error value (usually NULL or -1). It should not call one of the PyErr_*() functions — one has already been called by g. f ’s caller is then supposed to also return an error indication to its caller, again without callingPyErr_*(), and so on — the most detailed cause of the error was already reported by the function that first detected it. Once the error reaches the Python interpreter’s main loop, this aborts the currently executing Python code and tries to find an exception handler specified by the Python programmer.

当函数f 调用另一个函数g并检测到后者调用失败时,f 函数自身应返回一个错误值(通常是NULL 或 -1)。f不应调用任何 PyErr_*()函数 —— 其中某个已由函数g 调用。f 的调用者也应当同样返回一个错误指示符,同样不调用 PyErr_*() 函数,等等—— 错误发生最详细的原因已由最先检测到错误的函数报告。一旦错误发生在Python的主程序(Main loop)中,这将中止当前Python代码的执行,并尝试找到由程序员指定的相应的异常处理句柄。

(There are situations where a module can actually give a more detailed error message by calling another PyErr_-*() function, and in such cases it is fine to do so. As a general rule, however, this is not necessary, and can cause information about the cause of the error to be lost: most operations can fail for a variety of reasons.)

(在有些情况下,模块(module)实际上能通过调用另外的 PyErr_*()函数给出更详细的错误信息,并且这些情况下这样做更好。不过一般来说,这样是没有必要的并且可能导致错误发生原因的信息丢失:大多数操作可能由于各种原因而调用失败。)

To ignore an exception set by a function call that failed, the exception condition must be cleared explicitly by calling PyErr_Clear(). The only time C code should callPyErr_Clear() is if it doesn’t want to pass the error on to the interpreter but wants to handle it completely by itself (possibly by trying something else, or pretending nothing went wrong).

为忽略一个由函数调用失败而设置的异常,异常条件必须通过调用PyErr_Clear()显式地清除,仅当不想将错误传递给解释器而想自己完全地处理时,才应当在 C代码中调用 PyErr_Clear()(也可能想做其他的一些事,或当作没有错误发生)。

Every failing malloc() call must be turned into an exception — the direct caller ofmalloc() (or realloc()) must call PyErr_NoMemory() and return a failure indicator itself. All the object-creating functions (for example, PyInt_FromLong()) already do this, so this note is only relevant to those who call malloc() directly.

每次malloc()的调用失败必须转换成一个异常——malloc()的直接调用者(或realloc())必须调用PyErr_NoMemory()并返回一个调用失败的标识符。所有创建对象的函数(例如:PyInt_FromLong ())都已经这样做了,所以这个提示仅仅与直接调用 malloc() 的人有关

Also note that, with the important exception of PyArg_ParseTuple() and friends, functions that return an integer status usually return a positive value or zero for success and -1 for failure, like UNIX system calls.

也应注意到,对于PyArg_ParseTuple()和类似函数的异常来说,函数返回一个整型数表征状态,通常正值或零表示调用成功,-1表示失败,类似 UNIX 系统中的调用。

Finally, be careful to clean up garbage (by making Py_XDECREF() or Py_DECREF()calls for objects you have already created) when you return an error indicator!

最后,当你返回一个错误指示符时,注意垃圾清理(通过对你已经创建的对象调用Py_XDECREF() 和 Py_DECREF())

The choice of which exception to raise is entirely yours. There are predeclared C objects corresponding to all built-in Python exceptions, such as PyExc_ZeroDivisionError, which you can use directly. Of course, you should choose exceptions wisely — don’t usePyExc_TypeError to mean that a file couldn’t be opened (that should probably bePyExc_IOError). If something’s wrong with the argument list, the PyArg_- ParseTuple() function usually raises PyExc_TypeError. If you have an argument whose value must be in a particular range or must satisfy other conditions,PyExc_ValueError is appropriate.

抛出异常的选择完全在于你。所有的 Python 内建异常都有对应的 C 中预定义的对象,例如PyExc_Zero- DivisionError,你可以直接地使用。当然,你应明智地选择使用异常——不要使用PyExc_TypeError去表示文件不能打开(应当为:PyExc_IOError)。若参数值列表有错误,PyArg_Par-seTuple()函数通常抛出PyExc_TypeError 异常。如果你有一个参数,其值必须在某特定的范围内或必须符合其他的条件,则PyExc_ValueError是适用的。

You can also define a new exception that is unique to your module. For this, you usually declare a static object variable at the beginning of your file:

你也可以定义你的模块特有的新异常。这种情况下,你通常在代码文件开头定义一个静态对象变量:

static PyObject *SpamError; and initialize it in your module’s initialization function (initspam()) with an exception object (leaving out the error checking for now):

并在你的模块初始化函数(initspam())中用一个异常对象对其进行初始化(目前先忽略错误核查)

PyMODINIT_FUNC

initspam(void)

{

    PyObject *m;

    m = Py_InitModule("spam", SpamMethods);

    SpamError = PyErr_NewException("spam.error", NULL, NULL);

    Py_INCREF(SpamError);

    PyModule_AddObject(m, "error", SpamError);

}

Note that the Python name for the exception object is spam.error. ThePyErr_NewException() function may create a class with the base class being Exception(unless another class is passed in instead of NULL), described in the Python Library Referenceunder “Built-in Exceptions.”

注意异常在 Python 中的名字为:spam.error。PyErr_NewException()函数通过继承基类Exception (除非另一个类传递进来取代NULL),来创建一个异常类,这部分在Python Library Reference的“内建异常”(“Built-in Exceptions.”)目录描述了。

Note also that the SpamErrorvariable retains a reference to the newly created exception class; this is intentional! Since the exception could be removed from the module by external code, an owned reference to the class is needed to ensure that it will not be discarded, causing SpamErrorto become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. We discuss the use of PyMODINIT_FUNC as a function return type later in this sample.

同样注意到SpamError变量存储了一个新创建异常类的引用;这样做是有意为之的!由于异常可以被模块之外的代码移除,为确保不被废弃而导致 SpamError 成为一个悬吊着的指针,一个自有的对异常类的引用是必要的。倘若 SpamError 成为一个悬吊的指针,抛出异常的C代码可能引发内存溢出或其他不可预期的副作用。PyMODINIT_FUNC作为函数返回类型的使用,我们后面将在这个例子中讨论。

No comments:

Post a Comment