Supporting t-strings from Python 3.14

t-strings have been merged into Python 3.14 for its beta 1 release (May 7).

Since reading the PEP and its associated example repo, I’ve been noodling about how Django could use t-strings. I am sure others are eager to think about this too. Here are some ideas, I wonder what everyone else thinks.

1. format_html() and format_html_join()

Extend format_html() and format_html_join() to support t-string templates:

from django.utils.html import format_html

format_html(t"Hello <strong>{user.name}</strong>")

This would be a natural extension to these functions. The t-string example in the Python documentation shows a theoretical html function, but provides no implementation. I think it makes sense that we’d make Django’s existing tool support this new syntax.

The documentation does show an “advanced” use case, where a t-string detects a dict argument and expands that into HTML atttributes:

attributes = {"src": "shrubbery.jpg", "alt": "looks nice"}
template = t"<img {attributes}>"
assert html(template) == '<img src="shrubbery.jpg" alt="looks nice" class="looks-nice">'

I’m not sure what others think, but I am cautious about playing with such features in Django, especially the pre-existing format_html(). We can leave the experimentation to other t-string-powered HTML packages. I think supporting the most basic use cases only is sufficient for format_html(): after all, if you want more complexity, you can reach for Django templates.

One note: Previously, we deprecated calling format_html() with one argument, because developers would often put an f-string there without correct escaping (blog post). Adding t-string to suport to format_html() would require us to allow a single-argument form but only if the given argument is a t-string Template.

2. Raw SQL

There are various ways to run raw SQL in Django:

  1. RawSQL
  2. Manager.raw() (and QuerySet.raw())
  3. connection.cursor()

It would be neat if we could support t-strings in these cases too, like:

with connection.cursor() as cursor:
    cursor.execute(t"SELECT * FROM example_book WHERE id = {id}")

I think we could use a simple implementation where we transform templated variables to placeholders, and store variables in a tuple, making the above example equivalent to:

cursor.execute("SELECT * FROM example_book WHERE id = %s", (id,))

I can see one risk here, which is that if the DB-API ends up being extended to support t-strings with some different interface, we might not be able to support that.


Those are the ideas I’ve thought about. Any other suggestions? Maybe translations?

4 Likes

We should definitely play here!

The PEP 750 examples repo has an implementation of that html() function. I don’t know how robust it is, so we’d need to go with care.

The rest of that file looks awfully like the core of a template backend (if we wanted to implement one) :thinking:

Did you not grab thedjango-t-strings PyPI name yet? :winking_face_with_tongue:

1 Like

Thanks a bunch Adam for bringing this up and Carlton for pointing me at this.

First, as catch-up: a LOT has been going on behind the scenes. Including the start of an HTML templating system. I almost have the site ready, but I…don’t. So I’ll share the news here, first time ever: tdom.

  • Written by Andreas from PyScript based on his LONG experience doing this exact thing, repeatedly, in JavaScript frontends
  • Browser-first (specifically, MicroPython friendly)
  • Tooling-friendly

But most of all: the start of a community. “Yay, another templating system.” My hope: we build a community of interoperability, tooling, and middleware. We define a standard Node interface for interchange. A standard component definition about passing props. Whatever.

On to Adam’s points. I want to start with shippable component systems and themes that can be consumed a la carte in Django and/or Jinja2. We don’t have enough high-quality design skill in Python. We should stop spreading that skill across N systems. I’d love for folks to sit in a Django template and pull in well-tested, maintained components in a natural way. Hopefully beyond the __html__ protocol.

I have a PoC for Jinja integration, where components written in a new system get parsed into AST entry points. The hope: people make high-quality, tooling-friendly components that Jinja folks can easily consume. Probably even more natural in Django, which is more friendly to new “tags”.

In fact, what if Django could help invent the new tag/component syntax? Ruff and all the Python tooling would appreciate a standard.

For SQL, Phil Jones sql-tstring that was interesting.

Then after that, there’s a LOT we could all do to bring modern web DX to all of Python. I hope to build a place where we can all discuss, find some agreements, and make a big jump, fast.

3 Likes

@pauleveritt also is on a TalkPython episode TODAY that talks more about this: Episode #505 - t-strings in Python (PEP 750) | Talk Python To Me Podcast

This is great! The maintainer of psycopg is also doing some experiments with t-strings for the psycopg.sql query building module, that I think would likely be very in line with the SQL idea you mention.

Anthony has also has a great video on t-strings.