mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
bpo-37070: Cleanup fstring debug handling (GH-13607)
* Clean up some comments, fix potential memory leaks, clarify literal and expr_text.
This commit is contained in:
parent
0ae022c6a4
commit
f83d1dbd3b
2 changed files with 19 additions and 27 deletions
|
@ -284,6 +284,7 @@ class AnnotationsFutureTestCase(unittest.TestCase):
|
||||||
eq("(x:=10)")
|
eq("(x:=10)")
|
||||||
eq("f'{(x:=10):=10}'")
|
eq("f'{(x:=10):=10}'")
|
||||||
|
|
||||||
|
def test_fstring_debug_annotations(self):
|
||||||
# f-strings with '=' don't round trip very well, so set the expected
|
# f-strings with '=' don't round trip very well, so set the expected
|
||||||
# result explicitely.
|
# result explicitely.
|
||||||
self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'")
|
self.assertAnnotationEqual("f'{x=!r}'", expected="f'x={x!r}'")
|
||||||
|
|
45
Python/ast.c
45
Python/ast.c
|
@ -5010,8 +5010,8 @@ fstring_parse(const char **str, const char *end, int raw, int recurse_lvl,
|
||||||
|
|
||||||
*expression is set to the expression. For an '=' "debug" expression,
|
*expression is set to the expression. For an '=' "debug" expression,
|
||||||
*expr_text is set to the debug text (the original text of the 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
|
including the '=' and any whitespace around it, as a string object). If
|
||||||
*not a debug expression, *expr_text set to NULL. */
|
not a debug expression, *expr_text set to NULL. */
|
||||||
static int
|
static int
|
||||||
fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
|
fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
|
||||||
PyObject **expr_text, expr_ty *expression,
|
PyObject **expr_text, expr_ty *expression,
|
||||||
|
@ -5039,6 +5039,8 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
|
||||||
Py_ssize_t nested_depth = 0;
|
Py_ssize_t nested_depth = 0;
|
||||||
char parenstack[MAXLEVEL];
|
char parenstack[MAXLEVEL];
|
||||||
|
|
||||||
|
*expr_text = NULL;
|
||||||
|
|
||||||
/* Can only nest one level deep. */
|
/* Can only nest one level deep. */
|
||||||
if (recurse_lvl >= 2) {
|
if (recurse_lvl >= 2) {
|
||||||
ast_error(c, n, "f-string: expressions nested too deeply");
|
ast_error(c, n, "f-string: expressions nested too deeply");
|
||||||
|
@ -5214,8 +5216,6 @@ fstring_find_expr(const char **str, const char *end, int raw, int recurse_lvl,
|
||||||
if (!*expr_text) {
|
if (!*expr_text) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
*expr_text = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for a conversion char, if present. */
|
/* Check for a conversion char, if present. */
|
||||||
|
@ -5281,6 +5281,7 @@ unexpected_end_of_string:
|
||||||
/* Falls through to error. */
|
/* Falls through to error. */
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
Py_XDECREF(*expr_text);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5603,7 +5604,8 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str,
|
||||||
|
|
||||||
/* Parse the f-string. */
|
/* Parse the f-string. */
|
||||||
while (1) {
|
while (1) {
|
||||||
PyObject *literal[2] = {NULL, NULL};
|
PyObject *literal = NULL;
|
||||||
|
PyObject *expr_text = NULL;
|
||||||
expr_ty expression = NULL;
|
expr_ty expression = NULL;
|
||||||
|
|
||||||
/* If there's a zero length literal in front of the
|
/* If there's a zero length literal in front of the
|
||||||
|
@ -5611,34 +5613,23 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str,
|
||||||
the f-string, expression will be NULL (unless result == 1,
|
the f-string, expression will be NULL (unless result == 1,
|
||||||
see below). */
|
see below). */
|
||||||
int result = fstring_find_literal_and_expr(str, end, raw, recurse_lvl,
|
int result = fstring_find_literal_and_expr(str, end, raw, recurse_lvl,
|
||||||
&literal[0], &literal[1],
|
&literal, &expr_text,
|
||||||
&expression, c, n);
|
&expression, c, n);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Add the literals, if any. */
|
/* Add the literal, if any. */
|
||||||
for (int i = 0; i < 2; i++) {
|
if (literal && FstringParser_ConcatAndDel(state, literal) < 0) {
|
||||||
if (!literal[i]) {
|
Py_XDECREF(expr_text);
|
||||||
/* Do nothing. Just leave last_str alone (and possibly
|
return -1;
|
||||||
NULL). */
|
}
|
||||||
} else if (!state->last_str) {
|
/* Add the expr_text, if any. */
|
||||||
/* Note that the literal can be zero length, if the
|
if (expr_text && FstringParser_ConcatAndDel(state, expr_text) < 0) {
|
||||||
input string is "\\\n" or "\\\r", among others. */
|
return -1;
|
||||||
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 literals now. They can't be leaked on further
|
/* We've dealt with the literal and expr_text, their ownership has
|
||||||
errors. */
|
been transferred to the state object. Don't look at them again. */
|
||||||
assert(literal[0] == NULL);
|
|
||||||
assert(literal[1] == NULL);
|
|
||||||
|
|
||||||
/* See if we should just loop around to get the next literal
|
/* See if we should just loop around to get the next literal
|
||||||
and expression, while ignoring the expression this
|
and expression, while ignoring the expression this
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue