Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is eval('"\x27"') == eval('"\\x27"')?

I'm very confused with python's eval():

I tried eval('"\x27"') == eval('"\\x27"') and it evaluates to True. Can somebody explain why this is the case? Both expressions evaluate to "'". I understand why eval('"\x27"') does (the string evaluated has a single character, which is an escaped hexadecimal representing an apostrophe), but shouldn't eval('"\\x27"') be equal to "\\x27"?

Secondly, adding to the confusion, if I set the following variables,

s = "\x27"
t = "\\x27"

then eval('s') is again "'", but eval('t') is "\\x27". Why is that?

like image 841
titiree Avatar asked Jul 03 '18 15:07

titiree


2 Answers

According to the docs, eval "parses and evaluates the argument as a python expression". In other words, it's applying the same processing that is applied if you write x = "foobar \n" inside a program or the IDLE. In this example, \n gets turned into a newline-character (which, note, is not identical to the literal \n).

If you typed x = "\x27" into the IDLE, you'd get x == "'". The x27 is escaped because of the backslash and thus changed during evaluation. If you escape the backslash, then x27 is not changed during evaluation. Instead, you simply get a string with a backslash followed by x27.

Now if you evaluated that string again, you only have one backslash left - seemingly escaping x27. Thus, it is changed to '.

Another way to look at this: eval("\x27") evaluates the argument twice, but it is only changed the first time, to "'". eval("\\x27") also evaluates the argument twice, first to \x27, then to "'".


Here's an easier example to demonstrate how this works:

>>> x = "\"foobar\""
>>> x == "foobar"
False
>>> x == "\"foobar\""
True
>>> x = eval(x) # changes value of x from "foobar" to just foobar. Still string though, thus still ""
>>> x == "foobar"
True
>>> x == "\"foobar\""
False

Look at it like this: The right hand side of y = "2" contains two components: the information that y should be of type string, expressed using the two ", and the value of that string, expressed by the character 2. The separation of these two aspects is done during evaluation of the code you write. The string object itself never sees the " during initialization.

So in the above example, after the first line, we have x of type str with value "foobar". If you evaluate that again, the " are interpreted this time not as part of the value of x but as the type of x. So eval("\"foobar\"") basically transforms the string "foobar" to foobar, which, if you want to use that using the language Python, you have to write as "\"foobar\"" and "foobar".

like image 57
Nearoo Avatar answered Nov 02 '22 22:11

Nearoo


For '"\x27"' the backslash escape is expanded during parsing, so this is literally '"\'"'. '"\\x27"' only strips the backslash, i.e. it is equal to r'"\x27"'.

A direct eval() on the string literals adds another iteration of special character expansion: In the first case, \' is a valid escape sequence yielding '. The second case is the same as above.

When you use variable names, only one round of unescaping is performed when you assign the values. eval('s') simply expands to the value of s without further unescaping. If you want an emulation of the first case, you need to eval(s), i.e. evaluate the value of the string referenced by s.

like image 39
dhke Avatar answered Nov 02 '22 23:11

dhke