mirror of
https://github.com/python/cpython.git
synced 2025-10-03 05:35:59 +00:00
bpo-32121: Add most_recent_first parameter to tracemalloc.Traceback.format (#4534)
* Add most_recent_first parameter to tracemalloc.Traceback.format to allow reversing the order of the frames in the output * Reversed default sorting of tracemalloc.Traceback frames * Allowed negative limit, truncating from the other side.
This commit is contained in:
parent
859f7ce7a4
commit
706e10b186
7 changed files with 70 additions and 26 deletions
|
@ -650,8 +650,8 @@ Traceback
|
||||||
|
|
||||||
.. class:: Traceback
|
.. class:: Traceback
|
||||||
|
|
||||||
Sequence of :class:`Frame` instances sorted from the most recent frame to
|
Sequence of :class:`Frame` instances sorted from the oldest frame to the
|
||||||
the oldest frame.
|
most recent frame.
|
||||||
|
|
||||||
A traceback contains at least ``1`` frame. If the ``tracemalloc`` module
|
A traceback contains at least ``1`` frame. If the ``tracemalloc`` module
|
||||||
failed to get a frame, the filename ``"<unknown>"`` at line number ``0`` is
|
failed to get a frame, the filename ``"<unknown>"`` at line number ``0`` is
|
||||||
|
@ -663,11 +663,17 @@ Traceback
|
||||||
The :attr:`Trace.traceback` attribute is an instance of :class:`Traceback`
|
The :attr:`Trace.traceback` attribute is an instance of :class:`Traceback`
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
.. method:: format(limit=None)
|
.. versionchanged:: 3.7
|
||||||
|
Frames are now sorted from the oldest to the most recent, instead of most recent to oldest.
|
||||||
|
|
||||||
Format the traceback as a list of lines with newlines. Use the
|
.. method:: format(limit=None, most_recent_first=False)
|
||||||
:mod:`linecache` module to retrieve lines from the source code. If
|
|
||||||
*limit* is set, only format the *limit* most recent frames.
|
Format the traceback as a list of lines with newlines. Use the
|
||||||
|
:mod:`linecache` module to retrieve lines from the source code.
|
||||||
|
If *limit* is set, format the *limit* most recent frames if *limit*
|
||||||
|
is positive. Otherwise, format the ``abs(limit)`` oldest frames.
|
||||||
|
If *most_recent_first* is ``True``, the order of the formatted frames
|
||||||
|
is reversed, returning the most recent frame first instead of last.
|
||||||
|
|
||||||
Similar to the :func:`traceback.format_tb` function, except that
|
Similar to the :func:`traceback.format_tb` function, except that
|
||||||
:meth:`.format` does not include newlines.
|
:meth:`.format` does not include newlines.
|
||||||
|
|
|
@ -751,6 +751,10 @@ Changes in the Python API
|
||||||
avoid a warning escape them with a backslash.
|
avoid a warning escape them with a backslash.
|
||||||
(Contributed by Serhiy Storchaka in :issue:`30349`.)
|
(Contributed by Serhiy Storchaka in :issue:`30349`.)
|
||||||
|
|
||||||
|
* :class:`tracemalloc.Traceback` frames are now sorted from oldest to most
|
||||||
|
recent to be more consistent with :mod:`traceback`.
|
||||||
|
(Contributed by Jesse Bakker in :issue:`32121`.)
|
||||||
|
|
||||||
.. _Unicode Technical Standard #18: https://unicode.org/reports/tr18/
|
.. _Unicode Technical Standard #18: https://unicode.org/reports/tr18/
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,9 @@ class TestTracemallocEnabled(unittest.TestCase):
|
||||||
|
|
||||||
traces = tracemalloc._get_traces()
|
traces = tracemalloc._get_traces()
|
||||||
|
|
||||||
|
obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
|
||||||
|
obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
|
||||||
|
|
||||||
trace1 = self.find_trace(traces, obj1_traceback)
|
trace1 = self.find_trace(traces, obj1_traceback)
|
||||||
trace2 = self.find_trace(traces, obj2_traceback)
|
trace2 = self.find_trace(traces, obj2_traceback)
|
||||||
domain1, size1, traceback1 = trace1
|
domain1, size1, traceback1 = trace1
|
||||||
|
@ -537,11 +540,11 @@ class TestSnapshot(unittest.TestCase):
|
||||||
def test_trace_format(self):
|
def test_trace_format(self):
|
||||||
snapshot, snapshot2 = create_snapshots()
|
snapshot, snapshot2 = create_snapshots()
|
||||||
trace = snapshot.traces[0]
|
trace = snapshot.traces[0]
|
||||||
self.assertEqual(str(trace), 'a.py:2: 10 B')
|
self.assertEqual(str(trace), 'b.py:4: 10 B')
|
||||||
traceback = trace.traceback
|
traceback = trace.traceback
|
||||||
self.assertEqual(str(traceback), 'a.py:2')
|
self.assertEqual(str(traceback), 'b.py:4')
|
||||||
frame = traceback[0]
|
frame = traceback[0]
|
||||||
self.assertEqual(str(frame), 'a.py:2')
|
self.assertEqual(str(frame), 'b.py:4')
|
||||||
|
|
||||||
def test_statistic_format(self):
|
def test_statistic_format(self):
|
||||||
snapshot, snapshot2 = create_snapshots()
|
snapshot, snapshot2 = create_snapshots()
|
||||||
|
@ -574,17 +577,32 @@ class TestSnapshot(unittest.TestCase):
|
||||||
side_effect=getline):
|
side_effect=getline):
|
||||||
tb = snapshot.traces[0].traceback
|
tb = snapshot.traces[0].traceback
|
||||||
self.assertEqual(tb.format(),
|
self.assertEqual(tb.format(),
|
||||||
[' File "a.py", line 2',
|
[' File "b.py", line 4',
|
||||||
' <a.py, 2>',
|
' <b.py, 4>',
|
||||||
' File "b.py", line 4',
|
' File "a.py", line 2',
|
||||||
' <b.py, 4>'])
|
' <a.py, 2>'])
|
||||||
|
|
||||||
self.assertEqual(tb.format(limit=1),
|
self.assertEqual(tb.format(limit=1),
|
||||||
[' File "a.py", line 2',
|
[' File "a.py", line 2',
|
||||||
' <a.py, 2>'])
|
' <a.py, 2>'])
|
||||||
|
|
||||||
self.assertEqual(tb.format(limit=-1),
|
self.assertEqual(tb.format(limit=-1),
|
||||||
[])
|
[' File "b.py", line 4',
|
||||||
|
' <b.py, 4>'])
|
||||||
|
|
||||||
|
self.assertEqual(tb.format(most_recent_first=True),
|
||||||
|
[' File "a.py", line 2',
|
||||||
|
' <a.py, 2>',
|
||||||
|
' File "b.py", line 4',
|
||||||
|
' <b.py, 4>'])
|
||||||
|
|
||||||
|
self.assertEqual(tb.format(limit=1, most_recent_first=True),
|
||||||
|
[' File "a.py", line 2',
|
||||||
|
' <a.py, 2>'])
|
||||||
|
|
||||||
|
self.assertEqual(tb.format(limit=-1, most_recent_first=True),
|
||||||
|
[' File "b.py", line 4',
|
||||||
|
' <b.py, 4>'])
|
||||||
|
|
||||||
|
|
||||||
class TestFilters(unittest.TestCase):
|
class TestFilters(unittest.TestCase):
|
||||||
|
|
|
@ -941,11 +941,11 @@ class PyWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase):
|
||||||
expected = textwrap.dedent('''
|
expected = textwrap.dedent('''
|
||||||
{fname}:5: ResourceWarning: unclosed file <...>
|
{fname}:5: ResourceWarning: unclosed file <...>
|
||||||
f = None
|
f = None
|
||||||
Object allocated at (most recent call first):
|
Object allocated at (most recent call last):
|
||||||
File "{fname}", lineno 3
|
|
||||||
f = open(__file__)
|
|
||||||
File "{fname}", lineno 7
|
File "{fname}", lineno 7
|
||||||
func()
|
func()
|
||||||
|
File "{fname}", lineno 3
|
||||||
|
f = open(__file__)
|
||||||
''')
|
''')
|
||||||
expected = expected.format(fname=support.TESTFN).strip()
|
expected = expected.format(fname=support.TESTFN).strip()
|
||||||
self.assertEqual(stderr, expected)
|
self.assertEqual(stderr, expected)
|
||||||
|
|
|
@ -171,16 +171,18 @@ class Frame:
|
||||||
@total_ordering
|
@total_ordering
|
||||||
class Traceback(Sequence):
|
class Traceback(Sequence):
|
||||||
"""
|
"""
|
||||||
Sequence of Frame instances sorted from the most recent frame
|
Sequence of Frame instances sorted from the oldest frame
|
||||||
to the oldest frame.
|
to the most recent frame.
|
||||||
"""
|
"""
|
||||||
__slots__ = ("_frames",)
|
__slots__ = ("_frames",)
|
||||||
|
|
||||||
def __init__(self, frames):
|
def __init__(self, frames):
|
||||||
Sequence.__init__(self)
|
Sequence.__init__(self)
|
||||||
# frames is a tuple of frame tuples: see Frame constructor for the
|
# frames is a tuple of frame tuples: see Frame constructor for the
|
||||||
# format of a frame tuple
|
# format of a frame tuple; it is reversed, because _tracemalloc
|
||||||
self._frames = frames
|
# returns frames sorted from most recent to oldest, but the
|
||||||
|
# Python API expects oldest to most recent
|
||||||
|
self._frames = tuple(reversed(frames))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._frames)
|
return len(self._frames)
|
||||||
|
@ -209,11 +211,19 @@ class Traceback(Sequence):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Traceback %r>" % (tuple(self),)
|
return "<Traceback %r>" % (tuple(self),)
|
||||||
|
|
||||||
def format(self, limit=None):
|
def format(self, limit=None, most_recent_first=False):
|
||||||
lines = []
|
lines = []
|
||||||
if limit is not None and limit < 0:
|
if limit is not None:
|
||||||
return lines
|
if limit > 0:
|
||||||
for frame in self[:limit]:
|
frame_slice = self[-limit:]
|
||||||
|
else:
|
||||||
|
frame_slice = self[:limit]
|
||||||
|
else:
|
||||||
|
frame_slice = self
|
||||||
|
|
||||||
|
if most_recent_first:
|
||||||
|
frame_slice = reversed(frame_slice)
|
||||||
|
for frame in frame_slice:
|
||||||
lines.append(' File "%s", line %s'
|
lines.append(' File "%s", line %s'
|
||||||
% (frame.filename, frame.lineno))
|
% (frame.filename, frame.lineno))
|
||||||
line = linecache.getline(frame.filename, frame.lineno).strip()
|
line = linecache.getline(frame.filename, frame.lineno).strip()
|
||||||
|
|
|
@ -62,7 +62,7 @@ def _formatwarnmsg_impl(msg):
|
||||||
tb = None
|
tb = None
|
||||||
|
|
||||||
if tb is not None:
|
if tb is not None:
|
||||||
s += 'Object allocated at (most recent call first):\n'
|
s += 'Object allocated at (most recent call last):\n'
|
||||||
for frame in tb:
|
for frame in tb:
|
||||||
s += (' File "%s", lineno %s\n'
|
s += (' File "%s", lineno %s\n'
|
||||||
% (frame.filename, frame.lineno))
|
% (frame.filename, frame.lineno))
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
Made ``tracemalloc.Traceback`` behave more like the traceback module,
|
||||||
|
sorting the frames from oldest to most recent. ``Traceback.format()``
|
||||||
|
now accepts negative *limit*, truncating the result to the ``abs(limit)``
|
||||||
|
oldest frames. To get the old behaviour, one can use the new
|
||||||
|
*most_recent_first* argument to ``Traceback.format()``.
|
||||||
|
(Patch by Jesse Bakker.)
|
Loading…
Add table
Add a link
Reference in a new issue