diff -r 46e329ef13ae Lib/test/test_generators.py --- a/Lib/test/test_generators.py Tue Dec 20 16:07:18 2016 +0900 +++ b/Lib/test/test_generators.py Fri Dec 30 15:29:21 2016 -0800 @@ -734,14 +734,16 @@ ... yield 1 Traceback (most recent call last): .. -SyntaxError: 'return' with argument inside generator (, line 3) + File "" line 3 +SyntaxError: 'return' with argument inside generator >>> def f(): ... yield 1 ... return 22 Traceback (most recent call last): .. -SyntaxError: 'return' with argument inside generator (, line 3) + File "", line 3 +SyntaxError: 'return' with argument inside generator "return None" is not the same as "return" in a generator: @@ -750,7 +752,8 @@ ... return None Traceback (most recent call last): .. -SyntaxError: 'return' with argument inside generator (, line 3) + File "", line 3 +SyntaxError: 'return' with argument inside generator These are fine: @@ -879,7 +882,9 @@ ... if 0: ... yield 2 # because it's a generator (line 10) Traceback (most recent call last): -SyntaxError: 'return' with argument inside generator (, line 10) + .. + File "", line 10 +SyntaxError: 'return' with argument inside generator This one caused a crash (see SF bug 567538): @@ -1571,7 +1576,8 @@ >>> def f(): return lambda x=(yield): 1 Traceback (most recent call last): ... -SyntaxError: 'return' with argument inside generator (, line 1) + File "", line 1 +SyntaxError: 'return' with argument inside generator >>> def f(): x = yield = y Traceback (most recent call last): diff -r 46e329ef13ae Lib/test/test_syntax.py --- a/Lib/test/test_syntax.py Tue Dec 20 16:07:18 2016 +0900 +++ b/Lib/test/test_syntax.py Fri Dec 30 15:29:21 2016 -0800 @@ -5,7 +5,9 @@ >>> def f(x): ... global x Traceback (most recent call last): -SyntaxError: name 'x' is local and global (, line 1) + ... + File "", line 1 +SyntaxError: name 'x' is local and global The tests are all raise SyntaxErrors. They were created by checking each C call that raises SyntaxError. There are several modules that diff -r 46e329ef13ae Lib/test/test_traceback.py --- a/Lib/test/test_traceback.py Tue Dec 20 16:07:18 2016 +0900 +++ b/Lib/test/test_traceback.py Fri Dec 30 15:29:21 2016 -0800 @@ -38,6 +38,11 @@ def syntax_error_bad_indentation2(self): compile(" print(2)", "?", "exec") + def make_raiser(self, error): + def raiser(): + raise error + return raiser + def test_caret(self): err = self.get_exception_format(self.syntax_error_with_caret, SyntaxError) @@ -69,6 +74,20 @@ self.assertIn("^", err[2]) self.assertTrue(err[1].find("2") == err[2].find("^")) + def test_no_lineno(self): + e = SyntaxError("message", ("myfile.py", None, None, None)) + err = self.get_exception_format(self.make_raiser(e), SyntaxError) + self.assertEqual("".join(err), "SyntaxError: message (myfile.py)\n") + + def test_long_indentation(self): + e = SyntaxError("message", ("myfile.py", 3, 10, "hello")) + err = self.get_exception_format(self.make_raiser(e), SyntaxError) + self.assertEqual("".join(err), """\ + File "myfile.py", line 3 + hello + ^ +SyntaxError: message\n""") + def test_bug737473(self): import os, tempfile, time diff -r 46e329ef13ae Lib/traceback.py --- a/Lib/traceback.py Tue Dec 20 16:07:18 2016 +0900 +++ b/Lib/traceback.py Fri Dec 30 15:29:21 2016 -0800 @@ -178,26 +178,40 @@ return [_format_final_exc_line(stype, value)] # It was a syntax error; show exactly where the problem was found. + # This code roughly follows PyErr_Display lines = [] try: - msg, (filename, lineno, offset, badline) = value.args - except Exception: - pass + # Mimic parse_syntax_error + msg = value.msg + filename = str(value.filename) if value.filename is not None else None + lineno = int(value.lineno) + offset = int(value.offset) if value.offset is not None else -1 + text = str(value.text) if value.text is not None else None + except: + msg = value else: - filename = filename or "" + filename = "" if filename is None else filename lines.append(' File "%s", line %d\n' % (filename, lineno)) - if badline is not None: - lines.append(' %s\n' % badline.strip()) - if offset is not None: - caretspace = badline.rstrip('\n') - offset = min(len(caretspace), offset) - 1 - caretspace = caretspace[:offset].lstrip() - # non-space whitespace (likes tabs) must be kept for alignment - caretspace = ((c.isspace() and c or ' ') for c in caretspace) - lines.append(' %s^\n' % ''.join(caretspace)) - value = msg + if text is not None: + # Mimic print_error_text + if offset >= 0: + if offset > 0 and offset == len(text) and text[-1] == '\n': + offset -= 1 + nl = text.rfind('\n', 0, offset) + if nl >= 0: + text = text[(nl+1):] + offset -= (nl+1) + for i, c in enumerate(text): + if c not in (' ', '\t'): + break + text = text[i:] + offset -= i + added_newline = '\n' if text == "" or text[-1] != '\n' else '' + lines.append(' %s%s' % (text, added_newline)) + if offset != -1: + lines.append(" " + " " * (offset - 1) + "^\n") - lines.append(_format_final_exc_line(stype, value)) + lines.append("%s: %s\n" % (stype, msg)) return lines def _format_final_exc_line(etype, value):