And you add the safety inside db.safe explicitly instead of implicitly in db.execute.
If you want to be fancy you can also assign name to db.foos inside db.safe to use it later (even in execute).
And you add the safety inside db.safe explicitly instead of implicitly in db.execute.
If you want to be fancy you can also assign name to db.foos inside db.safe to use it later (even in execute).
I think one thing you might be missing is that in the t-string version, `db.execute` is not taking a string; a t-string resolves to an object of a particular type. So it is doing your `db.safe` operation, but automatically.
At least db.safe says what it does, unlike t".
Template is also more powerful/concise in that the stringify function can handle the "formatting" args however it looks.
Note also, that there's no requirement that the template ever become a str to be used.
`db.execute(Template)` and `db.unsafeExecute(str)`
also, you're trusting that the library implementer raises a runtime exception if a string a passed where a template is expected. it's not enough to rely on type-checks/linting. and there is probably going to be a temptation to accept `db.execute(sql: Union[str, Template])` because this is non-breaking, and sql without params doesn't need to be templated - so it's breaking some stuff that doesn't need to be broken.
i'm not saying templates aren't a good step forward, just that they're also susceptible to the same problems we have now if not used correctly.
To illustrate the question further, consider a similar html.safe: f"<a href={html.safe(url)}>{html.safe(desc)</a>" - the two calls to html.safe require completely different escaping, how does it know which to apply?