gh-132661: Disallow Template/str concatenation after PEP 750 spec update (#135996)

Co-authored-by: sobolevn <mail@sobolevn.me>
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
This commit is contained in:
Dave Peck 2025-07-20 23:44:26 -07:00 committed by GitHub
parent 246be21de1
commit c5e77af131
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 2097 additions and 2133 deletions

View file

@ -987,7 +987,10 @@ tstring[expr_ty] (memo):
_PyPegen_template_str(p, a, (asdl_expr_seq*)b, c)) } _PyPegen_template_str(p, a, (asdl_expr_seq*)b, c)) }
string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) } string[expr_ty]: s[Token*]=STRING { _PyPegen_constant_from_string(p, s) }
strings[expr_ty] (memo): a[asdl_expr_seq*]=(fstring|string|tstring)+ { _PyPegen_concatenate_strings(p, a, EXTRA) } strings[expr_ty] (memo):
| invalid_string_tstring_concat
| a[asdl_expr_seq*]=(fstring|string)+ { _PyPegen_concatenate_strings(p, a, EXTRA) }
| a[asdl_expr_seq*]=tstring+ { _PyPegen_concatenate_tstrings(p, a, EXTRA) }
list[expr_ty]: list[expr_ty]:
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) } | '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
@ -1553,6 +1556,12 @@ invalid_tstring_conversion_character:
| '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: missing conversion character") } | '!' &(':' | '}') { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: missing conversion character") }
| '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: invalid conversion character") } | '!' !NAME { RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("t-string: invalid conversion character") }
invalid_string_tstring_concat:
| a=(fstring|string)+ b[expr_ty]=tstring {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(PyPegen_last_item(a, expr_ty), b, "cannot mix t-string literals with string or bytes literals") }
| a=tstring+ b[expr_ty]=(fstring|string) {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(PyPegen_last_item(a, expr_ty), b, "cannot mix t-string literals with string or bytes literals") }
invalid_arithmetic: invalid_arithmetic:
| sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") } | sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_factor: invalid_factor:

View file

@ -626,35 +626,11 @@ def _write_ftstring(self, values, prefix):
) )
self._ftstring_helper(fstring_parts) self._ftstring_helper(fstring_parts)
def _tstring_helper(self, node):
if not node.values:
self._write_ftstring([], "t")
return
last_idx = 0
for i, value in enumerate(node.values):
# This can happen if we have an implicit concat of a t-string
# with an f-string
if isinstance(value, FormattedValue):
if i > last_idx:
# Write t-string until here
self._write_ftstring(node.values[last_idx:i], "t")
self.write(" ")
# Write f-string with the current formatted value
self._write_ftstring([node.values[i]], "f")
if i + 1 < len(node.values):
# Only add a space if there are more values after this
self.write(" ")
last_idx = i + 1
if last_idx < len(node.values):
# Write t-string from last_idx to end
self._write_ftstring(node.values[last_idx:], "t")
def visit_JoinedStr(self, node): def visit_JoinedStr(self, node):
self._write_ftstring(node.values, "f") self._write_ftstring(node.values, "f")
def visit_TemplateStr(self, node): def visit_TemplateStr(self, node):
self._tstring_helper(node) self._write_ftstring(node.values, "t")
def _write_ftstring_inner(self, node, is_format_spec=False): def _write_ftstring_inner(self, node, is_format_spec=False):
if isinstance(node, JoinedStr): if isinstance(node, JoinedStr):

View file

@ -999,13 +999,6 @@ def test_tstring(self):
self.assertIsInstance(tree.body[0].value.values[0], ast.Constant) self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation) self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
# Test AST for implicit concat of t-string with f-string
tree = ast.parse('t"Hello {name}" f"{name}"')
self.assertIsInstance(tree.body[0].value, ast.TemplateStr)
self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
self.assertIsInstance(tree.body[0].value.values[2], ast.FormattedValue)
class CopyTests(unittest.TestCase): class CopyTests(unittest.TestCase):
"""Test copying and pickling AST nodes.""" """Test copying and pickling AST nodes."""

View file

