WEBVTT

00:00:00.020 --> 00:00:04.280
Python has many string formatting styles, which have been added to the language over the years.

00:00:04.740 --> 00:00:13.160
Early Python used the percent operator to inject formatted values into strings, and we have string.format, which offers several powerful styles.

00:00:14.000 --> 00:00:21.000
Both were verbose and indirect, so fstrings were added in Python 3.6, but these fstrings lacked security features.

00:00:21.360 --> 00:00:22.240
Think about little bobby tables.

00:00:22.800 --> 00:00:26.380
And they manifested as fully formed strings to run timecode.

00:00:26.820 --> 00:00:31.740
Today, we talk about the next evolution of Python string formatting for advanced use cases.

00:00:32.380 --> 00:00:34.920
Think SQL, HTML, DSLs, and so on.

00:00:35.520 --> 00:00:35.860
t-strings.

00:00:37.060 --> 00:00:42.960
We have Paul Everitt, Dave Peck, and Jim Baker on the show to introduce the upcoming language feature.

00:00:43.600 --> 00:00:48.100
This is Talk Python To Me, episode 505, recorded May 13th, 2025.

00:00:49.400 --> 00:00:49.700
Are

00:00:49.700 --> 00:00:51.080
you ready for your host, please?

00:00:52.040 --> 00:00:54.800
You're listening to Michael Kennedy on Talk Python To Me.

00:00:55.480 --> 00:00:58.500
Live from Portland, Oregon, and this segment was made with Python.

00:01:24.960 --> 00:01:30.880
Subscribe to our YouTube channel over at talkpython.fm/youtube and get notified about upcoming shows.

00:01:31.480 --> 00:01:35.180
This episode is sponsored by Posit Connect from the makers of Shiny.

00:01:35.740 --> 00:01:39.540
Publish, share, and deploy all of your data projects that you're creating using Python.

00:01:40.140 --> 00:01:46.200
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, Reports, Dashboards, and APIs.

00:01:47.140 --> 00:01:48.760
Posit Connect supports all of them.

00:01:48.920 --> 00:01:54.360
Try Posit Connect for free by going to talkpython.fm/Posit, P-O-S-I-T.

00:01:55.160 --> 00:01:57.200
And it's brought to you by Auth0.

00:01:57.900 --> 00:02:02.160
Auth0 is an easy-to-implement, adaptable authentication and authorization platform.

00:02:02.960 --> 00:02:09.380
Think easy user logins, social sign-on, multi-factor authentication, and robust role-based access control.

00:02:09.759 --> 00:02:14.700
With over 30 SDKs and quick starts, Auth0 scales with your product at every stage.

00:02:15.300 --> 00:02:20.840
Get 25,000 monthly active users for free at talkpython.fm/auth0.

00:02:22.000 --> 00:02:26.160
Dave, Jim, and Paul, welcome to all three of you to Talk Python To Me.

00:02:26.720 --> 00:02:27.380
Paul, welcome back.

00:02:27.840 --> 00:02:29.520
Dave and Jim, welcome for the first time.

00:02:29.610 --> 00:02:30.220
Awesome to have you here.

00:02:30.420 --> 00:02:30.680
Thanks.

00:02:31.180 --> 00:02:31.380
Thanks.

00:02:31.740 --> 00:02:37.080
We are going to carry on an amazing tradition of Python string formatting.

00:02:38.120 --> 00:02:39.540
We've had the percent.

00:02:40.440 --> 00:02:41.360
We've had the plus.

00:02:41.960 --> 00:02:43.360
We've had the dot format.

00:02:44.000 --> 00:02:44.940
We've had the dot join.

00:02:45.540 --> 00:02:49.260
Everyone was very excited about fstrings back in 3.6 when they came out.

00:02:49.420 --> 00:02:58.020
And now, after quite a bit of work from the three of you and others, we're going to be talking about PEP 750 t-strings.

00:02:58.660 --> 00:02:58.800
Amazing.

00:02:59.160 --> 00:03:01.040
So congratulations on all of that.

00:03:01.460 --> 00:03:02.640
I think it's going to be a lot of fun.

00:03:02.960 --> 00:03:09.660
People may be surprised to think that, oh, there's an entire episode with three whole people on string formatting.

00:03:10.100 --> 00:03:10.280
Yes.

00:03:10.640 --> 00:03:11.120
Yes, that's right.

00:03:11.280 --> 00:03:12.180
There's actually a lot to it.

00:03:12.700 --> 00:03:15.840
This is a pretty rich API and a pretty big advancement.

00:03:16.220 --> 00:03:18.060
So we're going to dive into that.

00:03:18.280 --> 00:03:20.500
But before we do, how about a round of introductions?

00:03:21.280 --> 00:03:21.340
Dave?

00:03:21.860 --> 00:03:23.140
Hey, I'm Dave Peck.

00:03:23.300 --> 00:03:27.840
I'm a freelance software developer based in sunny Seattle, Washington.

00:03:28.640 --> 00:03:31.860
A freelance software developer is a polite way of saying I'm a computer weirdo.

00:03:32.240 --> 00:03:38.720
And I get to get into hopefully all kinds of good computer mischief, like working with Jim and Paul on new peps for Python.

00:03:39.080 --> 00:03:39.320
Awesome.

00:03:39.650 --> 00:03:40.320
Great to have you here.

00:03:40.480 --> 00:03:44.060
And always good to have another person from the Pacific Northwest.

00:03:44.800 --> 00:03:45.560
Jim, welcome.

00:03:45.920 --> 00:03:46.080
Hi.

00:03:46.540 --> 00:03:49.220
So, yeah, I'm a longtime Python user.

00:03:50.050 --> 00:04:00.300
I used to be very actively involved in the Jython project, which let me really learn the internals of both Jython and CPython, since we had to maintain a compatible implementation.

00:04:01.860 --> 00:04:06.480
So, you know, got deep into things like compilers and the standard library.

00:04:07.019 --> 00:04:13.100
And, yeah, it's great to talk about template strings or t-strings with you today.

00:04:14.040 --> 00:04:15.280
Paul, welcome back.

00:04:15.580 --> 00:04:17.200
How many episodes have you been on? Many.

00:04:17.620 --> 00:04:20.900
I don't know. Not enough, because Brett's ahead of me.

00:04:21.200 --> 00:04:21.799
I know.

00:04:22.420 --> 00:04:27.060
Brett Cannon, you're not allowed to be on Talk Python for five years until I catch up, and then you can come back.

00:04:28.060 --> 00:04:33.960
Well, you should also send your message to Anthony Shaw, who I think is currently on the leaderboard, number one.

00:04:34.240 --> 00:04:34.720
He's been on a lot.

00:04:34.780 --> 00:04:35.000
Yeah,

00:04:35.120 --> 00:04:39.420
and then you were talking with Chris McDonough, who was like episode three or something

00:04:39.420 --> 00:04:40.160
like that way

00:04:40.160 --> 00:04:40.860
back in the day.

00:04:41.620 --> 00:04:43.280
Briefly, I'm Paul. Hi, I'm Paul.

00:04:44.080 --> 00:04:47.960
I am now head of developer advocacy at JetBrains, the PieCharm people.

00:04:48.230 --> 00:04:51.400
I make coffee for Jim and Dave on this project.

00:04:52.620 --> 00:05:02.380
I let Jim do all the big thinking, and I let Dave go out and explain to complainers that we heard the same complaints for f-strings back in the day.

00:05:02.860 --> 00:05:04.660
So this has been a super fun

00:05:04.660 --> 00:05:05.500
project to be on.

00:05:05.500 --> 00:05:10.280
Yeah, it's very exciting, and I'm super interested to dive into it.

00:05:10.540 --> 00:05:12.300
Something I didn't really know a lot about.

00:05:12.840 --> 00:05:13.960
Let's dive into it, right?

00:05:14.060 --> 00:05:18.300
what are t-strings and why do they exist?

00:05:20.000 --> 00:05:21.000
Weren't f-strings good enough?

00:05:21.240 --> 00:05:23.420
I mean, they look kind of like f-strings when we use them.

00:05:23.800 --> 00:05:25.940
Yeah, that's a great part about them, right?

00:05:25.940 --> 00:05:27.100
They look like f-strings.

00:05:27.580 --> 00:05:39.260
They behave a lot like f-strings in terms of technical issues like the scoping of the interpolation expressions, but they're not f-strings in a very profound way.

00:05:40.100 --> 00:05:54.100
One of the things that we wanted to prevent was stuff like, you know, the classic XKCD comic about the Bobby Tables sequel injection, okay, where it becomes a drop tables inadvertently.

00:05:54.480 --> 00:05:55.520
And we can get, yeah, exactly.

00:05:56.000 --> 00:05:58.000
The traditional exploits of a mom.

00:05:58.540 --> 00:06:00.600
This is your son's skull calling.

00:06:00.980 --> 00:06:02.340
Did he break something well in a way?

00:06:02.660 --> 00:06:02.880
Yeah.

00:06:03.160 --> 00:06:11.020
And so this is, you know, the original language of the PEP actually referenced directly Bobby Tables in this comic.

00:06:11.080 --> 00:06:17.059
For whatever reason, we removed that, but we don't have to do that today in this conversation.

00:06:17.940 --> 00:06:19.820
It was a big driver for this work.

00:06:20.060 --> 00:06:23.480
And the reason is that f-strings are great.

00:06:23.820 --> 00:06:33.500
They're great for formatting, but they're also sort of a trap, I think, for people to use because it's you're formatting things, everything is working.

00:06:33.820 --> 00:06:45.780
and, oh, by the way, you just introduced the HTML injection or SQL injection, or you just happen to have something that doesn't format properly because you didn't consider a particular corner case.

00:06:46.180 --> 00:07:03.180
What we really wanted to do was move a lot of that out of the usual user experience and into a library that could go, some library, something for HTML, something for SQL, something for some other domain-specific language that would allow it to go and handle that appropriately.

00:07:03.780 --> 00:07:07.900
We'll get into more about what that means, I guess, throughout this conversation.

00:07:08.180 --> 00:07:09.720
But that's really where we started.

00:07:09.780 --> 00:07:14.020
We wanted the simplicity of using f-strings.

00:07:14.580 --> 00:07:18.200
We can get into more about what that means, but without some of the hazards.

00:07:18.720 --> 00:07:20.480
Excellent. Excellent introduction.

00:07:20.940 --> 00:07:23.300
And it's important that this is part of the language.

00:07:23.680 --> 00:07:26.880
It's officially accepted in Python 3.14.

00:07:27.500 --> 00:07:30.980
Oh, will the jokes overflow about Py, this version of Python,

00:07:31.860 --> 00:07:31.980
when

00:07:31.980 --> 00:07:32.400
it comes

00:07:32.400 --> 00:07:32.960
out in the fall?

00:07:33.060 --> 00:07:34.460
It's a memorative edition of Python.

00:07:35.040 --> 00:07:36.040
We're so glad to get into it.

00:07:36.540 --> 00:07:37.200
That's right.

00:07:37.320 --> 00:07:38.140
It's super cool.

00:07:39.080 --> 00:07:40.800
So it's going to be part of the language.

00:07:41.360 --> 00:07:51.740
And the reason I bring this up is it seems like it needs to be part of the language, not just an external library, to really take advantage of it and not be just some API.

00:07:51.890 --> 00:08:00.320
You construct strings in some weird string builder way, but through literally just, quote, start typing stuff, that has to be part of the language, right?

00:08:00.560 --> 00:08:01.740
Yeah, that's exactly right.

00:08:01.980 --> 00:08:04.700
I mean, as Jim mentioned, f-strings are wonderful.

