mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-37050: Remove expr_text from FormattedValue ast node, use Constant node instead (GH-13597)
When using the "=" debug functionality of f-strings, use another Constant node (or a merged constant node) instead of adding expr_text to the FormattedValue node.
This commit is contained in:
parent
695b1dd8cb
commit
6f6ff8a565
9 changed files with 87 additions and 100 deletions
90
Python/ast.c
90
Python/ast.c
|
@ -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
|
||||
*not a debug expression, *expr_text set to NULL. */
|
||||
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,7 +5201,6 @@ 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
|
||||
|
@ -5206,7 +5208,14 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
|
|||
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);
|
||||
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 @@ error:
|
|||
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,7 +5603,7 @@ 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
|
||||
|
@ -5612,31 +5611,34 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str,
|
|||
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;
|
||||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue