mirror of
https://github.com/python/cpython.git
synced 2025-08-02 16:13:13 +00:00
gh-124363: Treat debug expressions in f-string as raw strings (#128399)
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
This commit is contained in:
parent
ba9a4b6215
commit
60a3a0dd6f
3 changed files with 46 additions and 44 deletions
|
@ -1649,6 +1649,14 @@ x = (
|
||||||
#self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
|
#self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
|
||||||
#self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
|
#self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
|
||||||
|
|
||||||
|
def test_debug_expressions_are_raw_strings(self):
|
||||||
|
|
||||||
|
self.assertEqual(f'{b"\N{OX}"=}', 'b"\\N{OX}"=b\'\\\\N{OX}\'')
|
||||||
|
self.assertEqual(f'{r"\xff"=}', 'r"\\xff"=\'\\\\xff\'')
|
||||||
|
self.assertEqual(f'{r"\n"=}', 'r"\\n"=\'\\\\n\'')
|
||||||
|
self.assertEqual(f"{'\''=}", "'\\''=\"'\"")
|
||||||
|
self.assertEqual(f'{'\xc5'=}', r"'\xc5'='Å'")
|
||||||
|
|
||||||
def test_walrus(self):
|
def test_walrus(self):
|
||||||
x = 20
|
x = 20
|
||||||
# This isn't an assignment expression, it's 'x', with a format
|
# This isn't an assignment expression, it's 'x', with a format
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Treat debug expressions in f-string as raw strings. Patch by Pablo Galindo
|
|
@ -969,8 +969,6 @@ _PyPegen_check_fstring_conversion(Parser *p, Token* conv_token, expr_ty conv)
|
||||||
return result_token_with_metadata(p, conv, conv_token->metadata);
|
return result_token_with_metadata(p, conv, conv_token->metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static asdl_expr_seq *
|
|
||||||
unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions);
|
|
||||||
ResultTokenWithMetadata *
|
ResultTokenWithMetadata *
|
||||||
_PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec, int lineno, int col_offset,
|
_PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec, int lineno, int col_offset,
|
||||||
int end_lineno, int end_col_offset, PyArena *arena)
|
int end_lineno, int end_col_offset, PyArena *arena)
|
||||||
|
@ -1279,9 +1277,9 @@ _PyPegen_decode_fstring_part(Parser* p, int is_raw, expr_ty constant, Token* tok
|
||||||
p->arena);
|
p->arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
static asdl_expr_seq *
|
expr_ty
|
||||||
unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions)
|
_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
|
||||||
{
|
|
||||||
/* The parser might put multiple f-string values into an individual
|
/* The parser might put multiple f-string values into an individual
|
||||||
* JoinedStr node at the top level due to stuff like f-string debugging
|
* JoinedStr node at the top level due to stuff like f-string debugging
|
||||||
* expressions. This function flattens those and promotes them to the
|
* expressions. This function flattens those and promotes them to the
|
||||||
|
@ -1289,44 +1287,14 @@ unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions)
|
||||||
* of the regular output, so this is not necessary if you are not going
|
* of the regular output, so this is not necessary if you are not going
|
||||||
* to expose the output AST to Python level. */
|
* to expose the output AST to Python level. */
|
||||||
|
|
||||||
Py_ssize_t i, req_size, raw_size;
|
|
||||||
|
|
||||||
req_size = raw_size = asdl_seq_LEN(raw_expressions);
|
|
||||||
expr_ty expr;
|
|
||||||
for (i = 0; i < raw_size; i++) {
|
|
||||||
expr = asdl_seq_GET(raw_expressions, i);
|
|
||||||
if (expr->kind == JoinedStr_kind) {
|
|
||||||
req_size += asdl_seq_LEN(expr->v.JoinedStr.values) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
asdl_expr_seq *expressions = _Py_asdl_expr_seq_new(req_size, p->arena);
|
|
||||||
if (expressions == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_ssize_t raw_index, req_index = 0;
|
|
||||||
for (raw_index = 0; raw_index < raw_size; raw_index++) {
|
|
||||||
expr = asdl_seq_GET(raw_expressions, raw_index);
|
|
||||||
if (expr->kind == JoinedStr_kind) {
|
|
||||||
asdl_expr_seq *values = expr->v.JoinedStr.values;
|
|
||||||
for (Py_ssize_t n = 0; n < asdl_seq_LEN(values); n++) {
|
|
||||||
asdl_seq_SET(expressions, req_index, asdl_seq_GET(values, n));
|
|
||||||
req_index++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
asdl_seq_SET(expressions, req_index, expr);
|
|
||||||
req_index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expressions;
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ty
|
|
||||||
_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b) {
|
|
||||||
|
|
||||||
asdl_expr_seq *expr = unpack_top_level_joined_strs(p, raw_expressions);
|
|
||||||
Py_ssize_t n_items = asdl_seq_LEN(expr);
|
Py_ssize_t n_items = asdl_seq_LEN(expr);
|
||||||
|
Py_ssize_t total_items = n_items;
|
||||||
|
for (Py_ssize_t i = 0; i < n_items; i++) {
|
||||||
|
expr_ty item = asdl_seq_GET(expr, i);
|
||||||
|
if (item->kind == JoinedStr_kind) {
|
||||||
|
total_items += asdl_seq_LEN(item->v.JoinedStr.values) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const char* quote_str = PyBytes_AsString(a->bytes);
|
const char* quote_str = PyBytes_AsString(a->bytes);
|
||||||
if (quote_str == NULL) {
|
if (quote_str == NULL) {
|
||||||
|
@ -1334,7 +1302,7 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b
|
||||||
}
|
}
|
||||||
int is_raw = strpbrk(quote_str, "rR") != NULL;
|
int is_raw = strpbrk(quote_str, "rR") != NULL;
|
||||||
|
|
||||||
asdl_expr_seq *seq = _Py_asdl_expr_seq_new(n_items, p->arena);
|
asdl_expr_seq *seq = _Py_asdl_expr_seq_new(total_items, p->arena);
|
||||||
if (seq == NULL) {
|
if (seq == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1342,6 +1310,31 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b
|
||||||
Py_ssize_t index = 0;
|
Py_ssize_t index = 0;
|
||||||
for (Py_ssize_t i = 0; i < n_items; i++) {
|
for (Py_ssize_t i = 0; i < n_items; i++) {
|
||||||
expr_ty item = asdl_seq_GET(expr, i);
|
expr_ty item = asdl_seq_GET(expr, i);
|
||||||
|
|
||||||
|
// This should correspond to a JoinedStr node of two elements
|
||||||
|
// created _PyPegen_formatted_value. This situation can only be the result of
|
||||||
|
// a f-string debug expression where the first element is a constant with the text and the second
|
||||||
|
// a formatted value with the expression.
|
||||||
|
if (item->kind == JoinedStr_kind) {
|
||||||
|
asdl_expr_seq *values = item->v.JoinedStr.values;
|
||||||
|
if (asdl_seq_LEN(values) != 2) {
|
||||||
|
PyErr_Format(PyExc_SystemError,
|
||||||
|
"unexpected JoinedStr node without debug data in f-string at line %d",
|
||||||
|
item->lineno);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ty first = asdl_seq_GET(values, 0);
|
||||||
|
assert(first->kind == Constant_kind);
|
||||||
|
asdl_seq_SET(seq, index++, first);
|
||||||
|
|
||||||
|
expr_ty second = asdl_seq_GET(values, 1);
|
||||||
|
assert(second->kind == FormattedValue_kind);
|
||||||
|
asdl_seq_SET(seq, index++, second);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item->kind == Constant_kind) {
|
if (item->kind == Constant_kind) {
|
||||||
item = _PyPegen_decode_fstring_part(p, is_raw, item, b);
|
item = _PyPegen_decode_fstring_part(p, is_raw, item, b);
|
||||||
if (item == NULL) {
|
if (item == NULL) {
|
||||||
|
@ -1360,7 +1353,7 @@ _PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* raw_expressions, Token*b
|
||||||
}
|
}
|
||||||
|
|
||||||
asdl_expr_seq *resized_exprs;
|
asdl_expr_seq *resized_exprs;
|
||||||
if (index != n_items) {
|
if (index != total_items) {
|
||||||
resized_exprs = _Py_asdl_expr_seq_new(index, p->arena);
|
resized_exprs = _Py_asdl_expr_seq_new(index, p->arena);
|
||||||
if (resized_exprs == NULL) {
|
if (resized_exprs == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue