summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorRobert Collins <rbtcollins@hp.com>2015-03-04 23:07:57 (GMT)
committerRobert Collins <rbtcollins@hp.com>2015-03-04 23:07:57 (GMT)
commit6bc2c1e7ebf359224e5e547f58ffc2c42cb36a39 (patch)
treea4fa620827bc620fff1bc68f480f35c849ee91e1 /Lib/test
parent0bfd0a40486d1eac92572e7272d25e3e405b7aef (diff)
downloadcpython-6bc2c1e7ebf359224e5e547f58ffc2c42cb36a39.zip
cpython-6bc2c1e7ebf359224e5e547f58ffc2c42cb36a39.tar.gz
cpython-6bc2c1e7ebf359224e5e547f58ffc2c42cb36a39.tar.bz2
Issue #17911: traceback module overhaul
Provide a way to seed the linecache for a PEP-302 module without actually loading the code. Provide a new object API for traceback, including the ability to not lookup lines at all until the traceback is actually rendered, without any trace of the original objects being kept alive.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_linecache.py44
-rw-r--r--Lib/test/test_traceback.py196
2 files changed, 240 insertions, 0 deletions
diff --git a/Lib/test/test_linecache.py b/Lib/test/test_linecache.py
index 5fe0554..2fb8662 100644
--- a/Lib/test/test_linecache.py
+++ b/Lib/test/test_linecache.py
@@ -7,6 +7,7 @@ from test import support
FILENAME = linecache.__file__
+NONEXISTENT_FILENAME = FILENAME + '.missing'
INVALID_NAME = '!@$)(!@#_1'
EMPTY = ''
TESTS = 'inspect_fodder inspect_fodder2 mapping_tests'
@@ -126,6 +127,49 @@ class LineCacheTests(unittest.TestCase):
self.assertEqual(line, getline(source_name, index + 1))
source_list.append(line)
+ def test_lazycache_no_globals(self):
+ lines = linecache.getlines(FILENAME)
+ linecache.clearcache()
+ self.assertEqual(False, linecache.lazycache(FILENAME, None))
+ self.assertEqual(lines, linecache.getlines(FILENAME))
+
+ def test_lazycache_smoke(self):
+ lines = linecache.getlines(NONEXISTENT_FILENAME, globals())
+ linecache.clearcache()
+ self.assertEqual(
+ True, linecache.lazycache(NONEXISTENT_FILENAME, globals()))
+ self.assertEqual(1, len(linecache.cache[NONEXISTENT_FILENAME]))
+ # Note here that we're looking up a non existant filename with no
+ # globals: this would error if the lazy value wasn't resolved.
+ self.assertEqual(lines, linecache.getlines(NONEXISTENT_FILENAME))
+
+ def test_lazycache_provide_after_failed_lookup(self):
+ linecache.clearcache()
+ lines = linecache.getlines(NONEXISTENT_FILENAME, globals())
+ linecache.clearcache()
+ linecache.getlines(NONEXISTENT_FILENAME)
+ linecache.lazycache(NONEXISTENT_FILENAME, globals())
+ self.assertEqual(lines, linecache.updatecache(NONEXISTENT_FILENAME))
+
+ def test_lazycache_check(self):
+ linecache.clearcache()
+ linecache.lazycache(NONEXISTENT_FILENAME, globals())
+ linecache.checkcache()
+
+ def test_lazycache_bad_filename(self):
+ linecache.clearcache()
+ self.assertEqual(False, linecache.lazycache('', globals()))
+ self.assertEqual(False, linecache.lazycache('<foo>', globals()))
+
+ def test_lazycache_already_cached(self):
+ linecache.clearcache()
+ lines = linecache.getlines(NONEXISTENT_FILENAME, globals())
+ self.assertEqual(
+ False,
+ linecache.lazycache(NONEXISTENT_FILENAME, globals()))
+ self.assertEqual(4, len(linecache.cache[NONEXISTENT_FILENAME]))
+
+
def test_main():
support.run_unittest(LineCacheTests)
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 6bd6fa6..9ff7548 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -1,6 +1,8 @@
"""Test cases for traceback module"""
+from collections import namedtuple
from io import StringIO
+import linecache
import sys
import unittest
import re
@@ -12,6 +14,11 @@ import textwrap
import traceback
+test_code = namedtuple('code', ['co_filename', 'co_name'])
+test_frame = namedtuple('frame', ['f_code', 'f_globals'])
+test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno', 'tb_next'])
+
+
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.
@@ -477,6 +484,195 @@ class MiscTracebackCases(unittest.TestCase):
self.assertEqual(len(inner_frame.f_locals), 0)
+class TestFrame(unittest.TestCase):
+
+ def test_basics(self):
+ linecache.clearcache()
+ linecache.lazycache("f", globals())
+ f = traceback.FrameSummary("f", 1, "dummy")
+ self.assertEqual(
+ ("f", 1, "dummy", '"""Test cases for traceback module"""'),
+ tuple(f))
+ self.assertEqual(None, f.locals)
+
+ def test_lazy_lines(self):
+ linecache.clearcache()
+ f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
+ self.assertEqual(None, f._line)
+ linecache.lazycache("f", globals())
+ self.assertEqual(
+ '"""Test cases for traceback module"""',
+ f.line)
+
+ def test_explicit_line(self):
+ f = traceback.FrameSummary("f", 1, "dummy", line="line")
+ self.assertEqual("line", f.line)
+
+
+class TestStack(unittest.TestCase):
+
+ def test_walk_stack(self):
+ s = list(traceback.walk_stack(None))
+ self.assertGreater(len(s), 10)
+
+ def test_walk_tb(self):
+ try:
+ 1/0
+ except Exception:
+ _, _, tb = sys.exc_info()
+ s = list(traceback.walk_tb(tb))
+ self.assertEqual(len(s), 1)
+
+ def test_extract_stack(self):
+ s = traceback.StackSummary.extract(traceback.walk_stack(None))
+ self.assertIsInstance(s, traceback.StackSummary)
+
+ def test_extract_stack_limit(self):
+ s = traceback.StackSummary.extract(traceback.walk_stack(None), limit=5)
+ self.assertEqual(len(s), 5)
+
+ def test_extract_stack_lookup_lines(self):
+ linecache.clearcache()
+ linecache.updatecache('/foo.py', globals())
+ c = test_code('/foo.py', 'method')
+ f = test_frame(c, None)
+ s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=True)
+ linecache.clearcache()
+ self.assertEqual(s[0].line, "import sys")
+
+ def test_extract_stackup_deferred_lookup_lines(self):
+ linecache.clearcache()
+ c = test_code('/foo.py', 'method')
+ f = test_frame(c, None)
+ s = traceback.StackSummary.extract(iter([(f, 6)]), lookup_lines=False)
+ self.assertEqual({}, linecache.cache)
+ linecache.updatecache('/foo.py', globals())
+ self.assertEqual(s[0].line, "import sys")
+
+ def test_from_list(self):
+ s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')])
+ self.assertEqual(
+ [' File "foo.py", line 1, in fred\n line\n'],
+ s.format())
+
+ def test_format_smoke(self):
+ # For detailed tests see the format_list tests, which consume the same
+ # code.
+ s = traceback.StackSummary([('foo.py', 1, 'fred', 'line')])
+ self.assertEqual(
+ [' File "foo.py", line 1, in fred\n line\n'],
+ s.format())
+
+
+
+class TestTracebackException(unittest.TestCase):
+
+ def test_smoke(self):
+ try:
+ 1/0
+ except Exception:
+ exc_info = sys.exc_info()
+ exc = traceback.TracebackException(*exc_info)
+ expected_stack = traceback.StackSummary.extract(
+ traceback.walk_tb(exc_info[2]))
+ self.assertEqual(None, exc.__cause__)
+ self.assertEqual(None, exc.__context__)
+ self.assertEqual(False, exc.__suppress_context__)
+ self.assertEqual(expected_stack, exc.stack)
+ self.assertEqual(exc_info[0], exc.exc_type)
+ self.assertEqual(str(exc_info[1]), str(exc))
+
+ def test_from_exception(self):
+ # Check all the parameters are accepted.
+ def foo():
+ 1/0
+ try:
+ foo()
+ except Exception as e:
+ exc_info = sys.exc_info()
+ self.expected_stack = traceback.StackSummary.extract(
+ traceback.walk_tb(exc_info[2]), limit=1, lookup_lines=False,
+ capture_locals=True)
+ self.exc = traceback.TracebackException.from_exception(
+ e, limit=1, lookup_lines=False, capture_locals=True)
+ expected_stack = self.expected_stack
+ exc = self.exc
+ self.assertEqual(None, exc.__cause__)
+ self.assertEqual(None, exc.__context__)
+ self.assertEqual(False, exc.__suppress_context__)
+ self.assertEqual(expected_stack, exc.stack)
+ self.assertEqual(exc_info[0], exc.exc_type)
+ self.assertEqual(str(exc_info[1]), str(exc))
+
+ def test_cause(self):
+ try:
+ try:
+ 1/0
+ finally:
+ exc_info_context = sys.exc_info()
+ exc_context = traceback.TracebackException(*exc_info_context)
+ cause = Exception("cause")
+ raise Exception("uh oh") from cause
+ except Exception:
+ exc_info = sys.exc_info()
+ exc = traceback.TracebackException(*exc_info)
+ expected_stack = traceback.StackSummary.extract(
+ traceback.walk_tb(exc_info[2]))
+ exc_cause = traceback.TracebackException(Exception, cause, None)
+ self.assertEqual(exc_cause, exc.__cause__)
+ self.assertEqual(exc_context, exc.__context__)
+ self.assertEqual(True, exc.__suppress_context__)
+ self.assertEqual(expected_stack, exc.stack)
+ self.assertEqual(exc_info[0], exc.exc_type)
+ self.assertEqual(str(exc_info[1]), str(exc))
+
+ def test_context(self):
+ try:
+ try:
+ 1/0
+ finally:
+ exc_info_context = sys.exc_info()
+ exc_context = traceback.TracebackException(*exc_info_context)
+ raise Exception("uh oh")
+ except Exception:
+ exc_info = sys.exc_info()
+ exc = traceback.TracebackException(*exc_info)
+ expected_stack = traceback.StackSummary.extract(
+ traceback.walk_tb(exc_info[2]))
+ self.assertEqual(None, exc.__cause__)
+ self.assertEqual(exc_context, exc.__context__)
+ self.assertEqual(False, exc.__suppress_context__)
+ self.assertEqual(expected_stack, exc.stack)
+ self.assertEqual(exc_info[0], exc.exc_type)
+ self.assertEqual(str(exc_info[1]), str(exc))
+
+ def test_limit(self):
+ def recurse(n):
+ if n:
+ recurse(n-1)
+ else:
+ 1/0
+ try:
+ recurse(10)
+ except Exception:
+ exc_info = sys.exc_info()
+ exc = traceback.TracebackException(*exc_info, limit=5)
+ expected_stack = traceback.StackSummary.extract(
+ traceback.walk_tb(exc_info[2]), limit=5)
+ self.assertEqual(expected_stack, exc.stack)
+
+ def test_lookup_lines(self):
+ linecache.clearcache()
+ e = Exception("uh oh")
+ c = test_code('/foo.py', 'method')
+ f = test_frame(c, None)
+ tb = test_tb(f, 6, None)
+ exc = traceback.TracebackException(Exception, e, tb, lookup_lines=False)
+ self.assertEqual({}, linecache.cache)
+ linecache.updatecache('/foo.py', globals())
+ self.assertEqual(exc.stack[0].line, "import sys")
+
+
def test_main():
run_unittest(__name__)