@ -150,7 +150,6 @@ def test_raw_tstrings(self):
t = tr"{path}\Documents" t = tr"{path}\Documents"
self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")]) self.assertTStringEqual(t, ("", r"\Documents"), [(path, "path")])
def test_template_concatenation(self): def test_template_concatenation(self):
# Test template + template # Test template + template
t1 = t"Hello, " t1 = t"Hello, "
@ -161,9 +160,10 @@ def test_template_concatenation(self):
# Test template + string # Test template + string
t1 = t"Hello" t1 = t"Hello"
combined = t1 + ", world" expected_msg = 'can only concatenate string.templatelib.Template ' \
self.assertTStringEqual(combined, ("Hello, world",), ()) '\\(not "str"\\) to string.templatelib.Template'
self.assertEqual(fstring(combined), "Hello, world") with self.assertRaisesRegex(TypeError, expected_msg):
t1 + ", world"
# Test template + template with interpolation # Test template + template with interpolation
name = "Python" name = "Python"
@ -174,9 +174,10 @@ def test_template_concatenation(self):
self.assertEqual(fstring(combined), "Hello, Python") self.assertEqual(fstring(combined), "Hello, Python")
# Test string + template # Test string + template
t = "Hello, " + t"{name}" expected_msg = 'can only concatenate str ' \
self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")]) '\\(not "string.templatelib.Template"\\) to str'
self.assertEqual(fstring(t), "Hello, Python") with self.assertRaisesRegex(TypeError, expected_msg):
"Hello, " + t"{name}"
def test_nested_templates(self): def test_nested_templates(self):
# Test a template inside another template expression # Test a template inside another template expression
@ -241,52 +242,28 @@ def test_literal_concatenation(self):
self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")]) self.assertTStringEqual(t, ("Hello, ", ""), [(name, "name")])
self.assertEqual(fstring(t), "Hello, Python") self.assertEqual(fstring(t), "Hello, Python")
# Test concatenation with string literal # Test disallowed mix of t-string and string/f-string (incl. bytes)
name = "Python"
t = t"Hello, {name}" "and welcome!"
self.assertTStringEqual(
t, ("Hello, ", "and welcome!"), [(name, "name")]
)
self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
# Test concatenation with Unicode literal
name = "Python"
t = t"Hello, {name}" u"and welcome!"
self.assertTStringEqual(
t, ("Hello, ", "and welcome!"), [(name, "name")]
)
self.assertEqual(fstring(t), "Hello, Pythonand welcome!")
# Test concatenation with f-string literal
tab = '\t'
t = t"Tab: {tab}. " f"f-tab: {tab}."
self.assertTStringEqual(t, ("Tab: ", ". f-tab: \t."), [(tab, "tab")])
self.assertEqual(fstring(t), "Tab: \t. f-tab: \t.")
# Test concatenation with raw string literal
tab = '\t'
t = t"Tab: {tab}. " r"Raw tab: \t."
self.assertTStringEqual(
t, ("Tab: ", r". Raw tab: \t."), [(tab, "tab")]
)
self.assertEqual(fstring(t), "Tab: \t. Raw tab: \\t.")
# Test concatenation with raw f-string literal
tab = '\t'
t = t"Tab: {tab}. " rf"f-tab: {tab}. Raw tab: \t."
self.assertTStringEqual(
t, ("Tab: ", ". f-tab: \t. Raw tab: \\t."), [(tab, "tab")]
)
self.assertEqual(fstring(t), "Tab: \t. f-tab: \t. Raw tab: \\t.")
what = 't' what = 't'
expected_msg = 'cannot mix bytes and nonbytes literals' expected_msg = 'cannot mix t-string literals with string or bytes literals'
for case in ( for case in (
"t'{what}-string literal' 'str literal'",
"t'{what}-string literal' u'unicode literal'",
"t'{what}-string literal' f'f-string literal'",
"t'{what}-string literal' r'raw string literal'",
"t'{what}-string literal' rf'raw f-string literal'",
"t'{what}-string literal' b'bytes literal'", "t'{what}-string literal' b'bytes literal'",
"t'{what}-string literal' br'raw bytes literal'", "t'{what}-string literal' br'raw bytes literal'",
"'str literal' t'{what}-string literal'",
"u'unicode literal' t'{what}-string literal'",
"f'f-string literal' t'{what}-string literal'",
"r'raw string literal' t'{what}-string literal'",
"rf'raw f-string literal' t'{what}-string literal'",
"b'bytes literal' t'{what}-string literal'",
"br'raw bytes literal' t'{what}-string literal'",
): ):
with self.assertRaisesRegex(SyntaxError, expected_msg): with self.subTest(case):
eval(case) with self.assertRaisesRegex(SyntaxError, expected_msg):
eval(case)
def test_triple_quoted(self): def test_triple_quoted(self):
# Test triple-quoted t-strings # Test triple-quoted t-strings

View file

