[Python-ideas] Arguments to exceptions

Steven D'Aprano steve at pearwood.info
Tue Jul 4 11:33:12 EDT 2017


On Sun, Jul 02, 2017 at 12:19:54PM -0700, Ken Kundert wrote:

>     class BaseException:
>         def __init__(self, *args, **kwargs):
>             self.args = args
>             self.kwargs = kwargs
> 
>         def __str__(self):
>             template = self.kwargs.get('template')
>             if template is None:
>                 sep = self.kwargs.get('sep', ' ')
>                 return sep.join(str(a) for a in self.args)
>             else:
>                 return template.format(*self.args, **self.kwargs)

I think this API is too general. It accepts arbitrary keyword arguments 
and stores them in the exception. I think that makes for a poor 
experience for the caller:

try:
    something()
except MyParsingError as err:
    if 'column' in err.kwargs:
        ...
    elif 'col' in err.kwargs:
        ...
    elif 'x' in err.kwargs:  # 'x' is the column, or is it the row?
        ...


The problem here is, unless the exception class offers a real API 
for extracting the column, how do you know what key to use? You can't expect 
BaseException to force the user of MyParsingError to be consistent:

    raise MyParsingError(17, 45, template='Error at line {} column {}')

    raise MyParsingError(45, 17, template='Error at column {} line {}')

    raise MyParsingError(45, 17, temlpate='Error at col {} row {}') # oops

    raise MyParsingError(17, template='Error at line {}')

    raise MyParsingError(99, 45, 17, template='Error code {} at column {} line {}')

Unless MyParsingError offers a consistent (preferably using named 
attributes) API for extracting the data, pulling it out of args is just 
as much of a hack as scraping it from the error message.

It seems to me that we can't solve this problem at BaseException. It has 
to be tackled by each exception class. Only the author of each exception 
class knows what information it carries and should be provided via named 
attributes.

I think OSError has a good approach.

https://docs.python.org/3/library/exceptions.html#OSError

For backwards compatibility, when you raise OSError with a single 
argument, it looks like this:

raise OSError('spam is my problem')
=> OSError: spam is my problem

When you offer two or up to five arguments, they get formatted into a 
nicer error message, and stored into named attributes:

raise OSError(99, 'spam is my problem', 'xxx', 123, 'yyy')
=> OSError: [Errno 99] foo is my problem: 'xxx' -> 'yyy'

Missing arguments default to None.

I don't think there is a generic recipe that will work for all 
exceptions, or even all exceptions in the standard exception heirarchy, 
that can make that pleasant. Perhaps I'm insufficiently imaginative, but 
I don't think this problem can be solved with a quick hack of the 
BaseException class.


-- 
Steve


More information about the Python-ideas mailing list