<div dir="auto"><div>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:</div><div dir="auto"><br></div><div dir="auto"><br></div><div dir="auto">----</div><div dir="auto"><br></div><div dir="auto"># For some reason, imports might not be ready immediately</div><div dir="auto"># Maybe flaky network drive, maybe need to install modules, etc</div><div dir="auto"># The overall program can do things too make them available</div><div dir="auto">lazy_import("foo", "bar", "baz", "blat")</div><div dir="auto"><br></div><div dir="auto">while True:</div><div dir="auto">    try:</div><div dir="auto">        x = foo(1) * bar(2) + baz(3)**blat(4)</div><div dir="auto">        break</div><div dir="auto">    except NameError as err:</div><div dir="auto">        lazy_import(<a href="http://err.name">err.name</a>)</div><div dir="auto">        sleep(1)</div><div dir="auto"><br></div><div dir="auto">----</div><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><div dir="auto">Of course I could write this in other ways also. But this one feels natural and concise.</div><div dir="auto"><br></div><div dir="auto">Of course, I probably *could* monkey-patch NameError with no language change. But not needing to would be a nice pattern.</div><div dir="auto"><div class="gmail_extra" dir="auto"><br><div class="gmail_quote">On Jul 4, 2017 7:04 AM, "Steven D'Aprano" <<a href="mailto:steve@pearwood.info">steve@pearwood.info</a>> wrote:<br type="attribution"><blockquote class="quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="quoted-text">On Mon, Jul 03, 2017 at 01:59:02AM -0700, Ken Kundert wrote:<br>
> I think in trying to illustrate the existing behavior I made things more<br>
> confusing than they needed to be.  Let me try again.<br>
<br>
</div>I understood you the first time :-)<br>
<br>
I agree that scraping the name from the NameError exception is a fragile<br>
hack. What I'm questioning is *how often that needs be done*.<br>
<br>
As I see it, there are broadly two use-cases for wanting the name from<br>
NameError (or AttributeError, or the index from IndexError, or the key<br>
from KeyError -- for brevity, I will use "name" and "NameError" stand in<br>
for *all* these cases).<br>
<br>
1. You are a developer reading an unexpected NameError exception, and<br>
now you need to debug the code and fix the bug.<br>
<br>
In this case, just reading the error message is sufficient. There's no<br>
need to programmatically extract the name.<br>
<br>
2. You have a `try...except` block and you've just caught NameError and<br>
want to handle it programmatically.<br>
<br>
In that second case, needing to extract the name from the exception is a<br>
code smell. A *strong* code smell -- it suggests that you're doing too<br>
much in the try... block. You should already know which name lookup<br>
failed, and so extracting the name from the exception is redundant:<br>
<br>
try:<br>
    unicode<br>
except NameError:<br>
    # Python 2/3 compatibility<br>
    unicode = str<br>
<br>
What other name could it be?<br>
<br>
I believe that if you are dealing with a NameError where you want to<br>
programmatically deal with a missing name, but you don't know what that<br>
name is, you're already in deep, deep trouble and the fact that you have<br>
to scrape the error message for the name is the least of your problems:<br>
<br>
try:<br>
    # Feature detection for Python 2/3 compatibility.<br>
    unicode<br>
    ascii<br>
    reduce<br>
    callable<br>
except NameError as err:<br>
    name = extract_name(err)  # somehow...<br>
    if name == 'unicode':<br>
        unicode = str<br>
    elif name == 'ascii':<br>
        ascii = ...<br>
    elif name == 'reduce':<br>
        from functools import reduce<br>
    elif name == 'callable':<br>
        def callable(obj): ...<br>
