PyErr_SetObject weirdness (HELP!)

Bernhard Herzog herzog at online.de
Fri Mar 3 14:15:15 CET 2000


Jason Tackaberry <tack-py at sault.org> writes:

> I'm experiencing some weird behaviour with PyErr_SetObject, and I need
> some guidance from you Python Gods. :)
> 
> I have a function that passes an instance of a class as the value
> parameter of PyErr_SetObject. But in fact, I'm experiencing the
> weirdness described below when I pass any object (except a string
> object), so for the sake of simplicity, let's say I'm passiing a dict.
> Consider:
> 
> PyObject *dict = PyDict_New();
> PyDict_SetItemString(dict, "foo", PyString_FromString("bar"));
> // UserException is defined somewhere else
> PyErr_SetObject(UserException, dict);

The definition of UserException does matter. I'll assume that it was
created with PyErr_NewException and is derived directly from Exception
and doesn't add new methods (the traceback below confirms this)

Also note that dict is not an instance of UserException, so it's subject
to 'normalization'. Normalizatin occurs for instance, when an exception
is caught in python code and makes sure that it the type of an exception
is a class that the value is an instance of that class. If the value is
not already an instance, the class is instantiated with the value as
parameter. This seems to happen in your case.

> Now when I call this code from Python:
> 
> try: call_C_function_that_raises_exception()
> except UserException, data:
>    print data["foo"]
> 
> I would expect this should work, but it doesn't! Referencing
> data["foo"] raises this exception:
> 
> Traceback (innermost last):
>   File "./blah", line 15, in ?
>     print data["foo"]
>   File "/usr/lib/python1.5/exceptions.py", line 77, in __getitem__
>     return self.args[i]
> TypeError: sequence index must be integer

The answer to this lies in the definition of Exception:

class Exception:
    """Proposed base class for all exceptions."""
    def __init__(self, *args):
        self.args = args

    def __str__(self):
        if not self.args:
            return ''
        elif len(self.args) == 1:
            return str(self.args[0])
        else:
            return str(self.args)

    def __getitem__(self, i):
        return self.args[i]

and in the fact that normalization occurred. Exception is instantiated
with your dictionary as single parameter so that args is a tuple with
the dict as the only item.

Since args is a tuple, __getitem__ will only succeed with an integer
index.

> In fact, the magic incantation is:
>    print data[0]["foo"]
> 
> I have _no_ idea why it is doing this, especially because:
> 
>    print data
>    print data[0]
> yields:
>    {'foo': 'bar'}
>    {'foo': 'bar'}
> 
> Which clearly _look_ the same.

The __str__ method of Exception explains this as well. If the args tuple
has only one element, str(data) is in fact str(data[0]).

The thing I usually do if I'm puzzled by Python's behaviour is to print
out the types of the objects. In your case this would tell you that data
is not a dictionary but a class instance.

> My question is why is this happening, and how can I make it work the
> way one would expect?

The best way would probably be to define a new exception class that
overrides at least the __getitem__ method.

HTH


-- 
Bernhard Herzog   | Sketch, a drawing program for Unix
herzog at online.de  | http://sketch.sourceforge.net/



More information about the Python-list mailing list