@ -206,10 +206,6 @@ def test_tstrings(self):
self.check_ast_roundtrip("t'foo'") self.check_ast_roundtrip("t'foo'")
self.check_ast_roundtrip("t'foo {bar}'") self.check_ast_roundtrip("t'foo {bar}'")
self.check_ast_roundtrip("t'foo {bar!s:.2f}'") self.check_ast_roundtrip("t'foo {bar!s:.2f}'")
self.check_ast_roundtrip("t'foo {bar}' f'{bar}'")
self.check_ast_roundtrip("f'{bar}' t'foo {bar}'")
self.check_ast_roundtrip("t'foo {bar}' fr'\\hello {bar}'")
self.check_ast_roundtrip("t'foo {bar}' u'bar'")
def test_strings(self): def test_strings(self):
self.check_ast_roundtrip("u'foo'") self.check_ast_roundtrip("u'foo'")

View file

@ -0,0 +1,5 @@
Reflect recent :pep:`750` change.
Disallow concatenation of ``string.templatelib.Template`` and :class:`str`.
Also, disallow implicit concatenation of t-string literals with string or
f-string literals.

View file

@ -30,7 +30,8 @@ templateiter_next(PyObject *op)
Py_SETREF(item, PyIter_Next(self->interpolationsiter)); Py_SETREF(item, PyIter_Next(self->interpolationsiter));
self->from_strings = 1; self->from_strings = 1;
} }
} else { }
else {
item = PyIter_Next(self->interpolationsiter); item = PyIter_Next(self->interpolationsiter);
self->from_strings = 1; self->from_strings = 1;
} }
@ -245,54 +246,6 @@ template_iter(PyObject *op)
return (PyObject *)iter; return (PyObject *)iter;
} }
static PyObject *
template_strings_append_str(PyObject *strings, PyObject *str)
{
Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
PyObject *string = PyTuple_GET_ITEM(strings, stringslen - 1);
PyObject *concat = PyUnicode_Concat(string, str);
if (concat == NULL) {
return NULL;
}
PyObject *newstrings = PyTuple_New(stringslen);
if (newstrings == NULL) {
Py_DECREF(concat);
return NULL;
}
for (Py_ssize_t i = 0; i < stringslen - 1; i++) {
PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
}
PyTuple_SET_ITEM(newstrings, stringslen - 1, concat);
return newstrings;
}
static PyObject *
template_strings_prepend_str(PyObject *strings, PyObject *str)
{
Py_ssize_t stringslen = PyTuple_GET_SIZE(strings);
PyObject *string = PyTuple_GET_ITEM(strings, 0);
PyObject *concat = PyUnicode_Concat(str, string);
if (concat == NULL) {
return NULL;
}
PyObject *newstrings = PyTuple_New(stringslen);
if (newstrings == NULL) {
Py_DECREF(concat);
return NULL;
}
PyTuple_SET_ITEM(newstrings, 0, concat);
for (Py_ssize_t i = 1; i < stringslen; i++) {
PyTuple_SET_ITEM(newstrings, i, Py_NewRef(PyTuple_GET_ITEM(strings, i)));
}
return newstrings;
}
static PyObject * static PyObject *
template_strings_concat(PyObject *left, PyObject *right) template_strings_concat(PyObject *left, PyObject *right)
{ {
@ -344,47 +297,17 @@ template_concat_templates(templateobject *self, templateobject *other)
return newtemplate; return newtemplate;
} }
static PyObject *
template_concat_template_str(templateobject *self, PyObject *other)
{
PyObject *newstrings = template_strings_append_str(self->strings, other);
if (newstrings == NULL) {
return NULL;
}
PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
Py_DECREF(newstrings);
return newtemplate;
}
static PyObject *
template_concat_str_template(templateobject *self, PyObject *other)
{
PyObject *newstrings = template_strings_prepend_str(self->strings, other);
if (newstrings == NULL) {
return NULL;
}
PyObject *newtemplate = _PyTemplate_Build(newstrings, self->interpolations);
Py_DECREF(newstrings);
return newtemplate;
}
PyObject * PyObject *
_PyTemplate_Concat(PyObject *self, PyObject *other) _PyTemplate_Concat(PyObject *self, PyObject *other)
{ {
if (_PyTemplate_CheckExact(self) && _PyTemplate_CheckExact(other)) { if (_PyTemplate_CheckExact(self) && _PyTemplate_CheckExact(other)) {
return template_concat_templates((templateobject *) self, (templateobject *) other); return template_concat_templates((templateobject *) self, (templateobject *) other);
} }
else if ((_PyTemplate_CheckExact(self)) && PyUnicode_Check(other)) {
return template_concat_template_str((templateobject *) self, other); PyErr_Format(PyExc_TypeError,
} "can only concatenate string.templatelib.Template (not \"%T\") to string.templatelib.Template",
else if (PyUnicode_Check(self) && (_PyTemplate_CheckExact(other))) { other);
return template_concat_str_template((templateobject *) other, self); return NULL;
}
else {
Py_RETURN_NOTIMPLEMENTED;
}
} }
static PyObject * static PyObject *