00:08:04.940 --> 00:08:06.420
The syntax is powerful, it's robust.

00:08:06.930 --> 00:08:09.240
It became very popular because of that.

00:08:09.890 --> 00:08:16.680
But of course, there are these foot guns of using them for, you know, SQL and HTML where you might run into some injection problems.

00:08:17.880 --> 00:08:28.660
With t-strings, the key difference is that while f-strings evaluate to the standard string type, t-strings evaluate to a new type that's shipping in Python 3.14 later this year called template.

00:08:29.480 --> 00:08:37.380
And so while they look like the literal form looks like any other string, they're quite different in that respect.

00:08:37.560 --> 00:08:48.920
And the key thing about the template type is it lets you, as a programmer, decide what parts of the string were statically authored and what parts of the string were dynamically substituted or interpolated.

00:08:49.360 --> 00:08:53.100
And so just being able to do that as a language feature is a really big deal.

00:08:53.160 --> 00:08:55.380
It's not something you can do with fstrings directly.

00:08:55.940 --> 00:09:00.460
And once you know those things, you can start to say, okay, this is a SQL statement.

00:09:00.780 --> 00:09:07.520
Maybe I need to do something with the interpolation like escape its value before I construct the final string that I'm going to send over to my database.

00:09:07.980 --> 00:09:08.400
Yeah.

00:09:08.640 --> 00:09:13.560
If somebody were to work with an F string, it does the cool interpolation at runtime.

00:09:14.320 --> 00:09:22.900
However, the thing that your API or any sort of code gets access to is just the resulting string that comes out.

00:09:23.040 --> 00:09:27.960
And I think this is probably the biggest piece of understanding that people need to wrap their heads around.

00:09:28.440 --> 00:09:31.560
And we'll dive into some of the things people do need to wrap their heads around and how it's different.

00:09:31.920 --> 00:09:38.200
But instead of getting just here's the string result, which who knows why it says --drop table there.

00:09:38.580 --> 00:09:39.960
Maybe that's what they want.

00:09:40.540 --> 00:09:41.080
You don't know.

00:09:41.760 --> 00:09:41.880
Right?

00:09:42.020 --> 00:09:44.200
But here you would have here's part of the string.

00:09:44.660 --> 00:09:47.640
And then here's a place where a value goes with a name.

00:09:47.760 --> 00:09:52.780
And that thing, if you look it up by name, its value is --drop tables.

00:09:53.020 --> 00:09:55.520
well, maybe we'll escape that or whatever.

00:09:55.680 --> 00:09:56.240
You know what I mean?

00:09:56.980 --> 00:09:57.180
Right.

00:09:57.260 --> 00:10:01.620
You and Brian covered it twice in Python Bytes.

00:10:01.780 --> 00:10:08.180
And the first time, I'm going to exaggerate a little bit, but Brian was like, what is this?

00:10:08.880 --> 00:10:08.980
Why

00:10:08.980 --> 00:10:09.440
do we need it?

00:10:09.820 --> 00:10:10.660
We have enough, right?

00:10:11.280 --> 00:10:13.820
The second time, Brian was like, oh, I get it now.

00:10:14.520 --> 00:10:15.600
There's going to be a lot of that, Paul.

00:10:15.680 --> 00:10:16.480
And we certainly

00:10:16.480 --> 00:10:17.480
will have some explaining

00:10:17.480 --> 00:10:17.920
to you.

00:10:17.920 --> 00:10:18.720
Yeah, there's going to be a lot of this.

00:10:18.820 --> 00:10:20.200
As Dave said, being able

00:10:20.200 --> 00:10:20.840
to get at this.

00:10:21.140 --> 00:10:21.280
Sorry.

00:10:21.640 --> 00:10:21.780
What's that?

00:10:21.840 --> 00:10:23.100
Sorry, there's going to be a lot of that.

00:10:23.240 --> 00:10:24.740
There's going to be a lot of like, we already have f-strings.

00:10:24.980 --> 00:10:25.440
What is this?

00:10:25.980 --> 00:10:26.320
Yep, sure.

00:10:26.640 --> 00:10:28.080
An F string gives you a string.

00:10:28.500 --> 00:10:29.860
A template string gives you a template.

00:10:30.180 --> 00:10:31.700
And then later you will get a string.

00:10:32.160 --> 00:10:34.420
Later means going through a function.

00:10:34.900 --> 00:10:37.900
They can go and do cool things for you like escape values.

00:10:38.460 --> 00:10:41.020
Or you're doing an HTML templating system.

00:10:41.440 --> 00:10:48.040
Pass it a dictionary and it will generate a style attribute with some policies along the way.

00:10:48.160 --> 00:10:54.520
pass it a false or a Boolean and it will elide the entire thing or do all kinds of other things.

00:10:54.900 --> 00:11:05.220
Having the template structure that Dave described gives business value DSLs a chance to step in and operate on structure rather than strings.

00:11:05.440 --> 00:11:23.480
So when I write this code, I say value equals T, some interpolated string sort of thing. Is it interoperable with a string in the sense that if I have a function that expects a string, could I pass it? Much like path from pathlib kind of goes where string file names go,

00:11:23.900 --> 00:11:24.600
or does

00:11:24.600 --> 00:11:27.200
the thing working with it need to know, no,

00:11:27.200 --> 00:11:27.580
it needs

00:11:27.580 --> 00:11:34.400
to be transformed, like you say, like process this to a SQL statement or process this to an HTML fragment, and then you get a string.

00:11:34.640 --> 00:11:47.680
Yeah, and for templates, the answer is, very intentionally, they are not the same as strings. And so library authors are going to have to update their SQL libraries and their HTML libraries to expect this new template type.

00:11:49.019 --> 00:11:50.180
And that's very intentional.

00:11:50.860 --> 00:11:54.420
The other thing I would say is that templates don't have a direct route to strings.

00:11:54.580 --> 00:12:00.000
So if you call str of template, you just get the default repper back, right?

00:12:00.180 --> 00:12:05.020
Which is probably not something you want to be showing your users or it's not HTML that you're trying to render.

00:12:06.120 --> 00:12:06.320
And that,

00:12:06.520 --> 00:12:08.740
again, is very intentional.

00:12:08.940 --> 00:12:12.340
It's also unlikely to cause injection attacks and whatnot as well.

00:12:12.700 --> 00:12:13.180
you're getting that

00:12:13.180 --> 00:12:18.900
reference. So much, you know, all those parentheses and braces and whatnot, not likely to happen.

00:12:19.220 --> 00:12:40.760
Yeah, absolutely. And so library authors will have to update their libraries to add new methods that expect this template type. And presumably those methods won't also take a string. You know, if you're concerned about injection vulnerabilities, presumably you want to take this new dynamic template type that tracks what parts of the string are interpolated and what parts came from the developer themselves.

00:12:41.320 --> 00:13:01.620
Right. And treat them differently. Yeah. Yeah. Very interesting. So let's just talk about that a little bit. I see some use cases in the chat, but maybe we could list out some more. So for example, Nym4 says structured logging, preventing cross-site scripting, SQL injection. What are some of the motivations here?

00:13:01.780 --> 00:13:08.960
Yeah, those are all great use cases. Any type of injection vulnerability. So SQL and HTML were obviously at the top of our minds.

00:13:09.180 --> 00:13:19.840
But another one that, in fact, there's potentially a new PEP coming around for that's being discussed right now on the Python discussion boards has to do with command injection.

00:13:20.120 --> 00:13:25.260
So when you shell out, you're worried about the user-provided values there as well.

00:13:26.580 --> 00:13:27.120
Give me a sec.

00:13:27.220 --> 00:13:28.900
I'll try and remember what the actual PEP

00:13:28.900 --> 00:13:29.440
number is.

00:13:29.520 --> 00:13:30.920
There's os.system,

00:13:31.520 --> 00:13:31.780
and then

00:13:31.780 --> 00:13:34.760
you can give it a command like echo some string from the user.

00:13:35.200 --> 00:13:40.760
But it could, of course, be some string, quote, ampersand, ampersand, rm-rf,

00:13:41.360 --> 00:13:42.500
or something like that, right?

00:13:44.040 --> 00:14:01.780
And of course, as Paul mentioned, once you have the tools to help you prevent injection vulnerabilities in sort of a more general way in this general purpose fashion, you also have the ability to do all kinds of other fun and powerful things like what Paul mentioned with HTML, which is, hey, I don't just want to write, you know, a single attribute.

00:14:02.020 --> 00:14:06.760
I want to give you a dictionary and you write 20 attributes based on the contents of my Python dictionary.

00:14:08.140 --> 00:14:10.500
And there's no reason you can't do that now, which is exciting.

00:14:11.760 --> 00:14:11.960
Yeah, I

00:14:11.960 --> 00:14:12.600
would add to that

00:14:12.600 --> 00:14:26.940
if you wanted to, for example, do some interesting complex analytical SQL, where you're using common table expressions, or even you just want to go and have your analytical SQL.

00:14:27.200 --> 00:14:30.760
I want to be able to do it on this column name versus some other column name.

00:14:30.800 --> 00:14:33.860
We want to make it possible to do that in a safe fashion.

00:14:34.460 --> 00:14:42.940
These are things that are difficult to express with some of the typical wrappers that we might use with SQL, like SQLAlchemy.

00:14:43.680 --> 00:14:48.560
Maybe like, I don't think you can really do like CTEs and Django query sets and whatnot.

00:14:48.630 --> 00:14:53.100
But what I'm trying to say is that both of those tools do allow you to access raw SQL.

00:14:53.690 --> 00:14:57.240
But now you need to be able to compose that raw SQL in a way that's safe.

00:14:57.620 --> 00:15:06.760
and uses the information about where this interpolation is happening in order to properly put it in place.

00:15:06.880 --> 00:15:19.980
So if you are in a situation where maybe that CTE is something you can access, you can go and say, okay, I can insert it into the SQL or maybe a little more common usage here for a lot of people.

00:15:20.000 --> 00:15:22.040
I don't know how many people use common table expressions.

00:15:22.580 --> 00:15:22.960
I do.

00:15:22.960 --> 00:16:01.140
is to go and, you know, I just want to, again, select from this particular table name or this particular column name in a safe fashion. Or I want to go and use values that can't be properly expressed with placeholders. But maybe if I use like a, you know, a temporary table to insert it in. These are all techniques that we use when we do this sort of work. But now we have this opportunity to bring it into directly in the library and then make it possible for users to use it without really having to think, where did this come from? How do I go and make this such that it can be used in a where in clause, for example, that sort of thing.

00:16:02.460 --> 00:17:10.480
This portion of Talk Python To Me is brought to you by the folks at Posit. Posit has made a huge investment in the Python community lately. Known originally for our studio, they've been building out a suite of tools and services for Team Python. Today, I want to focus on hosting your Python-based data science workloads. This includes dashboards, reports, plots, interactive web apps, all the way to custom Flask and Django apps. Their service is Posit Connect. Posit Connect makes it easy for data scientists to share work built with Python code. If you have a Streamlit app, Dash, dashboard, Plotly interactive plot, a FastAPI service, or even a Quarto report, just give Posit Connect the code it needs to maintain the asset and Connect automatically does the rest. Connect will manage your APIs and serve your interactive apps for you. And if you want, you can update your reports and dashboards on a scheduled basis. That's right. No need to explain to the stakeholders why that dashboard or plot stopped updating last week. You get to focus on your data science and leveraging your skill set while Connect makes you look good, keeping your code running and private.

