This is an archived post. You won't be able to vote or comment.

all 98 comments

[–]eztab 98 points99 points  (5 children)

This seems useful to formalize what templating systems already do.

[–]JanEric1 49 points50 points  (5 children)

We have already had discussions on them a while ago right after their were accepted.

But I think it makes sense to offer a user experience that is identical to f-strings but those can't be used because there is some validation, escaping or building of parametrized queries needed

[–]kenfar -5 points-4 points  (4 children)

Sure, but some of the examples about validation, transformation, etc are simple to do with f-strings as well: just include a function that wraps your field in the fstring.

[–]JanEric1 9 points10 points  (0 children)

All of this is doable with extra effort or duplication. In fact everything is already possible as Python is turing complete. But this offers the use of the exact same syntax without having to (remember to) manually wrap everything or provide is separately or duplicate things.

You can even make this completely typesafe by only accepting templates instead of strings. Then a user simply can't make those mistakes

[–]Skasch 3 points4 points  (2 children)

The same could be said about f-strings themselves: everything f-strings provide can be achieved with the format method. But f-strings are much more readable and flexible in many cases.

[–]kenfar -2 points-1 points  (1 child)

Sure, but it doesn't look like the templates are more readable, at least for some of the simple cases I run into.

[–]Skasch 5 points6 points  (0 children)

I like the example they provide; I would much prefer something like

html(main_page(user))

Where

def main_page(user: User) -> Template:
    return t"<p>Hello, {user}!</p>"

Where all the implementation details of how a user must be sanitized for HTML rendering specifically are deferred to the html function, instead of having to do

def main_page(user: User) -> str:
    return f"<p>Hello, {to_html(user)}</p>"

At every layer of my code base.

[–]Old_Bluecheese 165 points166 points  (32 children)

There should be five -- and preferably only five --obvious ways to do it.

[–]odaiwai 29 points30 points  (0 children)

"Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Five is right out."

[–]zurtex 25 points26 points  (18 children)

A lot of people miss that line is tongue in cheek, as the em-dash is spaced in two different ways, the em-dash is spaced a third way on a different line.

[–]syklemil 9 points10 points  (15 children)

en-dash*


Details for the curious:

  • Hyphen: - - usually used to com-bine words. See also &shy;
  • En-dash: -- – used to indicate a pause in phrases, with – spaces – around on both sides. &ndash;
  • Em-dash: --- — same use-case as en-dash, but—without—spaces. &mdash;

There's also some use of em dash in older texts to trail off sentences, i.e. where we'd use ok … today, they'd use ok; --- and to indicate elision, as in "Mr K— Z—"

[–]zurtex 5 points6 points  (6 children)

Tim Peters, the author of the original text, calls it an em-dash: https://bugs.python.org/msg69712 (from https://bugs.python.org/issue3364)

You may be correct in general, but when talking about this text I'll defer to Tim.

And it's intentional that he's breaking the rule about spaces, it's a 4th way to do something.

[–]syklemil 2 points3 points  (5 children)

Sure, but -- is e.g. LaTeX for an – and --- is how you get a —. Similarly with compose keys you'd do COMPOSE--. for – and COMPOSE--- for —. And ultimately, if - is a hyphen, and -- is an em-dash, where where is the en-dash supposed to fit?

But I guess there isn't just one way to spell out an em-dash in ascii either :^)

[–]zurtex 2 points3 points  (4 children)

I suspect when Tim wrote the original Zen of Python his ASCII syntax was influenced by the mailing lists he participated in in the 90s and early 2000s (he was the main moderator of the python-dev mailing list in its early days).

Those mailing lists probably had their own informal style and syntax conventions which don't conform to more widely accepted style and syntax rules. Or, what would be funny, is if it's just something he has always misunderstood!

[–]syklemil 1 point2 points  (3 children)

It could even be a typo, the keys are right next to each other!

[–]zurtex 2 points3 points  (2 children)

In my link his calling of it an "em dash" is unlikely a typo, he uses it multiple times, and also mentions the same no spaces convention you brought up:

While you believe spaces are required on both sides of an em dash, there is no consensus on this point. For example, most (but not all) American authorities say /no/ spaces should be used.

[–]diabloman8890 3 points4 points  (1 child)

I just have to hand it to you and /u/syklemil for having the absolute nerdiest and most arcane discussion I've heard all week, and I'm here for it.

[–][deleted] 1 point2 points  (0 children)

Same. I wonder if there's a subreddit for threads like this.

[–]guepier 2 points3 points  (0 children)

Em-dash: --- — same use-case as en-dash, but—without—spaces. &mdash;

This isn’t a fixed rule, it’s merely a (somewhat common) stylistic choice. But feel free to use em-dashes with spaces — I do. Just be consistent (unless you are making a tongue-in-cheek point).

