4 ms·
I’m not convinced that a language level feature is worth it for this. You could achieve the same thing with a function returning an f-string no? And if you want
by ratorx 1y ago
I’m not convinced that a language level feature is worth it for this. You could achieve the same thing with a function returning an f-string no? And if you want injection safety, just use a tag type and a sanitisation function that takes a string and returns the type. Then the function returning the f-string could take the Sanitised string as an argument to prevent calling it with unsanitised input.
I guess it’s more concise, but differentiating between eager and delayed execution with a single character makes the language less readable for people who are not as familiar with Python (especially latest update syntax etc).
EDIT: to flesh out with an example:
class Sanitised(str):
# init function that sanitises or just use as a tag type that has an external sanitisation function.
def sqltemplate(name: Sanitised) -> str:
return f”select * from {name}”
# Usage
sqltemplate(name=sanitise(“some injection”))
# Attempt to pass unsanitised
sqltemplate(name=“some injection”) # type check error
- vjerancrnjak 1y agoIt's worse than function returning an f-string. Template type is very flat, you won't know which arguments are left unbound. modules, classes, protocols, functions returning functions, all options in Python, each work well for reuse, no need to use more than 2 at once, yet the world swims upstream.
- shikon7 1y agoIf its only use is to make injecton safety a bit easier to achieve, it's worth it to me.
- ratorx 1y agoDoes it make it easier? The “escape” for both is to just use unsafe version of the Template -> string function or explicitly mark an unsafe string as sanitised. Both seem similar in (un)safety
- davepeck 1y ago> the Template -> string function There is no such function; Template.__str__() returns Template.__repr__() which is very unlikely to be useful. You pretty much have to process your Template instance in some way before converting to a string.
- ratorx 1y agoRight, but it is possible to write a template -> string function that doesn’t sanitise and use it (or more realistically use the wrong one). Just as it’s possible to unsafely cast an unsafe string to a sanitised one and use it (rather than use a sanitise function that returns the wrapper type). They are both similar in their unsafety.
- itishappy 1y agoI don't see how this prevents calling your returned f-string with unsensitized inputs. evil = "<script>alert('evil')</script>" sanitized = Sanitized(evil) whoops = f"<p>{evil}</p>"
- ratorx 1y agoI’m not sure you understood my example. The f-string is within a function. The function argument only accepts sanitised input type. If you create a subclass of str which has an init function that sanitises, then you can’t create a Sanitised type by casting right? And even if you could, there is also nothing stopping you from using a different function to “html” that just returns the string without sanitising. They are on the same relative level of safety.
- itishappy 1y agoOh, I'm pretty sure I didn't understand your example and am probably missing something obvious. That's why I'm here asking dumb questions! I think I'm following more, and I see how you can accomplish this by encapsulating the rendering, but I'm still not seeing how this is possible with user facing f-strings. Think you can write up a quick example?
- ratorx 1y agoAdded example to parent comment.
- itishappy 1y agoThanks mate! (BTW: Indenting code with four spaces makes HN format it like code.) So the thing I'm still not getting from your example is allowing the template itself to be customized. evil = "<script>alert('evil')</script>" template1 = t"<p>{evil}</p>" template2 = t"<h1>{evil}</h1>" html(template1) html(template2)
- 1y ago
- nhumrich 1y ago> You could achieve the same thing with a function returning an f-string no no. > just use a tag type and a sanitisation function that takes a string and returns the type Okay, so you have a `sqlstring(somestring)` function, and the dev has to call it. But... what if they pass in an f-string? `sqlstring(f'select from mytable where col = {value}')` You havent actually prevented/enforced anything. With template strings, its turtles all the way down. You can enforce they pass in a template and you can safely escape anything that is a variable because its impossible to have a variable type (possible injection) in the template literal.
- ratorx 1y agoAdded example to parent comment. This example still works, the entire f-string is sanitised (including whatever the value of name was). Assuming sqlstring is the sanitisation function. The “template” would be a separate function that returns an f-string bound from function arguments.
- nhumrich 1y agoYes. Only if your dev remembers to use sanatized all the time. This is how most SQL works today. You could also forget and accidentally write a f-string, or because you dont know. But with t-strings you can actually prevent unsanatized inputs. With your example, you need to intentionally sanitize still. You cant throw an error on unsanitized because the language has no way to know if its sanitized or not. Either way, its just a string. "returning an f-string" is equivalent to returning a normal string at runtime.
- ratorx 1y agoWell you enforce this with types. That’s how every other language does it. By specifying that the type of the function has to be a sanitised string, it will reject unsanitised string with the type checker. > it has no way of knowing if it’s sanitised or not It does. You define the SanitisedString class. Constructing one sanitises the string. Then when you specify that as the argument, it forces the user to sanitise the string. If you want to do it without types, you can check with `isinstance` at runtime, but that is not as safe.
- BiteCode_dev 1y agoYou don't need iterators or decorators. But normalizing one pattern ensures the whole community build API around it. This creates a unified ecosystem. And it's a very clean API that is a no brainer for the string user.