Skip to content(if available)orjump to list(if available)

Python's new t-strings

Python's new t-strings

55 comments

·April 21, 2025

sashank_1509

Why does this need to be a language feature. This could just be a separate library, we could use brackets instead of a letter before a string. I fear, Python is going down the path of C++

jsnell

It being a language feature gives you controlled access to the lexical scope, such that the template string can refer to variables by name rather than having to pass each value as a parameter. Doing it via parameters is repetitive and/or error-prone.

TekMol

Will this allow neat SQL syntax like the following?

    city = 'London'
    min_age = 21
    # Find all users in London who are 21 or older:
    users = db.get(t'
        SELECT * FROM users
        WHERE city={city} AND age>{min_age}
    ')
If the db.get() function accepts a template, it should, right?

This would be the nicest way to use SQL I have seen yet.

fweimer

The SQLite extension for Tcl offers something similar:

    db1 eval {INSERT INTO t1 VALUES(5,$bigstring)} 
https://sqlite.org/tclsqlite.html#the_eval_method

jbaiter

Thanks, I hate it. While it's nice syntactic sugar, the difference between an SQL injection vulnerability and a properly parametrized query is now a single letter that's easily missed

JimDabell

The t-string produces a Template object without a __str__() method. You can’t mistakenly use an f-string in its place. Either the code expects a string, in which case passing it a Template would blow it up, or the code expects a Template, in which case passing it a string would blow it up.

baggiponte

Type checkers to the rescue ahaha I think db.get could also raise if the type does not match?

TekMol

I guess that is a misunderstanding on your side, about how templates work. Less hate and more love might help to avoid this type of hotheaded misconception ;-)

Why do you think changing a letter would cause a vulnerability? Which letter do you mean?

hyperbovine

OP is referring to swapping t with f.

codesnik

f'' vs t'' probably.

null

[deleted]

gnabgib

Big discussion (414 points, 10 days ago, 324 comments) https://news.ycombinator.com/item?id=43647716

runekaagaard

It feels a bit like "cheating" that new x-string features are built-in only. It would be cool to be able to do:

    from foo import bar
    bar"zoop"

masklinn

A t-string is a literal for a Template object which is a data hoilder, it doesn't actually do anything, so you would simply call

    bar(t"zoop")

thund

Use a function?

    bar(“zoop”)
It’s just syntax, like we used to have

    print “foo” 
that later became

    print(“foo”)

Timwi

True. Then you could use

  sql"..."
  html"..."
for each of the given examples and achieve some runtime type safety.

sgt

Will it be a performance boost if Django's template engine started using t-strings internally?

nezirus

Maybe this could be useful to libraries like psycopg3 to use something more simple/natural instead of this:

https://www.psycopg.org/psycopg3/docs/api/sql.html

(while I also agree it gets crowded with yet another string prefix)

dtech

That's the exact use case. Basically these are syntactic sugar for a very similar function signature.

pizza

Looks useful for embedding interpreters

enescakir

Not sure about introducing yet another string prefix. Between f-strings, raw strings, and i18n stuff, it’s already getting crowded. Curious how readable this will be in large codebases.

wodenokoto

I'm of the opposite opinion. Let's set the prefixes free!

    from sql import sql

    query = sql"SELECT user_id, user_name FROM {user_table_versioned} WHERE user_name = {user_name}"

dmurray

How would this be different from a function sql() that operates on one of these new t-strings?

The syntactic sugar of changing it from sql(t"...") doesn't seem particularly valuable. The novel thing about t-strings is that they change the parsing at compile-time.

Timwi

> The syntactic sugar of changing it from sql(t"...") doesn't seem particularly valuable.

It's valuable because:

- IDEs could then syntax-highlight SQL inside of SQL strings and HTML inside of HTML strings

- You can't accidentally pass an HTML string to your SQL library

stavros

It wouldn't be different, but it would be more convenient because we no longer have to count the number of %s, you'd put the variable inline.

mcintyre1994

This is how JavaScript does it with tagged template literals.

Your sql there would just be a function that receives the array of strings/values and returns whatever.

albert_e

"Yet another" is not my main worry

The concept of prefixes itself feels a little deviant from readable code that is close to human language -- which is the spirit of Python

empiko

Additionally, it will probably be confusing that it is called a t-string but it is actually a constructor for a Template object and not string at all. I would rather see a new special term `template` than this.

Timwi

The single letter f or t does make it unnatural to read, but if it were sql"..." or html"...", I think that would help with that.

mulmboy

Are there string prefixes for i18n stuff?

Biganon

They're probably talking about the convention of using _ as an alias for `translate`

oulipo

Nice, so it's a kind of "limited DSL" inside python that's easy to extend

avereveard

what's the tldr difference between this and .format ?

jitl

It’s injection safe and compostable, and the resulting object retains the original values you interpolate in. This makes it useful for building SQL queries with prepared arguments for example.

hk__2

It’s on a meta level: instead of formatting the string, it returns an object that contains both the format string and its argument. Library author can then implement whatever format function they want, for example one that escapes the interpolated strings.

earnestinger

It’s custom .format implementation. (You access the placeholder and value and produce the string)

7734128

Sure, this avoids issues with SQL injections. However, I have a hard time imagining any developer who would both make such fundamental errors with f-strings currently and also switching to this option when it ships.

Seems like a self selection which renders this meaningless, to some extent :/

masklinn

> However, I have a hard time imagining any developer who would both make such fundamental errors with f-strings currently and also switching to this option when it ships.

t-strings are a different type (both static and dynamic), f-strings are not. So t-strings can be mandated at the API level, forcing the developer into "proper" usage.

That is, you need third-party tools to differentiate between

    some_call("safe literal string")
and

    some_call(f"unsafe dynamically created string")
That is not the case when it comes to t-strings, `some_call` can typecheck internally that it got a t-string, and reject regular strings entirely.

Although some space probably needs to be made for composing t-strings together in case of e.g. runtime variation in items or their count. Facetting for instance. I don't know if that's a native affordance of t-strings.

Timwi

But that would require any SQL library you're currently using to make the breaking change of no longer allowing strings.

baggiponte

sqlalchemy doesn’t really accepts strings - if you do, you need to pass them into a “conn.execute(text(…))”, so end users should not face a breaking change.

damnitbuilds

I enjoy f-strings, I guess some people need these.

And I love Python but, having been through 2->3 ( occasionally still going through it! ) whenever I see a new language feature my first thought is "Thank goodness it doesn't break everything that went before it".

stavros

Yeah but it's been 17 years, maybe it's time to put the PTSD behind us. We're almost at a point where the current generation of programmers wasn't even programming when that happened.

rglullis

> We're almost at a point where the current generation of programmers wasn't even programming when that happened

I've been programming with Python since 2006, I think most of the systems were based on 2.4 at the time. I've been one of those who switched to Python 3 somewhat late, waiting for some major libraries to ship python 3 packages - celery and Twisted were one of the biggest holdouts - so I remember that the first project where all my dependencies were ready for python 3 was around 2015.

This is to say: even seasoned developers who were conservative around the migration have spent more time working with Python 3 than Python 2. There simply is no reason anymore to be talking about python 2.

nightfly

Python2 code didn't disappear when Python3 came out. At my work we're _still_ occasionally having to help people migrate code that was written for python2