Skip to content

Commit 5bc4855

Browse files
committed
gh-140456: Clarify traceback source line documentation
1 parent 1b41c7b commit 5bc4855

3 files changed

Lines changed: 56 additions & 11 deletions

File tree

Doc/library/traceback.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,10 @@ Module-Level Functions
172172
:func:`extract_tb` or :func:`extract_stack`, return a list of strings ready
173173
for printing. Each string in the resulting list corresponds to the item with
174174
the same index in the argument list. Each string ends in a newline; the
175-
strings may contain internal newlines as well, for those items whose source
176-
text line is not ``None``.
175+
strings may contain internal newlines as well, for items with column position
176+
information whose source text spans multiple physical lines. When
177+
column position information is unavailable, only the first physical line is
178+
displayed. Old-style tuples never have column position information.
177179

178180

179181
.. function:: format_exception_only(exc, /[, value], *, show_group=False)
@@ -471,14 +473,20 @@ the module-level functions described above.
471473
:class:`FrameSummary` objects or old-style list of tuples. Each tuple
472474
should be a 4-tuple with *filename*, *lineno*, *name*, *line* as the
473475
elements.
476+
Old-style tuples do not have column position information, so only the
477+
first physical line of *line* is displayed when they are formatted. The
478+
same applies to :class:`FrameSummary` objects without column position
479+
information.
474480

475481
.. method:: format()
476482

477483
Returns a list of strings ready for printing. Each string in the
478484
resulting list corresponds to a single :ref:`frame <frame-objects>` from
479485
the stack.
480-
Each string ends in a newline; the strings may contain internal
481-
newlines as well, for those items with source text lines.
486+
Each string ends in a newline. For frames with column position
487+
information, the strings may contain internal newlines when the source
488+
text spans multiple physical lines. Otherwise, only the first physical
489+
line is displayed.
482490

483491
For long sequences of the same frame and line, the first few
484492
repetitions are shown, followed by a summary line stating the exact
@@ -540,7 +548,7 @@ in a :ref:`traceback <traceback-objects>`.
540548

541549
.. attribute:: FrameSummary.line
542550

543-
A string representing the source code for this frame, with leading and
551+
The first physical line of source code for this frame, with leading and
544552
trailing whitespace stripped.
545553
If the source is not available, it is ``None``.
546554

Lib/test/test_traceback.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3354,6 +3354,10 @@ def test_explicit_line(self):
33543354
f = traceback.FrameSummary("f", 1, "dummy", line="line")
33553355
self.assertEqual("line", f.line)
33563356

3357+
def test_explicit_multiline_line(self):
3358+
f = traceback.FrameSummary("f", 1, "dummy", line="line 1\nline 2")
3359+
self.assertEqual("line 1", f.line)
3360+
33573361
def test_len(self):
33583362
f = traceback.FrameSummary("f", 1, "dummy", line="line")
33593363
self.assertEqual(len(f), 4)
@@ -3415,6 +3419,29 @@ def test_from_list(self):
34153419
[' File "foo.py", line 1, in fred\n line\n'],
34163420
s.format())
34173421

3422+
def test_from_list_multiline_without_columns(self):
3423+
for frame in [
3424+
('foo.py', 1, 'fred', 'line 1\nline 2'),
3425+
traceback.FrameSummary(
3426+
'foo.py', 1, 'fred', line='line 1\nline 2'),
3427+
]:
3428+
with self.subTest(frame=frame):
3429+
s = traceback.StackSummary.from_list([frame])
3430+
self.assertEqual(
3431+
[' File "foo.py", line 1, in fred\n line 1\n'],
3432+
s.format())
3433+
3434+
def test_from_list_multiline_with_columns(self):
3435+
frame = traceback.FrameSummary(
3436+
'foo.py', 1, 'fred', line='line 1\nline 2',
3437+
end_lineno=2, colno=0, end_colno=6)
3438+
s = traceback.StackSummary.from_list([frame])
3439+
self.assertEqual(
3440+
[' File "foo.py", line 1, in fred\n'
3441+
' line 1\n'
3442+
' line 2\n'],
3443+
s.format())
3444+
34183445
def test_from_list_edited_stack(self):
34193446
s = traceback.StackSummary.from_list([('foo.py', 1, 'fred', 'line')])
34203447
s[0] = ('foo.py', 2, 'fred', 'line')

Lib/traceback.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ def format_list(extracted_list):
8484
8585
Each string in the resulting list corresponds to the item with the
8686
same index in the argument list. Each string ends in a newline;
87-
the strings may contain internal newlines as well, for those items
88-
whose source text line is not None.
87+
the strings may contain internal newlines as well, for items with
88+
column position information whose source text spans multiple physical
89+
lines. When column position information is unavailable, only
90+
the first physical line is displayed. Old-style tuples never have
91+
column position information.
8992
"""
9093
return StackSummary.from_list(extracted_list).format()
9194

@@ -324,8 +327,8 @@ class FrameSummary:
324327
active when the frame was captured.
325328
- :attr:`name` The name of the function or method that was executing
326329
when the frame was captured.
327-
- :attr:`line` The text from the linecache module for the line
328-
of code that was running when the frame was captured.
330+
- :attr:`line` The first physical line of code that was running when
331+
the frame was captured.
329332
- :attr:`locals` Either None if locals were not supplied, or a dict
330333
mapping the name to the repr() of the variable.
331334
- :attr:`end_lineno` The last line number of the source code for this frame.
@@ -558,6 +561,11 @@ def from_list(klass, a_list):
558561
"""
559562
Create a StackSummary object from a supplied list of
560563
FrameSummary objects or old-style list of tuples.
564+
565+
Old-style tuples do not have column position information, so only the
566+
first physical line of the line element is displayed when they are
567+
formatted. The same applies to FrameSummary objects without column
568+
position information.
561569
"""
562570
# While doing a fast-path check for isinstance(a_list, StackSummary) is
563571
# appealing, idlelib.run.cleanup_traceback and other similar code may
@@ -794,8 +802,10 @@ def format(self, **kwargs):
794802
795803
Returns a list of strings ready for printing. Each string in the
796804
resulting list corresponds to a single frame from the stack.
797-
Each string ends in a newline; the strings may contain internal
798-
newlines as well, for those items with source text lines.
805+
Each string ends in a newline. For frames with column position
806+
information, the strings may contain internal newlines when the source
807+
text spans multiple physical lines. Otherwise, only the first physical
808+
line is displayed.
799809
800810
For long sequences of the same frame and line, the first few
801811
repetitions are shown, followed by a summary line stating the exact

0 commit comments

Comments
 (0)