00:17:11.339 --> 00:17:26.420
With Connect, you get a private URL on your Connect server, ensuring that your asset is continuously available to your shareholders. And you can control which users have access to the asset. Let Posit Connect handle the delivery and DevOps involved in sharing your work.

00:17:26.839 --> 00:17:48.540
You focus on what you do best. So if you work on a data science team, you owe it to you and your org to check out Posit Connect. Visit talkpython.fm/connect today and get a three-month free trial to see if it's a good fit. That's talkpython.fm/connect. The link is in your podcast player show notes. Thank you, Deposit, for supporting Talk Python To Me.

00:17:50.440 --> 00:18:18.820
If I may, real quick, I got to give Jim a lot of credit on this as we've gone through the years on this. And Jim taught programming languages at University of Colorado Boulder. And so he knows all these patterns and all these languages and the implementations from his Jython years and stuff. And even now, he's still talking in chat about, hey, you know, Last year he was talking about how domain-specific languages could apply to Pandas and other data science stuff.

00:18:18.840 --> 00:18:26.960
And now he's talking about LLM interactions and chat and stuff like that, where you've got a little language, but it's got a little bit of structure.

00:18:27.580 --> 00:18:36.840
And what if you could, and then 500, you know, Discord messages later, Jim has invented something new and interesting.

00:18:38.120 --> 00:18:50.680
So it seems to me like there's also some interesting possibilities for like lazy evaluation for also some possibilities for like query optimization.

00:18:50.900 --> 00:19:01.160
You know, Jim was talking about databases, but there's a lot of different things, both from a performance perspective, but also potentially just from a tightly versus loosely coupled systems.

00:19:01.780 --> 00:19:08.260
Like maybe you could say something to the effect of like, and the username goes here, but that part of the system logging doesn't know what the username is.

00:19:08.660 --> 00:19:17.780
But somehow as the template gets rendered, the additional pieces could get filled in with real details, like the IP and the username is actually this, because over here we know.

00:19:17.840 --> 00:19:23.020
But when the logging happened, we got a template that just said, well, whatever you figure out, it goes here.

00:19:23.280 --> 00:19:24.720
How much of those things are possible now?

00:19:24.900 --> 00:19:37.960
Like, could you look at the structure bit of SQL and say, well, if we reorder this, or if we put something about, you know, some kind of SQL hint or something about indexes or other things in there, could you maybe get more from it?

00:19:38.140 --> 00:19:41.800
I was thinking someone might else answer this, but I will go for this.

00:19:42.040 --> 00:19:43.560
I mean, I think that's possible to do.

00:19:45.420 --> 00:19:56.500
In terms, you know, often we, you know, again, if we're thinking about SQL, we would, you know, dispatch this to, you know, some sort of, you know, the internal optimizer and, you know, whatever database you're working with.

00:19:56.800 --> 00:20:25.900
But, you know, again, the, you know, again, using the SQL example, you know, there's, there are multiple ways that you could potentially interface some, you know, more interesting, complicated ways of interacting with SQL that are kind of hard to, you know, either you can't express easily with placeholders, you know, the standard name placeholders or positional placeholders in SQL, or just, you know, basically you find people going composing SQL.

00:20:26.440 --> 00:20:28.480
and, you know, doing it in a bad way.

00:20:28.900 --> 00:20:42.120
But if you were to go and, you know, there's like a popular tool called SQL Glock, for example, that allows you to take SQL, re-express it as an abstract syntax tree, and then what can you do with that?

00:20:42.380 --> 00:20:49.420
Well, you know, the interesting thing about, you know, what we do with t-strings is it can be a front-end to such tools.

00:20:50.060 --> 00:21:39.680
So t-strings themselves are, you know, intentionally designed to have just this very limited, but at the same time, you know, let's say, you know, very powerful, you know, integration with Python, right? You have, you know, effectively an f-string specification of, you know, what's going to describe that template. You describe the interpolations, they can be nested and everything, but fundamentally at the end of the day, you get this template object out of it, which could be nested and whatnot, but it's still this simple, straightforward thing. And then you pass it to a function that can do interesting things with that. From a performance perspective, that's also something that we intentionally put together. I don't know, Dave, if you want to talk about that, what we did specifically for that, because I think that might be something you want to speak to.

00:21:39.940 --> 00:21:51.780
Yeah, absolutely. Well, if you take a look at what's inside the template type, there are basically two tuples that sit there. There's a tuple called strings and a tuple called values or interpolations.

00:21:51.950 --> 00:21:53.060
You can kind of work both ways.

00:21:54.500 --> 00:21:59.540
And so as you might expect, the strings are the static parts that are part of the literal string that you specify.

00:22:00.400 --> 00:22:03.620
And the values or interpolations are the dynamic parts.

00:22:04.600 --> 00:22:14.320
And we kind of made it so that no matter how you write your literal, it's always the case that there is at least one string in that strings tuple, even if it's the empty string.

00:22:14.760 --> 00:22:29.360
So T quote, quote, gives you a template with no interpolations, with an empty tuple for interpolations, and with just a single quote for strings. And we ensure that there's always one more string than there are values or interpolations.

00:22:29.940 --> 00:23:21.760
And the reason we do that is that allows you to basically, the string's tuple becomes a useful cache key in many cases for memorizing. So if you process a template once with your HTML function, you might sit back and say, okay, I've seen this structure of HTML before, and the next time I do it, all I need to do is fill in the gaps. And so that we think is just like a really basic optimization that you can use. And we're seeing that use, Paul, you might want to mention the TDOM library that I know you and others have been working on in the last little while. But it's just a basic optimization that we can offer. I think it is worth calling out, though, that, and I know we mentioned this earlier, but probably worth calling out once more just to make it explicit, that t-strings like f strings are eagerly evaluated, right? So if you have hello,

00:23:22.240 --> 00:23:23.240
curly

00:23:23.240 --> 00:23:28.320
braces name, and name isn't in scope, you will get a name error just like with an f string.

00:23:29.260 --> 00:23:49.760
And so you might say, well, that feels a little weird to me because don't I want to reuse these things multiple times? And this is one of those cases where the word template is just an overloaded term in the computing world. And so if you're coming from the Jinja world or the Django templates world, you're probably used to the idea that I have this structure and I'm going to keep substituting values into it.

00:23:50.580 --> 00:23:56.240
And that's a little bit different than the other use of the word template, which comes from, say, the JavaScript side of the world.

00:23:56.410 --> 00:23:58.700
So JavaScript has tag template literals.

00:23:58.840 --> 00:24:02.220
If you've written any JavaScript code, you've probably run across them before.

00:24:03.300 --> 00:24:06.520
And there, like with t-strings, they're eagerly evaluated.

00:24:07.200 --> 00:24:10.460
So, you know, of course, you have the full generality of Python available to you.

00:24:10.880 --> 00:24:20.600
So you can just write a function that takes the parameters that you want to substitute and returns a T string, call it multiple times with different values, and you'll get different template instances out the other side.

00:24:20.900 --> 00:24:27.280
Probably just worth calling that out so that it's clear to listeners that there is this kind of funny overloading the word template.

00:24:27.780 --> 00:24:29.900
But yeah, back to the point about optimization.

00:24:30.540 --> 00:24:45.160
We're pretty excited about just the basic opportunities for building optimized template processing code, like an HTML function or a SQL function that works on top of this basic structure that we provide in PEP 750.

00:24:45.280 --> 00:24:53.820
Yeah, and I think one thing you can think about is, again, we may be getting a little too into the weeds here, but let's get into the weeds.

00:24:55.120 --> 00:25:02.060
You can think about the template as basically providing this interesting way of describing your arguments.

00:25:03.460 --> 00:25:08.180
And then you pass it to some sort of function, HTML, SQL, you name it.

00:25:09.100 --> 00:25:13.140
And basically, it's going to evaluate with respect to those arguments.

00:25:13.740 --> 00:25:26.740
But, you know, if you, so we basically, you know, if I have an HTML function, I'm going to want to look it up with that dot strings, template dot strings.

00:25:27.420 --> 00:25:31.040
Say I parsed this and I know how to go and work with this fragment.

00:25:31.200 --> 00:25:40.240
I already know where all the pieces need to go, the holes that need to be filled in by these interpolations, and it could be done very quickly.

00:25:41.380 --> 00:25:46.300
And so we've put a lot of effort in terms of thinking about what is that optimization path.

00:25:46.540 --> 00:25:48.040
And there's also related aspects.

00:25:48.460 --> 00:25:55.700
Why is this another reason this is built into the language is because you can get lexical scope as opposed to dynamic scope.

00:25:56.120 --> 00:26:00.660
You think that lexical, you know, you might think that dynamic scope is always the thing that's going to work for you.

00:26:01.120 --> 00:26:04.440
But no, this is the thing that brought Paul and I together on this project.

00:26:04.920 --> 00:26:10.260
It's exactly, you know, you can't use dynamic scope when you would expect it.

00:26:10.600 --> 00:26:15.920
So, yeah, if we want to go to that scope issue when calling components from list comprehension.

00:26:16.850 --> 00:26:20.820
So, Paul, if you want to speak to this a little bit, those are the problems you experienced.

00:26:21.880 --> 00:26:30.360
Sure. And this is, oh, my gosh, Jim in the first round of this and Dave in the second round of this in the PEP and the comments.

00:26:31.160 --> 00:26:37.160
This this damn thing had more comments on it than every other PEP in the history of civilization.

00:26:37.440 --> 00:26:38.660
Almost it felt that way.

00:26:39.250 --> 00:26:45.380
Major changes between the first draft and the second draft from lazy evaluation, tag template functions, all those things.

00:26:46.360 --> 00:26:52.180
But one that comes up pretty frequently is I could just do this with this script frame.

00:26:52.680 --> 00:26:56.060
And you can, but you can't and you shouldn't either way.

00:26:56.800 --> 00:27:05.780
But there is a nuance about scoping that bit me right here on the screen in this ticket that was the origin story of Gemini talking to each other.

00:27:06.420 --> 00:27:39.080
2020, a predecessor that mimicked t-strings in sysget frame from the people behind the JavaScript package called HTM. They did a port to Python called HTMPy for doing powerful template languages kind of closer to the language, something that in the JavaScript world, this is a replacement for JSX. It doesn't need tooling. And so they wanted to bring that kind of experience, language-centric templating. They wanted to bring it to Python, but hit this issue about scope.

00:27:39.720 --> 00:27:59.520
Yeah. And it seems like it's just this obscure thing, but it isn't. You know, you use a nested function, it pops up. Use a generator expression, it pops up. Use a list comprehension, it pops up. So things that you might commonly want to use, now you need to know you shouldn't do that. So that's one thing. The second thing is it's slow. And it basically also breaks

00:27:59.520 --> 00:28:01.500
a lot of optimizations that have

00:28:01.500 --> 00:28:03.820
been introduced into Python in recent years.

00:28:04.400 --> 00:28:05.300
So you don't want to do that either.

00:28:06.360 --> 00:28:07.200
By using...

00:28:07.280 --> 00:28:07.600
And the

00:28:07.600 --> 00:28:09.980
creator of this hack, Barry said, don't use this hack.

00:28:10.580 --> 00:28:12.140
Well, I mean, there are

00:28:12.140 --> 00:28:16.500
good reasons to go and use sysgift frame, and especially there are historical good reasons.

00:28:17.140 --> 00:28:27.340
But I think we have, again, so it's something that comes up because, you know, if you have dug into the internals of Python, you've looked at the inspect, you know you can do certain things.

00:28:27.900 --> 00:28:32.080
And I think that's a fantastic way to explore new capabilities.

00:28:32.710 --> 00:28:35.520
But it is not necessarily what you want to go rely on because,

