GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — bpo-45711: Change exc_info related APIs to derive type and traceback from the exception instance by iritkatriel · Pull Request #29780 · python/cpython

Conversation

@iritkatriel

@iritkatriel iritkatriel commented Nov 25, 2021 •
edited by bedevere-bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

https://bugs.python.org/issue45711

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@the-knights-who-say-ni the-knights-who-say-ni added the CLA signed label Nov 25, 2021
@bedevere-bot bedevere-bot added the awaiting core review label Nov 25, 2021
@iritkatriel
iritkatriel force-pushed the 45711-change-apis branch from 18b2949 to 6a766ce Compare November 25, 2021 17:58
iritkatriel and others added 2 commits November 25, 2021 17:59
@iritkatriel
bpo-45711: Change exc_info related APIs to derive type and traceback …
04407ce
…from the exception instance
@blurb-it @iritkatriel
📜🤖 Added by blurb_it.
e8645ff
@iritkatriel
iritkatriel force-pushed the 45711-change-apis branch from 6a766ce to e8645ff Compare November 25, 2021 17:59
@iritkatriel
iritkatriel requested review from gvanrossum and markshannon November 25, 2021 18:37
gvanrossum
gvanrossum reviewed Nov 25, 2021
View reviewed changes

@gvanrossum gvanrossum left a comment

Copy link
Copy Markdown
Member

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Misc/NEWS.d/next/Core and Builtins/2021-11-25-17-51-29.bpo-45711.D2igmz.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/errors.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/errors.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

iritkatriel and others added 5 commits November 26, 2021 10:50
@iritkatriel @gvanrossum
Tweak news text
971887a
Co-authored-by: Guido van Rossum <[email protected]>
@iritkatriel
remove p_ prefix from arg names in PyErr_SetExcInfo
995ec59
@iritkatriel
type is None if value is None or NULL
92835f0
@iritkatriel
wrap news text
4602710
@iritkatriel
tweak doc wording to add 'and can be NULL'
7bc139f
@iritkatriel iritkatriel mentioned this pull request Nov 28, 2021
bpo-45711: Use traceback from exception instead of exc_info in reraise iritkatriel/cpython#32
Merged
@iritkatriel

iritkatriel commented Nov 28, 2021

Copy link
Copy Markdown
Member Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Nov 28, 2021

Copy link
Copy Markdown
Member

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 exc_info->exc_traceback equals whatever PyException_GetTraceback() returns.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel

iritkatriel commented Nov 28, 2021

Copy link
Copy Markdown
Member Author

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 exc_info->exc_traceback equals whatever PyException_GetTraceback() returns.

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’.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@gvanrossum

gvanrossum commented Nov 28, 2021

Copy link
Copy Markdown
Member

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.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel

iritkatriel commented Nov 28, 2021 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

FTR - an example of what will change for raise:

import sys

def f():
  raise TypeError(42)

try:
  f()
except Exception as e:
  exc = e
  print('1--', e.__traceback__, sys.exc_info()[2])
  try:
    raise e
  except:
    pass
  print('2--', e.__traceback__, sys.exc_info()[2])
  raise

Old:

iritkatriel@Irits-MBP cpython-1 % ../cpython/python.exe xx.py
1-- <traceback object at 0x10dc13980> <traceback object at 0x10dc13980>
2-- <traceback object at 0x10dc13840> <traceback object at 0x10dc13980>
Traceback (most recent call last):
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 7, in <module>
    f()
    ^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 4, in f
    raise TypeError(42)
    ^^^^^^^^^^^^^^^^^^^
TypeError: 42

New:

iritkatriel@Irits-MBP cpython-1 % ./python.exe xx.py
1-- <traceback object at 0x105748180> <traceback object at 0x105748180>
2-- <traceback object at 0x1057498c0> <traceback object at 0x1057498c0>
Traceback (most recent call last):
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 12, in <module>
    raise e
    ^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 7, in <module>
    f()
    ^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 4, in f
    raise TypeError(42)
    ^^^^^^^^^^^^^^^^^^^
TypeError: 42
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel

iritkatriel commented Nov 28, 2021 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

(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.)

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:

import sys
import traceback

class Calc:
  def __init__(self):
     self.value = None
     self.exc_info = None

  def calc(self):
     raise TypeError(12)

  def calc_and_cache(self):
    try:
      self.value = self.calc()
      return self.value
    except Exception as e:
      self.exc_info = sys.exc_info()
      raise

  def get_cached_value(self):
    if self.exc_info is not None:
      raise self.exc_info[1]
    return self.value

