Mercurial > cpython-lru_cache-weakref
view Lib/test/test_traceback.py @ 87690:66c1c9f32567 default tip
Add use_weakref option to functools.lru_cache
The use_weakref option makes lru_cache create weak references to the function
arguments when creating the cache key, which prevents them from staying in the
memory forever even after all other references to them are gone.
This is a proof of concept for http://bugs.python.org/issue19859
| author | Radomir Dopieralski <devel@sheep.art.pl> |
|---|---|
| date | Tue, 03 Dec 2013 16:02:48 +0100 |
| parents | 100606ef02cf |
| children |
line wrap: on
line source
"""Test cases for traceback module""" from _testcapi import traceback_print, exception_print from io import StringIO import sys import unittest import re from test.support import run_unittest, Error, captured_output from test.support import TESTFN, unlink import traceback class SyntaxTracebackCases(unittest.TestCase): # For now, a very minimal set of tests. I want to be sure that # formatting of SyntaxErrors works based on changes for 2.1. def get_exception_format(self, func, exc): try: func() except exc as value: return traceback.format_exception_only(exc, value) else: raise ValueError("call did not raise exception") def syntax_error_with_caret(self): compile("def fact(x):\n\treturn x!\n", "?", "exec") def syntax_error_with_caret_2(self): compile("1 +\n", "?", "exec") def syntax_error_bad_indentation(self): compile("def spam():\n print(1)\n print(2)", "?", "exec") def test_caret(self): err = self.get_exception_format(self.syntax_error_with_caret, SyntaxError) self.assertEqual(len(err), 4) self.assertTrue(err[1].strip() == "return x!") self.assertIn("^", err[2]) # third line has caret self.assertEqual(err[1].find("!"), err[2].find("^")) # in the right place err = self.get_exception_format(self.syntax_error_with_caret_2, SyntaxError) self.assertIn("^", err[2]) # third line has caret self.assertTrue(err[2].count('\n') == 1) # and no additional newline self.assertTrue(err[1].find("+") == err[2].find("^")) # in the right place def test_nocaret(self): exc = SyntaxError("error", ("x.py", 23, None, "bad syntax")) err = traceback.format_exception_only(SyntaxError, exc) self.assertEqual(len(err), 3) self.assertEqual(err[1].strip(), "bad syntax") def test_bad_indentation(self): err = self.get_exception_format(self.syntax_error_bad_indentation, IndentationError) self.assertEqual(len(err), 4) self.assertEqual(err[1].strip(), "print(2)") self.assertIn("^", err[2]) self.assertEqual(err[1].find(")"), err[2].find("^")) def test_base_exception(self): # Test that exceptions derived from BaseException are formatted right e = KeyboardInterrupt() lst = traceback.format_exception_only(e.__class__, e) self.assertEqual(lst, ['KeyboardInterrupt\n']) def test_format_exception_only_bad__str__(self): class X(Exception): def __str__(self): 1/0 err = traceback.format_exception_only(X, X()) self.assertEqual(len(err), 1) str_value = '<unprintable %s object>' % X.__name__ if X.__module__ in ('__main__', 'builtins'): str_name = X.__name__ else: str_name = '.'.join([X.__module__, X.__name__]) self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value)) def test_without_exception(self): err = traceback.format_exception_only(None, None) self.assertEqual(err, ['None\n']) def test_encoded_file(self): # Test that tracebacks are correctly printed for encoded source files: # - correct line number (Issue2384) # - respect file encoding (Issue3975) import tempfile, sys, subprocess, os # The spawned subprocess has its stdout redirected to a PIPE, and its # encoding may be different from the current interpreter, on Windows # at least. process = subprocess.Popen([sys.executable, "-c", "import sys; print(sys.stdout.encoding)"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = process.communicate() output_encoding = str(stdout, 'ascii').splitlines()[0] def do_test(firstlines, message, charset, lineno): # Raise the message in a subprocess, and catch the output try: output = open(TESTFN, "w", encoding=charset) output.write("""{0}if 1: import traceback; raise RuntimeError('{1}') """.format(firstlines, message)) output.close() process = subprocess.Popen([sys.executable, TESTFN], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = process.communicate() stdout = stdout.decode(output_encoding).splitlines() finally: unlink(TESTFN) # The source lines are encoded with the 'backslashreplace' handler encoded_message = message.encode(output_encoding, 'backslashreplace') # and we just decoded them with the output_encoding. message_ascii = encoded_message.decode(output_encoding) err_line = "raise RuntimeError('{0}')".format(message_ascii) err_msg = "RuntimeError: {0}".format(message_ascii) self.assertIn(("line %s" % lineno), stdout[1], "Invalid line number: {0!r} instead of {1}".format( stdout[1], lineno)) self.assertTrue(stdout[2].endswith(err_line), "Invalid traceback line: {0!r} instead of {1!r}".format( stdout[2], err_line)) self.assertTrue(stdout[3] == err_msg, "Invalid error message: {0!r} instead of {1!r}".format( stdout[3], err_msg)) do_test("", "foo", "ascii", 3) for charset in ("ascii", "iso-8859-1", "utf-8", "GBK"): if charset == "ascii": text = "foo" elif charset == "GBK": text = "\u4E02\u5100" else: text = "h\xe9 ho" do_test("# coding: {0}\n".format(charset), text, charset, 4) do_test("#!shebang\n# coding: {0}\n".format(charset), text, charset, 5) class TracebackFormatTests(unittest.TestCase): def some_exception(self): raise KeyError('blah') def check_traceback_format(self, cleanup_func=None): try: self.some_exception() except KeyError: type_, value, tb = sys.exc_info() if cleanup_func is not None: # Clear the inner frames, not this one cleanup_func(tb.tb_next) traceback_fmt = 'Traceback (most recent call last):\n' + \ ''.join(traceback.format_tb(tb)) file_ = StringIO() traceback_print(tb, file_) python_fmt = file_.getvalue() # Call all _tb and _exc functions with captured_output("stderr") as tbstderr: traceback.print_tb(tb) tbfile = StringIO() traceback.print_tb(tb, file=tbfile) with captured_output("stderr") as excstderr: traceback.print_exc() excfmt = traceback.format_exc() excfile = StringIO() traceback.print_exc(file=excfile) else: raise Error("unable to create test traceback string") # Make sure that Python and the traceback module format the same thing self.assertEqual(traceback_fmt, python_fmt) # Now verify the _tb func output self.assertEqual(tbstderr.getvalue(), tbfile.getvalue()) # Now verify the _exc func output self.assertEqual(excstderr.getvalue(), excfile.getvalue()) self.assertEqual(excfmt, excfile.getvalue()) # Make sure that the traceback is properly indented. tb_lines = python_fmt.splitlines() self.assertEqual(len(tb_lines), 5) banner = tb_lines[0] location, source_line = tb_lines[-2:] self.assertTrue(banner.startswith('Traceback')) self.assertTrue(location.startswith(' File')) self.assertTrue(source_line.startswith(' raise')) def test_traceback_format(self): self.check_traceback_format() def test_traceback_format_with_cleared_frames(self): # Check that traceback formatting also works with a clear()ed frame def cleanup_tb(tb): tb.tb_frame.clear() self.check_traceback_format(cleanup_tb) def test_stack_format(self): # Verify _stack functions. Note we have to use _getframe(1) to # compare them without this frame appearing in the output with captured_output("stderr") as ststderr: traceback.print_stack(sys._getframe(1)) stfile = StringIO() traceback.print_stack(sys._getframe(1), file=stfile) self.assertEqual(ststderr.getvalue(), stfile.getvalue()) stfmt = traceback.format_stack(sys._getframe(1)) self.assertEqual(ststderr.getvalue(), "".join(stfmt)) cause_message = ( "\nThe above exception was the direct cause " "of the following exception:\n\n") context_message = ( "\nDuring handling of the above exception, " "another exception occurred:\n\n") boundaries = re.compile( '(%s|%s)' % (re.escape(cause_message), re.escape(context_message))) class BaseExceptionReportingTests: def get_exception(self, exception_or_callable): if isinstance(exception_or_callable, Exception): return exception_or_callable try: exception_or_callable() except Exception as e: return e def zero_div(self): 1/0 # In zero_div def check_zero_div(self, msg): lines = msg.splitlines() self.assertTrue(lines[-3].startswith(' File')) self.assertIn('1/0 # In zero_div', lines[-2]) self.assertTrue(lines[-1].startswith('ZeroDivisionError'), lines[-1]) def test_simple(self): try: 1/0 # Marker except ZeroDivisionError as _: e = _ lines = self.get_report(e).splitlines() self.assertEqual(len(lines), 4) self.assertTrue(lines[0].startswith('Traceback')) self.assertTrue(lines[1].startswith(' File')) self.assertIn('1/0 # Marker', lines[2]) self.assertTrue(lines[3].startswith('ZeroDivisionError')) def test_cause(self): def inner_raise(): try: self.zero_div() except ZeroDivisionError as e: raise KeyError from e def outer_raise(): inner_raise() # Marker blocks = boundaries.split(self.get_report(outer_raise)) self.assertEqual(len(blocks), 3) self.assertEqual(blocks[1], cause_message) self.check_zero_div(blocks[0]) self.assertIn('inner_raise() # Marker', blocks[2]) def test_context(self): def inner_raise(): try: self.zero_div() except ZeroDivisionError: raise KeyError def outer_raise(): inner_raise() # Marker blocks = boundaries.split(self.get_report(outer_raise)) self.assertEqual(len(blocks), 3) self.assertEqual(blocks[1], context_message) self.check_zero_div(blocks[0]) self.assertIn('inner_raise() # Marker', blocks[2]) def test_context_suppression(self): try: try: raise Exception except: raise ZeroDivisionError from None except ZeroDivisionError as _: e = _ lines = self.get_report(e).splitlines() self.assertEqual(len(lines), 4) self.assertTrue(lines[0].startswith('Traceback')) self.assertTrue(lines[1].startswith(' File')) self.assertIn('ZeroDivisionError from None', lines[2]) self.assertTrue(lines[3].startswith('ZeroDivisionError')) def test_cause_and_context(self): # When both a cause and a context are set, only the cause should be # displayed and the context should be muted. def inner_raise(): try: self.zero_div() except ZeroDivisionError as _e: e = _e try: xyzzy except NameError: raise KeyError from e def outer_raise(): inner_raise() # Marker blocks = boundaries.split(self.get_report(outer_raise)) self.assertEqual(len(blocks), 3) self.assertEqual(blocks[1], cause_message) self.check_zero_div(blocks[0]) self.assertIn('inner_raise() # Marker', blocks[2]) def test_cause_recursive(self): def inner_raise(): try: try: self.zero_div() except ZeroDivisionError as e: z = e raise KeyError from e except KeyError as e: raise z from e def outer_raise(): inner_raise() # Marker blocks = boundaries.split(self.get_report(outer_raise)) self.assertEqual(len(blocks), 3) self.assertEqual(blocks[1], cause_message) # The first block is the KeyError raised from the ZeroDivisionError self.assertIn('raise KeyError from e', blocks[0]) self.assertNotIn('1/0', blocks[0]) # The second block (apart from the boundary) is the ZeroDivisionError # re-raised from the KeyError self.assertIn('inner_raise() # Marker', blocks[2]) self.check_zero_div(blocks[2]) def test_syntax_error_offset_at_eol(self): # See #10186. def e(): raise SyntaxError('', ('', 0, 5, 'hello')) msg = self.get_report(e).splitlines() self.assertEqual(msg[-2], " ^") def e(): exec("x = 5 | 4 |") msg = self.get_report(e).splitlines() self.assertEqual(msg[-2], ' ^') class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks reporting through the 'traceback' module, with both # format_exception() and print_exception(). # def get_report(self, e): e = self.get_exception(e) s = ''.join( traceback.format_exception(type(e), e, e.__traceback__)) with captured_output("stderr") as sio: traceback.print_exception(type(e), e, e.__traceback__) self.assertEqual(sio.getvalue(), s) return s class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks built-in reporting by the interpreter. # def get_report(self, e): e = self.get_exception(e) with captured_output("stderr") as s: exception_print(e) 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__) if __name__ == "__main__": test_main()
