[Python-ideas] Arguments to exceptions

David Mertz mertz at gnosis.cx
Tue Jul 4 15:32:41 EDT 2017

I don't see the usefulness rich exception data as at all as limited as
this. Here's some toy code that shows a use:


# For some reason, imports might not be ready immediately
# Maybe flaky network drive, maybe need to install modules, etc
# The overall program can do things too make them available
lazy_import("foo", "bar", "baz", "blat")

while True:
        x = foo(1) * bar(2) + baz(3)**blat(4)
    except NameError as err:


I'd like the expression defining x all in one place together. Then I'd like
to try again to import the functions used until they are available. In this
case, I have assumed the "making available" might take some time, so I
sleep in the loop.

Of course I could write this in other ways also. But this one feels natural
and concise.

Of course, I probably *could* monkey-patch NameError with no language
change. But not needing to would be a nice pattern.

On Jul 4, 2017 7:04 AM, "Steven D'Aprano" <steve at pearwood.info> wrote:

On Mon, Jul 03, 2017 at 01:59:02AM -0700, Ken Kundert wrote:
> I think in trying to illustrate the existing behavior I made things more
> confusing than they needed to be.  Let me try again.

I understood you the first time :-)

I agree that scraping the name from the NameError exception is a fragile
hack. What I'm questioning is *how often that needs be done*.

As I see it, there are broadly two use-cases for wanting the name from
NameError (or AttributeError, or the index from IndexError, or the key
from KeyError -- for brevity, I will use "name" and "NameError" stand in
for *all* these cases).

1. You are a developer reading an unexpected NameError exception, and
now you need to debug the code and fix the bug.

In this case, just reading the error message is sufficient. There's no
need to programmatically extract the name.

2. You have a `try...except` block and you've just caught NameError and
want to handle it programmatically.

In that second case, needing to extract the name from the exception is a
code smell. A *strong* code smell -- it suggests that you're doing too
much in the try... block. You should already know which name lookup
failed, and so extracting the name from the exception is redundant:

except NameError:
    # Python 2/3 compatibility
    unicode = str

What other name could it be?

I believe that if you are dealing with a NameError where you want to
programmatically deal with a missing name, but you don't know what that
name is, you're already in deep, deep trouble and the fact that you have
to scrape the error message for the name is the least of your problems:

    # Feature detection for Python 2/3 compatibility.
except NameError as err:
    name = extract_name(err)  # somehow...
    if name == 'unicode':
        unicode = str
    elif name == 'ascii':
        ascii = ...
    elif name == 'reduce':
        from functools import reduce
    elif name == 'callable':
        def callable(obj): ...

I trust that the problem with this is obvious. The only way to safely
write this code is to test for each name individually, in which case
we're back to point 2 above where you know what name failed and you
don't need to extract it at all.

It is my hand-wavy estimate that these two use-cases cover about 95% of
uses for the name. We might quibble over percentages, but I think we
should agree that whatever the number is, it is a great majority.

Any other use-cases, like your example of translating error messages, or
suggesting "Did you mean...?" alternatives, are fairly niche uses.

So my position is that given the use-cases for programmatically
extracting the name from NameError fall into a quite small niche, this
feature is a "Nice To Have" not a "Must Have". It seems to me that the
benefit is quite marginal, and so any more than the most trivial cost to
implement this is likely to be too much for the benefit gained.

I don't just mean the effort of implementing your suggested change. I
mean, if there is even a small chance that in the future people
will expect me (or require me!) to write code like this:

    raise NameError('foo', template='missing "{}"')

instead of

    raise NameError('missing "foo"')

then the cost of this new feature is more than the benefit to me, and I
don't want it.

I see little benefit and a small cost (more of a nuisance than a major
drama, but a nusiance is still a nagative). I wouldn't use the new style
if it were available (why should I bother?) and I'd be annoyed if I were
forced to use it.

Contrast that to OSError, where the ability to extract the errno and
errstr separately are *very* useful. When you catch an OSError or
IOError, you typically have *no idea* what the underlying errno is, it
could be one of many. I don't object to writing this:

    raise OSError(errno, 'message')

because I see real benefit.

> The above is an example. It is a bit contrived. I simply wanted to
> the basic issue in a few lines of code. However, my intent was also to
> illustrate what I see as a basic philosophical problem in the way we
> exceptions in Python:
>     It is a nice convenience that an error message is provided by the
source of
>     the error, but it should not have the final say on the matter.
>     Fundamentally, the code that actually presents the error to the user
is in
>     a better position to produce a message that is meaningful to the
user.  So,
>     when we raise exceptions, not only should we provide a convenient
>     readable error message, we should anticipate that the exception
handler may
>     need to reformat or reinterpret the exception and provide it with
what it
>     need to do so.

That argument makes a certain amount of sense, but its still a niche
use-case. In *general*, we don't do our users any favours by
reinterpreting standard error messages into something that they can't
google, so even if this were useful, I can't see it becoming used in a
widespread manner.

> What I am hoping to do with this proposal is to get the Python developer
> community to see that:
> 1. The code that handles the exception benefits from having access to the
>    components of the error message.

I don't think that's generally true.

>    In the least it can present the message to
>    the user is the best possible way. Perhaps that means enforcing a
>    style, or presenting it in the user's native language, or perhaps it
>    providing additional related information as in the example above.

And I think that's actually counter-productive, at least in general,
although I'm willing to accept that there may be situations were it is

> 2. The current approach to exceptions follows the opposite philosophy,
>    suggesting that the best place to construct the error message is at the
>    source of the error.

What else understands the error better than the source of the error?

>    It inadvertently puts obstacles in place that make it
>    difficult to customize the message in the handler.
> 3. Changing the approach in the BaseException class to provide the best
of both
>    approaches provides considerable value and is both trivial and backward
>    compatible.

I'll discuss your suggested API in a followup email.

Python-ideas mailing list
Python-ideas at python.org
Code of Conduct: http://python.org/psf/codeofconduct/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170704/986cb024/attachment.html>

More information about the Python-ideas mailing list