<br>
I trust that the problem with this is obvious. The only way to safely<br>
write this code is to test for each name individually, in which case<br>
we're back to point 2 above where you know what name failed and you<br>
don't need to extract it at all.<br>
<br>
It is my hand-wavy estimate that these two use-cases cover about 95% of<br>
uses for the name. We might quibble over percentages, but I think we<br>
should agree that whatever the number is, it is a great majority.<br>
<br>
Any other use-cases, like your example of translating error messages, or<br>
suggesting "Did you mean...?" alternatives, are fairly niche uses.<br>
<br>
So my position is that given the use-cases for programmatically<br>
extracting the name from NameError fall into a quite small niche, this<br>
feature is a "Nice To Have" not a "Must Have". It seems to me that the<br>
benefit is quite marginal, and so any more than the most trivial cost to<br>
implement this is likely to be too much for the benefit gained.<br>
<br>
I don't just mean the effort of implementing your suggested change. I<br>
mean, if there is even a small chance that in the future people<br>
will expect me (or require me!) to write code like this:<br>
<br>
    raise NameError('foo', template='missing "{}"')<br>
<br>
instead of<br>
<br>
    raise NameError('missing "foo"')<br>
<br>
then the cost of this new feature is more than the benefit to me, and I<br>
don't want it.<br>
<br>
I see little benefit and a small cost (more of a nuisance than a major<br>
drama, but a nusiance is still a nagative). I wouldn't use the new style<br>
if it were available (why should I bother?) and I'd be annoyed if I were<br>
forced to use it.<br>
<br>
Contrast that to OSError, where the ability to extract the errno and<br>
errstr separately are *very* useful. When you catch an OSError or<br>
IOError, you typically have *no idea* what the underlying errno is, it<br>
could be one of many. I don't object to writing this:<br>
<br>
    raise OSError(errno, 'message')<br>
<br>
because I see real benefit.<br>
<br>
<br>
[...]<br>
<div class="quoted-text">> The above is an example. It is a bit contrived. I simply wanted to illustrate<br>
> the basic issue in a few lines of code. However, my intent was also to<br>
> illustrate what I see as a basic philosophical problem in the way we approach<br>
> exceptions in Python:<br>
><br>
>     It is a nice convenience that an error message is provided by the source of<br>
>     the error, but it should not have the final say on the matter.<br>
>     Fundamentally, the code that actually presents the error to the user is in<br>
>     a better position to produce a message that is meaningful to the user.  So,<br>
>     when we raise exceptions, not only should we provide a convenient human<br>
>     readable error message, we should anticipate that the exception handler may<br>
>     need to reformat or reinterpret the exception and provide it with what it<br>
>     need to do so.<br>
<br>
</div>That argument makes a certain amount of sense, but its still a niche<br>
use-case. In *general*, we don't do our users any favours by<br>
reinterpreting standard error messages into something that they can't<br>
google, so even if this were useful, I can't see it becoming used in a<br>
widespread manner.<br>
<br>
<br>
[...]<br>
<div class="quoted-text">> What I am hoping to do with this proposal is to get the Python developer<br>
> community to see that:<br>
> 1. The code that handles the exception benefits from having access to the<br>
>    components of the error message.<br>
<br>
</div>I don't think that's generally true.<br>
<div class="quoted-text"><br>
<br>
>    In the least it can present the message to<br>
>    the user is the best possible way. Perhaps that means enforcing a particular<br>
>    style, or presenting it in the user's native language, or perhaps it means<br>
>    providing additional related information as in the example above.<br>
<br>
</div>And I think that's actually counter-productive, at least in general,<br>
although I'm willing to accept that there may be situations were it is<br>
helpful.<br>
<div class="quoted-text"><br>
<br>
> 2. The current approach to exceptions follows the opposite philosophy,<br>
>    suggesting that the best place to construct the error message is at the<br>
>    source of the error.<br>
<br>
</div>What else understands the error better than the source of the error?<br>
<div class="quoted-text"><br>
<br>
>    It inadvertently puts obstacles in place that make it<br>
>    difficult to customize the message in the handler.<br>
><br>
> 3. Changing the approach in the BaseException class to provide the best of both<br>
>    approaches provides considerable value and is both trivial and backward<br>
>    compatible.<br>
<br>
</div>I'll discuss your suggested API in a followup email.<br>
<font color="#888888"><br>
<br>
<br>
--<br>
Steve<br>
</font><div class="elided-text">______________________________<wbr>_________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/<wbr>mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/<wbr>codeofconduct/</a><br>
</div></blockquote></div><br></div></div></div>