[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