Handling T-Strings, Continued
00:00 In the previous lesson, I showed you how to iterate over a template object to process it. In this lesson, I’ll show you a few more handling patterns.
00:09 Before I show you the next example, a few odds and ends you can take advantage of when playing with t-strings. First, like with regular and f-strings, t-strings can be concatenated.
00:20 You do this using either the plus sign or grouping several together inside of parentheses. You can also have raw t-strings. A raw string is one that doesn’t require escaping.
00:32
For example, if you want to print out n
, with a raw string, you don’t have to escape the slash to stop it from turning into a new line. You make a raw string by prefixing it with an r
, and in the case of a t-string, you prefix it with rt
.
00:48 This feature is really very helpful with regexes in particular. Slashes are meaningful in regexes, and since they already need escaping, that means in some situations you have to escape the escaping that’s messy and hard to read, so a raw string is a better way of doing that.
01:04 This isn’t really a t-string specific thing, but the parts of a template can be used in a match expression. This can make the code in your iteration loop a little cleaner.
01:13 I’ll show you what that looks like in a minute.
01:16 Under the covers, f-strings used to be a bit of a hack. The original Python parser wasn’t capable of parsing the f-strings themselves, so there was a separate parser in the compiler specific to f-strings.
01:28 Python 3.9 switched to a new parser known as the PEG, and it was capable of handling f-strings and much more. The f-string handling got rewritten in Python 3.12 taking advantage of the PEG parser, and one of the side effects of that is you can now nest f-strings.
01:44 Since t-strings use the same mechanism under the hood, you can nest those as well. You probably didn’t need the full history lesson just to understand you can nest them, but well, if you’ve taken one of my courses before, you’ll know that a one-minute tangent is getting off easy.
02:00
Also, like f-strings, t-strings support the await
keyword, meaning you can use coroutines to do concurrent coding. Of course, if you do this, the underlying template handling code has to be async aware as well.
02:12 Let’s combine some of these ideas together into another example.
02:18 A pattern increasingly used in the logging world is something known as structured logging. This entails outputting your log info in an easy to parse manner so that you can use code to process your logs.
02:30 Making your log messages easier to read by a machine often means making it harder for a human to read. So one alternative is to log everything twice: one for people, and one for software.
02:41 This example uses a t-string to print out two different formats for the same message. The top part of the code here is similar to the example in the previous lesson.
02:50
I double check that the argument is a template, then iterate over it. Here, instead of using an if-else
block, this time I’m using the match
statement matching on the instance item
from the iterable.
03:03
Let me scroll down. The case
statement is like the matching part of the conditional and an if
. Here, the case
gets triggered if item contains a string. If it is, it puts its contents into a variable named text
, which I then append to our list.
03:20
The second case
statement gets triggered if item is an Interpolation
object. If so, it takes the attributes from the object and puts them into variables for our value
, expression
, and format)_spec
.
03:31 The underscore is a dummy holder telling Python you don’t care about the conversion attribute for this case. Like with the example in the previous lesson, I use an f-string to format the interpolation and this line adds our field to the dict, which will be used for our structured version of the output.
03:49 Speaking of, here are our two lines of output. The first being the human-readable content, and the second being the stringification of a dict, which for most things will be readable as JSON.
04:00 Let’s try this out, importing it,
04:16
and a t-string to log. My t-string argument is actually the concatenation of two t-strings, and the second t-string is a raw one, meaning the n
gets printed as a slash and an n
rather than as a new line.
04:30 The output from the function is two lines, one, human readable, and the other a dict for our structured logging.
04:39 Way back in lesson two, I outlined two limitations of f-strings, hinting that t-strings might solve them. Well, like f-strings, t-strings are also eagerly evaluated, meaning they get processed in place by the interpreter.
04:52 Lazy evaluation, where the processing gets delayed, was considered for t-strings, but ultimately rejected. The thinking was this isn’t their primary purpose.
05:02 I was actually a little disappointed by this decision as I’ve always wanted a solution to the logger problem. You still use C-style formatting with the logger, so the evaluation can be delayed until after the decision to log has occurred for efficiency reasons. There are some parcel solutions outlined in the PEP that describes t-strings though for lazy evaluation.
05:23 First, if you embed your t-string in a function, then the processing doesn’t happen unless the function gets called. This delays the execution, but it doesn’t really solve the logger problem as you would have to know what your message looked like and put it in a function.
05:38 The second workaround is to use a lambda inside the t-string. The t-string still gets processed immediately, but the lambda won’t get executed, just stored.
05:47 Inside your template handling code, you would have to be aware of this and if there was a lambda there, call it. This is lazy evaluation, but I don’t see anyone using this pattern in the logging problem as it would mean every single log message would need this lambda.
06:01 Granted, the performance cost of creating the string message really isn’t that high, so one compromise would be to use the eager evaluated t-string, but then use this lambda trick for anything that might be expensive to build.
06:13 This would be why I called both these approaches workarounds. They approach laziness but don’t quite feel right yet. The PEP explicitly says, “We hope that the community will develop best practices for lazy evaluation,” which is kind of a punt, but I guess it’s better than nothing.
06:31 This course is based on a very detailed tutorial article on the same topic. If you want to see more t-string handling examples, it might be worth a read. It has examples on an asynchronous handler, writing escape code to sanitize SQL queries, and more escape handling code for HTML, and a more detailed version of the structured logging example that I just showed you.
06:54 If you want to see an actual library instead, Trey Hunner has written a module that does escape processing on regular expressions, ensuring that user input doesn’t result in an injection attack on your regex. And there you have it—t-strings.
07:09 In the last lesson, I’ll summarize the course and point you at even more resources.
Become a Member to join the conversation.