Revisiting older conversations about Template concatenation, my read is that there are roughly three different perspectives. From most to least permissive:
-
The developer experience view. Reflected in the current PEP and implementation. A
Templateliteral looks like any other (f-)string. Python developers expect string-like values to combine with other string-like values. Prohibiting explicit or implicit concatenation ofTemplateandstrwould lead to needless surprise/frustration. (A variant holds that implicit concatenation can often be a footgun, so we’ll avoid extending it further.) -
The security view. Templates are a language level feature to help prevent injection vulnerabilities.
Template.stringsis synonymous with “trusted”;Template.interpolationswith “untrusted”. Concatenating aTemplatewith an arbitrarystris unsafe because we can’t know thestr’s trust level, so the operation should be disallowed. Developers can mark a string as trusted withTemplate(my_str)or untrusted withTemplate(Interpolation(my_str)). The current PEP treats allstras trusted by default; from the security view, a clear footgun. -
The DSL view. Templates are building blocks for domain-specific languages. Interesting template processing code parses template content against some backing grammar. Some grammars allow concatenating conforming strings; others do not. As a result, even allowing
Template + Templateis probably a mistake. Code that processes aTemplatecan instead return a domain type with__add__/__radd__when concatenation is appropriate, or rely on other composition mechanisms.
I suppose these views aren’t so far apart: if you take a more restrictive view than what ships in 3.14, you can configure a lint rule. Likewise, if you take a more permissive view, you can write a tiny helper function. For what it’s worth, I personally align with bucket (2): I think it’s likely to strike the right balance for the 80% case and lead to the least overall dev confusion + grumblingly extra configured lint rules. ![]()