Christos Hayward
Christos Hayward

Reputation: 5993

Why is Python's eval() rejecting this multiline string, and how can I fix it?

I am attempting to eval the following tab-indented string:

'''for index in range(10):
        os.system("echo " + str(index) + "")
'''

I get, "There was an error: invalid syntax , line 1"

What is it complaining about? Do I need to indent to match the eval() statement, or write it to a string file or temp file and execute that, or something else?

Thanks,

Upvotes: 29

Views: 30184

Answers (5)

eccstartup
eccstartup

Reputation: 517

A better version.

def multiline_eval(expr, context={}):
    "Evaluate several lines of input, returning the result of the last line"
    tree = ast.parse(expr)
    eval_exprs = []
    exec_exprs = []
    for module in tree.body:
        if isinstance(module, ast.Expr):
            eval_exprs.append(module.value)
        else:
            exec_exprs.append(module)
    exec_expr = ast.Module(exec_exprs, type_ignores=[])
    exec(compile(exec_expr, 'file', 'exec'), context)
    results = []
    for eval_expr in eval_exprs:
        results.append(eval(compile(ast.Expression((eval_expr)), 'file', 'eval'), context))
    return '\n'.join([str(r) for r in results])

So you can run code in Qt.

Upvotes: 1

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250951

To use such statements with eval you should convert them to code object first, using compile:

In [149]: import os

In [150]: cc = compile('''for index in range(10):
    os.system("echo " + str(index) + "")''','<string>','single')

In [154]: eval cc
--------> eval(cc)
0
Out[154]: 0
1
Out[154]: 0
2
Out[154]: 0
3
Out[154]: 0
4

In [159]: cc = compile("2+2", '<string>', 'single')  # works with simple expressions too 

In [160]: eval cc
--------> eval(cc)
Out[160]: 4

Upvotes: 18

Att Righ
Att Righ

Reputation: 1779

(See default security warning at end before you put code like this into production!)

The other answers do a good job of explaining the difference between exec and eval.

Nevertheless, I found myself wanting to take input like x=1; y=2; x+y rather than force people to write:

def f():
   x = 1
   y = 2
   return x + y

String manipulation of code to build this sort of function is a risky business.

I ended up using the following approach:

def multiline_eval(expr, context):
    "Evaluate several lines of input, returning the result of the last line"
    tree = ast.parse(expr)
    eval_expr = ast.Expression(tree.body[-1].value)
    exec_expr = ast.Module(tree.body[:-1])
    exec(compile(exec_expr, 'file', 'exec'), context)
    return eval(compile(eval_expr, 'file', 'eval'), context)

This parses python code; uses the ast library to rebuild an ast of everything apart from the last line; and the last line, execing the former and eval'ing the later.

Security warning

This is the obligatory security warning that you have to attach to eval. Eval'ing and exec'ing code that is provided by a non-privileged user is of course insecure. In these cases you may prefer to use another approach, or consider ast.literal_eval. eval and and exec tend to be bad ideas unless you actually want to give your user the full expressive power of python.

Upvotes: 7

Andy Hayden
Andy Hayden

Reputation: 375485

We evaluate (eval) expressions, and execute (exec) statements.

See: Expression Versus Statement.

Expression: Something which evaluates to a value. Example: 1+2/x
Statement: A line of code which does something. Example: GOTO 100

Upvotes: 7

Joran Beasley
Joran Beasley

Reputation: 113978

eval evaluates stuff like 5+3

exec executes stuff like for ...

>>> eval("for x in range(3):print x")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for x in range(3):print x
      ^
SyntaxError: invalid syntax
>>> exec("for x in range(3):print x")
0
1
2
>>> eval('5+3')
8

Upvotes: 38

Related Questions