Python eval a string representing dictionary containing network path (backslashes), weird behavior or expected?

StackOverflow https://stackoverflow.com/questions/20196912

  •  04-08-2022
  •  | 
  •  

Question

I am trying to eval a string that represents a dictionary and I am not sure why the eval is producing result that looks incorrect to me unless there is something I am missing.

Here is the eval

>>> a =  "{'a':'\\\\my_host\\my_path'}"
>>> a
"{'a':'\\\\my_host\\my_path'}"
>>> eval(a)
{'a': '\\my_host\\my_path'}
>>> 

The processing of the backslash characters by the eval looks incorrect. The 4 backslashes before my_host are converted into 2 backslashes. But the 2 backslashes before my_path is still 2 backslashes.

What am I missing here? How can I properly eval a string representing dictionary where the values represent a network path?

Thanks.

Was it helpful?

Solution

You need to provide one more backslash, like this -

a = "{'a':'\\\\\my_host\\my_path'}"

This is because in \\\\m 1st \ from left gets consumed in escaping the \ after it. Hence to protect the first backslash, we need to add one more before that.

or if you don't want all this fuss you can use r for raw string, like -

a = "{'a':r'\\\\my_host\\my_path'}"

This won't consider any character as special.

>>> a =  "{'a':r'\\\\my_host\\my_path'}"
>>> eval(a)
{'a': '\\\\my_host\\my_path'}
>>> a =  "{'a':'\\\\\my_host\\my_path'}"
>>> eval(a)
{'a': '\\\\my_host\\my_path'}

OTHER TIPS

Backslashes combined with eval can be really tricky. First consider these two strings:

>>> "\m"
"\\m"
>>> "\\m"
"\\m"

They both produce the same output. In the first case first backslash tries to consume next character, but m is not a special character. Therefore, backslash remains a single backslash (raw: \m). In normal strings backslashes are escaped, so Python escapes it for you. In the second case first backslash escapes second backslash, so we have a single backslash (raw: \m) which Python escapes for you.

Now, let's move to more backslashes. Consider a raw string \a. To get a single backslash we need to escape it in out string, so we need to evaluate a raw string \\a

>>> eval(r'"\\a"')
'\\a'

Which is indeed raw \a. However, if our initial string is not raw, Python performs first escaping before calling eval which gives

>>> eval('"\\a"')
'\x07'

Not what we need. What happened here? First, Python takes out string "\\a" and uses first backslash to escape second backslash. We end up with string "\a" which gets evaluated. Python looks for special characters again and treats \a as a special ASCII character BEL.

If wee want to have the same output is in the first example we need four backslashes. First and second are consumed to one backslash, third and fourth to another backslash, so we end up with \\a which is evaluated to raw \a

>>> eval('"\\\\a"')
'\\a'

Exactly what we need.

You should never leave odd number of backslashes because it can cause unexpected things. e.g.

>>> eval("{'a':'\\\\\my_host\\my_path'}")
{'a': '\\\\my_host\\my_path'}

Apparently it works fine, but what if the name started with b

>>> eval("{'a':'\\\\\by_host\\by_path'}")
{'a': '\\\x08y_host\x08y_path'}

Ugh...

The correct way to do this is:

>>> eval("{'a':'\\\\\\\\by_host\\\\by_path'}")
{'a': '\\\\by_host\\by_path'}

After double escaping, each four backslashes are merged into one, so our path is now \\by_host\by_path as we need.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top