Extending and Embedding Python, Release 3.9.5 2.1. Extending Python with C or C++ 7 (continues on next page) 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. 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 is$_{PyErr_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. You can test non-destructively whether an exception has been set with$_{PyErr_Occurred()}$ . This returns the cur- rent 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. 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 calling PyErr_*() , 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. (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.) 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 call$_{PyErr_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). Every failing$_{malloc()}$ call must be turned into an exception — the direct caller of$_{malloc()}$ (or$_{realloc()}$ ) must call$_{PyErr_NoMemory()}$ and return a failure indicator itself. All the object-creating functions (for example, PyLong_FromLong() ) already do this, so this note is only relevant to those who call$_{malloc()}$ directly. 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. 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! 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 use$_{PyExc_TypeError}$ to mean that a file couldn’t be opened (that should prob- ably be$_{PyExc_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. 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 ($_{PyInit_spam()}$ ) with an exception object: PyMODINIT_FUNC PyInit_spam( void ) { PyObject *m; m =PyModule_Create(&spammodule); if (m==NULL ) return NULL ; SpamError =PyErr_NewException( "spam.error",NULL ,NULL ); Py_XINCREF(SpamError);