Skip to content
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

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/traceback.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ Module-Level Functions

.. versionadded:: 3.5

.. versionchanged:: 3.14
This function previously returned a generator that would walk the stack
when first iterated over. The generator returned now is the state of the
stack when ``walk_stack`` is called.

.. function:: walk_tb(tb)

Walk a traceback following :attr:`~traceback.tb_next` yielding the frame and
Expand Down
10 changes: 8 additions & 2 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -3229,11 +3229,17 @@ class TestStack(unittest.TestCase):
def test_walk_stack(self):
def deeper():
return list(traceback.walk_stack(None))
s1 = list(traceback.walk_stack(None))
s2 = deeper()
s1, s2 = list(traceback.walk_stack(None)), deeper()
self.assertEqual(len(s2) - len(s1), 1)
self.assertEqual(s2[1:], s1)

def test_walk_innermost_frame(self):
def inner():
return list(traceback.walk_stack(None))
frames = inner()
innermost_frame, _ = frames[0]
self.assertEqual(innermost_frame.f_code.co_name, "inner")

def test_walk_tb(self):
try:
1/0
Expand Down
12 changes: 8 additions & 4 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,14 @@ def walk_stack(f):
current stack is used. Usually used with StackSummary.extract.
"""
if f is None:
f = sys._getframe().f_back.f_back.f_back.f_back
while f is not None:
yield f, f.f_lineno
f = f.f_back
f = sys._getframe().f_back

def walk_stack_generator(frame):
while frame is not None:
yield frame, frame.f_lineno
frame = frame.f_back

return walk_stack_generator(f)


def walk_tb(tb):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix bug in :func:`traceback.walk_stack` called with None where it was skipping
more frames than in prior versions. This bug fix also changes walk_stack to
walk the stack in the frame where it was called rather than where it first gets
used.
Loading