-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
bpo-45292: [PEP 654] Update traceback display code to work with exception groups #29207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
42bed01
a2daa23
261917a
7613b43
d98a72b
d69916e
f5cab69
5170f00
2052c77
5097300
dc21cf8
d4007b7
169934e
aa4da45
6ee84f7
ac7f34c
d0d4961
5c1015d
83abebd
16d077d
64fb164
88019f5
e963835
e85510a
c15a7bd
d8cc6e8
61fab3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -396,20 +396,21 @@ _Py_WriteIndent(int indent, PyObject *f) { | |||||||
| return 0; | ||||||||
| } | ||||||||
|
|
||||||||
| /* Writes indent spaces, followed by the margin if margin_char is not `\0`. | ||||||||
| /* Writes indent spaces, followed by the margin if it is not `\0`. | ||||||||
| */ | ||||||||
| int | ||||||||
| _Py_WriteIndentedMargin(int indent, char margin_char, PyObject *f) { | ||||||||
| _Py_WriteIndentedMargin(int indent, const char *margin, PyObject *f) { | ||||||||
|
iritkatriel marked this conversation as resolved.
Outdated
|
||||||||
| int err = 0; | ||||||||
| char margin[] = {margin_char, ' ', '\0' }; | ||||||||
| err |= _Py_WriteIndent(indent, f); | ||||||||
| err |= PyFile_WriteString(margin, f); | ||||||||
| if (margin) { | ||||||||
| err |= PyFile_WriteString(margin, f); | ||||||||
| } | ||||||||
| return err; | ||||||||
| } | ||||||||
|
|
||||||||
| int | ||||||||
| _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, | ||||||||
| int margin_indent, char margin_char, int *truncation, PyObject **line) | ||||||||
| static int | ||||||||
| display_source_line_with_margin(PyObject *f, PyObject *filename, int lineno, int indent, | ||||||||
| int margin_indent, const char *margin, int *truncation, PyObject **line) | ||||||||
| { | ||||||||
| int err = 0; | ||||||||
| int fd; | ||||||||
|
|
@@ -537,7 +538,7 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, | |||||||
| *truncation = i - indent; | ||||||||
| } | ||||||||
|
|
||||||||
| err |= _Py_WriteIndentedMargin(margin_indent, margin_char, f); | ||||||||
| err |= _Py_WriteIndentedMargin(margin_indent, margin, f); | ||||||||
| /* Write some spaces before the line */ | ||||||||
| err |= _Py_WriteIndent(indent, f); | ||||||||
|
|
||||||||
|
|
@@ -550,6 +551,16 @@ _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, | |||||||
| return err; | ||||||||
| } | ||||||||
|
|
||||||||
| int | ||||||||
| _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, | ||||||||
| int *truncation, PyObject **line) | ||||||||
| { | ||||||||
| return display_source_line_with_margin( | ||||||||
| f, filename, lineno, indent, | ||||||||
| 0, NULL, /* no margin */ | ||||||||
| truncation, line); | ||||||||
|
iritkatriel marked this conversation as resolved.
Outdated
|
||||||||
| } | ||||||||
|
|
||||||||
| /* AST based Traceback Specialization | ||||||||
| * | ||||||||
| * When displaying a new traceback line, for certain syntactical constructs | ||||||||
|
|
@@ -718,7 +729,7 @@ print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py | |||||||
|
|
||||||||
| static int | ||||||||
| tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int lineno, | ||||||||
| PyFrameObject *frame, PyObject *name, int margin_indent, char margin_char) | ||||||||
| PyFrameObject *frame, PyObject *name, int margin_indent, const char *margin) | ||||||||
| { | ||||||||
| int err; | ||||||||
| PyObject *line; | ||||||||
|
|
@@ -729,17 +740,17 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen | |||||||
| filename, lineno, name); | ||||||||
| if (line == NULL) | ||||||||
| return -1; | ||||||||
| err = _Py_WriteIndentedMargin(margin_indent, margin_char, f); | ||||||||
| err = _Py_WriteIndentedMargin(margin_indent, margin, f); | ||||||||
| err |= PyFile_WriteObject(line, f, Py_PRINT_RAW); | ||||||||
| Py_DECREF(line); | ||||||||
| if (err != 0) | ||||||||
| return err; | ||||||||
|
Comment on lines
+747
to
760
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't someone advise last time not to combine error checks like this?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, about PyList_Append calls which can crash. Here and in pythonrun.c (in main) the code seems to assume that PyFile_WriteXX calls can be called any number of times after failure (is this true?). There is a PyErr_Clear() call before each PyFile_WriteObject because that asserts that there is no error set, but the call is not avoided. There is another PyErr_Clear at the end (these function don't have a return value and don't raise exceptions). See this comment Line 955 in 19a6c41
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More specifically, there are calls to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a mess, so how about I make a PR to clean up the current code's error checking, we merge that, then I recreate this one. In the meantime we can sort out the last decisions: (1) Do we want the "full path" labels? (I think they don't add that much on top of index + indent, and they complicate the code. Let me know if you disagree.) (2) The box around the topmost EG's traceback? (3) We definitely need to limit the nested depth we display (to something like 10 or 15). Also the number of exceptions per EG, so we don't spam?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my 2c:
I think it's a great idea. Perhaps we should have an env var to set the limits.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why not keep this PR but merge from main once the cleanup PR has landed?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is only a limit on the number of traceback frames, which applies to chained exceptions too. Line 881 in d02ffd1
and 'unlimited' in python: Line 377 in 10bbd41
There is no limit on the number of chained exceptions being printed (which is why we needed the loop to avoid recursion in traceback.py).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That sounds like a design bug. That limit has been around since long before we had chaining; presumably nobody thought of adding a limit. Unlimited output seems unhelpful. A parameter that limits things seems useful though.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Change of plans - I found that its more delicate than I originally thought (see bpo-45635). I don't want to hold up this PR until that's sorted, so I'll make sure I'm not adding more error checking issues in the code I'm adding here, but leave the rest for later.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added two limits max_group_depth and max_group_width. If we roll everything into one limit I think we will get a constant stream of bug reports about edge cases where you got the wrong part of the output. Though it would probably be rare that it makes a difference in practice. I didn't add a limit on the length of context/cause chain, because that's outside the scope of this PR. I created bpo-45694 for that. |
||||||||
|
|
||||||||
| int truncation = _TRACEBACK_SOURCE_LINE_INDENT; | ||||||||
| PyObject* source_line = NULL; | ||||||||
| if (_Py_DisplaySourceLine(f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT, | ||||||||
| margin_indent, margin_char, | ||||||||
| &truncation, &source_line) != 0 || !source_line) { | ||||||||
| if (display_source_line_with_margin( | ||||||||
| f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT, | ||||||||
| margin_indent, margin, &truncation, &source_line) != 0 || !source_line) { | ||||||||
|
iritkatriel marked this conversation as resolved.
Outdated
|
||||||||
| /* ignore errors since we can't report them, can we? */ | ||||||||
| err = ignore_source_errors(); | ||||||||
| goto done; | ||||||||
|
|
@@ -824,7 +835,7 @@ tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int linen | |||||||
| end_offset = i + 1; | ||||||||
| } | ||||||||
|
|
||||||||
| err = _Py_WriteIndentedMargin(margin_indent, margin_char, f); | ||||||||
| err = _Py_WriteIndentedMargin(margin_indent, margin, f); | ||||||||
| err |= print_error_location_carets(f, truncation, start_offset, end_offset, | ||||||||
|
iritkatriel marked this conversation as resolved.
Outdated
|
||||||||
| right_start_offset, left_end_offset, | ||||||||
| primary_error_char, secondary_error_char); | ||||||||
|
|
@@ -855,7 +866,7 @@ tb_print_line_repeated(PyObject *f, long cnt) | |||||||
|
|
||||||||
| static int | ||||||||
| tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, | ||||||||
| int indent, char margin_char) | ||||||||
| int indent, const char *margin) | ||||||||
| { | ||||||||
| int err = 0; | ||||||||
| Py_ssize_t depth = 0; | ||||||||
|
|
@@ -889,7 +900,7 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, | |||||||
| cnt++; | ||||||||
| if (err == 0 && cnt <= TB_RECURSIVE_CUTOFF) { | ||||||||
| err = tb_displayline(tb, f, code->co_filename, tb->tb_lineno, | ||||||||
| tb->tb_frame, code->co_name, indent, margin_char); | ||||||||
| tb->tb_frame, code->co_name, indent, margin); | ||||||||
| if (err == 0) { | ||||||||
| err = PyErr_CheckSignals(); | ||||||||
| } | ||||||||
|
|
@@ -906,7 +917,7 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, | |||||||
| #define PyTraceBack_LIMIT 1000 | ||||||||
|
|
||||||||
| int | ||||||||
| PyTraceBack_Print_Indented(PyObject *v, int indent, char margin_char, PyObject *f) | ||||||||
| PyTraceBack_Print_Indented(PyObject *v, int indent, const char *margin, PyObject *f) | ||||||||
| { | ||||||||
| int err; | ||||||||
| PyObject *limitv; | ||||||||
|
|
@@ -929,17 +940,17 @@ PyTraceBack_Print_Indented(PyObject *v, int indent, char margin_char, PyObject * | |||||||
| return 0; | ||||||||
| } | ||||||||
| } | ||||||||
| err = _Py_WriteIndentedMargin(indent, margin_char, f); | ||||||||
| err = _Py_WriteIndentedMargin(indent, margin, f); | ||||||||
| err |= PyFile_WriteString("Traceback (most recent call last):\n", f); | ||||||||
| if (!err) | ||||||||
| err = tb_printinternal((PyTracebackObject *)v, f, limit, indent, margin_char); | ||||||||
| err = tb_printinternal((PyTracebackObject *)v, f, limit, indent, margin); | ||||||||
|
iritkatriel marked this conversation as resolved.
|
||||||||
| return err; | ||||||||
| } | ||||||||
|
|
||||||||
| int | ||||||||
| PyTraceBack_Print(PyObject *v, PyObject *f) | ||||||||
| { | ||||||||
| return PyTraceBack_Print_Indented(v, 0, '\0', f); | ||||||||
| return PyTraceBack_Print_Indented(v, 0, NULL, f); | ||||||||
| } | ||||||||
|
|
||||||||
| /* Format an integer in range [0; 0xffffffff] to decimal and write it | ||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.