View file

@ -56,7 +56,6 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "pycore_pyhash.h" // _Py_HashSecret_t #include "pycore_pyhash.h" // _Py_HashSecret_t
#include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding() #include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding()
#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_template.h" // _PyTemplate_Concat()
#include "pycore_tuple.h" // _PyTuple_FromArray() #include "pycore_tuple.h" // _PyTuple_FromArray()
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI #include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
#include "pycore_unicodeobject.h" // struct _Py_unicode_state #include "pycore_unicodeobject.h" // struct _Py_unicode_state
@ -11610,16 +11609,10 @@ PyUnicode_Concat(PyObject *left, PyObject *right)
return NULL; return NULL;
if (!PyUnicode_Check(right)) { if (!PyUnicode_Check(right)) {
if (_PyTemplate_CheckExact(right)) { PyErr_Format(PyExc_TypeError,
// str + tstring is implemented in the tstring type "can only concatenate str (not \"%.200s\") to str",
return _PyTemplate_Concat(left, right); Py_TYPE(right)->tp_name);
} return NULL;
else {
PyErr_Format(PyExc_TypeError,
"can only concatenate str (not \"%.200s\") to str",
Py_TYPE(right)->tp_name);
return NULL;
}
} }
/* Shortcuts */ /* Shortcuts */

View file

@ -1834,8 +1834,8 @@ _build_concatenated_joined_str(Parser *p, asdl_expr_seq *strings,
return _PyAST_JoinedStr(values, lineno, col_offset, end_lineno, end_col_offset, p->arena); return _PyAST_JoinedStr(values, lineno, col_offset, end_lineno, end_col_offset, p->arena);
} }
static expr_ty expr_ty
_build_concatenated_template_str(Parser *p, asdl_expr_seq *strings, _PyPegen_concatenate_tstrings(Parser *p, asdl_expr_seq *strings,
int lineno, int col_offset, int end_lineno, int lineno, int col_offset, int end_lineno,
int end_col_offset, PyArena *arena) int end_col_offset, PyArena *arena)
{ {
@ -1853,7 +1853,6 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
Py_ssize_t len = asdl_seq_LEN(strings); Py_ssize_t len = asdl_seq_LEN(strings);
assert(len > 0); assert(len > 0);
int t_string_found = 0;
int f_string_found = 0; int f_string_found = 0;
int unicode_string_found = 0; int unicode_string_found = 0;
int bytes_found = 0; int bytes_found = 0;
@ -1873,7 +1872,8 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
f_string_found = 1; f_string_found = 1;
break; break;
case TemplateStr_kind: case TemplateStr_kind:
t_string_found = 1; // python.gram handles this; we should never get here
assert(0);
break; break;
default: default:
f_string_found = 1; f_string_found = 1;
@ -1882,13 +1882,13 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
} }
// Cannot mix unicode and bytes // Cannot mix unicode and bytes
if ((unicode_string_found || f_string_found || t_string_found) && bytes_found) { if ((unicode_string_found || f_string_found) && bytes_found) {
RAISE_SYNTAX_ERROR("cannot mix bytes and nonbytes literals"); RAISE_SYNTAX_ERROR("cannot mix bytes and nonbytes literals");
return NULL; return NULL;
} }
// If it's only bytes or only unicode string, do a simple concat // If it's only bytes or only unicode string, do a simple concat
if (!f_string_found && !t_string_found) { if (!f_string_found) {
if (len == 1) { if (len == 1) {
return asdl_seq_GET(strings, 0); return asdl_seq_GET(strings, 0);
} }
@ -1902,11 +1902,6 @@ _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *strings,
} }
} }
if (t_string_found) {
return _build_concatenated_template_str(p, strings, lineno,
col_offset, end_lineno, end_col_offset, arena);
}
return _build_concatenated_joined_str(p, strings, lineno, return _build_concatenated_joined_str(p, strings, lineno,
col_offset, end_lineno, end_col_offset, arena); col_offset, end_lineno, end_col_offset, arena);
} }

3884
Parser/parser.c generated

File diff suppressed because it is too large Load diff

View file

