←back to thread

620 points tambourine_man | 1 comments | | HN request time: 0s | source
Show context
serbuvlad ◴[] No.43750075[source]
All things considered, this is pretty cool. Basically, this replaces

    db.execute("QUERY WHERE name = ?", (name,))
with

    db.execute(t"QUERY WHERE name = {name}")
Does the benefit from this syntactic sugar outweigh the added complexity of a new language feature? I think it does in this case for two reasons:

1. Allowing library developers to do whatever they want with {} expansions is a good thing, and will probably spawn some good uses.

2. Generalizing template syntax across a language, so that all libraries solve this problem in the same way, is probably a good thing.

replies(12): >>43750226 #>>43750250 #>>43750260 #>>43750279 #>>43750513 #>>43750750 #>>43752117 #>>43752173 #>>43752293 #>>43754738 #>>43756560 #>>43763190 #
Tenoke ◴[] No.43750250[source]
I don't see what it adds over f-string in that example?
replies(6): >>43750258 #>>43750261 #>>43750262 #>>43750265 #>>43750295 #>>43750581 #
ds_ ◴[] No.43750261[source]
The execute function can recognize it as a t-string and prevent SQL injection if the name is coming from user input. f-strings immediately evaluate to a string, whereas t-strings evaluate to a template object which requires further processing to turn it into a string.
replies(1): >>43750286 #
Tenoke ◴[] No.43750286[source]
Then the useful part is the extra execute function you have to write (it's not just a substitute like in the comment) and an extra function can confirm the safety of a value going into a f-string just as well.

I get the general case, but even then it seems like an implicit anti-pattern over doing db.execute(f"QUERY WHERE name = {safe(name)}")

replies(5): >>43750324 #>>43750380 #>>43750409 #>>43754093 #>>43756889 #
ubercore ◴[] No.43750324[source]
Problem with that example is where do you get `safe`? Passing a template into `db.execute` lets the `db` instance handle safety specifically for the backend it's connected to. Otherwise, you'd need to create a `safe` function with a db connection to properly sanitize a string.

And further, if `safe` just returns a string, you still lose out on the ability for `db.execute` to pass the parameter a different way -- you've lost the information that a variable is being interpolated into the string.

replies(1): >>43750412 #
Tenoke ◴[] No.43750412[source]
db.safe same as the new db.execute with safety checks in it you create for the t-string but yes I can see some benefits (though I'm still not a fan for my own codebases so far) with using the values further or more complex cases than this.
replies(1): >>43750482 #
ubercore ◴[] No.43750482[source]
Yeah but it would have to be something like `db.safe("SELECT * FROM table WHERE id = {}", row_id)` instead of `db.execute(t"SELECT * FROM table WHERE id = {row_id}")`.

I'd prefer the second, myself.

replies(2): >>43750548 #>>43753222 #
Tenoke ◴[] No.43750548[source]
No, just `db.execute(f"QUERY WHERE name = {db.safe(name)}")`

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).

replies(4): >>43750786 #>>43751243 #>>43751257 #>>43759309 #
ZiiS ◴[] No.43750786[source]
But if someone omits the `safe` it may still work but allow injection.
replies(1): >>43751449 #
thunky ◴[] No.43751449[source]
Same is true if someone forgets to use t" and uses f" instead.

At least db.safe says what it does, unlike t".

replies(2): >>43751947 #>>43751995 #
1. fwip ◴[] No.43751995[source]
Your linter can flag the type mismatch, and/or the function can reject f"" at runtime. This is because t"" yields a Template, not a str.

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.