Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get results out of a Python exec()/eval() call?

Tags:

python

eval

exec

I want to write a tool in Python to prepare a simulation study by creating for each simulation run a folder and a configuration file with some run-specific parameters.

study/
  study.conf
  run1
    run.conf
  run2
    run.conf

The tool should read the overall study configuration from a file including (1) static parameters (key-value pairs), (2) lists for iteration parameters, and (3) some small code snippets to calculate further parameters from the previous ones. The latter are run specific depending on the permutation of the iteration parameters used.

Before writing the run.conf files from a template, I need to run some code like this to determine the specific key-value pairs from the code snippets for that run

code = compile(code_str, 'foo.py', 'exec')
rv=eval(code, context, { })

However, as this is confirmed by the Python documentation, this just leads to a None as return value.

The code string and context dictionary in the example are filled elsewhere. For this discussion, this snippet should do it:

code_str="""import math
math.sqrt(width**2 + height**2)
"""

context = {
    'width' : 30,
    'height' : 10
}

I have done this before in Perl and Java+JavaScript. There, you just give the code snippet to some evaluation function or script engine and get in return a value (object) from the last executed statement -- not a big issue.

Now, in Python I struggle with the fact that eval() is too narrow just allowing one statement and exec() doesn't return values in general. I need to import modules and sometimes do some slightly more complex calculations, e.g., 5 lines of code.

Isn't there a better solution that I don't see at the moment?

During my research, I found some very good discussions about Pyhton eval() and exec() and also some tricky solutions to circumvent the issue by going via the stdout and parsing the return value from there. The latter would do it, but is not very nice and already 5 years old.

like image 346
John Avatar asked Sep 02 '25 16:09

John


1 Answers

The exec function will modify the global parameter (dict) passed to it. So you can use the code below

code_str="""import math
Result1 = math.sqrt(width**2 + height**2)
"""
context = {
    'width' : 30,
    'height' : 10
}

exec(code_str, context)

print (context['Result1']) # 31.6

Every variable code_str created will end up with a key:value pair in the context dictionary. So the dict is the "object" like you mentioned in JavaScript.

Edit1:

If you only need the result of the last line in code_str and try to prevent something like Result1=..., try the below code

code_str="""import math
math.sqrt(width**2 + height**2)
"""

context = { 'width' : 30, 'height' : 10 }

lines = [l for l in code_str.split('\n') if l.strip()]
lines[-1] = '__myresult__='+lines[-1] 

exec('\n'.join(lines), context)
print (context['__myresult__'])

This approach is not as robust as the former one, but should work for most case. If you need to manipulate the code in a sophisticated way, please take a look at the Abstract Syntax Trees

like image 159
gdlmx Avatar answered Sep 05 '25 10:09

gdlmx