@ -351,6 +351,7 @@ expr_ty _PyPegen_collect_call_seqs(Parser *, asdl_expr_seq *, asdl_seq *,
expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok); expr_ty _PyPegen_constant_from_token(Parser* p, Token* tok);
expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok); expr_ty _PyPegen_decoded_constant_from_token(Parser* p, Token* tok);
expr_ty _PyPegen_constant_from_string(Parser* p, Token* tok); expr_ty _PyPegen_constant_from_string(Parser* p, Token* tok);
expr_ty _PyPegen_concatenate_tstrings(Parser *p, asdl_expr_seq *, int, int, int, int, PyArena *);
expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *, int, int, int, int, PyArena *); expr_ty _PyPegen_concatenate_strings(Parser *p, asdl_expr_seq *, int, int, int, int, PyArena *);
expr_ty _PyPegen_FetchRawForm(Parser *p, int, int, int, int); expr_ty _PyPegen_FetchRawForm(Parser *p, int, int, int, int);
expr_ty _PyPegen_ensure_imaginary(Parser *p, expr_ty); expr_ty _PyPegen_ensure_imaginary(Parser *p, expr_ty);

View file

@ -663,27 +663,15 @@ build_ftstring_body(asdl_expr_seq *values, bool is_format_spec)
} }
static int static int
_write_values_subarray(PyUnicodeWriter *writer, asdl_expr_seq *values, Py_ssize_t first_idx, append_templatestr(PyUnicodeWriter *writer, expr_ty e)
Py_ssize_t last_idx, char prefix, PyArena *arena)
{ {
int result = -1; int result = -1;
PyObject *body = build_ftstring_body(e->v.TemplateStr.values, false);
asdl_expr_seq *new_values = _Py_asdl_expr_seq_new(last_idx - first_idx + 1, arena);
if (!new_values) {
return result;
}
Py_ssize_t j = 0;
for (Py_ssize_t i = first_idx; i <= last_idx; ++i) {
asdl_seq_SET(new_values, j++, asdl_seq_GET(values, i));
}
PyObject *body = build_ftstring_body(new_values, false);
if (!body) { if (!body) {
return result; return -1;
} }
if (-1 != append_char(writer, prefix) && if (-1 != append_charp(writer, "t") &&
-1 != append_repr(writer, body)) -1 != append_repr(writer, body))
{ {
result = 0; result = 0;
@ -692,72 +680,6 @@ _write_values_subarray(PyUnicodeWriter *writer, asdl_expr_seq *values, Py_ssize_
return result; return result;
} }
static int
append_templatestr(PyUnicodeWriter *writer, expr_ty e)
{
PyArena *arena = _PyArena_New();
if (!arena) {
return -1;
}
Py_ssize_t last_idx = 0;
Py_ssize_t len = asdl_seq_LEN(e->v.TemplateStr.values);
if (len == 0) {
int result = _write_values_subarray(writer, e->v.TemplateStr.values,
0, len - 1, 't', arena);
_PyArena_Free(arena);
return result;
}
for (Py_ssize_t i = 0; i < len; i++) {
expr_ty value = asdl_seq_GET(e->v.TemplateStr.values, i);
// Handle implicit concat of t-strings with f-strings
if (value->kind == FormattedValue_kind) {
if (i > last_idx) {
// Create a new TemplateStr with the values between last_idx and i
// and append it to the writer.
if (_write_values_subarray(writer, e->v.TemplateStr.values,
last_idx, i - 1, 't', arena) == -1) {
goto error;
}
if (append_charp(writer, " ") == -1) {
goto error;
}
}
// Append the FormattedValue to the writer.
if (_write_values_subarray(writer, e->v.TemplateStr.values,
i, i, 'f', arena) == -1) {
goto error;
}
if (i + 1 < len) {
if (append_charp(writer, " ") == -1) {
goto error;
}
}
last_idx = i + 1;
}
}
if (last_idx < len) {
if (_write_values_subarray(writer, e->v.TemplateStr.values,
last_idx, len - 1, 't', arena) == -1) {
goto error;
}
}
_PyArena_Free(arena);
return 0;
error:
_PyArena_Free(arena);
return -1;
}
static int static int
append_joinedstr(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec) append_joinedstr(PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
{ {

View file

@ -4081,16 +4081,6 @@ codegen_template_str(compiler *c, expr_ty e)
} }
else { else {
VISIT(c, expr, value); VISIT(c, expr, value);
Py_ssize_t j;
for (j = i + 1; j < value_count; j++) {
value = asdl_seq_GET(e->v.TemplateStr.values, j);
if (value->kind == Interpolation_kind) {
break;
}
VISIT(c, expr, value);
ADDOP_INPLACE(c, loc, Add);
}
i = j - 1;
stringslen++; stringslen++;
last_was_interpolation = 0; last_was_interpolation = 0;
} }