Skip to content

Commit 019eb1e

Browse files
committed
Adds how to create specialised exceptions in C.
1 parent 1415bff commit 019eb1e

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

doc/sphinx/source/exceptions.rst

+119
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,122 @@ The other thing to note is that if there are multiple calls to ``PyErr_SetString
8787
return NULL;
8888
}
8989
90+
---------------------------------
91+
Creating Specialised Excpetions
92+
---------------------------------
93+
94+
Often you need to create an Exception class that is specialised to a particular module. This can be done quite easily using either the ``PyErr_NewException`` or the ``PyErr_NewExceptionWithDoc`` functions. These create new exception classes that can be added to a module. For example:
95+
96+
97+
.. code-block:: c
98+
99+
/* Exception types as statics to be initialised during module initialisation. */
100+
static PyObject *ExceptionBase;
101+
static PyObject *SpecialisedError;
102+
103+
/* Standard module initialisation: */
104+
static PyModuleDef noddymodule = {
105+
PyModuleDef_HEAD_INIT,
106+
"noddy",
107+
"Example module that creates an extension type.",
108+
-1,
109+
NULL, NULL, NULL, NULL, NULL
110+
};
111+
112+
PyMODINIT_FUNC
113+
PyInit_noddy(void)
114+
{
115+
PyObject* m;
116+
117+
noddy_NoddyType.tp_new = PyType_GenericNew;
118+
if (PyType_Ready(&noddy_NoddyType) < 0)
119+
return NULL;
120+
121+
m = PyModule_Create(&noddymodule);
122+
if (m == NULL)
123+
return NULL;
124+
125+
Py_INCREF(&noddy_NoddyType);
126+
PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);
127+
128+
/* Initialise exceptions here.
129+
*
130+
* Firstly a base class exception that inherits from the builtin Exception.
131+
* This is acheieved by passing NULL as the PyObject* as the third argument.
132+
*
133+
* PyErr_NewExceptionWithDoc returns a new reference.
134+
*/
135+
ExceptionBase = PyErr_NewExceptionWithDoc(
136+
"noddy.ExceptionBase" /* char *name */,
137+
"Base exception class for the noddy module." /* char *doc */,
138+
NULL /* PyObject *base */,
139+
NULL /* PyObject *dict */);
140+
/* Error checking: this is oversimplified as it should decref
141+
* anything created above such as m.
142+
*/
143+
if (! ExceptionBase) {
144+
return NULL;
145+
} else {
146+
PyModule_AddObject(m, "ExceptionBase", ExceptionBase);
147+
}
148+
/* Now a sub-class exception that inherits from the base exception above.
149+
* This is acheieved by passing non-NULL as the PyObject* as the third argument.
150+
*
151+
* PyErr_NewExceptionWithDoc returns a new reference.
152+
*/
153+
SpecialisedError = PyErr_NewExceptionWithDoc(
154+
"noddy.SpecialsiedError" /* char *name */,
155+
"Some specialised problem description here." /* char *doc */,
156+
ExceptionBase /* PyObject *base */,
157+
NULL /* PyObject *dict */);
158+
if (! SpecialisedError) {
159+
return NULL;
160+
} else {
161+
PyModule_AddObject(m, "SpecialisedError", SpecialisedError);
162+
}
163+
/* END: Initialise exceptions here. */
164+
165+
return m;
166+
}
167+
168+
To illustrate how you raise one of these exceptions suppose we have a function to test raising one of these exceptions:
169+
170+
.. code-block:: c
171+
172+
static PyMethodDef Noddy_module_methods[] = {
173+
...
174+
{"_test_raise", (PyCFunction)Noddy__test_raise, METH_NOARGS, "Raises a SpecialisedError."},
175+
...
176+
{NULL, NULL, 0, NULL} /* Sentinel */
177+
};
178+
179+
180+
We can either access the exception type directly:
181+
182+
.. code-block:: c
183+
184+
static PyObject *Noddy__test_raise(PyObject */* mod */)
185+
{
186+
if (SpecialisedError) {
187+
PyErr_Format(SpecialisedError, "One %d two %d three %d.", 1, 2, 3);
188+
} else {
189+
PyErr_SetString(PyExc_RuntimeError, "Can not raise exception, module not initialised correctly");
190+
}
191+
return NULL;
192+
}
193+
194+
195+
Or fish it out of the module:
196+
197+
.. code-block:: c
198+
199+
static PyObject *Noddy__test_raise(PyObject *mod)
200+
{
201+
PyObject *err = PyDict_GetItemString(PyModule_GetDict(mod), "SpecialisedError");
202+
if (err) {
203+
PyErr_Format(err, "One %d two %d three %d.", 1, 2, 3);
204+
} else {
205+
PyErr_SetString(PyExc_RuntimeError, "Can not find exception in module");
206+
}
207+
return NULL;
208+
}

0 commit comments

Comments
 (0)