-
-
Notifications
You must be signed in to change notification settings - Fork 35k
bpo-37050: Remove expr_text from FormattedValue ast node, use Constant node instead #13597
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 all commits
0b36271
7c56446
3148fcf
f33e1d0
396e6ed
dea5632
b03f94f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Improve the AST for "debug" f-strings, which use '=' to print out the source | ||
| of the expression being evaluated. Delete expr_text from the FormattedValue | ||
| node, and instead use a Constant string node (possibly merged with adjacent | ||
| constant expressions inside the f-string). |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -5006,10 +5006,16 @@ fstring_parse(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| closing brace doesn't match an opening paren, for example. It | ||||||
| doesn't need to error on all invalid expressions, just correctly | ||||||
| find the end of all valid ones. Any errors inside the expression | ||||||
| will be caught when we parse it later. */ | ||||||
| will be caught when we parse it later. | ||||||
|
|
||||||
| *expression is set to the expression. For an '=' "debug" expression, | ||||||
| *expr_text is set to the debug text (the original text of the expression, | ||||||
| *including the '=' and any whitespace around it, as a string object). If | ||||||
|
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.
Suggested change
|
||||||
| *not a debug expression, *expr_text set to NULL. */ | ||||||
|
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.
Suggested change
|
||||||
| static int | ||||||
| fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | ||||||
| expr_ty *expression, struct compiling *c, const node *n) | ||||||
| PyObject **expr_text, expr_ty *expression, | ||||||
| struct compiling *c, const node *n) | ||||||
| { | ||||||
| /* Return -1 on error, else 0. */ | ||||||
|
|
||||||
|
|
@@ -5020,9 +5026,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| int conversion = -1; /* The conversion char. Use default if not | ||||||
| specified, or !r if using = and no format | ||||||
| spec. */ | ||||||
| int equal_flag = 0; /* Are we using the = feature? */ | ||||||
| PyObject *expr_text = NULL; /* The text of the expression, used for =. */ | ||||||
| const char *expr_text_end; | ||||||
|
|
||||||
| /* 0 if we're not in a string, else the quote char we're trying to | ||||||
| match (single or double quote). */ | ||||||
|
|
@@ -5198,15 +5201,21 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| expr_text. */ | ||||||
| if (**str == '=') { | ||||||
| *str += 1; | ||||||
| equal_flag = 1; | ||||||
|
|
||||||
| /* Skip over ASCII whitespace. No need to test for end of string | ||||||
| here, since we know there's at least a trailing quote somewhere | ||||||
| ahead. */ | ||||||
| while (Py_ISSPACE(**str)) { | ||||||
| *str += 1; | ||||||
| } | ||||||
| expr_text_end = *str; | ||||||
|
|
||||||
| /* Set *expr_text to the text of the expression. */ | ||||||
| *expr_text = PyUnicode_FromStringAndSize(expr_start, *str-expr_start); | ||||||
|
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. Note that |
||||||
| if (!*expr_text) { | ||||||
| goto error; | ||||||
| } | ||||||
| } else { | ||||||
| *expr_text = NULL; | ||||||
| } | ||||||
|
|
||||||
| /* Check for a conversion char, if present. */ | ||||||
|
|
@@ -5227,17 +5236,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| } | ||||||
|
|
||||||
| } | ||||||
| if (equal_flag) { | ||||||
| Py_ssize_t len = expr_text_end - expr_start; | ||||||
| expr_text = PyUnicode_FromStringAndSize(expr_start, len); | ||||||
| if (!expr_text) { | ||||||
| goto error; | ||||||
| } | ||||||
| if (PyArena_AddPyObject(c->c_arena, expr_text) < 0) { | ||||||
| Py_DECREF(expr_text); | ||||||
| goto error; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /* Check for the format spec, if present. */ | ||||||
| if (*str >= end) | ||||||
|
|
@@ -5261,16 +5259,16 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| assert(**str == '}'); | ||||||
| *str += 1; | ||||||
|
|
||||||
| /* If we're in = mode, and have no format spec and no explict conversion, | ||||||
| set the conversion to 'r'. */ | ||||||
| if (equal_flag && format_spec == NULL && conversion == -1) { | ||||||
| /* If we're in = mode (detected by non-NULL expr_text), and have no format | ||||||
| spec and no explict conversion, set the conversion to 'r'. */ | ||||||
| if (*expr_text && format_spec == NULL && conversion == -1) { | ||||||
| conversion = 'r'; | ||||||
| } | ||||||
|
|
||||||
| /* And now create the FormattedValue node that represents this | ||||||
| entire expression with the conversion and format spec. */ | ||||||
| *expression = FormattedValue(simple_expression, conversion, | ||||||
| format_spec, expr_text, LINENO(n), | ||||||
| format_spec, LINENO(n), | ||||||
| n->n_col_offset, n->n_end_lineno, | ||||||
| n->n_end_col_offset, c->c_arena); | ||||||
| if (!*expression) | ||||||
|
|
@@ -5313,7 +5311,7 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl, | |||||
| static int | ||||||
| fstring_find_literal_and_expr(const char **str, const char *end, int raw, | ||||||
| int recurse_lvl, PyObject **literal, | ||||||
| expr_ty *expression, | ||||||
| PyObject **expr_text, expr_ty *expression, | ||||||
| struct compiling *c, const node *n) | ||||||
| { | ||||||
| int result; | ||||||
|
|
@@ -5341,7 +5339,8 @@ fstring_find_literal_and_expr(const char **str, const char *end, int raw, | |||||
| /* We must now be the start of an expression, on a '{'. */ | ||||||
| assert(**str == '{'); | ||||||
|
|
||||||
| if (fstring_find_expr(str, end, raw, recurse_lvl, expression, c, n) < 0) | ||||||
| if (fstring_find_expr(str, end, raw, recurse_lvl, expr_text, | ||||||
| expression, c, n) < 0) | ||||||
| goto error; | ||||||
|
|
||||||
| return 0; | ||||||
|
|
@@ -5604,39 +5603,42 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str, | |||||
|
|
||||||
| /* Parse the f-string. */ | ||||||
| while (1) { | ||||||
| PyObject *literal = NULL; | ||||||
| PyObject *literal[2] = {NULL, NULL}; | ||||||
| expr_ty expression = NULL; | ||||||
|
|
||||||
| /* If there's a zero length literal in front of the | ||||||
| expression, literal will be NULL. If we're at the end of | ||||||
| the f-string, expression will be NULL (unless result == 1, | ||||||
| see below). */ | ||||||
| int result = fstring_find_literal_and_expr(str, end, raw, recurse_lvl, | ||||||
| &literal, &expression, | ||||||
| c, n); | ||||||
| &literal[0], &literal[1], | ||||||
| &expression, c, n); | ||||||
| if (result < 0) | ||||||
| return -1; | ||||||
|
|
||||||
| /* Add the literal, if any. */ | ||||||
| if (!literal) { | ||||||
| /* Do nothing. Just leave last_str alone (and possibly | ||||||
| NULL). */ | ||||||
| } else if (!state->last_str) { | ||||||
| /* Note that the literal can be zero length, if the | ||||||
| input string is "\\\n" or "\\\r", among others. */ | ||||||
| state->last_str = literal; | ||||||
| literal = NULL; | ||||||
| } else { | ||||||
| /* We have a literal, concatenate it. */ | ||||||
| assert(PyUnicode_GET_LENGTH(literal) != 0); | ||||||
| if (FstringParser_ConcatAndDel(state, literal) < 0) | ||||||
| return -1; | ||||||
| literal = NULL; | ||||||
| /* Add the literals, if any. */ | ||||||
| for (int i = 0; i < 2; i++) { | ||||||
| if (!literal[i]) { | ||||||
| /* Do nothing. Just leave last_str alone (and possibly | ||||||
| NULL). */ | ||||||
| } else if (!state->last_str) { | ||||||
| /* Note that the literal can be zero length, if the | ||||||
| input string is "\\\n" or "\\\r", among others. */ | ||||||
| state->last_str = literal[i]; | ||||||
| literal[i] = NULL; | ||||||
| } else { | ||||||
| /* We have a literal, concatenate it. */ | ||||||
| assert(PyUnicode_GET_LENGTH(literal[i]) != 0); | ||||||
| if (FstringParser_ConcatAndDel(state, literal[i]) < 0) | ||||||
| return -1; | ||||||
|
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.
|
||||||
| literal[i] = NULL; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /* We've dealt with the literal now. It can't be leaked on further | ||||||
| /* We've dealt with the literals now. They can't be leaked on further | ||||||
| errors. */ | ||||||
| assert(literal == NULL); | ||||||
| assert(literal[0] == NULL); | ||||||
| assert(literal[1] == NULL); | ||||||
|
|
||||||
| /* See if we should just loop around to get the next literal | ||||||
| and expression, while ignoring the expression this | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is worth to move this into separate method. It can even be decorated as
cpython_only. I think that if an alternate implementation is smart enough to roundtrip these case, it is still a valid Python implementation.