Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ed04d9

Browse files
[3.12] gh-124363: Treat debug expressions in f-string as raw strings (GH-128399) (#129190)
(cherry picked from commit 60a3a0d) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent e577ff4 commit 9ed04d9

File tree

3 files changed

+43
-42
lines changed

3 files changed

+43
-42
lines changed

Lib/test/test_fstring.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,14 @@ def __repr__(self):
17641764
# self.assertEqual(f'X{x =}Y', 'Xx\t='+repr(x)+'Y')
17651765
# self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
17661766

1767+
def test_debug_expressions_are_raw_strings(self):
1768+
1769+
self.assertEqual(f'{b"\N{OX}"=}', 'b"\\N{OX}"=b\'\\\\N{OX}\'')
1770+
self.assertEqual(f'{r"\xff"=}', 'r"\\xff"=\'\\\\xff\'')
1771+
self.assertEqual(f'{r"\n"=}', 'r"\\n"=\'\\\\n\'')
1772+
self.assertEqual(f"{'\''=}", "'\\''=\"'\"")
1773+
self.assertEqual(f'{'\xc5'=}', r"'\xc5'='Å'")
1774+
17671775
def test_walrus(self):
17681776
x = 20
17691777
# This isn't an assignment expression, it's 'x', with a format
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Treat debug expressions in f-string as raw strings. Patch by Pablo Galindo

Parser/action_helpers.c

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -909,8 +909,6 @@ _PyPegen_check_fstring_conversion(Parser *p, Token *conv_token, expr_ty conv) {
909909
return result_token_with_metadata(p, conv, conv_token->metadata);
910910
}
911911

912-
static asdl_expr_seq *
913-
unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions);
914912
ResultTokenWithMetadata *
915913
_PyPegen_setup_full_format_spec(Parser *p, Token *colon, asdl_expr_seq *spec,
916914
int lineno, int col_offset, int end_lineno,
@@ -1192,68 +1190,62 @@ static expr_ty _PyPegen_decode_fstring_part(Parser *p, int is_raw,
11921190
p->arena);
11931191
}
11941192

1195-
static asdl_expr_seq *
1196-
unpack_top_level_joined_strs(Parser *p, asdl_expr_seq *raw_expressions) {
1193+
expr_ty
1194+
_PyPegen_joined_str(Parser *p, Token* a, asdl_expr_seq* expr, Token*b) {
1195+
11971196
/* The parser might put multiple f-string values into an individual
11981197
* JoinedStr node at the top level due to stuff like f-string debugging
11991198
* expressions. This function flattens those and promotes them to the
12001199
* upper level. Only simplifies AST, but the compiler already takes care
12011200
* of the regular output, so this is not necessary if you are not going
12021201
* to expose the output AST to Python level. */
12031202

1204-
Py_ssize_t i, req_size, raw_size;
1205-
1206-
req_size = raw_size = asdl_seq_LEN(raw_expressions);
1207-
expr_ty expr;
1208-
for (i = 0; i < raw_size; i++) {
1209-
expr = asdl_seq_GET(raw_expressions, i);
1210-
if (expr->kind == JoinedStr_kind) {
1211-
req_size += asdl_seq_LEN(expr->v.JoinedStr.values) - 1;
1212-
}
1213-
}
1214-
1215-
asdl_expr_seq *expressions = _Py_asdl_expr_seq_new(req_size, p->arena);
1216-
if (expressions == NULL) {
1217-
return NULL;
1218-
}
1219-
1220-
Py_ssize_t raw_index, req_index = 0;
1221-
for (raw_index = 0; raw_index < raw_size; raw_index++) {
1222-
expr = asdl_seq_GET(raw_expressions, raw_index);
1223-
if (expr->kind == JoinedStr_kind) {
1224-
asdl_expr_seq *values = expr->v.JoinedStr.values;
1225-
for (Py_ssize_t n = 0; n < asdl_seq_LEN(values); n++) {
1226-
asdl_seq_SET(expressions, req_index, asdl_seq_GET(values, n));
1227-
req_index++;
1203+
Py_ssize_t n_items = asdl_seq_LEN(expr);
1204+
Py_ssize_t total_items = n_items;
1205+
for (Py_ssize_t i = 0; i < n_items; i++) {
1206+
expr_ty item = asdl_seq_GET(expr, i);
1207+
if (item->kind == JoinedStr_kind) {
1208+
total_items += asdl_seq_LEN(item->v.JoinedStr.values) - 1;
12281209
}
1229-
} else {
1230-
asdl_seq_SET(expressions, req_index, expr);
1231-
req_index++;
1232-
}
12331210
}
1234-
return expressions;
1235-
}
1236-
1237-
expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
1238-
Token *b) {
1239-
1240-
asdl_expr_seq *expr = unpack_top_level_joined_strs(p, raw_expressions);
1241-
Py_ssize_t n_items = asdl_seq_LEN(expr);
12421211

12431212
const char *quote_str = PyBytes_AsString(a->bytes);
12441213
if (quote_str == NULL) {
12451214
return NULL;
12461215
}
12471216
int is_raw = strpbrk(quote_str, "rR") != NULL;
12481217

1249-
asdl_expr_seq *seq = _Py_asdl_expr_seq_new(n_items, p->arena);
1218+
asdl_expr_seq *seq = _Py_asdl_expr_seq_new(total_items, p->arena);
12501219
if (seq == NULL) {
12511220
return NULL;
12521221
}
12531222

12541223
Py_ssize_t index = 0;
12551224
for (Py_ssize_t i = 0; i < n_items; i++) {
12561225
expr_ty item = asdl_seq_GET(expr, i);
1226+
// This should correspond to a JoinedStr node of two elements
1227+
// created _PyPegen_formatted_value. This situation can only be the result of
1228+
// a f-string debug expression where the first element is a constant with the text and the second
1229+
// a formatted value with the expression.
1230+
if (item->kind == JoinedStr_kind) {
1231+
asdl_expr_seq *values = item->v.JoinedStr.values;
1232+
if (asdl_seq_LEN(values) != 2) {
1233+
PyErr_Format(PyExc_SystemError,
1234+
"unexpected JoinedStr node without debug data in f-string at line %d",
1235+
item->lineno);
1236+
return NULL;
1237+
}
1238+
1239+
expr_ty first = asdl_seq_GET(values, 0);
1240+
assert(first->kind == Constant_kind);
1241+
asdl_seq_SET(seq, index++, first);
1242+
1243+
expr_ty second = asdl_seq_GET(values, 1);
1244+
assert(second->kind == FormattedValue_kind);
1245+
asdl_seq_SET(seq, index++, second);
1246+
1247+
continue;
1248+
}
12571249
if (item->kind == Constant_kind) {
12581250
item = _PyPegen_decode_fstring_part(p, is_raw, item, b);
12591251
if (item == NULL) {
@@ -1272,7 +1264,7 @@ expr_ty _PyPegen_joined_str(Parser *p, Token *a, asdl_expr_seq *raw_expressions,
12721264
}
12731265

12741266
asdl_expr_seq *resized_exprs;
1275-
if (index != n_items) {
1267+
if (index != total_items) {
12761268
resized_exprs = _Py_asdl_expr_seq_new(index, p->arena);
12771269
if (resized_exprs == NULL) {
12781270
return NULL;

0 commit comments

Comments
 (0)