https://github.com/python/cpython/commit/0a02026a084213cbd09c66534c55104ab460c686
commit: 0a02026a084213cbd09c66534c55104ab460c686
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: pablogsal <[email protected]>
date: 2024-08-19T15:35:47Z
summary:

[3.13] gh-123123: Fix display of syntax errors covering multiple lines 
(GH-123131) (#123147)

gh-123123: Fix display of syntax errors covering multiple lines (GH-123131)
(cherry picked from commit 48856ead6ae023b2819ee63cb6ff97a0976a2cc3)

Signed-off-by: Pablo Galindo <[email protected]>
Co-authored-by: Pablo Galindo Salgado <[email protected]>

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-08-18-18-25-54.gh-issue-123123.0ZcaEB.rst
M Lib/test/test_traceback.py
M Lib/traceback.py

diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 5035de114b5e9d..574d5dab97b568 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -696,6 +696,35 @@ def f_with_multiline():
         result_lines = self.get_exception(f_with_multiline)
         self.assertEqual(result_lines, expected_f.splitlines())
 
+        # Check custom error messages covering multiple lines
+        code = textwrap.dedent("""
+        dummy_call(
+            "dummy value"
+            foo="bar",
+        )
+        """)
+
+        def f_with_multiline():
+            # Need to defer the compilation until in self.get_exception(..)
+            return compile(code, "?", "exec")
+
+        lineno_f = f_with_multiline.__code__.co_firstlineno
+
+        expected_f = (
+            'Traceback (most recent call last):\n'
+            f'  File "{__file__}", line {self.callable_line}, in 
get_exception\n'
+            '    callable()\n'
+            '    ~~~~~~~~^^\n'
+            f'  File "{__file__}", line {lineno_f+2}, in f_with_multiline\n'
+            '    return compile(code, "?", "exec")\n'
+            '  File "?", line 3\n'
+            '    "dummy value"\n'
+            '    ^^^^^^^^^^^^^'
+            )
+
+        result_lines = self.get_exception(f_with_multiline)
+        self.assertEqual(result_lines, expected_f.splitlines())
+
     def test_caret_multiline_expression_bin_op(self):
         # Make sure no carets are printed for expressions spanning multiple
         # lines.
@@ -2309,19 +2338,22 @@ def test_message_none(self):
     def test_syntax_error_various_offsets(self):
         for offset in range(-5, 10):
             for add in [0, 2]:
-                text = " "*add + "text%d" % offset
+                text = " " * add + "text%d" % offset
                 expected = ['  File "file.py", line 1']
                 if offset < 1:
                     expected.append("    %s" % text.lstrip())
                 elif offset <= 6:
                     expected.append("    %s" % text.lstrip())
-                    expected.append("    %s^" % (" "*(offset-1)))
+                    # Set the caret length to match the length of the text 
minus the offset.
+                    caret_length = max(1, len(text.lstrip()) - offset + 1)
+                    expected.append("    %s%s" % (" " * (offset - 1), "^" * 
caret_length))
                 else:
+                    caret_length = max(1, len(text.lstrip()) - 4)
                     expected.append("    %s" % text.lstrip())
-                    expected.append("    %s^" % (" "*5))
+                    expected.append("    %s%s" % (" " * 5, "^" * caret_length))
                 expected.append("SyntaxError: msg")
                 expected.append("")
-                err = self.get_report(SyntaxError("msg", ("file.py", 1, 
offset+add, text)))
+                err = self.get_report(SyntaxError("msg", ("file.py", 1, offset 
+ add, text)))
                 exp = "\n".join(expected)
                 self.assertEqual(exp, err)
 
diff --git a/Lib/traceback.py b/Lib/traceback.py
index 6ee1a50ca6804a..3e708c6f86a4c5 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -1292,11 +1292,15 @@ def _format_syntax_error(self, stype, **kwargs):
                 yield '    {}\n'.format(ltext)
             else:
                 offset = self.offset
-                end_offset = self.end_offset if self.end_offset not in {None, 
0} else offset
+                if self.lineno == self.end_lineno:
+                    end_offset = self.end_offset if self.end_offset not in 
{None, 0} else offset
+                else:
+                    end_offset = len(rtext) + 1
+
                 if self.text and offset > len(self.text):
-                    offset = len(self.text) + 1
+                    offset = len(rtext) + 1
                 if self.text and end_offset > len(self.text):
-                    end_offset = len(self.text) + 1
+                    end_offset = len(rtext) + 1
                 if offset >= end_offset or end_offset < 0:
                     end_offset = offset + 1
 
diff --git a/Misc/NEWS.d/next/Core and 
Builtins/2024-08-18-18-25-54.gh-issue-123123.0ZcaEB.rst b/Misc/NEWS.d/next/Core 
and Builtins/2024-08-18-18-25-54.gh-issue-123123.0ZcaEB.rst
new file mode 100644
index 00000000000000..824d307bb270a2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and 
Builtins/2024-08-18-18-25-54.gh-issue-123123.0ZcaEB.rst 
@@ -0,0 +1,2 @@
+Fix displaying :exc:`SyntaxError` exceptions covering multiple lines. Patch
+by Pablo Galindo

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to