I am checking the types of user input like so:
PyObject *key = PyTuple_GetItem(tuple, 0);
if (!PyObject_TypeCheck(key, &PyBaseString_Type) {
PyErr_SetString(PyExc_TypeError, "Key must be str");
return NULL;
}
However, In the exception message, I would like to include the bad type the user has submitted, to make debugging for him easier.
Is there a simple or otherwise idiomatic way to achieve this?
The only way I can think of is the following:
PyObject *key = PyTuple_GetItem(tuple, 0);
if (!PyObject_TypeCheck(key, &PyBaseString_Type) {
// This returns a new reference which must be Py_DECREFed
PyObject *bad_type_string = PyObject_Str((PyObject *)key->ob_type);
char *bad_type_char = PyString_AsString(bad_type_string);
PyErr_Format(PyExc_TypeError, "Key must be str, not %s", bad_type_char);
Py_DECREF(bad_type_string);
return NULL;
}
Which I suppose I could wrap in a macro:
# define CHECK_TYPE(expr, name, input) \
do { \
if (!(expr)) {
PyObject *bad_type_string = PyObject_Str((PyObject *)input->ob_type); \
char *bad_type_char = PyString_AsString(bad_type_string); \
PyErr_Format(PyExc_TypeError, "%s must be str, not %s", name bad_type_char); \
Py_DECREF(bad_type_string); \
goto error; \
}
} while (0);
And used like such:
static PyObject *foo(PyObject *self, PyObject *args) {
// ...
PyObject *key = PyTuple_GetItem(tuple, 0);
CHECK_TYPE(PyObject_TypeCheck(key, &PyBaseString_Type, 'key', key);
// ...
error:
return NULL;
}