summaryrefslogtreecommitdiffstats
path: root/Python/pythonrun.c
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2020-05-15 02:22:48 (GMT)
committerGitHub <noreply@github.com>2020-05-15 02:22:48 (GMT)
commit15bc9ab301d73f20bff47a12ef05326feb40f797 (patch)
treee29afe19a6ce7aaa5b7f1ea3639810080ea6d2d5 /Python/pythonrun.c
parent1aa8767baf498a920f0461d1088772a12dcb4d20 (diff)
downloadcpython-15bc9ab301d73f20bff47a12ef05326feb40f797.zip
cpython-15bc9ab301d73f20bff47a12ef05326feb40f797.tar.gz
cpython-15bc9ab301d73f20bff47a12ef05326feb40f797.tar.bz2
bpo-40612: Fix SyntaxError edge cases in traceback formatting (GH-20072)
This fixes both the traceback.py module and the C code for formatting syntax errors (in Python/pythonrun.c). They now both consistently do the following: - Suppress caret if it points left of text - Allow caret pointing just past end of line - If caret points past end of line, clip to *just* past end of line The syntax error formatting code in traceback.py was mostly rewritten; small, subtle changes were applied to the C code in pythonrun.c. There's still a difference when the text contains embedded newlines. Neither handles these very well, and I don't think the case occurs in practice. Automerge-Triggered-By: @gvanrossum
Diffstat (limited to 'Python/pythonrun.c')
-rw-r--r--Python/pythonrun.c66
1 files changed, 47 insertions, 19 deletions
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 45f08b7..160f44d 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -554,37 +554,65 @@ finally:
static void
print_error_text(PyObject *f, int offset, PyObject *text_obj)
{
- const char *text;
- const char *nl;
-
- text = PyUnicode_AsUTF8(text_obj);
+ /* Convert text to a char pointer; return if error */
+ const char *text = PyUnicode_AsUTF8(text_obj);
if (text == NULL)
return;
- if (offset >= 0) {
- if (offset > 0 && (size_t)offset == strlen(text) && text[offset - 1] == '\n')
- offset--;
- for (;;) {
- nl = strchr(text, '\n');
- if (nl == NULL || nl-text >= offset)
- break;
- offset -= (int)(nl+1-text);
- text = nl+1;
+ /* Convert offset from 1-based to 0-based */
+ offset--;
+
+ /* Strip leading whitespace from text, adjusting offset as we go */
+ while (*text == ' ' || *text == '\t' || *text == '\f') {
+ text++;
+ offset--;
+ }
+
+ /* Calculate text length excluding trailing newline */
+ Py_ssize_t len = strlen(text);
+ if (len > 0 && text[len-1] == '\n') {
+ len--;
+ }
+
+ /* Clip offset to at most len */
+ if (offset > len) {
+ offset = len;
+ }
+
+ /* Skip past newlines embedded in text */
+ for (;;) {
+ const char *nl = strchr(text, '\n');
+ if (nl == NULL) {
+ break;
}
- while (*text == ' ' || *text == '\t' || *text == '\f') {
- text++;
- offset--;
+ Py_ssize_t inl = nl - text;
+ if (inl >= (Py_ssize_t)offset) {
+ break;
}
+ inl += 1;
+ text += inl;
+ len -= inl;
+ offset -= (int)inl;
}
+
+ /* Print text */
PyFile_WriteString(" ", f);
PyFile_WriteString(text, f);
- if (*text == '\0' || text[strlen(text)-1] != '\n')
+
+ /* Make sure there's a newline at the end */
+ if (text[len] != '\n') {
PyFile_WriteString("\n", f);
- if (offset == -1)
+ }
+
+ /* Don't print caret if it points to the left of the text */
+ if (offset < 0)
return;
+
+ /* Write caret line */
PyFile_WriteString(" ", f);
- while (--offset > 0)
+ while (--offset >= 0) {
PyFile_WriteString(" ", f);
+ }
PyFile_WriteString("^\n", f);
}