diff options
-rw-r--r-- | Doc/library/tracemalloc.rst | 18 | ||||
-rw-r--r-- | Doc/whatsnew/3.7.rst | 4 | ||||
-rw-r--r-- | Lib/test/test_tracemalloc.py | 30 | ||||
-rw-r--r-- | Lib/test/test_warnings/__init__.py | 6 | ||||
-rw-r--r-- | Lib/tracemalloc.py | 26 | ||||
-rw-r--r-- | Lib/warnings.py | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2017-11-24-00-59-12.bpo-32121.ePbmwC.rst | 6 |
7 files changed, 68 insertions, 24 deletions
diff --git a/Doc/library/tracemalloc.rst b/Doc/library/tracemalloc.rst index 048ee64..2d327c0 100644 --- a/Doc/library/tracemalloc.rst +++ b/Doc/library/tracemalloc.rst @@ -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. - 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. + .. 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, 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. diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index e4600fe..5c00159 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -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/ diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index 533ba6d..b0a0e1b 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -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 "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), + self.assertEqual(tb.format(limit=1, most_recent_first=True), [' File "a.py", line 2', ' <a.py, 2>']) - self.assertEqual(tb.format(limit=-1), - []) + self.assertEqual(tb.format(limit=-1, most_recent_first=True), + [' File "b.py", line 4', + ' <b.py, 4>']) class TestFilters(unittest.TestCase): diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index f2fdaa5..e60bc4d 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -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) diff --git a/Lib/tracemalloc.py b/Lib/tracemalloc.py index 597a297..2c1ac3b 100644 --- a/Lib/tracemalloc.py +++ b/Lib/tracemalloc.py @@ -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() diff --git a/Lib/warnings.py b/Lib/warnings.py index d7ea057..4e7241f 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -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)) diff --git a/Misc/NEWS.d/next/Library/2017-11-24-00-59-12.bpo-32121.ePbmwC.rst b/Misc/NEWS.d/next/Library/2017-11-24-00-59-12.bpo-32121.ePbmwC.rst new file mode 100644 index 0000000..7701c86 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-11-24-00-59-12.bpo-32121.ePbmwC.rst @@ -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.) |