[Pyrex] Pyrex exception type checking broken under Python 2.5 (and earlier)
Stefan Behnel
behnel_ml at gkec.informatik.tu-darmstadt.de
Mon Jun 26 06:06:25 UTC 2006
Hi Greg,
Greg Ewing wrote:
> Stefan Behnel wrote:
>> Pyrex uses this code in __Pyx_Raise() to check if an exception object
>> has the right type for being raised:
>>
>> The problem is that exceptions in Py2.5 have become new-style classes
>> which no longer fall under any of the above checks.
>>
>> Also, support for string exceptions is deprecated and supposed to be
>> removed in later Python versions. I therefore propose to actually
>> remove the above check from Pyrex completely.
>
> What I'd really like is an API call for raising an exception that
> does all the same things as the Python raise statement. There
> didn't seem to be such a call at the time I wrote that code.
There still doesn't seem to be a function that checks the argument types.
> I'd be wary about removing the checks altogether, because I
> wouldn't be surprised if you could cause a crash by stuffing
> arbitrary values into the exception variables.
I don't quite buy that argument - it's unlikely that it crashes, at least, and
having the Python interpreter raise exceptions for wrong arguments should be
fine. But since 2.5's interpreter actually checks for exception inheritance in
do_raise(), Pyrex can well do that, too.
Ok, so the only new thing is that instances of new-style classes are
'objects', just as new-style classes themselves. What Python 2.5 does is check
that they inherit from BaseException. Since that's not available in earlier
versions, what about checking that raised new-style objects (which are not
currently allowed at all in Pyrex) inherit from PyErr_Exception? I find that a
reasonable restriction.
The following is based on do_raise() in Py2.5. You can remove string support
completely by simply dropping the "if (PyString_CheckExact(type)) { } else"
part, however, Py*_CheckExact tends to be pretty fast, so there's not much you
loose by printing a warning as Py2.5 does.
I couldn't test it on earlier Python versions than 2.4, BTW, but it 'should'
work on 2.2.
Stefan
static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb) {
Py_XINCREF(type);
Py_XINCREF(value);
Py_XINCREF(tb);
/* First, check the traceback argument, replacing None with NULL. */
if (tb == Py_None) {
Py_DECREF(tb);
tb = 0;
}
else if (tb != NULL && !PyTraceBack_Check(tb)) {
PyErr_SetString(PyExc_TypeError,
"raise: arg 3 must be a traceback or None");
goto raise_error;
}
/* Next, replace a missing value with None */
if (value == NULL) {
value = Py_None;
Py_INCREF(value);
}
/* Next, repeatedly, replace a tuple exception with its first item */
while (PyTuple_Check(type) && PyTuple_Size(type) > 0) {
PyObject *tmp = type;
type = PyTuple_GET_ITEM(type, 0);
Py_INCREF(type);
Py_DECREF(tmp);
}
if (PyString_CheckExact(type)) {
/* Raising builtin string is deprecated but still allowed --
* do nothing. Raising an instance of a new-style str
* subclass is right out. */
if (PyErr_Warn(PyExc_DeprecationWarning,
"raising a string exception is deprecated"))
goto raise_error;
}
else if (PyType_Check(type) || PyClass_Check(type))
; /* PyErr_NormalizeException(&type, &value, &tb); */
else if (PyInstance_Check(type)) {
/* Raising an instance. The value should be a dummy. */
if (value != Py_None) {
PyErr_SetString(PyExc_TypeError,
"instance exception may not have a separate value");
goto raise_error;
}
else {
/* Normalize to raise <class>, <instance> */
Py_DECREF(value);
value = type;
type = (PyObject*) ((PyInstanceObject*)type)->in_class;
Py_INCREF(type);
}
}
else if (PyType_IsSubtype(type->ob_type, (PyTypeObject*)PyExc_Exception)){
/* Raising a new-style object (in Py2.5).
The value should be a dummy. */
if (value != Py_None) {
PyErr_SetString(PyExc_TypeError,
"instance exception may not have a separate value");
goto raise_error;
}
else {
/* Normalize to raise <class>, <instance> */
Py_DECREF(value);
value = type;
type = type->ob_type;
Py_INCREF(type);
}
}
else {
/* Not something you can raise. You get an exception
anyway, just not what you specified :-) */
PyErr_Format(PyExc_TypeError,
"exceptions must be classes, instances, or "
"strings (deprecated), not %s",
type->ob_type->tp_name);
goto raise_error;
}
PyErr_Restore(type, value, tb);
return;
raise_error:
Py_XDECREF(value);
Py_XDECREF(type);
Py_XDECREF(tb);
return;
}
More information about the Pyrex
mailing list