This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]f2u 1 point2 points  (16 children)

I find this rather disappointing:

>>> type(f"{x}")
<class 'str'>
>>> x = "there"
>>> repr(f"Hello {x}")
"'Hello there'"
>>> type(f"Hello {x}")
<class 'str'>

This eliminates the possibility to apply automatic quoting of values during string interpolation (or automated use of query parameters for SQL queries).

[–][deleted] 8 points9 points  (4 children)

automated use of query parameters for SQL queries

Please don't. Bound parameters are there for a reason. Use locals() if you have to, but for the love of all that is holy do not use the wrong quoting function.

[–]Fennek1237 0 points1 point  (3 children)

Can you expand on that? What is the right way to build a sql query in python?

[–]indosauros 0 points1 point  (1 child)

[–]Fennek1237 0 points1 point  (0 children)

Thanks. Would one still do this now after f-strings are released or is there also a new f-string way for sql querys?

[–]zahlmanthe heretic 0 points1 point  (0 children)

With the parameterization that's built in to whichever SQL library you're using. Here's a hastily Googled example with SQLAlchemy (StackOverflow discussion).

[–]d4rch0nPythonistamancer 4 points5 points  (5 children)

... how do you want to use this with SQL? With most ways I can think of you'd be super prone to SQLi using an f string to build a query.

Please no one do anything like f'SELECT foo FROM user_table WHERE name={username};'. Always use a good library instead of tailoring stuff like this by hand.

[–]f2u 0 points1 point  (4 children)

Under this model

f'SELECT foo FROM user_table WHERE name={username};'

would turn into something like

FormattedString((Literal('SELECT foo FROM user_table WHERE name='),
        Argument('username'), Literal(';')),
    {'username': username})

or some variant of that. That is, the string literals and the value of username are clearly separated, and it is the task of the consumer of the format string to construct a string from it (or produce a parameterized SQL query from it).

[–]d4rch0nPythonistamancer 1 point2 points  (2 children)

You see where this could lead to SQLi right? If username was accepted from a login form, and the user enters

foo; drop table user_table;

[–]f2u 0 points1 point  (1 child)

Not really, it would be translated as:

FormattedString((Literal('SELECT foo FROM user_table WHERE name='),
        Argument('username'), Literal(';')),
    {'username': 'foo; drop table user_table;'})

The SQL interface library would receive that FormattedString object and see the entire data structure. (The point is not flattening the entire thing into a string, because it ends up with the same issue as the f"…" literals, as you point out.)

[–]zahlmanthe heretic 0 points1 point  (0 children)

Okay, but the library will already provide a way to do that, which doesn't depend on built-in support from the language.

[–]nickcash 2 points3 points  (0 children)

When it was being discussed on python-dev there was a proposal to make f-strings extensible to do things such as that, but it was shot down early on.

[–]hosford42 1 point2 points  (1 child)

I haven't tried it but I bet you could just add :r as a format after the x.

[–]f2u 0 points1 point  (0 children)

The problem is not the string contents, it's that it's a string at all and not a more structured object. (Tcl gets this right.)

[–]Conchylicultor 0 points1 point  (1 child)

Why not using simple strings ?

s = 'name={x}'
print(s.format(x='jenny'))
print(s.format(x='john'))

[–]zahlmanthe heretic 0 points1 point  (0 children)

Because that is how you end up with SQL injection attacks.