Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion Lib/test/test_tstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ def upper(self):
)
self.assertEqual(fstring(t), "Name: Bob, Age: 30")

def test_interpolation_expression_whitespace(self):
x = 42
cases = [
(t"{x}", "x"),
(t"{x }", "x "),
(t"{ x}", " x"),
(t"{ x }", " x "),
(t"{ x }", " x "),
(t"{x !r}", "x "),
(t"{x :}", "x "),
(t"{x = }", "x "),
(t"{x == 42 = }", "x == 42 "),
(t"""{
x
}""", "\n x\n"),
]
for template, expected in cases:
with self.subTest(template=template):
self.assertEqual(
template.interpolations[0].expression, expected
)

def test_format_specifiers(self):
# Test basic format specifiers
value = 3.14159
Expand Down Expand Up @@ -136,7 +158,7 @@ def test_debug_specifier(self):
# Test white space in debug specifier
t = t"Value: {value = }"
self.assertTStringEqual(
t, ("Value: value = ", ""), [(value, "value", "r")]
t, ("Value: value = ", ""), [(value, "value ", "r")]
)
self.assertEqual(fstring(t), "Value: value = 42")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Whitespace immediately before ``}``, ``!``, ``:``, or ``=`` is now included
in :attr:`string.templatelib.Interpolation.expression` for interpolations
created from t-string literals.
20 changes: 11 additions & 9 deletions Parser/action_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1540,21 +1540,21 @@ _get_interpolation_conversion(Parser *p, Token *debug, ResultTokenWithMetadata *
}

static PyObject *
_strip_interpolation_expr(PyObject *exprstr)
_strip_interpolation_debug_expr(PyObject *exprstr)
{
Py_ssize_t len = PyUnicode_GET_LENGTH(exprstr);

for (Py_ssize_t i = len - 1; i >= 0; i--) {
Py_UCS4 c = PyUnicode_READ_CHAR(exprstr, i);
if (_PyUnicode_IsWhitespace(c) || c == '=') {
len--;
}
else {
/* Discard whitespace after the debug "=" but preserve whitespace before it. */
while (len > 0) {
Py_UCS4 c = PyUnicode_READ_CHAR(exprstr, len - 1);
if (!_PyUnicode_IsWhitespace(c)) {
break;
}
len--;
}
assert(len > 0 && PyUnicode_READ_CHAR(exprstr, len - 1) == '=');

return PyUnicode_Substring(exprstr, 0, len);
return PyUnicode_Substring(exprstr, 0, len - 1);
}

expr_ty _PyPegen_interpolation(Parser *p, expr_ty expression, Token *debug, ResultTokenWithMetadata *conversion,
Expand Down Expand Up @@ -1585,7 +1585,9 @@ expr_ty _PyPegen_interpolation(Parser *p, expr_ty expression, Token *debug, Resu
}

assert(exprstr != NULL);
PyObject *final_exprstr = _strip_interpolation_expr(exprstr);
PyObject *final_exprstr = debug
? _strip_interpolation_debug_expr(exprstr)
: Py_NewRef(exprstr);
if (!final_exprstr || _PyArena_AddPyObject(arena, final_exprstr) < 0) {
Py_XDECREF(final_exprstr);
return NULL;
Expand Down
Loading