Python exec() function provides support for dynamic code execution.
Python exec()
Python exec() function signature is:
exec(object, globals, locals)
object – It should be either a string or code object. If it’s string, then it’s parsed as a block of python statements and then execute it.
globals – used to specify global functions available to be executed by exec() function. It must be a dictionary. If the __builtins__ is not defined, then all the built-in functions are available for exec function.
locals – used to specify local functions and variables available. It could be any mapping object. However, a dictionary is preferred for similarity with globals variable.
Python exec() function returns None. So it can’t be used with return and yield statements.
Python exec() vs eval()
Python exec() and eval() functions are very similar. Their usage and support for dynamic code execution is also very similar.
However, there is one major difference between exec() and eval() functions. Python exec() function doesn’t return anything whereas eval() function evaluates the expression and returns the value to the calling code.
Python exec() example
Let’s look at a simple example of python exec() function.
x = 1
exec('print(x==1)')
exec('print(x+2)')
Output:
True
3
Python exec() dynamic code execution
Let’s look at another example where the user will enter the code to be executed by our program.
from math import *
for l in range(1, 3):
func = input("Enter Code Snippet to execute:n")
try:
exec(func)
except Exception as ex:
print(ex)
break
print('Done')
Example Output:
Enter Code Snippet to execute:
print(sqrt(16))
4.0
Enter Code Snippet to execute:
print(min(2,1))
1
Done
Notice that I am using sqrt() from math module, print() and min() are built-in functions.
Python exec() security risks
Since we can execute any code, there are security risks associated with exec() function. What if someone imports os module and issue os.system('rm -rf /')
command. This will crash our system because all the files will be deleted. This is when globals and locals parameters come in handy to have restricted access.
Python exec() globals and locals
Before we decide on the functions that should be available to exec(), it’s a good idea to get the list of available functions and modules using dir()
function.
from math import *
def square_root(n):
return sqrt(n)
exec('print(dir())') # list of names in the current local scope
Output:
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'square_root', 'tan', 'tanh', 'tau', 'trunc']
That’s a lot of functions, ranging from the builtins module, math module and our defined function square_root.
Let’s see what happens if we provide globals value as an empty dictionary.
exec('print(dir())',{})
Output:
['__builtins__']
So built-in functions are available if globals don’t have a key for it. Let’s see how to specify access to only a few functions from the builtins module.
exec('print(min(1,2))',{"__builtins__":{"min": min, "print": print}}) #1
Let’s look at another example where we will define both globals and locals parameter values.
y=5
exec('print(y+1)',{"__builtins__": None}, {"y": y, "print": print}) # 6
Let’s look at a final example where we will provide access to only a few math module functions.
from math import *
for l in range(1, 3):
func = input("Enter Code Snippet with Math Function to execute.nFor Examples; print(squareRoot(x)) and print(pow(x,y)):n")
try:
exec(func, {"squareRoot": sqrt, "pow": pow})
except Exception as ex:
print(ex)
break
print('Done')
A Sample Output:
Enter Code Snippet with Math Function to execute.
For Examples; print(squareRoot(x)) and print(pow(x,y)):
print(squareRoot(100))
10.0
Enter Code Snippet with Math Function to execute.
For Examples; print(squareRoot(x)) and print(pow(x,y)):
print(max(2,4))
4
Done
We can specify a custom name to map with any function like we have defined squareRoot to be mapped to sqrt function.
Notice that built-in functions are available because I haven’t explicitly excluded them. So a better way to define exec() is:
exec(func, {"squareRoot": sqrt, "pow": pow, "__builtins__": None, "print": print})
A sample output will be:
Enter Code Snippet with Math Function to execute.
For Examples; print(squareRoot(x)) and print(pow(x,y)):
print(squareRoot(100))
10.0
Enter Code Snippet with Math Function to execute.
For Examples; print(squareRoot(x)) and print(pow(x,y)):
print(max(2,4))
'NoneType' object is not subscriptable
Done
Now the error is coming because max() function is not accessible to exec() function.
Summary
Just like eval(), python exec() function is very powerful. You shouldn’t allow any untrusted code to be executed using exec() as it can really harm your system.
Reference: API Doc