-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpo-45711: Change exc_info related APIs to derive type and traceback from the exception instance #29780
Conversation
18b2949
to
6a766ce
Compare
…from the exception instance
6a766ce
to
e8645ff
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice and straightforward. I have a few nits but really, this could go as-is.
Misc/NEWS.d/next/Core and Builtins/2021-11-25-17-51-29.bpo-45711.D2igmz.rst
Outdated
Show resolved
Hide resolved
Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
There is one more place where the move to using traceback from the exception can be visible from a program - in the eval loop when we reraise. See iritkatriel#32 . I think maybe we should add that change to this PR. |
It comes down to how certain we are that that's not going to break anything. I don't see an assertion in there that checks that |
It will break the same edge case as the other changes in this pr - when you change the traceback on the handled exception in an except clause and then do ‘raise’. |
Okay, that's fine then. I was worried that it might in some cases insert or leave out a frame, but it sounds like that would only happen if you mess around with the exception (e.g. raising and catching it, like you showed earlier). That edge case is acceptable to me as long as we announce it clearly in what's new. (I am still concerned about something else -- sometimes "raise e" and "raise" don't do the same thing even if "e" is the exception being handled. But that is not affected by these changes -- that difference will persist, and arguably it is correct (though it surprises me occasionally). Even if we wanted to change it, we couldn't, because it is not an obscure corner case -- the semantics are clearly described. Please ignore my mumbling here.) |
FTR - an example of what will change for raise:
Old:
New:
|
A related story: After we moved our system from python 2 to python 3 we got bug reports about nonsensical tracebacks. Turned out we had something like this:
The two tracebacks were the same in python 2, but in python 3 you get:
The fix was to change the raise to |
@@ -181,6 +181,12 @@ Other CPython Implementation Changes | |||
hash-based pyc files now use ``siphash13``, too. | |||
(Contributed by Inada Naoki in :issue:`29410`.) | |||
|
|||
* When an active exception is re-raised by a :keyword:`raise` statement with no parameters, | |||
the traceback attached to this exception is now always ``sys.exc_info()[1].__traceback__``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of sys.exc_info()[1]
always puts me off -- it's just too cryptic. Maybe "the traceback attached to this exception is now always its __traceback__
attribute? Or use the phrasing from the reference manual above?
Hm, this makes me realize that there may be a bug in asyncio. When you call a Future's result() method, if the Future represents an error, it raises self.exception. But that means that each time you attempt to get the result, you'll get an extra line (or more) added to the traceback. This seems quite unintended -- the author of the code (me) apparently wasn't aware of this behavior. Curiously, nobody's ever complained about nonsensical tracebacks. To fix it, we'd have to add a separate attribute to hold the traceback. Oh, and the same bug exists in concurrent.futures. (Which I did not write.) In fact now I suspect it exists all over the stdlib where exceptions are stored and raised later. |
What made our tracebacks obviously nonsensical is that we raise the same exception from two different places. There is no way to get I've created bpo-45924 to track this. |
🤖 New build scheduled with the buildbot fleet by @iritkatriel for commit 05af230 🤖 If you want to schedule another build, you need to add the ":hammer: test-with-buildbots" label again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Land any time!
* main: (21 commits) bpo-45876: Have stdev() also use decimal specific square root. (pythonGH-29869) bpo-45876: Correctly rounded stdev() and pstdev() for the Decimal case (pythonGH-29828) bpo-45711: Change exc_info related APIs to derive type and traceback from the exception instance (pythonGH-29780) bpo-30533:Add function inspect.getmembers_static that does not call properties or dynamic properties. (python#20911) bpo-45476: Disallow using asdl_seq_GET() as l-value (pythonGH-29866) bpo-45476: Add _Py_RVALUE() macro (pythonGH-29860) bpo-33381: [doc] strftime's %f option may pad zeros on the left or the right (pythonGH-29801) Fix EncodingWarning in Tools/freeze/test/freeze.py (pythonGH-29742) no-issue: remove unused import from test_graphlib.py (pythonGH-29853) bpo-45931: Prevent Directory.Build.props/targets from leaking from directories above the repo when building on Windows (pythonGH-29854) bpo-45653: fix test_embed on windows (pythonGH-29814) bpo-45917: Add math.exp2() method - return 2 raised to the power of x (pythonGH-29829) bpo-43905: Expand dataclasses.astuple() and asdict() docs (pythonGH-26154) bpo-44391: Remove unused argument from a varargs call. (pythonGH-29843) bpo-45881: configure --with-freeze-module --with-build-python (pythonGH-29835) bpo-45847: PY_STDLIB_MOD_SIMPLE now checks py_stdlib_not_available (pythonGH-29844) bpo-45828: Use unraisable exceptions within sqlite3 callbacks (FH-29591) bpo-40280: Emscripten systems use .wasm suffix by default (pythonGH-29842) bpo-45723: Sort the grand AC_CHECK_HEADERS check (pythonGH-29846) bpo-45847: Make socket module conditional (pythonGH-29769) ...
* main: (21 commits) bpo-45876: Have stdev() also use decimal specific square root. (pythonGH-29869) bpo-45876: Correctly rounded stdev() and pstdev() for the Decimal case (pythonGH-29828) bpo-45711: Change exc_info related APIs to derive type and traceback from the exception instance (pythonGH-29780) bpo-30533:Add function inspect.getmembers_static that does not call properties or dynamic properties. (python#20911) bpo-45476: Disallow using asdl_seq_GET() as l-value (pythonGH-29866) bpo-45476: Add _Py_RVALUE() macro (pythonGH-29860) bpo-33381: [doc] strftime's %f option may pad zeros on the left or the right (pythonGH-29801) Fix EncodingWarning in Tools/freeze/test/freeze.py (pythonGH-29742) no-issue: remove unused import from test_graphlib.py (pythonGH-29853) bpo-45931: Prevent Directory.Build.props/targets from leaking from directories above the repo when building on Windows (pythonGH-29854) bpo-45653: fix test_embed on windows (pythonGH-29814) bpo-45917: Add math.exp2() method - return 2 raised to the power of x (pythonGH-29829) bpo-43905: Expand dataclasses.astuple() and asdict() docs (pythonGH-26154) bpo-44391: Remove unused argument from a varargs call. (pythonGH-29843) bpo-45881: configure --with-freeze-module --with-build-python (pythonGH-29835) bpo-45847: PY_STDLIB_MOD_SIMPLE now checks py_stdlib_not_available (pythonGH-29844) bpo-45828: Use unraisable exceptions within sqlite3 callbacks (FH-29591) bpo-40280: Emscripten systems use .wasm suffix by default (pythonGH-29842) bpo-45723: Sort the grand AC_CHECK_HEADERS check (pythonGH-29846) bpo-45847: Make socket module conditional (pythonGH-29769) ...
modified traceback. Previously, the exception was re-raised with the | ||
traceback it had when it was caught. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, the exception was re-raised with the traceback it had when it was caught.
Isn't this a bug and that particular change should therefore be backported? I recently got surprised by this behavior of raise
when using BaseException.with_traceback
in Python 3.10: I was able to set e.__traceback__
to a custom traceback with e.with_traceback(my_custom_tb)
, even verified with e.__traceback__ == my_custom_tb
, yet a bare raise
raised the exception like no changes had been made. (Also adding to this confusion: Changes to the original traceback like e.__traceback__.tb_next = None
did successfully show up with bare raise
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I originally wanted to post this as an issue but discovered that the behavior seemingly disappeared when using Python 3.11. With some help I found out about this PR where one of the changes fixes the behavior of bare raise
. So I thought it would be the best idea to ask here via comment since @iritkatriel is also listed as the maintainer for the traceback module anyway? I hope that's okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a short example to explain what I mean:
Python 3.10.11 (main, May 4 2023, 06:08:16) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(): g()
...
>>> def g(): h()
...
>>> def h(): raise Exception
...
>>> # Trying to show only the "most recent" step of the traceback; doesn't work:
>>> try:
... f()
... except Exception as e:
... tb = e.__traceback__
... while tb.tb_next is not None:
... tb = tb.tb_next
... e.with_traceback(tb)
... raise
...
Exception()
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 1, in f
File "<stdin>", line 1, in g
File "<stdin>", line 1, in h
Exception
>>> # However, modifying the traceback is possible in general. For example,
>>> # reducing the traceback to the "earliest" step of the original traceback:
>>> try:
... f()
... except Exception as e:
... e.__traceback__.tb_next = None
... raise
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Exception
>>>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't backport this change, it's way too invasive for that. This behaviour existed since 3.0, unfortunately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you "raise e" instead of "raise" then you will see the edited traceback (but with the current frame added).
https://bugs.python.org/issue45711