summaryrefslogtreecommitdiff
path: root/Python
diff options
context:
space:
mode:
authorJohn Belmonte <john@neggie.net>2022-07-11 15:40:53 +0900
committerGitHub <noreply@github.com>2022-07-11 07:40:53 +0100
commitda717519ecd17bf6c7ed334c12ff861f63b0f14f (patch)
tree582df2633e6f0e769a797bb496aea0944c58a111 /Python
parentc9118afd045a64ca22d4a8cc5d43532607083b2d (diff)
downloadcpython-git-da717519ecd17bf6c7ed334c12ff861f63b0f14f.tar.gz
gh-93883: elide traceback indicators when possible (#93994)
* gh-93883: elide traceback indicators when possible Elide traceback column indicators when the entire line of the frame is implicated. This reduces traceback length and draws even more attention to the remaining (very relevant) indicators. Example: ``` Traceback (most recent call last): File "query.py", line 99, in <module> bar() File "query.py", line 66, in bar foo() File "query.py", line 37, in foo magic_arithmetic('foo') File "query.py", line 18, in magic_arithmetic return add_counts(x) / 25 ^^^^^^^^^^^^^ File "query.py", line 24, in add_counts return 25 + query_user(user1) + query_user(user2) ^^^^^^^^^^^^^^^^^ File "query.py", line 32, in query_user return 1 + query_count(db, response['a']['b']['c']['user'], retry=True) ~~~~~~~~~~~~~~~~~~^^^^^ TypeError: 'NoneType' object is not subscriptable ``` Rather than going out of our way to provide indicator coverage in every traceback test suite, the indicator test suite should be responible for sufficient coverage (e.g. by adding a basic exception group test to ensure that margin strings are covered).
Diffstat (limited to 'Python')
-rw-r--r--Python/traceback.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/Python/traceback.c b/Python/traceback.c
index 439689b32a..de658b9103 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -592,7 +592,6 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent,
* Traceback (most recent call last):
* File "/home/isidentical/cpython/cpython/t.py", line 10, in <module>
* add_values(1, 2, 'x', 3, 4)
- * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values
* return a + b + c + d + e
* ~~~~~~^~~
@@ -736,7 +735,7 @@ print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py
int special_chars = (left_end_offset != -1 || right_start_offset != -1);
const char *str;
while (++offset <= end_offset) {
- if (offset <= start_offset || offset > end_offset) {
+ if (offset <= start_offset) {
str = " ";
} else if (special_chars && left_end_offset < offset && offset <= right_start_offset) {
str = secondary;
@@ -792,6 +791,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
int code_offset = tb->tb_lasti;
PyCodeObject* code = frame->f_frame->f_code;
+ const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line);
int start_line;
int end_line;
@@ -813,7 +813,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
//
// ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE
// ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~
- // | |-> left_end_offset | |-> left_offset
+ // | |-> left_end_offset | |-> end_offset
// |-> start_offset |-> right_start_offset
//
// In general we will only have (start_offset, end_offset) but we can gather more information
@@ -822,6 +822,9 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
// the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the
// AST information or we cannot identify special ranges within it, then left_end_offset and
// right_end_offset will be set to -1.
+ //
+ // To keep the column indicators pertinent, they are not shown when the primary character
+ // spans the whole line.
// Convert the utf-8 byte offset to the actual character offset so we print the right number of carets.
assert(source_line);
@@ -859,7 +862,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
goto done;
}
- Py_ssize_t i = PyUnicode_GET_LENGTH(source_line);
+ Py_ssize_t i = source_line_len;
while (--i >= 0) {
if (!IS_WHITESPACE(source_line_str[i])) {
break;
@@ -869,6 +872,13 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen
end_offset = i + 1;
}
+ // Elide indicators if primary char spans the frame line
+ Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT;
+ bool has_secondary_ranges = (left_end_offset != -1 || right_start_offset != -1);
+ if (end_offset - start_offset == stripped_line_len && !has_secondary_ranges) {
+ goto done;
+ }
+
if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) {
err = -1;
goto done;