Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly interpret a single line of python code?

I need to execute a line of python code that is entered by the user. If it is a statement I want to execute it, but if it is an expression, I want the result to be returned and do some fancy stuff with it. The problem is that python has two different functions for that, namely exec and eval.

Currently I just try to evaluate the string that the user entered. If that raises a SyntaxError, this may indicate that the string is an statement instead, so I try to execute it.

try:
    result = eval(command, scope)
except SyntaxError:
    # Probably command is a statement, not an expression
    try:
        exec(command, scope)
    except Exception as e:
        return command + ' : ' + str(e)
except Exception as e:
    return command + ' : ' + str(e)
else:
    pass # Some fancy stuff

This feels rather hacky. Is there a neater, more pythonic way to do this?

like image 869
chtenb Avatar asked May 09 '14 21:05

chtenb


1 Answers

While I think your existing code is probably reasonably Pythonic (under the doctrine that it's "easier to ask forgiveness than permission"), I suspect the best alternative approach is to use the ast module to inspect the code in your string:

tree = ast.parse(some_input_string)
if len(tree.body) == 1 and isinstance(tree.body[0], ast.Expr):
    result = eval(some_input_string, scope)
else:
    exec(some_input_string, scope)
    result = None

Note that some common statements are really "expression statements". So, an input string like 'do_stuff("args")' will use the eval branch of the code above, rather than the exec branch. I don't think this will have any adverse consequences, but you never know.

It is also possible to compile the tree that has been parsed and then pass the result into the eval or exec calls later. I found it rather fiddly to get right though (you need to wrap the ast.Expr's value attribute in an ast.Expression in the top branch) and so I went with the simpler (to read and understand) alternative of just passing in the string and letting Python parse it again.

like image 51
Blckknght Avatar answered Oct 21 '22 23:10

Blckknght