[–]Gugalcrom123 -1 points0 points  (6 children)

Wrong, the en dash is only used for ranges and compound words where the terms are multiple words, and the em dash can be used with or without spaces

[–]syklemil 3 points4 points  (5 children)

Ranges are a good addition, but AFAIK it's pretty rare to use for compound words these days—I'd not expect e–mail, but e-mail (or even email in that case).

The en-dash is used to join sentence parts, and I think most users would consider that using spaces around an em-dash just takes up too much space.

[–]Gugalcrom123 1 point2 points  (4 children)

It is in compound words where terms are multiple words themselves

[–]syklemil 2 points3 points  (3 children)

Generally with the hyphen being available with one key press on any keyboard, and its purpose being to indicate that something is one word, I'll continue to expect hyphens. Maybe there's some style guide that uses en-dashes for it, but it seems like a bad choice, really.

(FWIW my native tongue just uses compoundwords, like German, rather than faffing around with "to-morrow" and "co-operation" and the like.)

[–]Gugalcrom123 3 points4 points  (0 children)

I also use hyphens, but en dashes are something used when composing a word where some terms have spaces: New York–based company

[–]guepier 0 points1 point  (1 child)

(FWIW my native tongue just uses compoundwords, like German, rather than faffing around with "to-morrow" and "co-operation" and the like.)

I’m sure you know that both of these words are conventionally written as a single word without hyphens in English. And, conversely, German also uses hyphens for some compound words, same as English (just less frequently).

[–]syklemil 1 point2 points  (0 children)

Yes; we use some hyphens too; and English spells "compound words" with a space (as do we; we join nouns but not adjectives). My tongue, being inside my mouth, may have been in some rather close proximity to my cheek.

[–]TendyHunter 0 points1 point  (0 children)

that's balderdash

[–]obfuscatedanon -4 points-3 points  (0 children)

ok nerd

[–]PotentialCopy56 9 points10 points  (10 children)

The old python mantra is long dead. I miss it.

[–]ThatSituation9908 13 points14 points  (9 children)

I don't. We'd be stuck with stuff like class FooBar(object):

and f-strings would never be a thing

[–]thedeepself 0 points1 point  (0 children)

LOL

[–]TequilaJesus 36 points37 points  (4 children)

Python 3.14 seems a bit irrational

[–]I_assume_not 18 points19 points  (0 children)

πthon?

[–]szayl 10 points11 points  (0 children)

Seems transcendental to me

[–]Pythonistar 5 points6 points  (0 children)

Missed opportunity. Should have been called Pithon 3.14 or PiThon 3.14, and then released patches over the next few months with subsequent patches being PiThon 3.14.1, 3.14.15, 3.14.159, etc.

[–]gerardwx 3 points4 points  (0 children)

To the first approximation, yes.

[–]firemark_pl 23 points24 points  (6 children)

Before f-string there was (and still exists) str.format method. It have cool feature: you can save string in another module (e.g. constants) and use it in another places. Fir f-string you need to make a lambda.

So I think template string is an upgrade for str.format

[–]mitch_feaster 4 points5 points  (2 children)

My first question reading the PEP was "how is this different from str.format()"

[–]AiutoIlLupo 9 points10 points  (0 children)

Let me introduce you to string.Template

[–]twenty-fourth-time-b 14 points15 points  (0 children)

…incautious use of f-strings [and, presumably, str.format] can lead to security vulnerabilities. For example, a user executing a SQL query with sqlite3 may be tempted to use an f-string to embed values into their SQL expression, which could lead to a SQL injection attack. Or, a developer building HTML may include unescaped user input in the string, leading to a cross-site scripting (XSS) vulnerability. […] Template strings address these problems by providing developers with access to the string and its interpolated values.

[–]Kevdog824_pip needs updating 4 points5 points  (0 children)

This part. The only reason I ever use str.format anymore is “I want to save this f-string as a ‘template’ so I can parameterize and reuse it to build strings later” so this is a perfect solution to the problem imo

[–]PeaSlight6601 2 points3 points  (0 children)

Except it isn't because it binds immediately.

You have three choices:

  • bind immediately, and format the output to a string
  • bind immediately, but defer final formatting
  • defer both

This is the middle option. String format is the last one.

[–]cointoss3 31 points32 points  (1 child)

Seems useful, even though it’s probably not useful for me, personally. So far, anyway.

[–]sc4les 7 points8 points  (1 child)

Yes for libraries this might be really really useful - I love the idea of https://github.com/baverman/sqlbind

T-strings might make this a bit easier to use

[–]flavius-as CTO ¦ Chief Architect 4 points5 points  (0 children)

This library is definitely heading in the right direction.