00:28:36.080 --> 00:28:36.700
again,

00:28:36.900 --> 00:28:42.380
the thing I would have taught in the class, what is the difference between dynamic and lexical scope?

00:28:42.880 --> 00:28:44.500
Well, here's where it really bites you.

00:28:46.320 --> 00:28:49.400
This portion of Talk Python To Me is brought to you by Auth0.

00:28:49.960 --> 00:28:51.080
Do you struggle with authentication?

00:28:52.080 --> 00:28:53.820
Sure, you can start with usernames and passwords.

00:28:54.160 --> 00:28:58.500
But what about single sign-on, social auth, integration with AI agents?

00:28:59.140 --> 00:29:01.320
It can quickly become a major time sink.

00:29:01.790 --> 00:29:04.440
And rarely is authentication your core business.

00:29:05.100 --> 00:29:09.680
It's just table stakes that you got to get right before you can move on to building your actual product.

00:29:10.280 --> 00:29:12.120
That's why you should consider Auth0.

00:29:12.620 --> 00:29:16.940
Auth0 is an easy to implement, adaptable authentication and authorization platform.

00:29:17.660 --> 00:29:24.060
Think easy user logins, social sign-on, multi-factor authentication, and robust role-based access control.

00:29:24.780 --> 00:29:31.180
With over 30 different SDKs and quick starts, Auth0 scales with your product at every stage.

00:29:31.860 --> 00:29:37.000
Auth0 lets you implement secure authentication and authorization for your preferred deployment environment.

00:29:37.370 --> 00:29:45.380
You can use all of your favorite tools and frameworks, whether it's Flask, Django, FastAPI, or something else, to manage user logins, roles, and permissions.

00:29:46.200 --> 00:29:51.040
Leave authentication to Auth0 so that you can start focusing on the features your users will love.

00:29:51.900 --> 00:29:56.340
Auth0's latest innovation, Auth4Gen AI, which is now available in developer preview.

00:29:56.880 --> 00:30:07.720
Secure your agentic apps and integrate with the Gen AI ecosystem using features like user authentication for AI agents, token vault, async authorization, and FGA for RAG.

00:30:08.480 --> 00:30:21.480
So if you're a Python developer or data scientist looking for an easy and powerful way to secure your applications, Get started now with up to 25,000 monthly active users for free at talkpython.fm/auth0.

00:30:22.120 --> 00:30:24.280
That's talkpython.fm/auth0.

00:30:24.400 --> 00:30:26.140
The link is in your podcast player's show notes.

00:30:26.820 --> 00:30:28.800
Thank you to Auth0 for supporting the show.

00:30:29.820 --> 00:30:34.240
Let's talk about HTML templates because I know, Paul, that was a lot of your interest in this, right?

00:30:34.920 --> 00:30:35.120
Indeed.

00:30:35.960 --> 00:30:38.160
Michael, you and I are like web originals,

00:30:38.280 --> 00:30:38.400
right?

00:30:39.540 --> 00:30:41.280
You're a big fan of Chameleon.

00:30:41.600 --> 00:30:46.720
Whenever you encounter a new web framework, the first thing you do is make a package for that that gives a

00:30:46.720 --> 00:30:47.320
chameleon support.

00:30:47.780 --> 00:30:48.020
That's right.

00:30:48.080 --> 00:30:53.640
You want FastAPI, just put the FastAPI chameleon decorator on there and you're back to have proper language.

00:30:55.100 --> 00:30:55.460
Yes.

00:30:56.680 --> 00:31:04.820
But my feeling has been that Python web development, particularly templating, is stuck in the early 2000s.

00:31:05.020 --> 00:31:06.240
I went and looked up recently.

00:31:06.740 --> 00:31:09.760
I think Jinja was first released in 2008.

00:31:10.100 --> 00:31:17.060
And there was a feeling back then that HTML and web design was something different than software.

00:31:17.560 --> 00:31:20.720
Separation of concerns, but more importantly, separation of people.

00:31:21.130 --> 00:31:23.840
You wanted to keep the templating people away from the software.

00:31:24.160 --> 00:31:27.820
You've got your designers and you've got your developers and they exchanged

00:31:27.820 --> 00:31:28.460
the CSS.

00:31:29.000 --> 00:31:32.060
I'm going to give you this sandbox where you can't break things.

00:31:32.170 --> 00:31:35.220
And it's going to be like Python, but not really Python.

00:31:36.100 --> 00:31:38.580
And the world doesn't work that way anymore.

00:31:38.940 --> 00:31:41.440
Front-end development is software.

00:31:41.890 --> 00:31:43.860
In the JavaScript world, it's software.

00:31:44.100 --> 00:31:44.760
It's components.

00:31:45.360 --> 00:31:45.980
It's tooling.

00:31:46.520 --> 00:31:47.880
It's testing.

00:31:48.440 --> 00:31:53.560
It's all of these things where people expect powerful tooling as part of software.

00:31:54.140 --> 00:31:54.840
We haven't.

00:31:54.970 --> 00:32:02.420
And there have been some things starting to get into this area, but this was what I was originally interested in, what Jim and I had talked about quite a bit.

00:32:02.640 --> 00:32:04.220
Dave comes from a similar background.

00:32:04.900 --> 00:32:11.760
And we want, in a general sense, we want DSLs, domain-specific languages, that are closer to Python.

00:32:12.200 --> 00:32:15.200
So when you say, here's a symbol, it's a symbol.

00:32:15.530 --> 00:32:17.880
And you know the scoping rules because it's just Python.

00:32:18.070 --> 00:32:20.220
It isn't some invented scoping rule.

00:32:20.540 --> 00:32:23.100
And you can use your formatter on it.

00:32:23.100 --> 00:32:24.780
And you can use mypy on it.

00:32:24.780 --> 00:32:26.440
And you can use all blah, blah, blah.

00:32:26.550 --> 00:32:31.280
You can get IDE assistance on it because it's just Python.

00:32:31.780 --> 00:32:46.000
And so that, from an HTML perspective and a web perspective, we're trying to move from PSP, JSP, PHP-style templating as a separate non-Python concern over into this new world.

00:32:46.260 --> 00:32:47.420
Dave, how did I do on that?

00:32:47.460 --> 00:32:47.980
Do you buy what

00:32:47.980 --> 00:32:48.540
I just said?

00:32:48.540 --> 00:32:49.360
I absolutely buy it.

00:32:49.380 --> 00:33:01.660
And like Paul mentioned, that's sort of how I came into this crazy rollercoaster ride, too, which is I had been working with a number of nonprofits over the last couple of years, building Python backends, doing a lot of front-end development with Python code.

00:33:02.120 --> 00:33:07.980
and feeling like, and in particular, one of the projects I worked on a couple years ago used HTMX.

00:33:08.020 --> 00:33:10.420
I think it was one of my first experiences with HTMX.

00:33:10.960 --> 00:33:17.820
And for those who haven't used it, you often write big templates and then occasionally you need to send lots of little blobs of HTML back.

00:33:18.260 --> 00:33:28.160
And it really starts to get difficult to use a tool like Jinja or a tool like Django templates to produce lots of little blobs of HTML while still keeping everything cleanly organized.

00:33:28.380 --> 00:33:32.520
You end up with lots of sort of include statements or Jinja macros.

00:33:33.120 --> 00:33:41.260
And those are great, powerful features, but they aren't quite as powerful as what Paul was saying, which is the full generality of the Python programming language to begin with.

00:33:41.820 --> 00:33:49.940
And so, you know, we started resorting to using HTML builders like HTTPY in my case, but there are a few others, Dominic and others.

00:33:51.200 --> 00:33:59.940
And those felt great, but they also felt to me like what building web stuff felt like in the JavaScript ecosystem circa 2010, right?

00:34:00.440 --> 00:34:05.920
Before tag template strings came along and ultimately in the JavaScript world before JSX came along as well.

00:34:07.320 --> 00:34:10.700
And so it did feel like there was really an opportunity to revisit that question.

00:34:10.820 --> 00:34:18.419
And that's sort of how I found Jim and Paul, who were thinking about this stuff and have been thinking about this stuff for far longer than I in the Python ecosystem.

00:34:19.260 --> 00:34:19.800
Yeah, very neat.

00:34:19.830 --> 00:34:21.860
I pulled up an example from Vue.js.

00:34:22.480 --> 00:34:24.799
And Dave, you mentioned HTMX.

00:34:25.780 --> 00:34:32.120
And one of the big ideas that Carson Gross from HTMX promotes is locality of behavior.

00:34:32.520 --> 00:34:36.980
Like I should be able to look kind of here on my editor and see all the stuff's going on.

00:34:37.080 --> 00:34:41.800
There shouldn't be four different places and then a convention that pulls in a fifth place that then makes a thing happen.

00:34:42.340 --> 00:34:47.560
And there's a lot of that in the JavaScript world of like, well, here's your behavior.

00:34:47.740 --> 00:34:54.980
And literally, here's the fragment of HTML that we're going to work on with capturing values and different substitutions and stuff.

00:34:55.100 --> 00:34:59.900
And it feels like new templating, and by templating, you're the one who said this silver letter, right?

00:35:00.220 --> 00:35:00.480
Yeah.

00:35:00.740 --> 00:35:11.300
New templating options like Chameleon 2 or Jinja 3 or whatever could come along and build on the ideas using some of the things here.

00:35:11.580 --> 00:35:12.340
Yeah, absolutely.

00:35:12.540 --> 00:35:18.380
And I'm really hoping we see an explosion of that as after Python 3.14 ships out the door.

00:35:18.800 --> 00:35:26.460
So a lot of this work that the three of you plus others, I want to be super clear that there are other folks at the top of this PEP here.

00:35:26.720 --> 00:35:27.060
Yes.

00:35:27.880 --> 00:35:30.060
About this who have also put a lot of work into it.

00:35:30.840 --> 00:35:31.680
Yeah, not just at the

00:35:31.680 --> 00:35:36.620
top of the pep, but also, of course, the broader Python community in the discussions without which.

00:35:37.860 --> 00:35:40.740
And then Paul mentioned the discussions are longer than for any PEP.

00:35:41.100 --> 00:35:44.040
But without that community feedback, I don't think we have landed

00:35:44.040 --> 00:35:44.800
where we are now.

00:35:45.920 --> 00:35:48.360
So if I print this, how long would this be?

00:35:49.140 --> 00:35:49.820
How many pages?

00:35:51.380 --> 00:35:56.440
206 pages to print the discussion of the TagStrings announcement.

00:35:56.490 --> 00:35:57.480
And that was the first.

00:35:57.490 --> 00:35:58.220
There were two discussions.

00:35:59.300 --> 00:35:59.800
Even better

00:35:59.800 --> 00:36:00.580
than Novella.

00:36:02.760 --> 00:36:03.460
Yeah, that's incredible.

00:36:03.900 --> 00:36:04.300
Also

00:36:04.300 --> 00:36:04.560
great

00:36:04.560 --> 00:36:09.640
feedback in other locations as well that we were also really trying to respond to.

00:36:10.210 --> 00:36:12.360
They were talking about Reddit or Hacker News.

00:36:14.980 --> 00:36:17.400
And a lot of credit to Jim on this.

00:36:17.460 --> 00:36:22.560
in the first round, Jim was really about lazy evaluation.

00:36:23.120 --> 00:36:34.200
And it was part of bigger ideas about inside out or outside in rendering and streaming and generators and a lot of things connected in his brain about it.

00:36:34.520 --> 00:36:41.640
And there was a lot of practical pushback, including Astral, about, hey, we're going to struggle to statically analyze that for Ruff.

