diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 0533beaf8c3..b68a8f1e197 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -129,6 +129,13 @@ The module defines the following functions: A shorthand for ``format_list(extract_stack(f, limit))``. +.. function:: clear_frames(tb) + + Clears the local variables of all the stack frames in a traceback *tb* + by calling the :meth:`clear` method of each frame object. + + .. versionadded:: 3.4 + .. _traceback-example: diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst index e91ce2a675c..89912a9cf71 100644 --- a/Doc/whatsnew/3.4.rst +++ b/Doc/whatsnew/3.4.rst @@ -377,6 +377,14 @@ plain tuple. (Contributed by Claudiu Popa in :issue:`18901`.) :meth:`sunau.open` now supports the context manager protocol (:issue:`18878`). +traceback +--------- + +A new :func:`traceback.clear_frames` function takes a traceback object +and clears the local variables in all of the frames it references, +reducing the amount of memory consumed (:issue:`1565525`). + + urllib ------ diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 66a12bf12c1..96ef951139e 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -388,6 +388,36 @@ class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): return s.getvalue() +class MiscTracebackCases(unittest.TestCase): + # + # Check non-printing functions in traceback module + # + + def test_clear(self): + def outer(): + middle() + def middle(): + inner() + def inner(): + i = 1 + 1/0 + + try: + outer() + except: + type_, value, tb = sys.exc_info() + + # Initial assertion: there's one local in the inner frame. + inner_frame = tb.tb_next.tb_next.tb_next.tb_frame + self.assertEqual(len(inner_frame.f_locals), 1) + + # Clear traceback frames + traceback.clear_frames(tb) + + # Local variable dict should now be empty. + self.assertEqual(len(inner_frame.f_locals), 0) + + def test_main(): run_unittest(__name__) diff --git a/Lib/traceback.py b/Lib/traceback.py index 3aa1578f4e4..d5b37528bab 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -7,7 +7,8 @@ import operator __all__ = ['extract_stack', 'extract_tb', 'format_exception', 'format_exception_only', 'format_list', 'format_stack', 'format_tb', 'print_exc', 'format_exc', 'print_exception', - 'print_last', 'print_stack', 'print_tb'] + 'print_last', 'print_stack', 'print_tb', + 'clear_frames'] # # Formatting and printing lists of traceback lines. @@ -299,3 +300,13 @@ def extract_stack(f=None, limit=None): stack = list(_extract_stack_iter(_get_stack(f), limit=limit)) stack.reverse() return stack + +def clear_frames(tb): + "Clear all references to local variables in the frames of a traceback." + while tb is not None: + try: + tb.tb_frame.clear() + except RuntimeError: + # Ignore the exception raised if the frame is still executing. + pass + tb = tb.tb_next diff --git a/Misc/NEWS b/Misc/NEWS index e4325674e2a..f1375532717 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -32,6 +32,11 @@ Library faulthandler module if the variable is non-empty. Same behaviour than other variables like :envvar:`PYTHONDONTWRITEBYTECODE`. +- Issue #1565525: New function ``traceback.clear_frames`` will clear + the local variables of all the stack frames referenced by a traceback + object. + + Tests -----