Meanwhile, pytest is still not part of the standard library.
Other languages have a policy of prototyping such things out of core, and only adding it to the core language if it gains traction. Of course that works better if the language has a mechanism for extending the syntax out of core.
t-strings don't interact with anything else in the language; they, as you yourself pointed out, could almost be an isolated library. That makes them low impact.
This is also true syntactically; they're just another type of string, denoted by "t" instead of "f". That's easy to fit into one's existing model of the language.
Moreover, even semantically, from the point of view of most language users, they are equivalent to f-strings in every way, so there's nothing to learn, really. It's only the library writers who need to learn about them.
Then we have to consider the upsides - the potential to eliminate SQL and HTML injection attacks. The value/cost is so high the feature a no-brainer.
This dummy example splits a string that it was given, then if one of those values is in the callers context it saves those in self.context, and has an output function to assemble it all together. Obviously this example is not very useful, but it shows how a library could do this as a class or function without the user having to pass in locals().
import inspect
class MyXString:
""" will split on whitespace and replace any string that is a variable name
with the result of str(variable)"""
def __init__(self, string):
self.string = string
caller_locals = inspect.currentframe().f_back.f_locals
self.context = {}
for key in set(string.split()):
if key in caller_locals:
self.context[key] = caller_locals[key]
def output(self):
output = self.string
for k,v in self.context.items():
output = output.replace(k,str(v))
return output
For this reason, I think it's not true that this absolutely had to be a language feature rather than a library. A template class written in pure Python could have done the same lookup in its __init__.
current_frame = inspect.currentframe()
env = current_frame.f_back.f_locals.copy()
I did it in uplaybook so that you can do things like: for module in ['foo', 'bar', 'baz']:
ln(path="/etc/apache2/mods-enabled", src="/etc/apache2/mods-available/{{ module }}.load")
This is an ansible-like tooling with a python instead of YAML syntax, hence the Jinja2 templating.