26 ms·
PEP 750 – Template Strings
- spullara 1y agoGood addition. Java has done a few previews of this and is still trying to figure out the best way. Maybe the feedback on the Python version will help.
- pphysch 1y agoThis could be a game-changer for applications that involve lots of HTML, SQL, or other languages that tend to be embedded within Python programs. Frankly, HTML templating is the worst part of Python webdev DX for me. Excited to see what libraries and tooling comes out of this.
- ohnoesjmr 1y agoWhats wrong with jinja?
- pphysch 1y agoJinja et al. are fine. But they still forces me to choose Python OR HTML. I have to jump between files and LSPs. t-strings pave the way for true Python+HTML seamless DX.
- paulddraper 1y agoThese developer ergonomics reduce injection 90% in my experience. When concatenating strings is the harder approach, it is really beautiful.
- neilv 1y agoAgreed, although this doesn't work as well for HTML or SQL that doesn't fit on one line. Since having everything in a string literal is a little messy, and doesn't have the benefit of autoindent, syntax coloring, etc. This is one place where s-expressions of Lisp make embedding these DSLs syntactically easier. To borrow the PEP's HTML examples: #lang racket/base (require html-template) (define evil "<script>alert('evil')</script>") (html-template (p (% evil))) ; <p><script>alert('evil')</script></p> (define attributes '((src "shrubbery.jpg") (alt "looks nice"))) (html-template (img (@ (%sxml attributes)))) ; <img src="shrubbery.jpg" alt="looks nice"> You can see how the parentheses syntax will practically scale better, to a larger and more complex mix of HTML and host language expressions. (A multi-line example using the normal text editor autoindent is on "https://docs.racket-lang.org/html-template/ https://docs.racket-lang.org/html-template/".)
- davepeck 1y ago> Agreed, although this doesn't work as well for HTML or SQL that doesn't fit on one line. PEP 750 t-strings literals work with python's tripe-quote syntax (and its lesser-used implicit string concat syntax): lots_of_html = t""" <div> <main> <h1>Hello</h1> </main> </div> """ My hope is that we'll quickly see the tooling ecosystem catch up and -- just like in JavaScript-land -- support syntax coloring and formatting specific types of content in t-strings, like HTML.
- neilv 1y agoYeah, editor support will help a lot. Though the PEP's way of doing things doesn't specify the language of the template where the literal occurs, so detection of that might have to be kludged. (Or, in a sufficiently working program, an editor with semantic analysis access could use something like type inference in the Python side, to determine the language in a less-kludgey way.)
- davepeck 1y ago> doesn't specify the language of the template where the literal occurs Yeah, something we spent a bunch of time considering. In the end, we decided it probably needed to stay out of scope for the PEP. You're right that JavaScript has an easier time here. Most of the JS tools we looked at simply inspect the name of the tag and if it's (say) html, they attempt to color/format string content as HTML regardless of what the html() function actually does or the string's contents. Currently, tools like black have no knowledge of types. I'm guessing some amount of kludging is to be expected on day one. But my hope is over the long term, we'll see a story emerge for how annotations can indicate the expected content type.
- kazinator 1y agoBut that will not apply proper HTML escaping to the evil script element, allowing injection, and allows tags not to be closed: lots_of_html = t""" <div> <main> <p>{evil}>/p> <main> </span> """
- kstrauser 1y agoMost excellent! I love f-strings and replaced all the various other string interpolation instances in my code with them, but they have the significant issue that you can't defer evaluating them. For instance, you can write: >>> template = 'Hello, {name}' >>> template.format(name='Bob') 'Hello, Bob' Until this, there wasn't a way to use f-strings formatting without interpolating the results at that moment: >>> template = f'Hello, {name}' Traceback (most recent call last): File "<python-input-5>", line 1, in <module> template = f'Hello, {name}' ^^^^ NameError: name 'name' is not defined It was annoying being able to use f-strings almost everywhere, but str.format in enough odd corners that you have to put up with it.
- ossopite 1y agoI'm not sure if t-strings help here? unless I misread the PEP, it seems like they still eagerly evaluate the interpolations. There is an observation that you can use `lambda` inside to delay evaluation of an interpolation, but I think this lambda captures any variables it uses from the context.
- tylerhou 1y agoYou could do something like t”Hello, {“name”}” (or wrap “name” in a class to make it slightly less hacky).
- notpushkin 1y ago> An early version of this PEP proposed that interpolations should be lazily evaluated. [...] This was rejected for several reasons [...] Bummer. This could have been so useful: statement_endpoint: Final = "/api/v2/accounts/{iban}/statement" (Though str.format isn’t really that bad here either.)
- LordShredda 1y agoWould be useful in that exact case, but would be an absolute nightmare to debug, on par with using global variables as function inputs
- simonw 1y agoI'm excited about this. I really like how JavaScript's tagged template literals https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... can help handle things like automatic HTML escaping or SQL parameterization, it looks like these will bring the same capability to Python.
- davepeck 1y agoYes! PEP 750 landed exactly there: as a pythonic parallel to JavaScript's tagged template strings. I'm hopeful that the tooling ecosystem will catch up soon so we see syntax coloring, formatting of specific t-string content types, etc. in the future.
- its-summertime 1y agoI just wish it didn't get Pythonified in the process, e.g. needing to be a function call because backtick is hard to type on some keyboards, nearly having a completely separate concept of evaluating the arguments, etc. x`` vs x(t'') is a 2x blowup in terms of line-noise at worst.
- itishappy 1y agoI don't think that's the reason. https://peps.python.org/pep-0750/#arbitrary-string-literal-prefixes https://peps.python.org/pep-0750/#arbitrary-string-literal-p...
- its-summertime 1y agothat gets solved by using a different quoting character for template literals. https://discuss.python.org/t/pep-750-tag-strings-for-writing-domain-specific-languages/60408/57 https://discuss.python.org/t/pep-750-tag-strings-for-writing... > Backticks are traditionally banned from use in future language features, due to the small symbol. No reader should need to distinguish ` from ' at a glance. It’s entirely possible that the prevailing opinion on this has changed, but it’s certainly going to be easier to stick to the letter prefixes and regular quotes. and earlier in the thread the difficulty in typing it for some is cited as another reason
- unsnap_biceps 1y agoThis is super exciting but I wish they added a function to render the template to a string rather then having everyone write their own version of basic rendering.
- davepeck 1y agoOne place we landed with PEP 750 is that Template instances have no natural string rendering. That is, you must process a Template in some way to get a useful string out the other side. This is why Template.__str__() is spec'd to be the same as Template.__repr__(). If you want to render a Template like an f-string for some reason, the pep750 examples repo contains an implementation of an `f(template: Template) -> str` method: https://github.com/davepeck/pep750-examples/blob/main/pep/fstring.py https://github.com/davepeck/pep750-examples/blob/main/pep/fs... This could be revisited, for instance to add `Template.format()` in the future.
- unsnap_biceps 1y agoI totally agree with `Template.__str__()` == `Template.__repr__()`, but I would strongly be for adding a `Template.format()`. I expect that over time I'll be passed template strings where I expected normal strings and adding that example `f(template: Template)` everywhere to my code bases is just a lot of repeated code for no real value. That said, I think this is a great bit of work and I look forward to getting to use it! Thank you!
- zahlman 1y ago> and adding that example `f(template: Template)` everywhere to my code bases is just a lot of repeated code for no real value. I'd agree that it should be somewhere within the library, even if it's just a separate top-level function in the described `string.templatelib`. If it isn't, I'm sure someone will make a PyPI package for it right away.
- deleted 1y ago[deleted]
- 1y ago
- ydnaclementine 1y agocan't wait to have my linter tell me I should be using t-strings instead of f-strings. apologies for not being patient enough to read through this to find it, but I hope they can remove f-strings: > There should be one-- and preferably only one --obvious way to do it.
- itishappy 1y agoThey do different things. You can implement f-strings in t-strings, but it's extra work. The obvious way is therefore: Use f-strings if you can, otherwise use t-strings.
- gnfedhjmm2 1y agoJust use ChatGPT to change it.
- behnamoh 1y agoIs it a replacement for Jinja2 templates? I use them a lot in LLM pipelines (e.g., to fill in the system prompt and provide more context).
- netghost 1y agoIf you just need value replacement yes. But you could also already do that with str.format. I think this gives you slightly more control before interpolating. If you want control flow inside a template, jinja and friends are probably still useful.
- nhumrich 1y agoNo. Not really intended to be a replacement for jinja. But it also depends on how you use jinja. If you use only very basic functionality of jinja, maybe.
- jt_b 1y agodon't see any looping expression functionality in the PEP
- ratorx 1y agoI’m not convinced that a language level feature is worth it for this. You could achieve the same thing with a function returning an f-string no? And if you want injection safety, just use a tag type and a sanitisation function that takes a string and returns the type. Then the function returning the f-string could take the Sanitised string as an argument to prevent calling it with unsanitised input. I guess it’s more concise, but differentiating between eager and delayed execution with a single character makes the language less readable for people who are not as familiar with Python (especially latest update syntax etc). EDIT: to flesh out with an example: class Sanitised(str): # init function that sanitises or just use as a tag type that has an external sanitisation function. def sqltemplate(name: Sanitised) -> str: return f”select * from {name}” # Usage sqltemplate(name=sanitise(“some injection”)) # Attempt to pass unsanitised sqltemplate(name=“some injection”) # type check error
- vjerancrnjak 1y agoIt's worse than function returning an f-string. Template type is very flat, you won't know which arguments are left unbound. modules, classes, protocols, functions returning functions, all options in Python, each work well for reuse, no need to use more than 2 at once, yet the world swims upstream.
- shikon7 1y agoIf its only use is to make injecton safety a bit easier to achieve, it's worth it to me.
- 1y ago
- throwawayffffas 1y agoSo we are well on our way to turning python to PHP. Edit: Sorry I was snarky, its late here. I already didn't like f-strings and t-strings just add complexity to the language to fix a problem introduced by f-strings. We really don't need more syntax for string interpolation, in my opinion string.format is the optimal. I could even live with % just because the syntax has been around for so long. I'd rather the language team focus on more substantive stuff.
- turtledragonfly 1y ago> turning python to PHP. Why stop there? Go full Perl (: I think Python needs more quoting operators, too. Maybe qq{} qq() q// ... [I say this as someone who actually likes Perl and chuckles from afar at such Python developments. May you get there one day!]
- tdeck 1y agoQuoting operators are something I actually miss in Python whereas t-strings are something I have never wanted in 17 years of writing Python.
- nhumrich 1y agoPretty sure PHP does not have this feature. Can you give me an example?
- fshr 1y agoI believe that jab was that PHP has a bunch of ways to do similar things and Python, in their view, is turning out that way, too.
- throwawayffffas 1y agoOn a more philosophical level php is this feature. At least as it was used originally and how it's mostly used today. PHP was and is embedded in html code. If you have a look at a wordpress file you are going to see something like this: <?php ... ?><some_markup>...<? php ... ?><some_more_markup here>...
- sgarland 1y agoAm I missing something, or is this a fancier string.Template [0]? Don't get me wrong, it looks very useful, especially the literals. [0]: https://docs.python.org/3/library/string.html#template-strings https://docs.python.org/3/library/string.html#template-strin...
- zahlman 1y agostring.Template does fundamentally the same sort of thing as str.format, or str.__mod__ (i.e. the % operator on strings). It was in a sense a prototype for str.format, introduced way back in Python 2.4 (https://peps.python.org/pep-0292/ https://peps.python.org/pep-0292/). It does create an instance of a separate type, but it neither pre-parses the string (the substitution is implemented using regexes that reprocess the original string each time) nor eagerly evaluates the values to substitute. Further, in order to change how the formatting is done, you'd have to subclass (with the new system, you just write a function that accepts the Template as a parameter).
- sgarland 1y agoThank you for the historical context and details!
- btilly 1y agoI dislike this feature. The stated use case is to avoid injection attacks. However the primary reason why injection attacks work is that the easiest way to write the code makes it vulnerable to injection attacks. This remains true, and so injection attacks will continue to happen. Templates offer to improve this by adding interpolations, which are able to do things like escaping. However the code for said interpolations is now located at some distance from the template. You therefore get code that locally looks good, even if it has security mistakes. Instead of one source of error - the developer interpolated - you now have three. The developer forgot to interpolate, the developer chose the wrong interpolation, or the interpolation itself got it wrong. We now have more sources of error, and more action at a distance. Which makes it harder to audit the code for sources of potential error. This is something I've observed over my life. Developers don't notice the cognitive overhead of all of the abstractions that they have internalized. Therefore over time they add more. This results in code that works "by magic". And serious problems if the magic doesn't quite work in the way that developers are relying on. Templates are yet another step towards "more magic". With predictable consequences down the road.
- gnfedhjmm2 1y agoIt’s kinda like saying += is magic. Yeah the best kind.
- sodality2 1y agoThat's such a small scope of an addition I don't see the comparison. I suppose overloading arbitrary types with it can sometimes make the actions performed opaque.
- davepeck 1y agoI'm not sure I agree with this analysis. Template.__str__() is equivalent to Template.__repr__(), which is to say that these aren't f-strings in an important sense: you can't get a useful string out of them until you process them in some way. The expectation is that developers will typically make use of well-established libraries that build on top of t-strings. For instance, developers might grab a package that provides an html() function that accepts Template instances and returns some Element type, which can then be safely converted into a string. Stepping back, t-strings are a pythonic parallel to JavaScript's tagged template strings. They have many of the same advantages and drawbacks.
- nhumrich 1y agoNick Humrich here, the author who helped rewrite PEP 501 to introduce t-strings, which was the foundation for this PEP. I am not an author on this accepted PEP, but I know this PEP and story pretty well. Let me know if you have any questions. I am super excited this is finally accepted. I started working on PEP 501 4 years ago.
- _cs2017_ 1y agoThank you! Curious what options for deferred evalution were considered and rejected? IMHO, the main benefit of deferred evaluation isn't in the saving of a bit of code to define a deferred evaluation class, but in standardazing the API so that anyone can read the code without having to learn what it means in each project. Also: were prompt templates for LLM prompt chaining a use case that influenced the design in any way (examples being LangChain and dozens of other libraries with similar functionlity)?
- nhumrich 1y agoOne solution that existed for a while was using the `!` operator for deferred. `t!'my defered {str}'` The main reason for non having deferred evaluation was that it over-complicated the feature quite a bit and introduces a rune. Deferred evaluation also has the potential to dramatically increase complexity for beginners in the language, as it can be confusing to follow if you dont know what is going on. Which means "deferred by default" wasnt going to be accepted. As for LLM's, it was not the main consideration, as the PEP process here started before LLM's were popular.
- _cs2017_ 1y agoAh interesting, so the complexity wasn't in the API design or implementation, but only in the additional rune? Is that really such a big cost?
- davepeck 1y ago> were prompt templates for LLM prompt chaining a use case that influenced the design in any way Maybe not directly, but the Python community is full of LLM users and so I think there's a general awareness of the issues.
- pjmlp 1y agoYet another way to do strings in Python, I was more than happy with the original way with tupple parameters.
- meisel 1y agoAside from sanitization, this also allows replication of Ruby’s %W[…] syntax
- AlienRobot 1y agoThanks but I still use "%s" % (a,) the way I learned a dozen years ago and I'll keep doing it until the day I die.
- nhumrich 1y agoGood for you.
- pgjones 1y agoIf you want to see a usage for this I've built, and use, [SQL-tString](https://github.com/pgjones/sql-tstring https://github.com/pgjones/sql-tstring) as an SQL builder.
- actinium226 1y agoSo does this mean that any place where code exists that looks for `type(someinstance) == str` will break because the type will be `Template` even though `someinstance` could still be used in the following code?
- nhumrich 1y agoyes. t-strings are not `str`
- wruza 1y agoAs a python meh-er, this is actually good design. Everyone is jumping on C that it has no strings, but then other languages throw raw strings at you with some interpolation and call it a day. Also it's 2025 and people will still comment "do we need such a bloated string mechanism" and then watch new devs produce bulks of injectionable strings.
- fmajid 1y agoThese templates don’t seem to be semantically aware like Go’s html/template that takes care of mitigating XSS for you, among other things.
- nhumrich 1y agoCorrect. Intended for library authors to do that. A SQL library, for example, could accept a template type and mitigate against SQL injection for you.
- pansa2 1y agoPutting aside template strings themselves for the moment, I'm stunned by some of the code in this PEP. It's so verbose! For example, "Implementing f-strings with t-strings": def f(template: Template) -> str: parts = [] for item in template: match item: case str() as s: parts.append(s) case Interpolation(value, _, conversion, format_spec): value = convert(value, conversion) value = format(value, format_spec) parts.append(value) return "".join(parts) Is this what idiomatic Python has become? 11 lines to express a loop, a conditional and a couple of function calls? I use Python because I want to write executable pseudocode, not excessive superfluousness. By contrast, here's the equivalent Ruby: def f(template) = template.map { |item| item.is_a?(Interpolation) ? item.value.convert(item.conversion).format(item.format_spec) : item }.join
- the-grump 1y agoThis is how Python has always been. It's more verbose and IMO easier to grok, but it still lets you create expressive DSLs like Ruby does. Python has always been my preference, and a couple of my coworkers have always preferred Ruby. Different strokes for different folks.
- pansa2 1y ago> This is how Python has always been. Nah, idiomatic Python always used to prefer comprehensions over explicit loops. This is just the `match` statement making code 3x longer than it needs to be.
- the-grump 1y agoYou can express the loop as a list comprehension, and I would too. As for the logic, I would still use pattern matching for branching and destructuring, but I’d put it in a helper. More lines is not a negative in my book, though I admit the thing with convert and format is weird.
- 1y ago
- metadat 1y agoWhat kind of special string will be added next? We already have f-strings, .format, %s ...
- spankalee 1y agoMaintainer of lit-html here, which uses tagged template literals in JavaScript extensively. This looks really great! It's almost exactly like JavaScript tagged template literals, just with a fixed tag function of: (strings, ...values) => {strings, values}; It's pretty interesting how what would be the tag function in JavaScript, and the arguments to it, are separated by the Template class. At first it seems like this will add noise since it takes more characters to write, but it can make nested templates more compact. Take this type of nested template structure in JS: html`<ul>${items.map((i) => html`<li>${i}</li>`}</ul>` With PEP 750, I suppose this would be: html(t"<ul>{map(lambda i: t"<li>{i}</li>", items)}</ul>") Python's unfortunate lambda syntax aside, not needing html() around nested template could be nice (assuming an html() function would interpret plain Templates as HTML). In JavaScript reliable syntax highlighting and type-checking are keyed off the fact that a template can only ever have a single tag, so a static analyzer can know what the nested language is. In Python you could separate the template creation from the processing possibly introduce some ambiguities, but hopefully that's rare in practice. I'm personally would be interested to see if a special html() processing instruction could both emit server-rendered HTML and say, lit-html JavaScript templates that could be used to update the DOM client-side with new data. That could lead to some very transparent fine-grained single page updates, from what looks like traditional server-only code.
- davepeck 1y ago> assuming an html() function would interpret plain Templates as HTML Agreed; it feels natural to accept plain templates (and simple sequences of plain templates) as HTML; this is hinted at in the PEP. > html(t"<ul>{map(lambda i: t"<li>{i}</li>", items)}</ul>") Perhaps more idiomatically: html(t"<ul>{(t"<li>{i}</li>" for i in items)}</ul>") > syntax highlighting and type-checking are keyed off the fact that a template can only ever have a single tag Yes, this is a key difference and something we agonized a bit over as the PEP came together. In the (very) long term, I'm hopeful that we see type annotations used to indicate the expected string content type. In the nearer term, I think a certain amount of "clever kludginess" will be necessary in tools like (say) black if they wish to provide specialized formatting for common types. > a special html() processing instruction could both emit server-rendered HTML and say, lit-html JavaScript templates that could be used to update the DOM client-side with new data I'd love to see this and it's exactly the sort of thing I'm hoping emerges from PEP 750 over time. Please do reach out if you'd like to talk it over!
- ic_fly2 1y agoIn the past when I needed this I just made a function that processed the f string, often enough a simple lambda function would do. This looks like additional complexity for not a lot of gain.
- nhumrich 1y agoYou cant process an f-string the same way you can process a t-string. An f-string does not preserve which parts of it are static and dynamic, which you need to know to properly escape user input.
- bhargavtarpara 1y agoprob dont need jinja anymore then
- SuperV1234 1y agoThis is a great PEP. Very similar to what I wanted to achieve with my P1819 for C++, back in 2019: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1819r0.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p18...
- callamdelaney 1y agoI like this but am still not a fan of the constant adding of things to the language. It’s starting to feel like a language designed by committee, which it basically is.
- Daishiman 1y agoIt's a feature that removes the need for hundreds of specialized template classes in various frameworks and is a generalization of an existing feature in a way that doesn't add much in the way of grammar. The net complexity reduction is a win here.
- pansa2 1y ago> It’s starting to feel like a language designed by committee, which it basically is. That's exactly what it is. It's just that they use the word "council" instead of "committee".
- smitty1e 1y agoPython is released annually and having some new juice helps advertise the language. Whether or not this is technically a swift call is in the eye of the beholder.
- cbmask 1y ago[dead]
- whoiscroberts 1y agoWhy should I use this instead of Template class from string
- mortar 1y ago> If a single interpolation is expensive to evaluate, it can be explicitly wrapped in a lambda in the template string literal I’m having trouble understanding this - Can someone please help out with an example use case for this? It seems like before with an f string we had instant evaluation, now with a t string we control the evaluation, why would we further delay evaluation - Is it just to utilise running a function on a string first (i.e. save a foo = process(bar) line?)
- smitty1e 1y agoSo that you can calculate the string in A.py and then use it over in B.py .
- cstrahan 1y ago> It seems like before with an f string we had instant evaluation, now with a t string we control the evaluation You don't completely control the evaluation. From the PEP: > Template strings are evaluated eagerly from left to right, just like f-strings. This means that interpolations are evaluated immediately when the template string is processed, not deferred or wrapped in lambdas. If one of the things you are interpolating is, as a silly example, an invocation of a slow recursive fibonacci function, the template string expression itself (resulting in a Template object) will take a long while to evaluate.
- mortar 1y ago> the template string expression itself (resulting in a Template object) will take a long while to evaluate Are you saying that calling: template = t”{fib_slow()}” Will immediately run the function, as opposed to when the __str__ is called (or is it because of __repr__?) - Apparent I might just have to sit down with the code and grok it that way, but thanks for helping me understand!
- davepeck 1y agoYes, fib_slow() will be evaluated immediately, just like it would be if you were using an f-string.
- deleted 1y ago[deleted]
- Mawr 1y agoIt's fascinating how differently languages approach the string formatting design space. - Java's been trying to add f/t-strings, but its designers appear to be perfectionists to a fault, unable to accept anything that doesn't solve every single problem possible to imagine: [1]. - Go developers seem to have taken no more than 5 minutes considering the problem, then thoughtlessly discarded it: [2]. A position born from pure ignorance as far as I'm concerned. - Python, on the other hand, has consistently put forth a balanced approach of discussing each new way of formatting strings for some time, deciding on a good enough implementation and going with it. In the end, I find it hard to disagree with Python's approach. Its devs have been able to get value from first the best variant of sprintf in .format() since 2008, f-strings since 2016, and now t-strings. [1]: https://news.ycombinator.com/item?id=40737095 https://news.ycombinator.com/item?id=40737095 [2]: https://github.com/golang/go/issues/34174#issuecomment-1450932232 https://github.com/golang/go/issues/34174#issuecomment-14509...
- oliwarner 1y agoIt's especially weird how hard people have to fight for string interpolation given it has had implementations since the 1970s. Even PEP 498 (fstrings) was a battle.
- bjourne 1y agoSuperficially f-strings reminds you of php and everyone remembers how awful that was. But Python's implementation is leagues better and we also have better tooling (ie smart parsers) for handling fstrings.
- dionian 1y agoLooks great - unlike java which is somehow recommending the format: STR."Hello \{this.user.firstname()}, how are you?\nIt's \{tempC}°C today!" compared to scala s"Hello ${this.user.firstname()}, how are you?\nIt's ${tempC}°C today!" STR."" ? really?
- nsonha 1y agoalso a syntax for braces that looks like escaping
- smitty1e 1y agoFor a quick glance, I didn't see the corner case where the old-style `%` interpolation (may) shine over PEP750: building up a JSON document with a dictionary: >>> hello_world = {"hello":"HELL" ,"world":"O'WORLD"} >>> json_template='{"hello":"%(hello)s","world":"%(world)s"}' >>> print(json_template % hello_world) {"hello":"HELL","world":"O'WORLD"}
- sakesun 1y agoI always think the coming of this feature is inevitable.
- _el1s7 1y agoFeels unnecessary... Can't think of a good reason now on why I would need this rather than just a simple f-string. Any unsafe string input should normally be sanitized before being added in a template/concatenation, leaving the sanitization in the end doesn't seem like the best approach, but ok.
- stavros 1y agoYeah, I generally like most of the stuff that goes into Python, but I have to admit I can't really see the usefulness in this (or, at least, enough usefulness to warrant making it standard). One of the PEP's developers, Lysandros, presented this in our local meetup, so I am passingly familiar with it, but still, I might be missing something. I guess the crux of it is that I don't understand why it's `t"some string"` instead of `Template("some string")`. What do we gain by the shorthand?
- _el1s7 1y agoI guess this could be useful in SQL for escaping values, I saw a project on the comments here. With templates: mysql.execute(t"DELETE FROM table WHERE id={id} AND param1={param1}") Without templates: mysql.execute("DELETE FROM table WHERE id=%s AND param1=%s", [id, param1]) So one less argument to pass if we use templates. But yeah it does seem a bit confusing, and maybe kinda not pythonic? Not sure.
- zahlman 1y agoSQL templating is one of the explicitly cited motivating use cases.
- zahlman 1y ago> What do we gain by the shorthand? Because it's new syntax, it allows for parsing the literal ahead of time and eagerly evaluating the substitutions. Code like bar = 42 spam = t"foo {bar*bar} baz" essentially gets translated into bar = 42 spam = Template("foo ", Interpolation(bar*bar), " baz") That is: subsequent changes to `bar` won't affect the result of evaluating the template, but that evaluation can still apply custom rules.
- apothegm 1y agoAre these less useless for translation than f-strings?
- epistasis 1y agoI'm quite disappointed with the pace of development of the Python language. Five years of support for a particular version seems far too short. I mostly use Python in scientific contexts, and hitting end-of-life after five years means that for a lot project, code needs to transition language versions in the middle of a project. Not to mention the damage to reproducibility. Once something is marked "end of life" it means that future OS versions are going to have a really good reason to say "this code shouldn't even be able to run on our new OS." Template strings seem OK, but I would give up all new language features in a heartbeat to get a bit of long term support.
- anonylizard 1y agoYou can pay for long term support from private companies. And your scientific context is a distinct minority for python now. Most new development for python is for data/AI. Considering LLMs get updated every quarter, and depreciated every year, there is no appetite for code that doesn't get updated for 5 years.
- epistasis 1y agoHonestly it's very weird to have data/AI be outside of the scientific context! They both used to be a subset of scientific Python. The code will be updated over five years, but there's no need to be on continual version churn on the underlying language. And frankly I'm surprised that it's tolerated so widely in the community. Trying to run a Node project from 5 years ago is often an exercise in futility, and it will be a big shame when/if that happens to Python.
- infamia 1y agoYou can use uv to pin, download, and use a specific version of a Python interpreter on a per project basis. You shouldn't use your OS provided Python interpreter for long running projects for all the reasons you mentioned, including reproducibility. If you insist on using the vendor provided interpreter, then use RHEL (or a free clone) or any other long suppported Linux distro (there are many). Five years is a very long time in technology. It is not reasonable to expect more from core maintainers IMO. Especially considering we have better options.
- ratmeadow 1y agoCould someone explain more why this should be a language feature? My understanding of template strings is they are like f-strings but don't do the interpolation bit. The name binding is there but the values are not formatted into the string yet. So effectively this provides a "hook" into the stringification of the interpolated values, right? If so, this seems like a very narrow feature to bake into the language... Personally, I haven't had issues with introducing some abstraction like functions or custom types to do custom interpolation.
- rcxdude 1y agoYou can see some motivations for it further down the document. Basically it allows libraries to access the ease of use of f-strings for more than just formatting a string according to what the language allows. Structured logging is one area where I would like to use this.
- duped 1y agoIt's useful for doing things like writing parameterized SQL queries that avoid injection attacks. Or anything that quacks like that proverbial duck, where you are going to emit some plaintext and need to validate it as it is filled.
- yoru-sulfur 1y agoIt reminds me of javascripts template literals (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...) and .nets FormattableString (https://learn.microsoft.com/en-us/dotnet/api/system.formattablestring?view=net-9.0 https://learn.microsoft.com/en-us/dotnet/api/system.formatta...) The best use case I know of for these kinds of things is as a way to prevent sql injection. SQL injection is a really annoying attack because the "obvious" way to insert dynamic data into your queries is exactly the wrong way. With a template string you can present a nice API for your sql library where you just pass it "a string" but it can decompose that string into query and arguments for proper parameterization itself without the caller having to think about it.
- anonylizard 1y agoBecause python dominates AI, and python is dominating because of AI. And prompting really, really benefits from f-strings and templated strings. LLMs as a whole means the rise of unstructured data, and flexible string manipulation is really important for handling that.
- throwaway7783 1y agoMaking t-strings not actual strings but a different type is the genius here. It is now be a generic expression evaluator and a template rendered!
- est 1y ago> If a single interpolation is expensive to evaluate, it can be explicitly wrapped in a lambda in the template string literal > https://peps.python.org/pep-0750/#approaches-to-lazy-evaluation https://peps.python.org/pep-0750/#approaches-to-lazy-evaluat... Hmm, I have a feeling there's a pitfall.
- kazinator 1y ago15 minute implementation in TXR Lisp. Background: TXR already Lisp has quasi-string-literals, which are template strings that do implicit interpolation when evaluated. They do not produce an object where you can inspect the values and fixed strings and do things with these before the merge. 1> (let ((user "Bob") (greeting "how are you?")) `Hello @user, @greeting`) "Hello Bob, how are you?" The underlying syntax behind the `...` notation is the sys:quasi expression. We can quote the quasistring and look at the car (head symbol) and cdr (rest of the list): 2> (car '`Hello @user, @greeting`) sys:quasi 3> (cdr '`Hello @user, @greeting`) ("Hello " @user ", " @greeting) So that is a bit like f-strings. OK, now with those pieces, I just right now made a macro te that gives us a template object. 4> (load "template") nil You invoke it with one argument as (te <quasistring>) 5> (let ((user "Bob") (greeting "how are you?")) (te `Hello @user, @greeting`)) #S(template merge #<interpreted fun: lambda (#:self-0073)> strings #("Hello " ", ") vals #("Bob" "how are you?")) 6> *5.vals #("Bob" "how are you?") 7> *5.strings #("Hello " ", ") 8> *5.(merge) "Hello Bob, how are you?" 9> (set [*5.vals 0] "Alice") "Alice" 10> *5.(merge) "Hello Alice, how are you?" You can see the object captured the values from the lexical variables, and we can rewrite them, like changing Bob to Alice. When we call the merge method on the object, it combines the template and the values. (We cannot alter the strings in this implementation; they are for "informational purposes only"). Here is how the macro expands: 11> (macroexpand-1 '(te `Hello @user, @greeting`)) (new template merge (lambda (#:self-0073) (let* ((#:vals-0074 #:self-0073.vals) (#:var-0075 [#:vals-0074 0]) (#:var-0076 [#:vals-0074 1])) `Hello @{#:var-0075}, @{#:var-0076}`)) strings '#("Hello " ", ") vals (vec user greeting)) It produces a constructor invocation (new template ...) which specifies values for the slots merge, strings and vals. The initialization of strings is trivial: just a vector of the strings pulled from the quasistring. The vals slot is initialized by a `(vec ...)` call whose arguments are the expressions from the quasistring. This gets evaluated in the right lexical scope where the macro is expanded. This is how we capture those values. The most complicated part is the lambda expression that initializes merge. This takes a single argument, which is the self-object, anonymized by a gensym variable for hygiene. It binds the .vals slot of the object to another gensym lexical. Then a genyms local variable is bound for each value, referencing into consecutive elements of the value vector. E.g. #:var-0075 is bound to [#:vals-0074 0], the first value. The body of the let is a transformed version of the original template, in which the interpolated expressions are replaced by gensyms, which reference the bindings that index into the vector. The complete implementation in template.tl (referenced by (load "template") in command line 4) is: (defstruct template () merge strings vals) (defun compile-template (quasi) (match (@(eq 'sys:quasi) . @args) quasi (let ((gensyms (build-list)) (exprs (build-list)) (strings (build-list)) (xquasi (build-list '(sys:quasi))) (self (gensym "self-")) (vals (gensym "vals-"))) (while-true-match-case (pop args) ((@(eq 'sys:var) @(bindable @sym)) exprs.(add sym) (let ((g (gensym "var-"))) gensyms.(add g) xquasi.(add g))) ((@(eq 'sys:expr) @expr) exprs.(add expr) (let ((g (gensym "expr-"))) gensyms.(add g) xquasi.(add g))) (@(stringp @str) strings.(add str) xquasi.(add str)) (@else (compile-error quasi "invalid expression in template: ~s" else))) ^(new template merge (lambda (,self) (let* ((,vals (qref ,self vals)) ,*[map (ret ^(,@1 [,vals ,@2])) gensyms.(get) 0]) ,xquasi.(get))) strings ',(vec-list strings.(get)) vals (vec ,*exprs.(get)))))) (defmacro te (quasi) (compile-template quasi)) We can see an expansion: That Lisp Curse document, though off the mark in general, was right the observation that social problems in languages like Python are just technical problems in Lisp (and often minor ones). In Python you have to wait for some new PEP to be approved in order to get something that is like f-strings but gives you an object which intercepts the interpolation. Several proposals are tendered and then one is picked, etc. People waste their time producing rejected proposals, and time on all the bureucracy in general. In Lisp land, oh we have basic template strings already, let's make template objects in 15 minutes. Nobody else has to approve it or like it. It will backport into older versions of the language easily. P.S. I was going to have the template object carry a hash of those values that are produced by variables; while coding this, I forgot. If we know that an interpolation is @greeting, we'd like to be access something using the greeting symbol as a key. (I don't see any of this is as useful, so I don't plan on doing anything more to it. It has no place in Lisp, because for instance, we would not take anything resembling this approach for HTML generation, or anything else.)
- ray_v 1y agoWait, I wasn't supposed to be using f strings in SQL queries?! (don't @ me)
- oftenwrong 1y agoThis appears similar to Java's String Template preview feature that was withdrawn: https://openjdk.org/jeps/465 https://openjdk.org/jeps/465 This is probably the best overview of why it was withdrawn: https://mail.openjdk.org/pipermail/amber-spec-experts/2024-April/004106.html https://mail.openjdk.org/pipermail/amber-spec-experts/2024-A...
- wodenokoto 1y agoI've been toying with the idea of having arbitrary string types, like sql"SELECT FROM ..." or re"\d\d[abc]" that the development environment could highlight properly, that would ... I don't know. In the end t and f string don't do anything that a t() and f() function couldn't have done, except they are nice. So it would be nice to have more.
- zahlman 1y agoIIRC, previous versions of the PEP wanted things to work this way. You might also be interested in the "syntactic macros" PEP (https://peps.python.org/pep-0638/ https://peps.python.org/pep-0638/).
- eviks 1y agoWith the proliferation of string type prefixes will they add one that represents a r#""# real raw string capable of ending with a backslash?
- DonHopkins 1y agoI wonder what fun The Amazing David Beazley thinks PEP 750 t-strings are? I recently asked him: -- Hi David! I am a huge long time fan of SWIG and your numerous epic talks on Python. I remember watching you give a kinda recent talk where you made the point that it’s a great idea to take advantage of the latest features in Python, instead of wasting your time trying to be backwards compatible. I think you discussed how great f-strings were, which I was originally skeptical about, but you convinced me to change my mind. I’ve googled around and can’t find that talk any more, so maybe I was confabulating, or it had a weird name, or maybe you’ve just given so many great talks I couldn’t find the needle in the haystack. What made me want to re-watch and link my cow-orkers to your talk was the recent rolling out of PEP 701: Syntactic formalization of f-strings, which makes f-strings even better! Oh by the way, do you have any SWIG SWAG? I’d totally proudly wear a SWIG t-shirt! -Don -- He replied: Hi Don, It was probably the "Fun of Reinvention". https://www.youtube.com/watch?v=js_0wjzuMfc https://www.youtube.com/watch?v=js_0wjzuMfc If not, all other talks can be found at: https://www.dabeaz.com/talks.html https://www.dabeaz.com/talks.html As for swag, I got nothing. Sorry! Cheers, Dave -- Thank you! This must be some corollary of rule 34: https://www.swigwholesale.com/swig-swag https://www.swigwholesale.com/swig-swag (Don’t worry, sfw!) -Don -- The f-strings section starts at 10:24 where he's live coding Python on a tombstone with a dead parrot. But the whole talk is well worth watching, like all his talks! https://youtu.be/js_0wjzuMfc?t=624 https://youtu.be/js_0wjzuMfc?t=624
- otabdeveloper4 1y agoAw sweet, a third way to do string interpolation in Python. I'm really loving this lovecraftian space the "batteries included" and "one obvious way to do it" design philosophy brought us!
- chaz6 1y agoI like the implementation, but it looks like nobody has pointed out that Python already has a built-in string template system [1]. Granted, it's not quite as simple to use, but I have been using it for a while. [1] https://docs.python.org/3/library/string.html#template-strings https://docs.python.org/3/library/string.html#template-strin... edit: this was mentioned by milesrout in https://news.ycombinator.com/item?id=43649607 https://news.ycombinator.com/item?id=43649607
- casenmgreen 1y agoI read a fair part of the doc, from the start, I wanted to see how I would use t-strings in code; in the sense of, I know how I use f-strings now, in Python code, and I wanted to understand in the same way, how I would use t-strings in code. I have not understood how I would use t-strings.
- stonecharioteer 1y agoSame here. I'm trying to understand why I'd use this instead of Jinja templates.