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 (often a NULL
pointer). Exceptions
are stored in a static global variable in `Python/errors.c
'; if
this variable is NULL
no exception has occurred. A second
static global variable stores the `associated value' of the exception
- the second argument to raise
.
The file `errors.h
' declares a host of functions to set various
types of exceptions. The most common one is err_setstr()
-
its arguments are an exception object (e.g. RuntimeError
-
actually it can be any string object) and a C string indicating the
cause of the error (this is converted to a string object and stored as
the `associated value' of the exception). Another useful function is
err_errno()
, which only takes an exception argument and
constructs the associated value by inspection of the (UNIX) global
variable errno. The most general function is err_set()
, which
takes two object arguments, the exception and its associated value.
You don't need to INCREF()
the objects passed to any of these
functions.
You can test non-destructively whether an exception has been set with
err_occurred()
. However, most code never calls
err_occurred()
to see whether an error occurred or not, but
relies on error return values from the functions it calls instead.
When a function that calls another function detects that the called
function fails, it should return an error value (e.g. NULL
or
-1
) but not call one of the err_*
functions - one has
already been called. The caller is then supposed to also return an
error indication to its caller, again without calling
err_*()
, and so on - the most detailed cause of the error was
already reported by the function that first detected it. Once the
error has reached Python's interpreter main loop, this aborts the
currently executing Python code and tries to find an exception handler
specified by the Python programmer.
(There are situations where a module can actually give a more detailed
error message by calling another err_*
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.)
To ignore an exception set by a function call that failed, the
exception condition must be cleared explicitly by calling
err_clear()
. The only time C code should call
err_clear()
is if it doesn't want to pass the error on to the
interpreter but wants to handle it completely by itself (e.g. by
trying something else or pretending nothing happened).
Finally, the function err_get()
gives you both error variables
and clears them. Note that even if an error occurred the second
one may be NULL
. You have to XDECREF()
both when you
are finished with them. I doubt you will need to use this function.
Note that a failing malloc()
call must also be turned into an
exception - the direct caller of malloc()
(or
realloc()
) must call err_nomem()
and return a failure
indicator itself. All the object-creating functions
(newintobject()
etc.) already do this, so only if you call
malloc()
directly this note is of importance.
Also note that, with the important exception of getargs()
,
functions that return an integer status usually return 0
or a
positive value for success and -1
for failure.
Finally, be careful about cleaning up garbage (making XDECREF()
or DECREF()
calls for objects you have already created) when
you return an error!
The choice of which exception to raise is entirely yours. There are
predeclared C objects corresponding to all built-in Python exceptions,
e.g. ZeroDevisionError
which you can use directly. Of course,
you should chose exceptions wisely - don't use TypeError
to
mean that a file couldn't be opened (that should probably be
IOError
). If anything's wrong with the argument list the
getargs()
function raises TypeError
. If you have an
argument whose value which must be in a particular range or must
satisfy other conditions, ValueError
is appropriate.
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, e.g.
static object *FooError;
and initialize it in your module's initialization function
(initfoo()
) with a string object, e.g. (leaving out the error
checking for simplicity):
void initfoo() { object *m, *d; m = initmodule("foo", foo_methods); d = getmoduledict(m); FooError = newstringobject("foo.error"); dictinsert(d, "error", FooError); }