diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -204,16 +204,23 @@ class SyntaxTracebackCases(unittest.Test obj = PrintExceptionAtExit() """) rc, stdout, stderr = assert_python_ok('-c', code) expected = [b'Traceback (most recent call last):', b' File "", line 8, in __init__', b'ZeroDivisionError: division by zero'] self.assertEqual(stderr.splitlines(), expected) + def test_print_exception(self): + output = StringIO() + traceback.print_exception( + Exception, Exception("projector"), None, file=output + ) + self.assertEqual(output.getvalue(), "Exception: projector\n") + class TracebackFormatTests(unittest.TestCase): def some_exception(self): raise KeyError('blah') @cpython_only def check_traceback_format(self, cleanup_func=None): @@ -843,16 +850,21 @@ class TestTracebackException(unittest.Te linecache.updatecache('/foo.py', globals()) e = Exception("uh oh") c = test_code('/foo.py', 'method') f = test_frame(c, globals(), {'something': 1}) tb = test_tb(f, 6, None) exc = traceback.TracebackException(Exception, e, tb) self.assertEqual(exc.stack[0].locals, None) + def test_traceback_header(self): + # do not print traceback header if exc_traceback is None + # see issue #24695 + exc = traceback.TracebackException(Exception, Exception("haven"), None) + self.assertEqual(list(exc.format()), ["Exception: haven\n"]) class MiscTest(unittest.TestCase): def test_all(self): expected = set() blacklist = {'print_list'} for name in dir(traceback): if name.startswith('_') or name in blacklist: diff --git a/Lib/traceback.py b/Lib/traceback.py --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -448,16 +448,17 @@ class TracebackException: exc_value.__context__, exc_value.__context__.__traceback__, limit=limit, lookup_lines=False, capture_locals=capture_locals, _seen=_seen) else: context = None + self.exc_traceback = exc_traceback self.__cause__ = cause self.__context__ = context self.__suppress_context__ = \ exc_value.__suppress_context__ if exc_value else False # TODO: locals. self.stack = StackSummary.extract( walk_tb(exc_traceback), limit=limit, lookup_lines=lookup_lines, capture_locals=capture_locals) @@ -556,11 +557,12 @@ class TracebackException: if chain: if self.__cause__ is not None: yield from self.__cause__.format(chain=chain) yield _cause_message elif (self.__context__ is not None and not self.__suppress_context__): yield from self.__context__.format(chain=chain) yield _context_message - yield 'Traceback (most recent call last):\n' + if self.exc_traceback is not None: + yield 'Traceback (most recent call last):\n' yield from self.stack.format() yield from self.format_exception_only()