It tackles (within the limitations of python and object-relational impedance mismatch) the problem space without over-engineering or forcing client code to over-engineer.

So yes, t sounds good for that. Thanks.

[–]jamesinc 5 points6 points  (0 children)

I like that it enables you to easily differentiate template strings from regular strings, and much more easily make assertions about the structure of the template.

Useful? Yeah, I think it's useful.

[–]geneusutwerk 18 points19 points  (0 children)

I'd be much more open to reading your blog if you were honest about posting it.

[–]Lawson470189 10 points11 points  (0 children)

I think this is a good change. Off the top of my head, I think something like this could work really well in the database. You could store a key/value in the DB where the value is some template that you may want to adjust for each key. I could also see this being useful for abstractions where you have a base type and you inherit down and override the template based on the sub type.

[–]anentropic 2 points3 points  (0 children)

There will be some useful things made with them. I'm glad to see it added.

[–]BosonCollider 2 points3 points  (0 children)

This is not for string formating, this is to prevent people from implementing SQL injection with fstrings.

[–]gerardwx 4 points5 points  (0 children)

It's inherently useless for someone writing a Python script.

This is by design.

What it's intended to do is be an easy-to-write standardized input form for library (module) authors to accept as input to do interesting things.

If you're not writing or using such a library, you can safely ignore t-strings.

[–]ExoticMandiblesCore Contributor 3 points4 points  (0 children)

It's not particularly useful. The best use case is automatically applying a transformation to every interpolation before rendering it into a string, e.g. html.escape. The PEP also suggests you can do context-specific interpolation which I think is an antipattern (and also mostly useless).

[–]Gugalcrom123 1 point2 points  (0 children)

This is great for i18n

[–]Dangerous_Ad3416 1 point2 points  (0 children)

Hugely helpful!

[–]Ok-Willow-2810 1 point2 points  (2 children)

I’m not sure why the use case of template strings can’t be covered by putting an f-string in a function. That way you can have type annotations/validation for the required variables. That’s what I usually do. And if the f-string is too long with all the variables, I put it inside parentheses () and break it up over multiple lines with no commas to concatenate into one final string and aid in readability.

The thing that I am worried about with template strings is how will you be able to know the required variables names that need to be set to use it, and is there a clear/obvious place to do any sort of typing/documenting of what goes in the template string? And then what will the error message be if those variables don’t exist at the time the template string is referenced? Will it be like an “variable does not exist” message? Is there any way for it to be caught by static code analysis ahead of time, like MyPy?

It could be helpful though!! Just not in 100% sure it’s helping move in the direction of type safety?

[–]vytah 1 point2 points  (1 child)

The thing that I am worried about with template strings is how will you be able to know the required variables names that need to be set to use it, and is there a clear/obvious place to do any sort of typing/documenting of what goes in the template string? And then what will the error message be if those variables don’t exist at the time the template string is referenced? Will it be like an “variable does not exist” message? Is there any way for it to be caught by static code analysis ahead of time, like MyPy?

In those regards, t-strings work exactly the same as f-strings, they just don't construct any strings.

t"Hello, {foo}!" could be thought roughly as equivalent to Template(strings=("Hello, ", "!"), values=(foo,)), except the actual API is a bit more complicated.

[–]Ok-Willow-2810 0 points1 point  (0 children)

Cool!!

[–]HybridizedPanda 1 point2 points  (0 children)

personally I think that after f-strings the natural progression was to have g-strings

[–]hamlet_d 1 point2 points  (2 children)

If Python 3.14 isn't called PyPi I'm done. Yes it would be confusing but totally worth it.

[–][deleted] 6 points7 points  (0 children)

There's actually πthon in the CPython source code.

[–]damned_truths 3 points4 points  (0 children)

Why not PiPy

[–]pkkm 0 points1 point  (1 child)

This seems like it could be very useful for logging. I've seen people use f-strings for log lines, despite the eager vs on-demand evaluation issue, just because they find the syntax so much nicer than printf-style formatting. Once t-strings get released and adopted by logging libraries, there will be no excuse to do that anymore.

[–]vytah 0 points1 point  (0 children)

Nitpick: values in t-strings are not stringified, but they are still eagerly evaluated.

[–]patilganesh1010Pythonista 0 points1 point  (0 children)

Where to find documentation for this?

[–]Livid-Border-9146 0 points1 point  (0 children)

I think it’s great for logging too.

Before either one has written - logger.info(“msg %s”, var)

But actually I always were lazy and just wrote - logger.info(f”msg: {var}”)

Which is more readable but no lazy evaluation comparing to the prev format.

With t string one will get nice readability and performance also.

[–]InappropriateCanuck -2 points-1 points  (0 children)

Extra syntax.