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
|
@ -650,8 +650,8 @@ Traceback
|
|||
|
||||
.. class:: Traceback
|
||||
|
||||
Sequence of :class:`Frame` instances sorted from the most recent frame to
|
||||
the oldest frame.
|
||||
Sequence of :class:`Frame` instances sorted from the oldest frame to the
|
||||
most recent frame.
|
||||
|
||||
A traceback contains at least ``1`` frame. If the ``tracemalloc`` module
|
||||
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`
|
||||
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.
|
||||
|
||||
.. method:: format(limit=None, most_recent_first=False)
|
||||
|
||||
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, only format the *limit* most recent frames.
|
||||
: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
|
||||
:meth:`.format` does not include newlines.
|
||||
|
|
|
@ -751,6 +751,10 @@ Changes in the Python API
|
|||
avoid a warning escape them with a backslash.
|
||||
(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/
|
||||
|
||||
|
||||
|
|
|
@ -171,6 +171,9 @@ class TestTracemallocEnabled(unittest.TestCase):
|
|||
|
||||
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)
|
||||
trace2 = self.find_trace(traces, obj2_traceback)
|
||||
domain1, size1, traceback1 = trace1
|
||||
|
@ -537,11 +540,11 @@ class TestSnapshot(unittest.TestCase):
|
|||
def test_trace_format(self):
|
||||
snapshot, snapshot2 = create_snapshots()
|
||||
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
|
||||
self.assertEqual(str(traceback), 'a.py:2')
|
||||
self.assertEqual(str(traceback), 'b.py:4')
|
||||
frame = traceback[0]
|
||||
self.assertEqual(str(frame), 'a.py:2')
|
||||
self.assertEqual(str(frame), 'b.py:4')
|
||||
|
||||
def test_statistic_format(self):
|
||||
snapshot, snapshot2 = create_snapshots()
|
||||
|
@ -574,17 +577,32 @@ class TestSnapshot(unittest.TestCase):
|
|||
side_effect=getline):
|
||||
tb = snapshot.traces[0].traceback
|
||||
self.assertEqual(tb.format(),
|
||||
[' File "a.py", line 2',
|
||||
' <a.py, 2>',
|
||||
' File "b.py", line 4',
|
||||
' <b.py, 4>'])
|
||||
[' File "b.py", line 4',
|
||||
' <b.py, 4>',
|
||||
' File "a.py", line 2',
|
||||
' <a.py, 2>'])
|
||||
|
||||
self.assertEqual(tb.format(limit=1),
|
||||
[' File "a.py", line 2',
|
||||
' <a.py, 2>'])
|
||||
|
||||
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):
|
||||
|
|
|
@ -941,11 +941,11 @@ class PyWarningsDisplayTests(WarningsDisplayTests, unittest.TestCase):
|
|||
expected = textwrap.dedent('''
|
||||
{fname}:5: ResourceWarning: unclosed file <...>
|
||||
f = None
|
||||
Object allocated at (most recent call first):
|
||||
File "{fname}", lineno 3
|
||||
f = open(__file__)
|
||||
Object allocated at (most recent call last):
|
||||
File "{fname}", lineno 7
|
||||
func()
|
||||
File "{fname}", lineno 3
|
||||
f = open(__file__)
|
||||
''')
|
||||
expected = expected.format(fname=support.TESTFN).strip()
|
||||
self.assertEqual(stderr, expected)
|
||||
|
|
|
@ -171,16 +171,18 @@ class Frame:
|
|||
@total_ordering
|
||||
class Traceback(Sequence):
|
||||
"""
|
||||
Sequence of Frame instances sorted from the most recent frame
|
||||
to the oldest frame.
|
||||
Sequence of Frame instances sorted from the oldest frame
|
||||
to the most recent frame.
|
||||
"""
|
||||
__slots__ = ("_frames",)
|
||||
|
||||
def __init__(self, frames):
|
||||
Sequence.__init__(self)
|
||||
# frames is a tuple of frame tuples: see Frame constructor for the
|
||||
# format of a frame tuple
|
||||
self._frames = frames
|
||||
# format of a frame tuple; it is reversed, because _tracemalloc
|
||||
# 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):
|
||||
return len(self._frames)
|
||||
|
@ -209,11 +211,19 @@ class Traceback(Sequence):
|
|||
def __repr__(self):
|
||||
return "<Traceback %r>" % (tuple(self),)
|
||||
|
||||
def format(self, limit=None):
|
||||
def format(self, limit=None, most_recent_first=False):
|
||||
lines = []
|
||||
if limit is not None and limit < 0:
|
||||
return lines
|
||||
for frame in self[:limit]:
|
||||
if limit is not None:
|
||||
if limit > 0:
|
||||
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'
|
||||
% (frame.filename, frame.lineno))
|
||||
line = linecache.getline(frame.filename, frame.lineno).strip()
|
||||
|
|
|
@ -62,7 +62,7 @@ def _formatwarnmsg_impl(msg):
|
|||
tb = 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:
|
||||
s += (' File "%s", lineno %s\n'
|
||||
% (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…
Reference in New Issue