8000 [3.12] gh-124363: Treat debug expressions in f-string as raw strings … · python/cpython@9ed04d9 · GitHub
[go: up one dir, main page]

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
  • Lib/test
  • Misc/NEWS.d/next/Core_and_Builtins
  • Parser
  • 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)
    0