[Python-3000] How far to go with cleaning up exceptions

Brett Cannon brett at python.org
Thu Mar 1 18:09:26 CET 2007

I spent my sprint time cleaning up exceptions for Py3K and Guido
suggested I run some things by the group to make sure they don't make
transitioning from 2.6 too difficult.  After adding the proper
restrictions in terms of what can and cannot be raised or caught, I
began cleaning up BaseException's API per PEP 352.

First thing I did was remove slicing.  That wasn't too bad since I
just got exceptions about the lack of __getitem__ and went to the
affected line and tossed in '.args' between the index and the
exception.  Not sure if 2to3 can handle this as it would need to
realize when an exception is within scope and then insert '.args' when
it is be indexed on.

The next thing I did was strip out the use of '.args' and make
BaseException take a single argument that gets assigned to 'message'.
The fruits of this labour are in the p3yk_no_args_on_exc branch.  This
one turned out to be a pain in the rear.

Probably the biggest headache were the built-in exceptions themselves
and various exceptions specified by extension modules.  Many of the
built-in modules assumed 'args' would be there and so would pull from
them as needed instead of assigning None to their various attributes.
It also required implementing several __reduce__ methods as
BaseException's just return 'args' which kept things simple.  Plus
their constructors had to be made to use BaseException's more
restrictive constructor signature.

For the extension modules, though, the biggest pain was the fact that
most of them use PyErr_NewException() to create their exception
objects.  That provides no easy way of dealing with the more
restrictive constructor on BaseException.  For bsddb.DBError I just
made it take one argument and forced all calls to pass in a tuple.
For socket.error I defined the exception in Python and used a trick
_bsddb.c uses so that the __init__ for socket.error stuck any second
argument on to the 'errno' attribute.

In other words the more restrictive construtor is going to be what
causes the most pain.  Exceptions that expect more than one argument
are going to need to be rewritten so that if they get more than one
argument they call BaseException.__init__ with only one and store away
the rest in some other attribute.  This kind of sucks at the C level,
especially when most defined there just use PyErr_NewException().
Then the various accesses that would have gone to 'args' would need to
be changed to access the specific attribute.

For solving these issues, there a couple possibilities.  For those
exceptions that use PyErr_NewException() there could be a
PyErr_NewExceptionEx() that takes an init function for thew new
exception.  As for transforming the 'args' accesses to something else,
I guess 2to3 could automatically do 'args[0]' accesses to 'message',
but anything else will need to be changed by hand.

Now, for the question: is all of this worth it?  If this happens
exceptions will have a much nicer interface.  By default you will have
just 'message'.  it will also force exceptions to explicitly set
attributes instead of using the position within 'args' to get to a
specific piece of information.  But obviously there are transition
pains in doing this.  If you have an opinion on this please speak up.


More information about the Python-3000 mailing list