c = Calc()

print('------ 1 ------')
try:
   c.calc_and_cache()
except Exception as e:
   traceback.print_exception(e)

print('------ 2 ------')
try:
   c.get_cached_value()
except Exception as e:
   traceback.print_exception(e)

The two tracebacks were the same in python 2, but in python 3 you get:

------ 1 ------
Traceback (most recent call last):
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 29, in <module>
    c.calc_and_cache()
    ^^^^^^^^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 14, in calc_and_cache
    self.value = self.calc()
                 ^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 10, in calc
    raise TypeError(12)
    ^^^^^^^^^^^^^^^^^^^
TypeError: 12
------ 2 ------
Traceback (most recent call last):
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 35, in <module>
    c.get_cached_value()
    ^^^^^^^^^^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 22, in get_cached_value
    raise self.exc_info[1]
    ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 29, in <module>
    c.calc_and_cache()
    ^^^^^^^^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 14, in calc_and_cache
    self.value = self.calc()
                 ^^^^^^^^^^^
  File "/Users/iritkatriel/src/cpython-1/xx.py", line 10, in calc
    raise TypeError(12)
    ^^^^^^^^^^^^^^^^^^^
TypeError: 12

The fix was to change the raise to raise self.exc_info[1].with_traceback(self.exc_info[2]).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel
Use traceback from exception instead of exc_info in reraise
05af230
gvanrossum
gvanrossum reviewed Nov 29, 2021
View reviewed changes
Comment thread Doc/reference/simple_stmts.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/whatsnew/3.11.rst
(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__``.

@gvanrossum gvanrossum Nov 29, 2021

Copy link
Copy Markdown
Member

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@gvanrossum

gvanrossum commented Nov 29, 2021

Copy link
Copy Markdown
Member

A related story [...]

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel

iritkatriel commented Nov 29, 2021 •
edited by bedevere-bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Curiously, nobody's ever complained about nonsensical tracebacks.

What made our tracebacks obviously nonsensical is that we raise the same exception from two different places. There is no way to get calc_and_cache and get_cached_value in the same stack.

I've created bpo-45924 to track this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iritkatriel iritkatriel added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Nov 29, 2021
@bedevere-bot

bedevere-bot commented Nov 29, 2021

Copy link
Copy Markdown

🤖 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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Nov 29, 2021
@iritkatriel
Merge remote-tracking branch 'upstream/main' into 45711-change-apis
f17222d
gvanrossum
gvanrossum approved these changes Nov 30, 2021
View reviewed changes

@gvanrossum gvanrossum left a comment

Copy link
Copy Markdown
Member

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!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-bot bedevere-bot added awaiting merge and removed awaiting core review labels Nov 30, 2021
@iritkatriel
iritkatriel merged commit 8a45ca5 into python:main Nov 30, 2021
@bedevere-bot bedevere-bot removed the awaiting merge label Nov 30, 2021
@iritkatriel
iritkatriel deleted the 45711-change-apis branch November 30, 2021 22:53
shihai1991 added a commit to shihai1991/cpython that referenced this pull request Dec 1, 2021
@shihai1991
Merge branch 'main' into bpo_44133
6edb6f5
* 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)
  ...
shihai1991 added a commit to shihai1991/cpython that referenced this pull request Dec 1, 2021
@shihai1991
Merge branch 'main' into bpo_44133
b71b124
* 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)
  ...
finefoot
finefoot reviewed May 17, 2023
View reviewed changes
Comment thread Doc/reference/simple_stmts.rst
Comment on lines +661 to +662
modified traceback. Previously, the exception was re-raised with the
traceback it had when it was caught.

@finefoot finefoot May 17, 2023

Copy link
Copy Markdown
Contributor

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.)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@finefoot finefoot May 17, 2023

Copy link
Copy Markdown
Contributor

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@finefoot finefoot May 17, 2023

Copy link
Copy Markdown
Contributor

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
>>> 

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@iritkatriel iritkatriel May 17, 2023

Copy link
Copy Markdown
Member Author

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@iritkatriel iritkatriel May 17, 2023

Copy link
Copy Markdown
Member Author

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).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@gvanrossum gvanrossum gvanrossum approved these changes
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
+1 more reviewer
@finefoot finefoot finefoot left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@iritkatriel @gvanrossum @bedevere-bot @finefoot @the-knights-who-say-ni
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.