00:36:41.960 --> 00:36:47.420
And Jim thought about it pretty hard and talked with Dave a lot and talked with others about

00:36:47.440 --> 00:37:08.280
rolled that back. Wow. So there's a difference between, there's a difference here between lazy, which is they're evaluated eagerly, these t-strings, but maybe, and async, right? So can we do async stuff on our strings? Like you have to call a function given a template to render text. That seems like you could just write that, yeah?

00:37:08.500 --> 00:37:42.300
Let me unmute myself. So yeah, I mean, the basic thing here is that these are expressions that can be evaluated, right? And so just because you have a, you know, I'll go for something that's even, you know, broader than this. You look at something like, you know, common pattern like Django's query set, right? So it's lazily evaluated until you actually go and actually look at its contents. A query doesn't even happen, right? In terms of against the underlying SQL engine that you're using.

00:37:43.380 --> 00:37:49.560
And in any event, the point is that we can have laziness where we Lambda wrap it.

00:37:49.690 --> 00:37:53.520
That was the original idea, and there's a lot of potential value in that.

00:37:54.200 --> 00:38:01.840
But there's also this aspect that, in general, the Python community has come up with a number of ways to incorporate laziness.

00:38:02.560 --> 00:38:09.600
And specifically through maybe it's an interval, Maybe it's a query set that is also inevitable.

00:38:09.920 --> 00:38:11.840
Maybe it's through the use of async expressions.

00:38:12.120 --> 00:38:20.620
These are all expressions that can be passed around and then used by, you know, something that is evaluating with respect to that.

00:38:21.680 --> 00:38:23.960
So t-strings don't change any of that, right?

00:38:24.340 --> 00:38:38.020
Because, you know, if you have that expression and it's been evaluated, but it's lazily, it's been lazily so, it can still be used for however you're going to work with that template in the function that operates on it.

00:38:39.880 --> 00:39:01.060
And I think this is just going to get at most of the things that I was thinking about when we were originally looking at this from this more lazily done way without the typing issues that ultimately we decided would make it too difficult to work with.

00:39:01.720 --> 00:39:06.480
So I think this is just something where we looked at what is the Python ecosystem?

00:39:07.020 --> 00:39:08.360
How have we solved these problems?

00:39:09.760 --> 00:39:25.640
and even though there was, you know, I really wanted to go and have something that basically would allow for maybe some greater functionality along, you know, doing like a deferred or lazy type capability implicitly, you know, that can be something else.

00:39:26.140 --> 00:39:29.940
We can bring, you know, there are discussions around like bringing in macros.

00:39:30.160 --> 00:39:38.680
It's, you know, you could implement that using coding on your file.

00:39:39.180 --> 00:39:40.340
I don't know where that goes.

00:39:40.780 --> 00:39:43.440
It might even interface with what we've done with t-strings.

00:39:43.780 --> 00:39:51.620
But the important thing is that you don't have to bring all that in in order to go and benefit from what we're doing with this.

00:39:52.080 --> 00:39:52.160
And

00:39:52.160 --> 00:39:52.500
by

00:39:52.500 --> 00:40:02.700
simplifying what we've done, I think we've, you know, come up with something that works a lot better. And I really valued the great feedback we had from the community as far as that was concerned.

00:40:02.800 --> 00:40:33.080
Yeah, thanks. One of the things I hear a lot mentioned throughout the conversations and the PEP and stuff is DSLs. So a domain specific language and SQL is kind of a domain specific language. But I think a lot of times people are thinking something I make that is really simple, right? Maybe I want to make a language to talk to simple robots, right? And I want to use this to construct maybe a way to talk to it that's way simpler, but easier to parse because you can say, well, here's where the where piece went or whatever, right?

00:40:33.240 --> 00:40:35.820
You can pull them out of the interpolations and the values and stuff.

00:40:36.100 --> 00:40:39.960
Can you guys talk to maybe some examples of what you had in mind or what might be possible there?

00:40:40.220 --> 00:40:41.100
This is good to me.

00:40:41.980 --> 00:40:49.240
So the Python has been a very popular tool for constructing two types of DSLs.

00:40:49.580 --> 00:41:00.760
One would be like the internal DSL that you see in Pydantic or SymPy, that sort of thing, where it's basically taking advantage of operator overloading.

00:41:00.990 --> 00:41:01.980
So let's put that aside.

00:41:02.420 --> 00:41:11.900
We'll look at the other version, which is this external DSL, where we think about, again, canonically HTML, SQL being pretty large targets.

00:41:12.350 --> 00:41:18.320
But if you look at the PyParsing project, you'll see, I don't know, how many dozens of languages it supports.

00:41:18.740 --> 00:41:29.040
Not to mention the fact that it's straightforward to write your own grammar in it and parse it into an abstract syntax tree.

00:41:30.260 --> 00:41:41.660
Yeah, I mean, it's just like, as it says here, the use of parsing expression grammars, you know, really makes it very straightforward to write a cursor.

00:41:42.860 --> 00:41:58.460
So I highly recommend anyone who's like interested in writing their own toy language or maybe have greater ambitions, look into tools like this, because I think this is one of the fantastic advantages we have in the Python community is we have really great libraries like this that are widely used.

00:41:59.490 --> 00:42:06.260
And so for your example of writing something that would interface with a robot, absolutely, absolutely.

00:42:06.500 --> 00:42:08.800
That's something that you could express.

00:42:09.190 --> 00:42:17.320
And you think about, would that potentially be even something where it could be useful for education, for example?

00:42:17.760 --> 00:42:20.020
And how would it work with, again, t-strings?

00:42:20.260 --> 00:42:25.100
Well, again, you would basically say there's this domain of things that we know from the Python space.

00:42:25.859 --> 00:42:31.080
We're computing maybe some velocity that we want the robot to move at.

00:42:31.440 --> 00:42:39.180
We're computing which color of brick that we want to pick up or whatever it happens to be.

00:42:40.060 --> 00:42:46.060
And we can basically say that that's something that's happening in our Python code directly.

00:42:46.580 --> 00:42:50.000
And then there's this language that we're using to talk to the robot.

00:42:50.000 --> 00:43:06.580
Okay. And what it looks like in terms of the t-strings is you basically are saying, okay, I'm going to go and parse this language fragment with some parser that can be made to use placeholders that will work with interpolations.

00:43:06.980 --> 00:43:22.880
And I'm going to parse it. I'm going to get an abstract syntax tree out of that. I'm going to use that to then evaluate how I'm going to go and say, you know, send in a number or color or other information to that toy language or more ambitious language.

00:43:23.340 --> 00:43:32.540
and then send out whatever is necessary in order to complete that communication with the system that's implementing that.

00:43:32.800 --> 00:43:38.040
So it's going to be great for people who really love languages and want to play with them.

00:43:38.240 --> 00:43:40.100
And again, I would start with something like PyParsing.

00:43:40.420 --> 00:43:43.660
That was a great example of the type of tool you could use here.

00:43:43.840 --> 00:43:44.160
Excellent.

00:43:44.410 --> 00:43:46.900
Yeah, I'll put that link in the show notes so people can check it out.

00:43:47.540 --> 00:43:56.760
Yeah, maybe bringing it all back home for a second or maybe just because I've got my head wrapped in building slide deck for a lightning introduction, lightning talk introduction to t-strings.

00:43:57.540 --> 00:44:09.080
But, you know, a common use case here when you're writing code that takes a template is not returning a string, but as Jim is kind of deeply hinting at here, parsing and returning some kind of abstract syntax tree.

00:44:09.130 --> 00:44:29.780
So in the HTML case, and you can actually find examples of this, for example, in the PEP 750 examples repository, there's an HTML function there and it has a bunch of nice features, but you're returning an HTML element back from the code that processes the T-string, you don't, in fact, have to grab a string out of a T-string right away or ever if you don't want to.

00:44:30.280 --> 00:44:35.340
So there's an immense amount of flexibility here that we're excited to see people use.

00:44:35.720 --> 00:44:39.180
Yeah. Dave, under your account on GitHub, Dave Peck.

00:44:39.260 --> 00:44:39.600
Needs

00:44:39.600 --> 00:44:40.000
to move.

00:44:40.780 --> 00:44:43.900
You know what? It's fine. It's just as good as anywhere.

00:44:44.600 --> 00:44:47.440
You have a bunch of examples of things you might do

00:44:47.440 --> 00:44:48.920
with

00:44:48.920 --> 00:44:49.280
these.

00:44:49.540 --> 00:44:50.980
and one of them is HTML.

00:44:51.740 --> 00:44:54.220
People can see how that might work and so on.

00:44:54.340 --> 00:44:56.300
And what are some of the other notable ones in here?

00:44:56.400 --> 00:44:57.380
I know I was looking through this earlier.

00:44:57.720 --> 00:45:02.940
Well, we wanted to show some basic examples like, hey, t-strings have a syntax that looks a lot like f-strings.

00:45:03.180 --> 00:45:05.100
How could you build f-strings on top of t-strings?

00:45:05.280 --> 00:45:15.920
So there is an F paren-paren method that's implemented here that just kind of helps you understand, okay, I'm writing dynamic code to do the thing that f-strings would have done at compile time anyway.

00:45:17.280 --> 00:45:20.000
We also have a structured logging example.

00:45:20.150 --> 00:45:29.020
And I'm excited about this, you know, structured logging that Python has shipped a logging cookbook for a long time since the Python 2.something era.

00:45:29.730 --> 00:45:38.720
And one of the examples in that logging cookbook is structured logging, by which we mean like, hey, I want to write out a sentence or two that people can understand, that humans can read.

00:45:39.070 --> 00:45:42.040
But I also have some values that I'm substituting in that sentence.

00:45:42.170 --> 00:45:45.440
And I'd really like that to be written out in some schema compliant way.

00:45:46.560 --> 00:46:01.700
And with t-strings, you can kind of do that in a really elegant way, which is to say you write the T-string once, you send it to some kind of method that expects t-strings on logging, and it emits basically the equivalent of an F-string.

00:46:01.820 --> 00:46:03.160
So that's the human readable bit.

00:46:03.360 --> 00:46:09.160
And then maybe it uses JSON or something to emit a structured output to a different log stream entirely.

00:46:09.740 --> 00:46:19.860
So one of the examples in the PEP and also in this PEP 750 repo is, you know, we send the human readable stuff to standard out and we send the machine readable stuff to standard error.

00:46:20.540 --> 00:46:24.760
And you can imagine, you know, your logging system is going to redirect those somewhere else over

00:46:24.760 --> 00:46:24.900
time.

00:46:24.960 --> 00:46:25.120
Yeah.

00:46:25.590 --> 00:46:30.360
I mean, the example here, you got standard error, but you could easily do a document database with JSON as well.

00:46:30.510 --> 00:46:33.000
And then put an index on action and say, show me all the actions

00:46:33.000 --> 00:46:34.180
taken

00:46:34.180 --> 00:46:36.820
that have this commonality in it.

00:46:36.890 --> 00:46:37.020
Right.

00:46:37.220 --> 00:46:43.400
and do a querying, oh, bam, rather than really error-prone, like substring matching of text or whatever, right?

00:46:43.560 --> 00:46:45.200
Yeah, and I'm really excited about it.

00:46:45.200 --> 00:46:53.800
You write the T-string once, and you get the benefit of both the human readable and the structured schema-compliant output, and that's really exciting to me.

00:46:54.800 --> 00:46:54.860
Yeah,

00:46:55.240 --> 00:46:57.980
and I guess if you use Postgres, you could just put it in a JSON field as well.

