22

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,

Christos Hayward
  • 5,777
  • 17
  • 58
  • 113
  • Does this answer your question? [What's the difference between eval, exec, and compile?](https://stackoverflow.com/questions/2220699/whats-the-difference-between-eval-exec-and-compile) – SuperStormer Nov 07 '21 at 01:55

5 Answers5

32

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
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
17

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
darda
  • 3,597
  • 6
  • 36
  • 49
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
7

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

Community
  • 1
  • 1
Andy Hayden
  • 359,921
  • 101
  • 625
  • 535
6

(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.

Att Righ
  • 1,439
  • 1
  • 16
  • 29
0

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.

eccstartup
  • 501
  • 8
  • 24