`raise as` to raise with current exception as cause
Hi, I'd like to suggest an idea, that builds on PEPs 3134 and 409. This idea came up when discussing a problem on the django-developers mailing list: https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/django-developers/ibEOt3A9c2M/EP4gbQyTFwAJ I'll first explain my idea, and then the original problem. The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like this: try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever') What it does is a shorter version of this: try: 1/0 except ZeroDivisionError as error: raise ValueError('Whatever') from error Of course, this syntax could only be used inside an except clause, similarly to a blank `raise`. The `raise as` syntax was one of the rejected syntaxes in PEP 409, for a different use-case, of suppressing context: https://www.python.org/dev/peps/pep-0409/#alternatives I think that this syntax might be a good fit for this use-case. The reason I propose this, is that I had a hard time convincing the Django maintainers to use the current `raise foo from bar` syntax in cases where it's appropriate, i.e. when they wrap an exception with another exception. This is important because then the users see the correct message between the two tracebacks, which is "The above exception was the direct cause of the following exception:" rather than "During handling of the above exception, another exception occurred" which is for other cases. This is my first PR in this topic that was merged: https://github.com/django/django/pull/12263 Even though that first PR was merged, after we discussed adding this all over Django, and recommending that `raise foo from bar` be used in the style guide, Carlton Gibson changed his mind. He gave his arguments in the Django-developers thread I linked to at the top. That's sad for me, because if Django doesn't accept the new syntax, and is okay with the inaccurate "During handling of" message between exceptions, chances are low that there will be widespread adoption of the current `raise foo from bar` syntax. It's possible that introducing the simpler `raise as` would increase adoption and make users pay attention to the message between exception tracebacks. What do you think? Thanks, Ram.
07.02.20 16:28, Ram Rachum пише:
The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like this:
try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever')
What it does is a shorter version of this:
try: 1/0 except ZeroDivisionError as error: raise ValueError('Whatever') from error
How does it differ from try: 1/0 except ZeroDivisionError: raise ValueError('Whatever') ?
On 02/07/2020 07:33 AM, Serhiy Storchaka wrote:
07.02.20 16:28, Ram Rachum пише:
The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like this:
try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever')
What it does is a shorter version of this:
try: 1/0 except ZeroDivisionError as error: raise ValueError('Whatever') from error
How does it differ from
try: 1/0 except ZeroDivisionError: raise ValueError('Whatever')
Here's a diff: $ diff no_from.py with_from.py 3,4c3,4 - ... except ZeroDivisionError: - ... raise ValueError('Whatever') --- + ... except ZeroDivisionError as e: + ... raise ValueError('Whatever') from e 10c10 - During handling of the above exception, another exception occurred: --- + The above exception was the direct cause of the following exception: To me the difference between "raise from" and "raise" is the implicit meaning: - "During handling of ..." implies that the second exception should not have occurred and there is a bug in the exception handling code - "The above exception ..." implies that this code path is normal and extra information is being supplied Personally, my main use of "raise from" is "raise from None". I'm -1 on the new syntax: to me it looks much more like a "raise ... from None". -- ~Ethan~
Hi, In the Django thread, I suggested that an implicit "raise from" should be the behavior whenever an exception is raised directly in exception-handling code (that is, within an except: or finally: clause). Ram claimed there were problems with that, but gave no details; I would be happy to know what these problems are. The main problem I see with "raise from None" is that it removes the inner part of the traceback. It expresses the idea that everything that happened in lower levels is not really interesting -- you should have all the information for handling or debugging the problem by considering the flow from here up. I think in most cases where you'd want to change the type and/or value of an excpetion, this is unrealistic. With respect to Ram's current suggestion, I think that for it to really make a difference, the "raise as" should not be thought of as a shorthand/variant of "raise ... from", but rather as a variant of bare raise; that is, it should not create a chained exception, but an effect of re-raising while changing the exception value (and potentially type). This, I think, would address Ethan's claim that "this should really be raise from None", without the cons described earlier; it is a better expression of the idea "I just want to change the exception being raised". To summarize, I am suggesting that except ExceptionType: raise as OtherException(...) Have, more-or-less, the semantics of Python 2's: except ExceptionType: traceback = sys.exc_info()[2] raise OtherException, OtherException(...), traceback Thanks for your consideration, Shai.
On Sat, Feb 8, 2020 at 4:51 AM Shai Berger <shai@platonix.com> wrote:
To summarize, I am suggesting that
except ExceptionType: raise as OtherException(...)
Have, more-or-less, the semantics of Python 2's:
except ExceptionType: traceback = sys.exc_info()[2] raise OtherException, OtherException(...), traceback
-1. That would mean that a line of code could phantom-raise an exception that it has no idea even exists. I'd much rather it be defined with semantics based on existing exception chaining (which is what Ram's original proposal was). ChrisA
I would like to raise a sort-of ultimatum to everyone in this thread. As far as I know, the `raise foo from bar` syntax, and the distinction between the two exception-chaining messages, didn't really catch on. I know about them, like them and use them, but most Python developers and open-source packages don't. If I understand correctly, the `raise from` syntax was introduced in Python 3.0, so in 2008. It's been with us for 12 years. We finally made the transition to Python 3, and I feel that most Python developers I interact with are using Python 3. But the `raise from` syntax is something that's still considered esoteric. My point is that so far, its adoption was a failure. If we have any hopes that its adoption will be a success, we should think, what is our plan for its success? Here are a few options: 1. Keep waiting a few more years and see whether people will spontaneously feel like starting to use it. 2. Accept the fact that it's a failed feature that would never get adopted. 3. Start an effort to transition to `raise from` in appropriate cases in high-profile Python projects such as Django, hoping it'll trickle down from there to the wider public. 4. Figure out why people don't use `raise from`, and find solutions to these problems. If the Django maintainers don't use it because it's too verbose, introduce less verbose way. Even if it takes 6 years to get into Django because of the release delay, it's worth to start working on it now. 5. Accept the fact that Django isn't the beacon of Pythonic-ness that many of us see it as, and maybe it can stay behind in this regard while other, newer projects are adopting more Pythonic practices. Any other options? On Fri, Feb 7, 2020 at 8:11 PM Chris Angelico <rosuav@gmail.com> wrote:
On Sat, Feb 8, 2020 at 4:51 AM Shai Berger <shai@platonix.com> wrote:
To summarize, I am suggesting that
except ExceptionType: raise as OtherException(...)
Have, more-or-less, the semantics of Python 2's:
except ExceptionType: traceback = sys.exc_info()[2] raise OtherException, OtherException(...), traceback
-1. That would mean that a line of code could phantom-raise an exception that it has no idea even exists. I'd much rather it be defined with semantics based on existing exception chaining (which is what Ram's original proposal was).
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/3PXWFV... Code of Conduct: http://python.org/psf/codeofconduct/
On 2020-02-07 5:38 p.m., Ram Rachum wrote:
I would like to raise a sort-of ultimatum to everyone in this thread.
As far as I know, the `raise foo from bar` syntax, and the distinction between the two exception-chaining messages, didn't really catch on. I know about them, like them and use them, but most Python developers and open-source packages don't.
If I understand correctly, the `raise from` syntax was introduced in Python 3.0, so in 2008. It's been with us for 12 years. We finally made the transition to Python 3, and I feel that most Python developers I interact with are using Python 3. But the `raise from` syntax is something that's still considered esoteric.
My point is that so far, its adoption was a failure.
If we have any hopes that its adoption will be a success, we should think, what is our plan for its success? Here are a few options:
1. Keep waiting a few more years and see whether people will spontaneously feel like starting to use it. 2. Accept the fact that it's a failed feature that would never get adopted. 3. Start an effort to transition to `raise from` in appropriate cases in high-profile Python projects such as Django, hoping it'll trickle down from there to the wider public. 4. Figure out why people don't use `raise from`, and find solutions to these problems. If the Django maintainers don't use it because it's too verbose, introduce less verbose way. Even if it takes 6 years to get into Django because of the release delay, it's worth to start working on it now. 5. Accept the fact that Django isn't the beacon of Pythonic-ness that many of us see it as, and maybe it can stay behind in this regard while other, newer projects are adopting more Pythonic practices.
Any other options?
I'm gonna make a wild suggestion: An explicit "raise" in an "except" body gets rewritten as "raise from" the parent exception. Hopefully this doesn't break anything, but, who knows. This means you get: except Foo as e: raise Bar # raises Bar, because of Foo, as if by "raise Bar from e" except Foo as e: my_raise(Bar) # raises Bar while handling Foo, as it currently stands.
Hi Soni, I think that your suggestion is identical to Shai's. It would be good, but unfortunately it has the 3 problems I raised in my email from an hour ago. On Fri, Feb 7, 2020 at 11:00 PM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-02-07 5:38 p.m., Ram Rachum wrote:
I would like to raise a sort-of ultimatum to everyone in this thread.
As far as I know, the `raise foo from bar` syntax, and the distinction between the two exception-chaining messages, didn't really catch on. I know about them, like them and use them, but most Python developers and open-source packages don't.
If I understand correctly, the `raise from` syntax was introduced in Python 3.0, so in 2008. It's been with us for 12 years. We finally made the transition to Python 3, and I feel that most Python developers I interact with are using Python 3. But the `raise from` syntax is something that's still considered esoteric.
My point is that so far, its adoption was a failure.
If we have any hopes that its adoption will be a success, we should think, what is our plan for its success? Here are a few options:
1. Keep waiting a few more years and see whether people will spontaneously feel like starting to use it. 2. Accept the fact that it's a failed feature that would never get adopted. 3. Start an effort to transition to `raise from` in appropriate cases in high-profile Python projects such as Django, hoping it'll trickle down from there to the wider public. 4. Figure out why people don't use `raise from`, and find solutions to these problems. If the Django maintainers don't use it because it's too verbose, introduce less verbose way. Even if it takes 6 years to get into Django because of the release delay, it's worth to start working on it now. 5. Accept the fact that Django isn't the beacon of Pythonic-ness that many of us see it as, and maybe it can stay behind in this regard while other, newer projects are adopting more Pythonic practices.
Any other options?
I'm gonna make a wild suggestion:
An explicit "raise" in an "except" body gets rewritten as "raise from" the parent exception.
Hopefully this doesn't break anything, but, who knows. This means you get:
except Foo as e: raise Bar # raises Bar, because of Foo, as if by "raise Bar from e"
except Foo as e: my_raise(Bar) # raises Bar while handling Foo, as it currently stands. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XN7B2Z... Code of Conduct: http://python.org/psf/codeofconduct/
On 2020-02-07 6:05 p.m., Ram Rachum wrote:
Hi Soni,
I think that your suggestion is identical to Shai's. It would be good, but unfortunately it has the 3 problems I raised in my email from an hour ago. of which 2 and 3 are personal opinions.
(for 2: you can just use a function. I'd consider that "explicit syntax" tbh.) my point is tho: if your code implicitly raises during exception handling, that's likely a bug in your code. if it explicitly raises instead, that's likely intended instead. having the language reflect that would be useful IMO regardless of backwards compatibility concerns.
I'm gonna make a wild suggestion:
An explicit "raise" in an "except" body gets rewritten as "raise from" the parent exception.
Hopefully this doesn't break anything, but, who knows. This means you get:
except Foo as e: raise Bar # raises Bar, because of Foo, as if by "raise Bar from e"
except Foo as e: my_raise(Bar) # raises Bar while handling Foo, as it currently stands. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XN7B2Z... Code of Conduct: http://python.org/psf/codeofconduct/
My reaction to this whole discussion is to question whether it's worth having two different forms of exception chaining in the first place. It seems to be a subtle distinction that most programmers either aren't aware of or can't be bothered making when they write a 'raise' statement. I'm not convinced that the "bug in exception handler" case warrants special treatment. If there were a danger of the bug getting masked by another exception, that would be a concern. But that's not what happens -- the exception from the bug is the one that percolates up to the top level, and it's the one you see if you scroll down to the bottom of the traceback. The nature of that exception will usually make it clear that it's the result of a bug, even if there isn't any explicit message in the traceback saying that. So you fix the bug, run your code again, and the original exception is exposed. So my proposal would be to merge the two kinds of chaining into one, and use wording in the traceback that is neutral as to the relationship between the two exceptions. -- Greg
On Sat, Feb 08, 2020 at 11:29:00AM +1300, Greg Ewing wrote:
So my proposal would be to merge the two kinds of chaining into one, and use wording in the traceback that is neutral as to the relationship between the two exceptions.
-1 There's no harm in the current wording. If very few people care about the difference, that's fine. Let the minority who do care continue to care, and the majority will continue to treat both wordings as boilerplate that they don't even bother to read. In my strong opinion, there's no need to change anything. -- Steven
On Feb 7, 2020, at 12:43, Ram Rachum <ram@rachum.com> wrote:
I would like to raise a sort-of ultimatum to everyone in this thread.
Is that a raise from Django or a raise as? :)
As far as I know, the `raise foo from bar` syntax, and the distinction between the two exception-chaining messages, didn't really catch on.
I’m not sure that’s true. I think it’s just that the number of cases where the distinction between implicit and explicit chaining matters isn’t very large. In cases where it doesn’t matter, no matter what the default is or how the alternative is spelled, people are going to use the default. In cases where it does matter, people are going to make the distinction whether it takes three characters or seven. There just aren’t that many of them. Are there cases where it kinda matters but is no big deal, so it’s borderline, and it’s on exactly the right part of the borderline where three vs. seven characters would push it over the edge for any given developer? Maybe, but I doubt enough such cases to change things.
On Fri, Feb 07, 2020 at 10:38:42PM +0200, Ram Rachum wrote:
As far as I know, the `raise foo from bar` syntax, and the distinction between the two exception-chaining messages, didn't really catch on. I know about them, like them and use them, but most Python developers and open-source packages don't. [...] My point is that so far, its adoption was a failure.
Failure judged according to what standard? If a feature isn't used a lot, does that make it a failure? Personally, I use "from None" quite a lot, so to me that's a rather big success.
If we have any hopes that its adoption will be a success, we should think, what is our plan for its success? Here are a few options:
You missed the most important: Do nothing. Accept that "raise from error" offers little advantage over plain old "raise", and if the majority of people can't be bothered using it, don't stress over the microscopic difference in meaning between the default message "During handling of the above exception" and the "above exception was the direct cause..." message you get from explicitly setting the cause with "from error". YMMV and this is just my opinion, but personally I think that exception chaining is rarely useful even at the best of times. For me, the only time I care about "raise from" is to suppress exception chaining by setting the cause to None. If you disagree and want to chain your exceptions, please go right ahead doing what you are doing. -- Steven
On Fri, 7 Feb 2020 at 23:14, Steven D'Aprano <steve@pearwood.info> wrote:
YMMV and this is just my opinion, but personally I think that exception chaining is rarely useful even at the best of times. For me, the only time I care about "raise from" is to suppress exception chaining by setting the cause to None.
Oddly, I had an issue this week where exception chaining would have been ideal. However, it was on a codebase where I needed to support Python 2, so I couldn't use it. I went with a slightly less optimal solution that worked on Python 2. The main difference was that there is a little more clutter in the Python 3 error message, but that's not a big deal because so few people need the full traceback anyway ;-) If I had been on a Python 3 only codebase, I would have used exception chaining quite happily and naturally. Although I would *not* have been worried about the difference between the "during" vs "direct cause" wording. So I'd be hesitant about calling exception chaining a failure (like the OP, not Steven, did) for a while yet. And even then, I would't call it a failure, just a relatively niche feature that is helpful in some uncommon cases. And I'm definitely -1 on the proposal here to add an *extra* way to do exception chaining that adds nothing beyond a slightly abbreviated form of "raise from". Paul
On Sat, Feb 8, 2020 at 12:27 PM Paul Moore <p.f.moore@gmail.com> wrote:
So I'd be hesitant about calling exception chaining a failure (like the OP, not Steven, did) for a while yet. And even then, I would't call it a failure, just a relatively niche feature that is helpful in some uncommon cases.
I didn't call exception chaining a failure. What is currently a failure is the "raise foo from bar" syntax, and the accompanying difference between "Direct cause of" and "During handling of". I love this feature and I want it to succeed. The thing is, it's not enough for some packages to use "raise foo from bar", while others (like Django) don't. Most Python developers don't know about this difference between "During the handling of" and "The above exception was the direct cause", because "raise foo from bar" isn't that prevalent yet. This is a chicken-and-egg problem, because as long as project maintainers don't use "raise foo from bar", the exception chaining wouldn't show the correct text, and people would get used to the fact that they can't rely on this text to be correct. Like any chicken-and-egg problem of changing people's habits, the best we could hope is to move it forward at a glacial pace-- A situation somewhat similar to the move to Python 3. Thanks, Ram.
On Sat, Feb 08, 2020 at 10:07:27AM +1100, Steven D'Aprano wrote:
YMMV and this is just my opinion, but personally I think that exception chaining is rarely useful even at the best of times. For me, the only time I care about "raise from" is to suppress exception chaining by setting the cause to None.
Sorry, my language there is imprecise. The chaining you get when an exception is raised during the handling of another exception is certainly useful during debugging, when the second exception is itself a bug. When I said "Exception chaining", I meant the specific form of exception chaining where you explicitly set the cause using "raise something from error". I don't object to people using that feature, but for me, it seems like a feature looking for a use-case. -- Steven
On Fri, Feb 7, 2020 at 7:47 PM Shai Berger <shai@platonix.com> wrote:
Hi,
In the Django thread, I suggested that an implicit "raise from" should be the behavior whenever an exception is raised directly in exception-handling code (that is, within an except: or finally: clause). Ram claimed there were problems with that, but gave no details; I would be happy to know what these problems are.
1. Backward compatibility. The meaning of existing plain raises would change, and if irresponsible people depended on it, we'd now break their assumptions. It's annoying and unlikely, but as far as I know python-dev is conservative regarding backward compatibility for esoteric cases. (Remember the non-Ascii Windows version strings? ;) 2. You would then need an explicit way to raise inside an except clause while stating the previous exception wasn't a cause. The syntax `from None` is already taken as "suppress the previous exception", so you'd need to introduce new syntax for that too. 3. The fact that `raise foo` inside `except` means something different than `raise foo` in a function that is called by code inside except, is something that might seem dissonant to many people, myself included. There might be more problems, I don't know.
The main problem I see with "raise from None" is that it removes the inner part of the traceback. It expresses the idea that everything that happened in lower levels is not really interesting -- you should have all the information for handling or debugging the problem by considering the flow from here up. I think in most cases where you'd want to change the type and/or value of an excpetion, this is unrealistic.
Yeah, I feel the same. I can think of very few good places where `raise foo from None` is beneficial. God, all the times I was desperately troubleshooting a problem, hunting for any glimpse of information of what happened, like looking for drinkable water in a desert... I'd never want to have a traceback excluded, ever.
With respect to Ram's current suggestion, I think that for it to really make a difference, the "raise as" should not be thought of as a shorthand/variant of "raise ... from", but rather as a variant of bare raise; that is, it should not create a chained exception, but an effect of re-raising while changing the exception value (and potentially type). This, I think, would address Ethan's claim that "this should really be raise from None", without the cons described earlier; it is a better expression of the idea "I just want to change the exception being raised".
To summarize, I am suggesting that
except ExceptionType: raise as OtherException(...)
Have, more-or-less, the semantics of Python 2's:
except ExceptionType: traceback = sys.exc_info()[2] raise OtherException, OtherException(...), traceback
This is very interesting. Took me a few reads to understand what you mean, that's an ingenious idea. It's true that the traceback for the new exception would likely be useless and should be thrown away, if it's just one stack level with no information. However, it is quite a bit of magic, and that's a disadvantage. People don't generally have the expectation that a raise in one location would show a traceback for another location. They might think the lines under the except weren't called, when they were. I wouldn't want it for this reason.
On Fri, Feb 7, 2020 at 7:56 AM Ethan Furman <ethan@stoneleaf.us> wrote:
07.02.20 16:28, Ram Rachum пише:
The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like
On 02/07/2020 07:33 AM, Serhiy Storchaka wrote: this:
try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever')
What it does is a shorter version of this:
try: 1/0 except ZeroDivisionError as error: raise ValueError('Whatever') from error
How does it differ from
try: 1/0 except ZeroDivisionError: raise ValueError('Whatever')
Here's a diff:
$ diff no_from.py with_from.py 3,4c3,4 - ... except ZeroDivisionError: - ... raise ValueError('Whatever') --- + ... except ZeroDivisionError as e: + ... raise ValueError('Whatever') from e 10c10 - During handling of the above exception, another exception occurred: --- + The above exception was the direct cause of the following exception:
To me the difference between "raise from" and "raise" is the implicit meaning:
- "During handling of ..." implies that the second exception should not have occurred and there is a bug in the exception handling code
- "The above exception ..." implies that this code path is normal and extra information is being supplied
They also set different attributes on the exception to communicate the trigger of the exception, so there's also a structural difference beyond just the textual one.
If the only difference is the text between the stack traces, why aren't you suggesting to change that text?
On 7 Feb 2020, at 15:30, Ram Rachum <ram@rachum.com> wrote:
Hi,
I'd like to suggest an idea, that builds on PEPs 3134 and 409.
This idea came up when discussing a problem on the django-developers mailing list: https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/django-developers/ibEOt3A9c2M/EP4gbQyTFwAJ
I'll first explain my idea, and then the original problem.
The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like this:
try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever')
What it does is a shorter version of this:
try: 1/0 except ZeroDivisionError as error: raise ValueError('Whatever') from error
Of course, this syntax could only be used inside an except clause, similarly to a blank `raise`.
The `raise as` syntax was one of the rejected syntaxes in PEP 409, for a different use-case, of suppressing context: https://www.python.org/dev/peps/pep-0409/#alternatives
I think that this syntax might be a good fit for this use-case.
The reason I propose this, is that I had a hard time convincing the Django maintainers to use the current `raise foo from bar` syntax in cases where it's appropriate, i.e. when they wrap an exception with another exception. This is important because then the users see the correct message between the two tracebacks, which is "The above exception was the direct cause of the following exception:" rather than "During handling of the above exception, another exception occurred" which is for other cases.
This is my first PR in this topic that was merged: https://github.com/django/django/pull/12263
Even though that first PR was merged, after we discussed adding this all over Django, and recommending that `raise foo from bar` be used in the style guide, Carlton Gibson changed his mind. He gave his arguments in the Django-developers thread I linked to at the top.
That's sad for me, because if Django doesn't accept the new syntax, and is okay with the inaccurate "During handling of" message between exceptions, chances are low that there will be widespread adoption of the current `raise foo from bar` syntax.
It's possible that introducing the simpler `raise as` would increase adoption and make users pay attention to the message between exception tracebacks.
What do you think?
Thanks, Ram. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KM7NRN... Code of Conduct: http://python.org/psf/codeofconduct/
On 02/07/2020 07:44 AM, Anders Hovmöller wrote:
If the only difference is the text between the stack traces, why aren't you suggesting to change that text?
Because the text is important in what it implies. See my other message for details. -- ~Ethan~ Aside: please trim the parts of your previous post you are not replying to. It makes following threads much easier.
On 7 Feb 2020, at 16:59, Ethan Furman <ethan@stoneleaf.us> wrote:
On 02/07/2020 07:44 AM, Anders Hovmöller wrote:
If the only difference is the text between the stack traces, why aren't you suggesting to change that text?
Because the text is important in what it implies. See my other message for details.
Sorry but I still don't understand. I agree the difference in text is a vast improvement. But I don't understand why the implicit case should have the bad text. Is that text actually better ever? / Anders
On 02/07/2020 09:06 AM, Anders Hovmöller wrote:
On 7 Feb 2020, at 16:59, Ethan Furman <ethan@stoneleaf.us> wrote:
On 02/07/2020 07:44 AM, Anders Hovmöller wrote:
If the only difference is the text between the stack traces, why aren't you suggesting to change that text?
Because the text is important in what it implies. See my other message for details.
Sorry but I still don't understand.
I agree the difference in text is a vast improvement. But I don't understand why the implicit case should have the bad text. Is that text actually better ever?
The original text is not bad -- it says, "hey! you're error handler is busted!" -- ~Ethan~
On Feb 7, 2020, at 07:47, Anders Hovmöller <boxed@killingar.net> wrote: If the only difference is the text between the stack traces, why aren't you suggesting to change that text?
I think in the case where the exception handler code has an error in handling the exception (e.g., you try to log to a closed file), the existing text is exactly right, and the text used in the explicit chaining case would be misleading. However, Shai Berger had an interesting idea in that Django thread: treat a `raise` directly under an `except` block special. For example: except ZeroDivisionError: raise ValueError('whatever') # would produce a chaining error message except ZeroDivisionError: log(closedfile, “whatever”) # would produce an another error during handling error message except ZeroDivisionError: MyFancyError(stuff).throwme() # would produce error during handling like the log case # unless the author of throwme decides they want otherwise # in which case they just have to check and raise from I’m not sure if “special” should mean that it’s treated identical to a raise from (as Shai suggested), as that might cause backward compat issues? I haven’t thought it through. Alternatively, if we just stash a new flag somewhere that the traceback machinery can find, we could get the same behavior (or even a third distinct connecting string) without any such issues, at the cost of more complexity. There might be other problems I’m not seeing here. But it seems at least worth exploring.
On Fri, Feb 07, 2020 at 09:57:18AM -0800, Andrew Barnert via Python-ideas wrote:
However, Shai Berger had an interesting idea in that Django thread: treat a `raise` directly under an `except` block special. For example:
except ZeroDivisionError: raise ValueError('whatever') # would produce a chaining error message
Let's get the terminology right: exceptions inside an except block are always chained unless you explicitly disable the chaining with an excplit "raise SpamError() from None". Excluding the "from None" case, any exception raised inside a except block will have its `__context__` set. That's how you get chaining. The difference between the "During handling of the above exception" and "The above exception was the direct cause" messages comes down to whether or not the `__cause__` is set. The status quo requires you to set the cause explicitly: except ZeroDivisionError as error: raise ValueError('whatever') from error Shai Berger wants to set it implicily, based on where the raise is. If it is "directly" under the except line, implicitly set the cause. It seems like a truly bad idea to change the semantics of the exception depending on which of these I happen to write: # exception with __cause__ magically set except ZeroDivisionError: raise ValueError(generate_long_exception_text()) # exception with __context__ set but not __cause__ except ZeroDivisionError: msg = generate_long_exception_text() raise ValueError(msg) Having the raise directly under the except line is a special case of the more general case where the raise is *somewhere* in a multi-line except block. I'm pretty sure the Zen of Python has a relevent koan here about changing the rules for special cases...
There might be other problems I’m not seeing here. But it seems at least worth exploring.
Why? What actual problem is this trying to fix, aside from Ram's aethetic taste that he wants the message to read The above exception was the direct cause of the following exception instead of the default During handling of the above exception, another exception occurred between exception contexts? The status quo takes the conservative position that an exception inside an except block may not be directly caused by the caught exception. which is reasonable. The traceback only claims a direct cause if the developer explicitly sets the cause, but it still shows the context regardless of whether the cause is set or not. In terms of debugging, we see precisely the same traceback, the only difference is whether or not the interpreter states that one exception caused the other or not. -- Steven
On Feb 7, 2020, at 16:11, Steven D'Aprano <steve@pearwood.info> wrote:
Shai Berger wants to set it implicily, based on where the raise is. If it is "directly" under the except line, implicitly set the cause.
It seems like a truly bad idea to change the semantics of the exception depending on which of these I happen to write:
# exception with __cause__ magically set except ZeroDivisionError: raise ValueError(generate_long_exception_text())
# exception with __context__ set but not __cause__ except ZeroDivisionError: msg = generate_long_exception_text() raise ValueError(msg)
I interpreted “directly under” as meaning “directly lexically contained by”, not “on the next physical line”. And even if that isn’t what Shai meant, it’s what I meant. :) So both of these are the same case. The different case is where you call some function (or use some operator or syntax) that raises, instead of using a raise statement. Which I thought would have been obvious from the examples, but I guess not, so… now hopefully it’s clear? The idea is that when you explicitly raise, that’s probably because you wanted to raise, and that’s obvious to anyone reading your code, but when you called log and it happened to raise because you’re shutting down and the log file no longer exists, that’s probably an error. I’m not sure it is worth distinguishing these cases in the first place, but if it is, I think Shai’s idea (or mine, if I misinterpreted Shai’s) might be a decent match, and very simple.
On Fri, 7 Feb 2020 20:08:35 -0800 Andrew Barnert <abarnert@yahoo.com> wrote:
On Feb 7, 2020, at 16:11, Steven D'Aprano <steve@pearwood.info> wrote:
Shai Berger wants to set it implicily, based on where the raise is. If it is "directly" under the except line, implicitly set the cause.
I interpreted “directly under” as meaning “directly lexically contained by”, not “on the next physical line”. And even if that isn’t what Shai meant, it’s what I meant. :)
Just for the record, that *is* what I meant.
On 2/7/2020 11:08 PM, Andrew Barnert via Python-ideas wrote:
On Feb 7, 2020, at 16:11, Steven D'Aprano <steve@pearwood.info> wrote:
Shai Berger wants to set it implicily, based on where the raise is. If it is "directly" under the except line, implicitly set the cause.
It seems like a truly bad idea to change the semantics of the exception depending on which of these I happen to write:
# exception with __cause__ magically set except ZeroDivisionError: raise ValueError(generate_long_exception_text())
# exception with __context__ set but not __cause__ except ZeroDivisionError: msg = generate_long_exception_text() raise ValueError(msg) I interpreted “directly under” as meaning “directly lexically contained by”, not “on the next physical line”. And even if that isn’t what Shai meant, it’s what I meant. :)
So both of these are the same case. The different case is where you call some function (or use some operator or syntax) that raises, instead of using a raise statement. Which I thought would have been obvious from the examples, but I guess not, so… now hopefully it’s clear?
The idea is that when you explicitly raise, that’s probably because you wanted to raise, and that’s obvious to anyone reading your code, but when you called log and it happened to raise because you’re shutting down and the log file no longer exists, that’s probably an error.
I’m not sure it is worth distinguishing these cases in the first place, but if it is, I think Shai’s idea (or mine, if I misinterpreted Shai’s) might be a decent match, and very simple.
I'm -1 on this for several reasons, but the biggest is that if you refactored the code to call a function to handle the exception, you'll change the exception chaining. Erc
On Feb 7, 2020, at 06:32, Ram Rachum <ram@rachum.com> wrote: It's possible that introducing the simpler `raise as` would increase adoption and make users pay attention to the message between exception tracebacks.
From the Django thread that you linked, when you asked whether they’d use it, the reply was:
Maybe... it's still more verbose for no gain as I see it.
I think the default implicit chaining is correct in the default case. It's only if you want to adjust that (or suppress is with `from None`) that the extra clause comes in handy. I think using the default unless there's a reason not to is, in general, a good policy.
And everyone else commenting on the thread seems to be agreeing with Carlton. So it sounds like they’re probably not going to use the new syntax even if you get this feature into Python. And that raises the question of who _would_ use it. If this syntax is needed anywhere, it’s in a deep framework that puts multiple levels of wrapping around complex things; Django seems like the ideal use for it if anything is. And of course even if you did convince them, they wouldn’t start using it for Django 3.0 or 3.1, which have to run on Python 3.6+ and can’t suddenly start requiring a version of Python that’s not even in alpha yet. It looks like it would probably be about 3 years after Python 3.9 or 3.10 (or whenever you get this feature in) before you could change the next minor Django version after that. (Unless you can convince them that this new feature is not only worth using, but so compelling that it’s worth being much more aggressive than usual in requiring the latest Python, which doesn’t seem all that likely.)
Even apart from the long time delay, it seems clear that the Django developers rejected 'raise from' for design reasons. That might be right or wrong as a decision, but it's a separate project from Python itself. Nothing in the issue even hints that they would have accepted it *if only* the spelling were a few characters shorter. On Fri, Feb 7, 2020, 12:43 PM Andrew Barnert via Python-ideas < python-ideas@python.org> wrote:
On Feb 7, 2020, at 06:32, Ram Rachum <ram@rachum.com> wrote:
It's possible that introducing the simpler `raise as` would increase adoption and make users pay attention to the message between exception tracebacks.
From the Django thread that you linked, when you asked whether they’d use it, the reply was:
Maybe... it's still more verbose for no gain as I see it.
I think the default implicit chaining is correct in the default case. It's only if you want to adjust that (or suppress is with `from None`) that the extra clause comes in handy. I think using the default unless there's a reason not to is, in general, a good policy.
And everyone else commenting on the thread seems to be agreeing with Carlton.
So it sounds like they’re probably not going to use the new syntax even if you get this feature into Python. And that raises the question of who _would_ use it. If this syntax is needed anywhere, it’s in a deep framework that puts multiple levels of wrapping around complex things; Django seems like the ideal use for it if anything is.
And of course even if you did convince them, they wouldn’t start using it for Django 3.0 or 3.1, which have to run on Python 3.6+ and can’t suddenly start requiring a version of Python that’s not even in alpha yet. It looks like it would probably be about 3 years after Python 3.9 or 3.10 (or whenever you get this feature in) before you could change the next minor Django version after that. (Unless you can convince them that this new feature is not only worth using, but so compelling that it’s worth being much more aggressive than usual in requiring the latest Python, which doesn’t seem all that likely.)
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/S7JHJF... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, Feb 07, 2020 at 04:28:53PM +0200, Ram Rachum wrote:
The idea is to add `raise as` syntax, that raises an exception while setting the currently caught exception to be the cause. It'll look like this:
try: 1/0 except ZeroDivisionError: raise as ValueError('Whatever')
This new syntax does nothing that the "raise ValueError from error" form doesn't already do. It is redundant syntax for a feature that is rarely necessary. Is there any reason you think that the the only thing holding the Django project back from using the "raise from" form is the extra typing? If the Django devs don't want to use "raise from" to set the exception cause, making it less typing to do something they don't want to do is not going to convince them to do it. Especially since the earliest they could use it would be after they have dropped support for all versions of Python below 3.9.
That's sad for me, because if Django doesn't accept the new syntax, and is okay with the inaccurate "During handling of" message between exceptions,
Is it inaccurate? Never mind, it probably doesn't matter, that's Django's decision to make, not ours, and you obviously think it is.
chances are low that there will be widespread adoption of the current `raise foo from bar` syntax.
Is that important? Personally, I find that directly setting the exception cause to be a feature looking for a use-case, except for the "raise from None" variant which I use frequently. (Which is ironic, because the "from None" case was added to the language much later than the "from error" case.)
It's possible that introducing the simpler `raise as` would increase adoption and make users pay attention to the message between exception tracebacks.
Why do you want users to pay attention to the message between tracebacks? And what makes you think that if "raise from error" was adopted more the people reading tracebacks would pay attention to the message between tracebacks? In my experience, 9 times out of 10 the only relevant part of the traceback is the final line showing the direct failing line. It's relatively rare that I care about the rest of the traceback. I doubt I would often care about the difference between these two: During handling of the above exception, another exception occurred The above exception was the direct cause of the following exception when reading the traceback. Technically the message does add information to the traceback, but in my experience it is of marginal usefulness. -- Steven
participants (14)
-
Anders Hovmöller
-
Andrew Barnert
-
Brett Cannon
-
Chris Angelico
-
David Mertz
-
Eric V. Smith
-
Ethan Furman
-
Greg Ewing
-
Paul Moore
-
Ram Rachum
-
Serhiy Storchaka
-
Shai Berger
-
Soni L.
-
Steven D'Aprano