00:46:58.300 --> 00:47:00.660
Oh, yeah, we call that mullet

00:47:00.660 --> 00:47:01.520
schema, right?

00:47:01.940 --> 00:47:06.340
It's a business up front and partying back where the last column on every table is a JSON field.

00:47:07.000 --> 00:47:07.640
I've never heard that.

00:47:07.820 --> 00:47:08.640
That's incredible though.

00:47:09.040 --> 00:47:12.220
It's like, yeah, we look real formal, but don't look back there.

00:47:14.420 --> 00:47:15.420
Got it in the ponytail today.

00:47:15.820 --> 00:47:16.080
Yeah.

00:47:16.260 --> 00:47:16.600
Amazing.

00:47:18.900 --> 00:47:19.080
Okay.

00:47:19.680 --> 00:47:24.820
One thing I wanted to cover, there's a really interesting thing from somewhere in here.

00:47:24.920 --> 00:47:25.960
I have pulled this up, I believe.

00:47:26.160 --> 00:47:26.940
I'll just go to the PEP.

00:47:27.340 --> 00:47:31.000
And the PEP, there's a section that talks about how to teach this.

00:47:31.220 --> 00:47:39.180
And I think that that's, there's just a couple of ideas here that I think would make it really interesting for helping people understand better, right?

00:47:39.360 --> 00:47:45.500
Speaking to developers or maybe people who build libraries like web frameworks or logging libraries or something.

00:47:45.860 --> 00:47:51.960
Who wants to kind of give us a few of the ideas here that just maybe like, I know we touched on some of them a little bit.

00:47:52.040 --> 00:48:00.800
Yeah, I mean, I'm happy that I kind of stepped through the basics here, which is, you know, we touched on the fact that the syntax of t-strings and f-strings is essentially identical.

00:48:01.560 --> 00:48:07.520
Anytime you have an F, you can substitute with a T, but the result, the evaluated result is entirely different.

00:48:07.720 --> 00:48:12.500
You get a string or a template, and we've kind of talked about all the reasons why you might want to do that.

00:48:12.960 --> 00:48:28.580
I guess the key thing for developers to understand, and I'm going to divide developers into two groups here, those who use t-strings and pull in like an HTML or a SQL library, for example, and don't really think much about what's going on under the hood.

00:48:29.030 --> 00:48:35.180
For them, all they need to know is, hey, you write T of something and you send it to your SQL library and it's safe.

00:48:35.980 --> 00:48:38.460
And you don't really need to think much more about it than that.

00:48:38.760 --> 00:48:47.980
And then, of course, we had, sorry, but we had things like literal strings and other stuff in the type system that kind of tried to make type checkers check and

00:48:47.980 --> 00:48:49.360
catch when that would happen.

00:48:49.740 --> 00:48:56.260
Right. But this is more runtime catching because of the way it operates rather than, hey, it's a bad idea to do that.

00:48:56.360 --> 00:48:57.940
But, you know, Python typing can be ignored.

00:48:58.220 --> 00:48:58.280
So,

00:48:58.840 --> 00:49:01.440
yeah, it's probably worth mentioning the limits of that.

00:49:01.500 --> 00:49:08.680
I mean, template and interpolation, which are new types that ship with Python 3.14, are ones that you can cons up at runtime directly.

00:49:09.160 --> 00:49:10.720
You don't need to start with a literal.

00:49:11.230 --> 00:49:18.080
And so you can, of course, as a developer, do all kinds of goofy stuff and cons up all sorts of malicious templates if you want to.

00:49:18.460 --> 00:49:30.880
But generally speaking, if you're working from literals, which we sort of expect is the very common case, and you're using libraries that know about this new feature when Python 3.14 ships, generally speaking, you're staying on the safe side.

00:49:31.060 --> 00:49:32.740
You're staying in the bright, well-lit path.

00:49:33.380 --> 00:49:33.520
Yeah.

00:49:33.640 --> 00:49:44.020
One of the ideas I like to think about in APIs and design of software is like falling into the pit of success or falling into the pit of despair.

00:49:44.340 --> 00:49:49.420
You want to make it so the natural action is to fall into the pit of success and you've got to work your way out of there.

00:49:49.540 --> 00:49:49.900
You know what I mean?

00:49:50.040 --> 00:49:51.120
Yeah, you have to work hard to get there.

00:49:51.260 --> 00:49:51.340
Yeah,

00:49:51.400 --> 00:49:52.260
hopefully we've

00:49:52.260 --> 00:49:53.320
found a nice balance there.

00:49:53.580 --> 00:49:55.000
Yeah, it seems like it has.

00:49:55.480 --> 00:49:55.660
Yeah, I

00:49:55.660 --> 00:50:02.080
mean, I think from a teaching it perspective, you know, the pit of despair is so easy to fall into with f-strings, right?

00:50:02.500 --> 00:50:08.360
You know, so your structure logging, you know, I'm sorry, using f-strings in your logging is like, oh, I inadvertently log some passwords.

00:50:09.020 --> 00:50:10.220
Well, that's unfortunate, right?

00:50:10.720 --> 00:50:14.360
Or I, you know, I'm using f-strings with a SQL statement.

00:50:14.530 --> 00:50:16.740
It's like, oh, that really worked until it didn't.

00:50:17.010 --> 00:50:20.880
And so it's very easy to go down that into that pit of despair as you're describing.

00:50:21.360 --> 00:50:30.560
Whereas we're hopefully making it, you know, something where you have to work a little bit, know what you're doing, in fact, in order to go and actually do something that's bad.

00:50:31.580 --> 00:50:41.440
So, you know, I'm sure that, you know, someone can show me, you know, a couple steps that, you know, make it straightforward to get into that pit of despair.

00:50:42.060 --> 00:50:45.400
But I don't think it would be still the most obvious thing to do.

00:50:45.760 --> 00:50:50.400
So from a teaching perspective, we want to ensure that the most obvious thing to do is the right thing to do.

00:50:50.720 --> 00:51:00.800
And, you know, people can build libraries, you know, an HTML function or whatever in order to ensure that is, you know, going to work with that right path.

00:51:01.140 --> 00:51:01.580
But there's more.

00:51:01.580 --> 00:51:12.780
I think, Paul, you know, if you wanted to go and just how to teach this, I think part of this is just like, again, things like IDE integration and things like, you know, working with lenders and whatnot.

00:51:13.500 --> 00:51:15.540
That's also very exciting in terms of what can be done.

00:51:16.120 --> 00:51:17.800
And for me, that's kind of the exciting part.

00:51:17.820 --> 00:51:25.240
but there's a side about really lowering the bar for writing these functions that handle a template.

00:51:25.640 --> 00:51:27.700
You're not going to do anything with a template yourself.

00:51:28.000 --> 00:51:29.980
You're going to hand it to a function that's going to do something.

00:51:30.220 --> 00:51:35.640
And it's going to return a stringable, not a string, something that can be made later into a string.

00:51:36.020 --> 00:51:43.200
But it's the people on the other side that are going to be creating the values and the content and all that kind of stuff.

00:51:43.270 --> 00:51:45.680
And we have a real chance here to help them.

00:51:45.980 --> 00:51:50.060
We're not going to talk about double curly braces or single curly braces for the rest of time.

00:51:50.360 --> 00:51:52.380
It's single curly, just that alone.

00:51:53.060 --> 00:51:59.740
And what goes in there isn't going to be some invented language with a pipe that you send to safe or whatever.

00:51:59.840 --> 00:52:03.460
And you got to go memorize all these rules about how do I call a macro.

00:52:03.920 --> 00:52:12.160
It's just Python, which then means IDEs and tools and linters and formatters and type checkers all get to play a game.

00:52:12.600 --> 00:52:16.900
Go to mypy and file a ticket asking for type checking on Jinja.

00:52:17.260 --> 00:52:19.300
There is a ticket already and you can see their comments.

00:52:19.820 --> 00:52:21.020
They're not that interested.

00:52:21.500 --> 00:52:23.300
Same thing for a lot of other tools.

00:52:23.820 --> 00:52:33.700
But if they support 3.14, they support template strings and they support f-strings and they support this kind of everything because everything is just where it's supposed to be.

00:52:34.000 --> 00:52:39.420
And if I can go a little bit, Dave, is it okay if I go a little bit further on this riff about tooling?

00:52:39.860 --> 00:52:41.580
Let's see, you've got some points to make.

00:52:41.760 --> 00:52:42.460
Sorry, I'm on a roll.

00:52:43.300 --> 00:52:44.740
I'll try to wrap it up, I promise.

00:52:46.040 --> 00:52:55.740
This idea of a big ecosystem of tooling and all of that will give us a developer experience that's competitive again.

00:52:56.200 --> 00:52:58.900
All of these tools can start pitching in.

00:52:59.480 --> 00:53:00.700
I'm with the PyCharm team.

00:53:00.970 --> 00:53:04.900
Because this is f-strings with a T, they did it just like that.

00:53:05.280 --> 00:53:13.720
Dave did a fork of black where he got T string support basically just like that because it was a T instead of an F.

00:53:14.200 --> 00:53:18.720
Then we can get into some more interesting things like an HTML templating library.

00:53:18.960 --> 00:53:25.280
We are very fortunate in the world of Python to have Andrea over from JavaScript on the PyScript team.

00:53:25.620 --> 00:53:32.440
He's written 400,000 variations of JavaScript templating engines including tag template literals.

00:53:32.480 --> 00:53:32.640
He

00:53:32.640 --> 00:53:33.320
knows everything.

00:53:33.820 --> 00:53:36.920
He contributed a lot to this PEP such as that alternating thing.

00:53:37.280 --> 00:53:42.740
He's creating an HTML templating language that is browser first.

00:53:43.280 --> 00:53:48.140
Michael, you had a Vue.js up earlier that had like at click.

00:53:49.100 --> 00:53:58.020
There's going to be a lightning talk at PyCon about showing this in PyScript where something can run on the server and in the client and it's

00:53:58.020 --> 00:53:58.320
nuts.

00:53:58.530 --> 00:53:59.840
The whole thing is just...

00:54:00.460 --> 00:54:03.320
Server rendered templates, but also client-side stuff.

00:54:03.650 --> 00:54:04.040
Yeah, yeah,

00:54:04.280 --> 00:54:04.680
yeah, yeah.

00:54:05.380 --> 00:54:18.760
So there is a chance for us in the world of Python, it doesn't have to be our spelling, but if we can agree to structure and intermediate representations, Dave has ideas about how HTTPY could play ball.

00:54:19.120 --> 00:54:21.280
They can make components that we can consume.

00:54:21.350 --> 00:54:23.160
We can make components that they can consume.

00:54:23.500 --> 00:54:33.960
I'm really bullish on how we can change the developer experience in web development and more to be modern Python with tooling.

00:54:34.590 --> 00:54:34.800
Dave.

00:54:35.000 --> 00:54:36.320
I'm here for it. I'm here for it, Paul.

00:54:36.810 --> 00:54:38.440
Yeah, I'm just going to give you a what what here.

00:54:39.160 --> 00:54:39.280
Yeah.

00:54:41.020 --> 00:54:41.180
Yeah.

00:54:41.730 --> 00:54:51.800
I think if we really can unlock a language that works well on the front end, I know PyScript's come a long way, but I feel like we need a view-like thing, right?

00:54:52.100 --> 00:54:52.200
And

00:54:52.200 --> 00:54:52.900
it sounds like

00:54:52.900 --> 00:54:53.980
work is being done.

00:54:54.740 --> 00:54:55.280
I mean,

00:54:55.320 --> 00:54:59.980
if that comes along, there's going to be a lot of people that go, wait, we don't have to do JavaScript?

