Skip to content

Commit 9f5a8e0

Browse files
committed
address review
1 parent f54fc47 commit 9f5a8e0

5 files changed

Lines changed: 70 additions & 6 deletions

File tree

Lib/test/test_fstring.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,14 @@ def __repr__(self):
16781678
self.assertEqual(f'{" # nooo "=}', '" # nooo "=\' # nooo \'')
16791679
self.assertEqual(f'{" \" # nooo \" "=}', '" \\" # nooo \\" "=\' " # nooo " \'')
16801680

1681+
result = f'''{(
1682+
1, # Force lexer metadata reconstruction.
1683+
"\"#")=}'''
1684+
self.assertEqual(
1685+
result,
1686+
'(\n 1, \n "\\"#")=(1, \'"#\')',
1687+
)
1688+
16811689
self.assertEqual(f'{ # some comment goes here
16821690
"""hello"""=}', ' \n """hello"""=\'hello\'')
16831691
self.assertEqual(f'{"""# this is not a comment

Lib/test/test_tstring.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,43 @@ def test_debug_specifier(self):
140140
)
141141
self.assertEqual(fstring(t), "Value: value = 42")
142142

143+
# Explicit line continuations after the debug marker are part of
144+
# the debug text, not the interpolation expression.
145+
for template, strings, interpolation, rendered in (
146+
(
147+
t"""Value: {value =\
148+
}""",
149+
("Value: value =\\\n", ""),
150+
(value, "value ", "r"),
151+
"Value: value =\\\n42",
152+
),
153+
(
154+
t"""Value: {value =\
155+
!r}""",
156+
("Value: value =\\\n", ""),
157+
(value, "value ", "r"),
158+
"Value: value =\\\n42",
159+
),
160+
(
161+
t"""Value: {value =\
162+
:04}""",
163+
("Value: value =\\\n", ""),
164+
(value, "value ", None, "04"),
165+
"Value: value =\\\n0042",
166+
),
167+
(
168+
t"""Value: {value =\
169+
\
170+
}""",
171+
("Value: value =\\\n\\\n", ""),
172+
(value, "value ", "r"),
173+
"Value: value =\\\n\\\n42",
174+
),
175+
):
176+
with self.subTest(template=template):
177+
self.assertTStringEqual(template, strings, [interpolation])
178+
self.assertEqual(fstring(template), rendered)
179+
143180
def test_interpolation_expression_whitespace(self):
144181
x = 42
145182
for template, expected in (

Lib/test/test_unparse.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ def test_tstrings(self):
221221
1, # Force lexer metadata reconstruction.
222222
"\"#")}'''"""
223223
)
224+
self.check_ast_roundtrip(
225+
r'''t"""Value: {value =\
226+
}"""'''
227+
)
224228

225229
def test_tstring_with_nonsensical_str_field(self):
226230
# `value` suggests that the original code is `t'{test1}`, but `str` suggests otherwise
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
Whitespace immediately before ``}``, ``!``, ``:``, or ``=`` is now included
22
in :attr:`string.templatelib.Interpolation.expression` for interpolations
3-
created from t-string literals. Preserve escaped quotes while reconstructing
4-
interpolation expression metadata containing comments.
3+
created from t-string literals. Preserve escaped quotes while reconstructing
4+
t-string interpolation metadata and f-string debug expression text containing
5+
comments.

Parser/action_helpers.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,15 +1544,29 @@ _strip_interpolation_debug_expr(PyObject *exprstr)
15441544
{
15451545
Py_ssize_t len = PyUnicode_GET_LENGTH(exprstr);
15461546

1547-
/* Discard whitespace after the debug "=" but preserve whitespace before it. */
1547+
/* Discard whitespace and explicit line continuations after the debug "="
1548+
but preserve whitespace before it. */
15481549
while (len > 0) {
1549-
Py_UCS4 c = PyUnicode_READ_CHAR(exprstr, len - 1);
1550-
if (!_PyUnicode_IsWhitespace(c)) {
1550+
int has_newline = 0;
1551+
while (len > 0) {
1552+
Py_UCS4 c = PyUnicode_READ_CHAR(exprstr, len - 1);
1553+
if (!_PyUnicode_IsWhitespace(c)) {
1554+
break;
1555+
}
1556+
if (c == '\r' || c == '\n') {
1557+
has_newline = 1;
1558+
}
1559+
len--;
1560+
}
1561+
if (!has_newline || len == 0 ||
1562+
PyUnicode_READ_CHAR(exprstr, len - 1) != '\\')
1563+
{
15511564
break;
15521565
}
15531566
len--;
15541567
}
1555-
/* The debug marker may be absent from reconstructed lexer metadata. */
1568+
1569+
/* Preserve unexpected metadata instead of dropping source text. */
15561570
if (len == 0 || PyUnicode_READ_CHAR(exprstr, len - 1) != '=') {
15571571
return Py_NewRef(exprstr);
15581572
}

0 commit comments

Comments
 (0)