On 2017-07-03 22:44, Paul Moore wrote:
On 3 July 2017 at 21:56, Jeff Walker
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))