[Python-ideas] Arguments to exceptions

MRAB python at mrabarnett.plus.com
Mon Jul 3 19:04:16 EDT 2017


On 2017-07-03 22:44, Paul Moore wrote:
> On 3 July 2017 at 21:56, Jeff Walker <jeff.walker00 at yandex.com> wrote:
>> Paul,
>>      Indeed, nothing gets better until people change the way they do their
>> exceptions. Ken's suggested enhancement to BaseException does not
>> directly solve the problem, but it removes the roadblocks that discourage
>> people from passing the components to the message.
> 
> As noted, I disagree that people are not passing components because
> str(e) displays them the way it does. But we're both just guessing at
> people's motivations, so there's little point in speculating.
> 
>> Seems to me that to address this problem, four things are needed:
>> 1. Change BaseException. This allows people to pass the components
>>     to the message without ruining str(e).
> 
> I dispute this is the essential place to start. If nothing else, the
> proposed approach encourages people to use a position-based "args"
> attribute for exceptions, rather than properly named attributes.
> 
>> 2. A PEP that educates the Python community on this better way of
>>     writing exceptions.
> 
> Educating the community can be done right now, and doesn't need a PEP.
> Someone could write a blog post, or an article, that explains how to
> code exception classes, how to create the exceptions, and how client
> code can/should use the API. This can be done now, all you need to do
> is to start with "at the moment, BaseException doesn't implement these
> features, so you should create an application-specific base exception
> class to minimise duplication of code". If project authors take up the
> proposed approach, then that makes a good argument for moving the
> supporting code into the built in BaseException class.
> 
>> 3. Changes to the base language and standard library to employ the
>>     new approach. These would changes would be quite small and could
>>     be done opportunistically.
> 
> And I've never said that there's a problem with these. Although I do
> dispute that using an args list is the best approach here - I'd much
> rather see NameError instances have a "name" attribute that had the
> name that couldn't be found as its value. Opportunistic changes to
> built in exceptions can implement the most appropriate API for the
> given exception - why constrain such changes to a "lowest common
> denominator" API that is ideal for no-one?
> 
> class NameError(BaseException):
>      def __init__(self, name):
>          self.name = name
>      def __str__(self):
>          return f"name '{self.name}' is not defined"
> 
> Of course, that's not backward compatible as it stands, but it could
> probably be made so, just as easily as implementing the proposed
> solution.
> 
>> 4. Time. Over time things will just get better as more people see the
>>     benefits of this new approach to exceptions and adopt it. And if they
>>     don't, we are no worse off than we are now.
> 
> The same could be said of any improved practice. And I agree, let's
> encourage people to learn to write better code, and promote good
> practices. There's definitely no reason not to do this.
> 
>> And frankly, I don't see any downside. The changes he is proposing are
>> tiny and simple and backward compatible.
> 
> Well, the main downside I see is that I don't agree that the proposed
> changes are the best possible approach. Implementing them in the built
> in exceptions therefore makes it harder for people to choose better
> approaches (or at least encourages them not to look for better
> approaches). There's no way I'd consider that e.args[0] as a better
> way to get the name that triggered a NameError than e.name.
> 
> This seems to me to be something that should be experimented with and
> proven outside of the stdlib, before we rush to change the language. I
> don't see anything that makes that impossible.
> 
Maybe exceptions could put any keyword arguments into the instance's 
__dict__:

     class BaseException:
         def __init__(self, *args, **kwargs):
             self.args = args
             self.__dict__.update(kwargs)

You could then raise:

     raise NameError('name {!a} is not defined', name='foo')

and catch:

     try:
         ...
     except NameError as e:
         print('{}: nicht gefunden.'.format(e.name))


More information about the Python-ideas mailing list