8000 gh-96092: Fix traceback.walk_stack(None) skipping too many frames by ammaraskar · Pull Request #129330 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-96092: Fix traceback.walk_stack(None) skipping too many frames #129330

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

Merged
merged 1 commit into from
Feb 13, 2025

Conversation

ammaraskar
Copy link
Member
@ammaraskar ammaraskar commented Jan 27, 2025

Fixes #96092. Please see my comment in #99015 (comment) for more details on the root cause analysis of this issue.


This changes the walk_stack generator to capture the stack when walk_stack is called, rather than when it is first iterated over. Since this is technically a breaking change in behavior, I added a versionchanged to the documentation. In practice, this is unlikely to break anyone, you would have been needing to store the result of walk_stack and expecting it to change.

It also adds a direct test for what the innermost frame walk_stack returns is.


📚 Documentation preview 📚: https://cpython-previews--129330.org.readthedocs.build/

As it says in its documentation, walk_stack was meant to just
follow `f.f_back` like other functions in the traceback module.
Instead it was previously doing `f.f_back.f_back` and then this
changed to `f_back.f_back.f_back.f_back' in Python 3.11 breaking
its behavior for external users.

This happened because the walk_stack function never really had
any good direct tests and its only consumer in the traceback module was
`extract_stack` which passed the result into `StackSummary.extract`.
As a generator, it was previously capturing the state of the stack
when it was first iterated over, rather than the stack when `walk_stack`
was called. Meaning when called inside the two method deep
`extract` and `extract_stack` calls, two `f_back`s were needed.
When 3.11 modified the sequence of calls in `extract`, two more
`f_back`s were needed to make the tests happy.

This changes the generator to capture the stack when `walk_stack` is
called, rather than when it is first iterated over. Since this is
technically a breaking change in behavior, there is a versionchanged
to the documentation. In practice, this is unlikely to break anyone,
you would have been needing to store the result of `walk_stack` and
expecting it to change.
@ammaraskar
Copy link
Member Author
ammaraskar commented Jan 27, 2025

+@pablogsal as a reviewer. This was a bug I accidentally introduced in PEP 657's implementation

Finally getting around to fixing it. Technically the fix is a change in behavior itself but it is unlikely to break anyone, they would have had to store the result of walk_stack, e.g

walker = traceback.walk_stack(None)
...
"""
frames = list(walker)
# ^ Previously this would return the frames
# as of when the `list` was called.
# Now returns the frames where `traceback.walk_stack` was called.

A quick search of traceback.walk_stack shows that most people either pass it into StackSummary.extract or directly iterate over it as soon as its called.

Copy link
Member
@pablogsal pablogsal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pablogsal pablogsal enabled auto-merge (squash) February 13, 2025 01:43
@pablogsal pablogsal merged commit f9a7d41 into python:main Feb 13, 2025
45 checks passed
@ammaraskar ammaraskar deleted the gh-96092 branch March 10, 2025 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

traceback.walk_stack(None) does not behave the same as traceback.walk_stack(inspect.currentframe())
2 participants
0