Exception wrapping

The raw Python/C API has a protocol for allowing C extensions to use Python's exception mechanism. It is a goal of Pyd's that a user will never have to deal with this protocol. Instead, Pyd provides a mechanism for translating Python exceptions into D exceptions, and for translating D exceptions into Python exceptions.

When wrapping functions and classes, the latter is usually of more interest. All wrapped functions, methods, constructors, properties, &c, will catch any otherwise uncaught D exceptions and translate them into a Python RuntimeError. (In the future, I may define a new Python exception type for this purpose.) This is fairly important, as any uncaught D exceptions would otherwise crash the Python interpreter.

The PydObject class wraps a portion of the Python/C API. Whenever a Python exception is raised by a method of PydObject, it will be thrown as a PythonException. This is a normal D exception that knows how to carry the complete state of a Python exception. PythonException objects have three properties, type, value, and traceback, which each return an owned reference to a PyObject* corresponding to a different part of a Python exception. If the PythonException goes uncaught, the function wrapper will catch it, and translate it back into the original Python exception.

Pyd provides the following exception-related functions:

void handle_exception();
This checks if a Python exception has been set and, if it has, throws a PythonException containing the set exception. It will also clear the Python error condition.
T exception_catcher(T) (T delegate() dg);
This template function calls the passed delegate within a try ... catch block, and if any exception occurs, sets an appropriate Python error condidition and returns an "invalid" value. (These are null for PyObject* and -1 for int. These and void are the only acceptable types which may be passed to T.) Otherwise, it will simply return whatever the delegate does. It is recommended that any function that interfaces directly with Python place its contents within a function literal in a call to this function. (D can usually infer the return type of a function literal. If it can't, you might want to review D's syntax for function literals.) For example:
extern (C)
PyObject* some_func(PyObject* self) {
    return exception_catcher({
        PyObject* result;
        // do something useful
        return result;
    });
}