GNU Info

Info Node: (python2.1-api.info)Exceptions

(python2.1-api.info)Exceptions


Next: Embedding Python Prev: Objects Up: Introduction
Enter node , (file) or (file)node

Exceptions
==========

The Python programmer only needs to deal with exceptions if specific
error handling is required; unhandled exceptions are automatically
propagated to the caller, then to the caller's caller, and so on, until
they reach the top-level interpreter, where they are reported to the
user accompanied by a stack traceback.

For C programmers, however, error checking always has to be explicit.
All functions in the Python/C API can raise exceptions, unless an
explicit claim is made otherwise in a function's documentation.  In
general, when a function encounters an error, it sets an exception,
discards any object references that it owns, and returns an error
indicator -- usually `NULL' or `-1'.  A few functions return a Boolean
true/false result, with false indicating an error.  Very few functions
return no explicit error indicator or have an ambiguous return value,
and require explicit testing for errors with `PyErr_Occurred()' .

Exception state is maintained in per-thread storage (this is equivalent
to using global storage in an unthreaded application).  A thread can be
in one of two states: an exception has occurred, or not.  The function
`PyErr_Occurred()' can be used to check for this: it returns a borrowed
reference to the exception type object when an exception has occurred,
and `NULL' otherwise.  There are a number of functions to set the
exception state: `PyErr_SetString()'  is the most common (though not
the most general) function to set the exception state, and
`PyErr_Clear()'  clears the exception state.

The full exception state consists of three objects (all of which can be
`NULL'): the exception type, the corresponding exception value, and the
traceback.  These have the same meanings as the Python objects
`sys.exc_type', `sys.exc_value', and `sys.exc_traceback'; however, they
are not the same: the Python objects represent the last exception being
handled by a Python `try' ... `except' statement, while the C level
exception state only exists while an exception is being passed on
between C functions until it reaches the Python bytecode interpreter's
main loop, which takes care of transferring it to `sys.exc_type' and
friends.

Note that starting with Python 1.5, the preferred, thread-safe way to
access the exception state from Python code is to call the function
`sys.exc_info()', which returns the per-thread exception state for
Python code.  Also, the semantics of both ways to access the exception
state have changed so that a function which catches an exception will
save and restore its thread's exception state so as to preserve the
exception state of its caller.  This prevents common bugs in exception
handling code caused by an innocent-looking function overwriting the
exception being handled; it also reduces the often unwanted lifetime
extension for objects that are referenced by the stack frames in the
traceback.

As a general principle, a function that calls another function to
perform some task should check whether the called function raised an
exception, and if so, pass the exception state on to its caller.  It
should discard any object references that it owns, and return an error
indicator, but it should _not_ set another exception -- that would
overwrite the exception that was just raised, and lose important
information about the exact cause of the error.

A simple example of detecting exceptions and passing them on is shown
in the `sum_sequence()'  example above.  It so happens that that
example doesn't need to clean up any owned references when it detects
an error.  The following example function shows some error cleanup.
First, to remind you why you like Python, we show the equivalent Python
code:

     def incr_item(dict, key):
         try:
             item = dict[key]
         except KeyError:
             item = 0
         dict[key] = item + 1

Here is the corresponding C code, in all its glory:

     int incr_item(PyObject *dict, PyObject *key)
     {
         /* Objects all initialized to NULL for Py_XDECREF */
         PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
         int rv = -1; /* Return value initialized to -1 (failure) */
     
         item = PyObject_GetItem(dict, key);
         if (item == NULL) {
             /* Handle KeyError only: */
             if (!PyErr_ExceptionMatches(PyExc_KeyError))
                 goto error;
     
             /* Clear the error and use zero: */
             PyErr_Clear();
             item = PyInt_FromLong(0L);
             if (item == NULL)
                 goto error;
         }
         const_one = PyInt_FromLong(1L);
         if (const_one == NULL)
             goto error;
     
         incremented_item = PyNumber_Add(item, const_one);
         if (incremented_item == NULL)
             goto error;
     
         if (PyObject_SetItem(dict, key, incremented_item) < 0)
             goto error;
         rv = 0; /* Success */
         /* Continue with cleanup code */
     
      error:
         /* Cleanup code, shared by success and failure path */
     
         /* Use Py_XDECREF() to ignore NULL references */
         Py_XDECREF(item);
         Py_XDECREF(const_one);
         Py_XDECREF(incremented_item);
     
         return rv; /* -1 for error, 0 for success */
     }

This example represents an endorsed use of the `goto' statement in C!
It illustrates the use of `PyErr_ExceptionMatches()'  and
`PyErr_Clear()'  to handle specific exceptions, and the use of
`Py_XDECREF()'  to dispose of owned references that may be `NULL' (note
the `X' in the name; `Py_DECREF()' would crash when confronted with a
`NULL' reference).  It is important that the variables used to hold
owned references are initialized to `NULL' for this to work; likewise,
the proposed return value is initialized to `-1' (failure) and only set
to success after the final call made is successful.


automatically generated by info2www version 1.2.2.9