00:55:00.210 --> 00:55:01.220
Are you serious here?

00:55:01.460 --> 00:55:02.520
That would be pretty amazing.

00:55:02.960 --> 00:55:07.220
Just throw in your 100K MicroPython implementation of PyScript and

00:55:07.220 --> 00:55:07.980
go off

00:55:07.980 --> 00:55:08.280
to the races.

00:55:08.580 --> 00:55:08.920
Go ahead, Jim.

00:55:09.220 --> 00:55:22.980
Yeah, I was just going to say, and one other nice thing about t-strings and the fact that they look a lot like f-strings is that it's going to be a straightforward thing for us to, or I shouldn't say for us, but I think the community.

00:55:24.240 --> 00:55:33.200
I think there will be plenty of people who can work on this, fortunately, to incorporate t-strings into MicroPython.

00:55:33.590 --> 00:55:45.420
And now you, instead of having something that requires a somewhat heavier download in terms of CPython on the client side with PyScript, you can use MicroPython for that.

00:55:46.140 --> 00:55:46.960
I looked into it.

00:55:47.130 --> 00:55:47.760
It looks straightforward.

00:55:48.380 --> 00:55:52.360
I hope that we will see that development happen sooner than later.

00:55:52.720 --> 00:56:04.020
So a lot of opportunities to go and mix together client side and server side in some really interesting and very cool ways going forward with this work.

00:56:04.500 --> 00:56:09.060
What an interesting angle to say, we'll add t-strings to MicroPython.

00:56:09.580 --> 00:56:10.360
Yep, and keep that.

00:56:10.400 --> 00:56:10.620
So that

00:56:10.620 --> 00:56:13.780
it round trips to the front end of the browser in an efficient way.

00:56:14.260 --> 00:56:14.500
Yeah.

00:56:14.580 --> 00:56:14.700
Yeah.

00:56:15.040 --> 00:56:19.980
We've got a bunch of sprint days coming up with PyCon, so you never know.

00:56:21.240 --> 00:56:21.460
Okay.

00:56:21.500 --> 00:56:22.860
But Michael,

00:56:23.040 --> 00:56:31.580
when you mentioned that MicroPython is about 100K, which to my ears as an old school web person sounds huge, but also really tiny these days.

00:56:32.020 --> 00:56:40.280
Like when you build, when you do the default Next.js app install, which literally does nothing but ship a static looking page to you.

00:56:40.390 --> 00:56:45.060
Although, of course, static includes a whole bundle of React and other stuff with Next.js these days.

00:56:45.620 --> 00:56:46.920
That's about 130K.

00:56:47.270 --> 00:56:49.680
And that doesn't include an implementation of Python.

00:56:50.560 --> 00:56:50.820
Yeah.

00:56:50.860 --> 00:56:51.420
It's

00:56:51.420 --> 00:56:52.060
kind of amazing.

00:56:52.340 --> 00:56:55.640
Yeah, and I think it's pretty comparable to other things.

00:56:56.230 --> 00:56:58.840
I'm trying to poke around and find some examples.

00:56:59.200 --> 00:57:04.540
But yeah, I think 100K is certainly doable on a CDN and so on.

00:57:04.760 --> 00:57:05.340
It's a sweet spot.

00:57:05.480 --> 00:57:05.620
Yeah.

00:57:06.300 --> 00:57:06.540
Hey,

00:57:06.640 --> 00:57:07.680
Michael, I got one for you.

00:57:08.060 --> 00:57:11.980
And this is one that Jim and I started talking about and others piped in.

00:57:12.080 --> 00:57:16.260
Kudai, who's also working on this project, is doing stuff in this field.

00:57:17.000 --> 00:57:23.840
LLMs and code that needs to generate good LLM-generated results.

00:57:24.440 --> 00:57:38.480
If you had a template language that was closer to the language of Python and could be analyzed as such, and all the LLM and RAG tricks that people are applying to not just get code, but to get good quality code.

00:57:38.820 --> 00:57:47.760
Is this a moment where we could invent a web development story for Python that wasn't just cool because it was browser first?

00:57:48.090 --> 00:57:53.660
It was cool because it anticipates AI and generated code.

00:57:54.620 --> 00:58:00.740
You know, if you can infer types and things like that because you can look into the structure of the template.

00:58:01.320 --> 00:58:01.560
Yeah.

00:58:01.620 --> 00:58:02.060
and

00:58:02.060 --> 00:58:09.700
know things and they are Python rules inside the curly braces instead of invented stuff, you might have a better shot at machinery.

00:58:10.000 --> 00:58:10.980
Jim, what do you think?

00:58:11.010 --> 00:58:12.160
You had a lot to say on this.

00:58:12.420 --> 00:58:21.400
Yeah, I mean, I think a lot of people are looking at stuff around like in-context learning with how they work with prompts and everything.

00:58:22.760 --> 00:58:38.860
I do think that if I'm going to send a bunch of examples to like some sort of, you know, a mini shot, but I guess a few number of shots learning, it would be really nice to do it in a very structured way and really have good support for that.

00:58:39.140 --> 00:58:44.540
And so if you look at, you know, how people typically do this, well, they emit f-strings.

00:58:45.140 --> 00:58:46.360
And I'm sure it works most of the time.

00:58:46.660 --> 00:58:47.960
I just want it to work all the time.

00:58:49.160 --> 00:59:00.560
And so I think, you know, having something that can, you know, taint that structure, maybe even do some interesting, you know, like there's some stuff around like DSPY or whatever.

00:59:00.590 --> 00:59:11.920
I'm not certain actually how it's pronounced, but it basically does additional rounds of optimizing your in-context learning, prompting in general.

00:59:12.960 --> 00:59:15.320
Having access to that structure might be very helpful for it.

00:59:15.430 --> 00:59:15.900
Who knows?

00:59:16.380 --> 00:59:29.880
I think that there's a lot of interesting opportunities in this space where we maintain this structure, we're able to reflect on it, And then use that for whatever sort of interaction we have with other systems.

00:59:30.620 --> 00:59:33.060
And fundamentally, that's what this supports.

00:59:33.660 --> 00:59:35.560
Because you have access to those Python structures.

00:59:36.220 --> 00:59:39.840
You can figure out what that's supposed to be before you go and send it off to somewhere else.

00:59:40.320 --> 00:59:42.940
So what does that mean in terms of capabilities?

00:59:43.340 --> 00:59:44.040
It's very interesting.

00:59:44.440 --> 01:00:05.520
Paul, when you were talking about typing earlier and then talking about mypy, it just made me realize or think, I guess I thought about it before, But reemphasize that for me, the typing is a communication tool to me, mostly through things like PyCharm, but not 100%, but mostly through the editor saying, here's what it's supposed to be here.

01:00:05.640 --> 01:00:06.820
Here's what this is supposed to do.

01:00:06.870 --> 01:00:09.460
So I don't have to go to definition and try to decipher it.

01:00:10.140 --> 01:00:16.580
And I almost never use mypy or one of these Pyright or Pyre or, you know, you name it, because that's not my goal.

01:00:16.700 --> 01:00:21.280
My goal is to add typing so that I can stay out of the docs, except when I really must.

01:00:21.600 --> 01:00:26.520
And the same applies to what you share with chat and agenda code generators.

01:00:26.950 --> 01:00:33.640
They can look at those types and they can see much more information and it can make them way more accurate and productive, right?

01:00:33.980 --> 01:00:43.360
And I feel like this could communicate more information to them, like you were saying, so they have more structure to work with rather than just, here's a string, I don't know what it is.

01:00:43.480 --> 01:00:44.400
Can I riff on that for a second?

01:00:44.400 --> 01:00:44.740
Yes, riff.

01:00:45.880 --> 01:00:53.720
This is all not news to Dave, who he and I are writing our lightning talks and FlaskCon talk together.

01:00:54.240 --> 01:00:55.800
You're around on Friday afternoon.

01:00:56.120 --> 01:00:56.800
Come see Dave.

01:00:57.040 --> 01:00:57.620
Come see me.

01:00:57.760 --> 01:00:58.940
It's going to be mind

01:00:58.940 --> 01:00:59.200
-blowing.

01:00:59.800 --> 01:01:01.520
Lightning talks are going to be mind-blowing, too.

01:01:01.520 --> 01:01:04.020
Don't they normally publish those videos from FlaskCon as well?

01:01:04.210 --> 01:01:04.680
I think they do.

01:01:05.580 --> 01:01:05.800
Maybe.

01:01:06.140 --> 01:01:06.640
He needs help.

01:01:06.780 --> 01:01:07.000
Okay.

01:01:07.400 --> 01:01:08.160
Well, I'm hoping.

01:01:08.170 --> 01:01:10.620
Poor David is doing 500 jobs at once.

01:01:11.120 --> 01:01:12.580
Everyone support David Lord.

01:01:13.180 --> 01:01:30.140
I'm going to be giving a demo in PyCharm, at least, of T-String support with interpolation autocomplete and navigation and all the PyCharm tricks, but also a experimental plugin that does component support with TDOM.

01:01:30.340 --> 01:01:46.000
So it looks just like JSX, where you pass props to something and it obeys the can do not just autocomplete of the props, But if you're missing a required prop, it will know that because it looks at the function signature of the component.

01:01:46.420 --> 01:01:48.520
And it will match the types of the prop.

01:01:48.580 --> 01:01:51.260
If you pass a string for an int, it will squiggle you.

01:01:51.440 --> 01:02:02.120
In the future, we can go further like a lot of IDs can do and extract components to be standalone, to get big components into small components, all kinds of things.

01:02:02.320 --> 01:02:09.740
Once you get into the world of tooling, you know, you've got typing, you've got signatures, you've got all of these great things.

01:02:09.920 --> 01:02:13.540
mouse over for doc strings and doc hints and help.

01:02:14.120 --> 01:02:14.540
Lots of help.

01:02:14.660 --> 01:02:14.980
That's awesome.

01:02:15.400 --> 01:02:16.540
Well, I'm looking forward to it.

01:02:16.900 --> 01:02:17.860
Guys, we're about out of time.

01:02:18.050 --> 01:02:21.240
I want to wrap this up with one final thought here.

01:02:21.460 --> 01:02:27.740
So speaking to several audiences, let's say two audiences at the moment, give us some advice for potential adopters.

01:02:28.160 --> 01:02:38.140
So if I'm Sebastian from FastAPI or David from FlaskCourt or I'm some other library adopter, SQL, MicroSQLAlchemy, right?

01:02:38.900 --> 01:02:40.320
Or that's audience one.

01:02:40.650 --> 01:02:45.020
Audience two would just be people who thought f-strings were awesome and this is even more awesome.

01:02:45.200 --> 01:02:46.260
They want to try it in their code.

01:02:46.540 --> 01:02:49.420
What are some pitfalls and best practices for these folks?

01:02:49.490 --> 01:02:50.840
And what are the opportunities there?

01:02:51.100 --> 01:02:58.340
Well, I mean, I think, you know, in general, the fact that t-strings exist doesn't mean that f-strings need to disappear tomorrow.

01:02:58.560 --> 01:03:06.320
So I guess that would be the first thought I'd have for the community that isn't actively building libraries that might benefit from T string functionality, which is that the

01:03:06.320 --> 01:03:06.700
tool is

01:03:06.700 --> 01:03:07.180
still useful.

01:03:07.840 --> 01:03:12.140
And in fact, there are many cases where probably what you really want is an f-string rather than a t-string.

01:03:12.760 --> 01:03:13.360
It's exactly

01:03:13.360 --> 01:03:28.700
these cases where you want the interpolations to do something special, whether that's prevent injunction vulnerabilities or, you know, transliterate, you know, a single dictionary into a bunch of attributes in HTML where you might want to start playing with t-strings.

01:03:29.280 --> 01:03:35.440
And there, you know, I think if you're a library developer and you happen to be at PyCon, come say hello.

01:03:35.920 --> 01:03:39.620
we'd love to talk to you and kind of figure out how you're thinking about adopting these.

01:03:39.980 --> 01:03:46.360
I think the biggest one that came up in discussion amongst library developers that I saw was just, wait, how do I type my function?

01:03:46.620 --> 01:03:53.840
So if I'm writing a SQL layer and I've got an execute method, right now that takes a string or maybe it takes, you know, Michael, you mentioned a little string.

01:03:53.900 --> 01:03:57.960
It takes some variety of some special type that my library supports.

01:03:58.500 --> 01:04:00.080
Should I also take a template there?

01:04:00.300 --> 01:04:01.680
Should I allow a union of both?

01:04:02.140 --> 01:04:03.380
Probably that's a foot gun.

01:04:03.540 --> 01:04:06.120
you probably really want to think about separating these things out.

01:04:06.750 --> 01:04:10.200
And so I think we'll see best practices like that kind of emerge over time.

01:04:10.540 --> 01:04:12.560
Yeah, I would add a couple things.

01:04:12.860 --> 01:04:15.360
One, we actually love f-strings.

01:04:16.480 --> 01:04:16.920
When you

01:04:16.920 --> 01:04:25.960
look at the actual code that implements the t-string functionality in terms of like a library, like an HTML function or whatever, what do you find in there?

01:04:26.190 --> 01:04:32.100
Lots of uses of f-strings to actually go and pump out the actual string that you need.

01:04:33.000 --> 01:04:44.480
It's just done in this, you know, disciplined fashion that we can do because we have carefully separated out when the interpolations are being actually evaluated in that context, right?

01:04:45.660 --> 01:05:01.320
Second thing to think about is in terms of like a good best practice would be, you know, if you have a T-string, you know, and you say HTML, well, I don't think HTML should just like do something there or SQL or whatever.

01:05:01.380 --> 01:05:10.240
It should go and allow it to prepare, you know, a document object model or, you know, prepare a SQL query or whatever.

01:05:10.800 --> 01:05:13.920
And then you execute it or render it or whatever.

01:05:14.380 --> 01:05:23.240
So I would differentiate this sort of setup versus, you know, some final rendering would be a typical best practice that certainly I would recommend.

01:05:24.540 --> 01:05:33.880
And there are obviously more, you know, another best practice definitely is at some point, you probably want to think about the optimization of these things, right?

01:05:33.880 --> 01:05:41.980
And so we really put in some thought into how the template strings can be used as a memorization key efficiently.

01:05:43.290 --> 01:05:46.000
So you can look it up in terms of an LRU cache.

01:05:46.360 --> 01:05:52.580
Now, this is no different than if you looked at the internals of, say, the RE module, you know, sports regular

01:05:52.580 --> 01:05:53.000
expressions.

01:05:53.660 --> 01:05:53.820
Yeah.

01:05:54.870 --> 01:06:00.220
You know, there is this, at one point, you actually had to go into RE compile.

01:06:00.980 --> 01:06:06.760
And it's not a bad practice to do it, but if you look internally at it, it actually does the RE compile for you.

01:06:06.810 --> 01:06:13.120
So you don't pay the overhead of running through this Python parse every single time.

01:06:13.760 --> 01:06:14.700
Interesting. I didn't know that.

01:06:15.360 --> 01:06:15.460
Yeah.

01:06:15.460 --> 01:06:15.560
Yeah.

01:06:16.680 --> 01:06:29.780
But the actual implementation of that in terms of running the regular expression is done through this bytecode virtual machine that's specific to regular expressions.

01:06:30.360 --> 01:06:31.260
and can be very efficient.

01:06:31.630 --> 01:06:44.820
So again, we'll see some of these sorts of separations where we'll think about what it means to go and have a fast path where you actually are evaluating this HTML fragment or whatever, or working with SQL.

01:06:45.320 --> 01:06:49.540
And then there's some sort of, well, you have to pay the parse penalty at some point.

01:06:49.980 --> 01:06:57.940
But part of paying that parsing penalty is ensuring that things are done in a safe fashion, being written out in a structured way.

01:06:58.340 --> 01:07:12.340
So that's, you know, more than makes up for it compared to, again, just, you know, just using f-strings and hoping that your SQL statement didn't include, you know, in that interpolation, the Bobby tables evil injection.

01:07:13.320 --> 01:07:14.060
Yeah, excellent.

01:07:14.300 --> 01:07:24.160
For me, I'd say first a shout out to our PEP sponsor and the hero for the second half of all this, Lissandros, has been marvelous.

01:07:25.660 --> 01:07:30.380
getting the implementation and shaping in our interface to the rest of the core team and all that kind of stuff.

01:07:30.930 --> 01:07:33.740
Really fast turnaround, really joy to work with.

01:07:34.260 --> 01:07:37.900
Dave and Lissandro spent a lot of time ironing out all the little things.

01:07:38.260 --> 01:07:44.700
I think about this whole path since Guido and Jim sat down at a PyCon like three years ago on the implementation.

01:07:44.970 --> 01:07:49.340
I think it's been, this will be like the fourth PyCon since this whole thing first started.

01:07:50.380 --> 01:07:58.400
And Jim and Guido kind of laid the groundwork for where we are right now, which is t-strings is just the start.

01:07:58.630 --> 01:08:00.840
t-strings by itself ain't that much.

01:08:01.150 --> 01:08:07.960
And I think, Jim, you made some decisions to intentionally defer to future PEPs some of the things.

01:08:08.880 --> 01:08:14.140
Jim just said about these functions that don't immediately render.

01:08:14.230 --> 01:08:17.859
They prepare an intermediate representation that might later render.

01:08:18.220 --> 01:08:20.600
For me, that's my interest in this PyCon.

01:08:21.160 --> 01:08:22.120
Let's build a community.

01:08:22.600 --> 01:08:33.759
Let's build a community around interoperability so HTPy doesn't have to spell things our way, but we can talk to each other because we agree on a node interface or something like that for a DOM representation.

01:08:34.520 --> 01:08:38.100
The front-end world has gotten great value out of pluggability.

01:08:38.460 --> 01:08:45.319
The Vite system, for example, is a processing chain with a documented intermediate representation.

01:08:45.640 --> 01:08:48.819
It's easy to write a plug-in to go grab that and hack on it.

01:08:48.819 --> 01:08:50.859
You want to turn everything into a relative URL?

01:08:51.430 --> 01:08:56.400
Write a little plug-in, put it in the chain, and you'll be handed something that lets you operate on things.

01:08:57.180 --> 01:08:59.540
We can do something great together.

01:08:59.540 --> 01:09:06.020
We don't all have to do the XKCD to invent a new standard to replace all the other standards.

01:09:06.580 --> 01:09:08.299
We can still work with Jinja.

01:09:08.400 --> 01:09:14.580
We could still work with HTTPY and some of these other things, and hopefully we'll see some of that at PyCon.

01:09:14.819 --> 01:09:15.880
Awesome. Thank you, guys.

01:09:16.200 --> 01:09:21.060
And we're specifically recording this on a time frame that hopefully...

01:09:21.299 --> 01:09:22.299
Thank you so much, Michael.

01:09:22.600 --> 01:09:28.520
If people are dutiful and on top of their listening, we'll listen to this right before they go to PyCon.

01:09:28.670 --> 01:09:33.779
So I would like to encourage them to reach out to all of you and maybe go to some of the lightning talks.

01:09:34.020 --> 01:09:35.560
And if they have ideas, participate.

01:09:35.940 --> 01:09:36.560
Because now is the time.

01:09:36.779 --> 01:09:38.040
Thanks, Michael, for doing this.

01:09:38.049 --> 01:09:40.500
And we're excited to meet everybody at PyCon who's heading out that way.

01:09:40.799 --> 01:09:40.880
Yeah.

01:09:41.160 --> 01:09:41.560
Paul, you're welcome.

01:09:41.710 --> 01:09:42.720
And you too, Dave.

01:09:43.859 --> 01:09:46.180
Jim, Dave, and Paul, thank you all for being here.

01:09:46.480 --> 01:09:46.799
See you later.

01:09:47.180 --> 01:09:47.279
All

01:09:47.279 --> 01:09:47.420
right.

01:09:47.680 --> 01:09:48.359
Thanks for having us.

01:09:48.759 --> 01:09:49.000
Thank you.

01:09:49.400 --> 01:09:49.580
Bye-bye.

01:09:50.940 --> 01:09:53.359
This has been another episode of Talk Python To Me.

01:09:54.220 --> 01:09:55.060
Thank you to our sponsors.

01:09:55.540 --> 01:09:56.760
Be sure to check out what they're offering.

01:09:56.880 --> 01:09:58.180
It really helps support the show.

01:09:59.120 --> 01:10:02.800
This episode is sponsored by Posit Connect from the makers of Shiny.

01:10:03.280 --> 01:10:07.260
Publish, share, and deploy all of your data projects that you're creating using Python.

01:10:07.860 --> 01:10:13.860
Streamlit, Dash, Shiny, Bokeh, FastAPI, Flask, Quarto, reports, dashboards, and APIs.

01:10:14.820 --> 01:10:16.340
Posit Connect supports all of them.

01:10:16.680 --> 01:10:22.000
Try Posit Connect for free by going to talkpython.fm/posit, B-O-S-I-T.

01:10:23.120 --> 01:10:25.160
And it's brought to you by Auth0.

01:10:25.840 --> 01:10:30.100
Auth0 is an easy-to-implement, adaptable authentication and authorization platform.

01:10:30.820 --> 01:10:37.320
Think easy user login, social sign-on, multi-factor authentication, and robust role-based access control.

01:10:37.720 --> 01:10:42.660
With over 30 SDKs and quick starts, Auth0 scales with your product at every stage.

01:10:43.260 --> 01:10:48.800
Get 25,000 monthly active users for free at talkpython.fm/auth0.

01:10:49.880 --> 01:10:50.760
Want to level up your Python?

01:10:51.200 --> 01:10:54.880
We have one of the largest catalogs of Python video courses over at Talk Python.

01:10:55.340 --> 01:11:00.020
Our content ranges from true beginners to deeply advanced topics like memory and async.

01:11:00.420 --> 01:11:02.560
And best of all, there's not a subscription in sight.

01:11:03.040 --> 01:11:05.580
Check it out for yourself at training.talkpython.fm.

01:11:05.980 --> 01:11:10.440
Be sure to subscribe to the show, open your favorite podcast app, and search for Python.

01:11:10.890 --> 01:11:11.780
We should be right at the top.

01:11:12.280 --> 01:11:21.140
You can also find the iTunes feed at /itunes, the Google Play feed at /play, and the direct RSS feed at /rss on talkpython.fm.

01:11:21.800 --> 01:11:24.040
We're live streaming most of our recordings these days.

01:11:24.390 --> 01:11:31.880
If you want to be part of the show and have your comments featured on the air, be sure to subscribe to our YouTube channel at talkpython.fm/youtube.

01:11:32.900 --> 01:11:34.020
This is your host, Michael Kennedy.

01:11:34.440 --> 01:11:35.280
Thanks so much for listening.

01:11:35.540 --> 01:11:36.420
I really appreciate it.

01:11:36.800 --> 01:11:38.380
Now get out there and write some Python code.