Python exec()

The exec() method executes the dynamically created program, which is either a string or a code object.

The syntax of exec():

exec(object, globals, locals)

1. exec() Parameters

exec() takes three parameters:

  • object – Either a string or a code object
  • globals (optional) – a dictionary
  • locals (optional)- a mapping object. Dictionary is the standard and commonly used mapping type in Python.

The use of globals and locals will be discussed later in the article.

2. Return Value from exec()

exec() doesn’t return any value, it returns None.

3. Example 1: How exec() works?

program = 'a = 5\nb=10\nprint("Sum =", a+b)'
exec(program)

Output

Sum = 15

Here, the string object program is passed to exec() which executes the program. globals and locals are omitted in this case.

4. Example 2: Allow user to provide input

program = input('Enter a program:')
exec(program)

Output

Enter a program: [print(item) for item in [1, 2, 3]]
1
2
3

If you want to take Python code from the user which allows multiline code (using '\n'), you can use compile() method before using exec().

Learn more about compile() method in Python.

5. Be careful while using exec()

Consider a situation, you are using a Unix system (macOS, Linux etc) and you have imported os module. The os module provides a portable way to use operating system functionalities like read or write a file.

If you allow users to input a value using exec(input()), the user may issue commands to change file or even delete all the files using the command os.system('rm -rf *').

If you are using exec(input()) in your code, it’s a good idea to check which variables and methods the user can use. You can see which variables and methods are available using dir() method.

from math import *
exec('print(dir())')

Output

['In', 'Out', '_', '__', '___', '__builtin__', '__builtins__', '__name__', '_dh', '_i', '_i1', '_i2', '_ih', '_ii', '_iii', '_oh', '_sh', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exit', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'get_ipython', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'quit', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']

6. Restricting the Use of Available Methods and Variables in exec()

More often than not, all the available methods and variables used in exec() may not be needed, or even may have a security hole. You can restrict the use of these variables and methods by passing optional globals and locals parameters (dictionaries) to exec() method.

6.1. Both globals and locals parameters are omitted

If both parameters are omitted (as in our earlier examples), the code expected to be executed by exec() is executed in the current scope. You can check the available variables and methods using the following code:

exec('print(dir())')

6.2. Passing globals parameter; locals parameter is omitted

The globals and locals parameters (dictionaries) are used for global and local variables respectively. If locals dictionary is omitted, it defaults to globals dictionary. Meaning, globals will be used for both global and local variables.

Note: You can check the current global and local dictionary in Python using globals() and locals() built-in methods respectively.

6.3. Passing empty dictionary as globals parameter

from math import *
exec('print(dir())', {})

# This code will raise an exception
# exec('print(sqrt(9))', {})

If you pass an empty dictionary as globals, only the __builtins__ are available to the object (first parameter to the exec()). Even though we have imported math module in the above program, trying to access any of the functions provided by the math module will raise an exception.

Output

['__builtins__']

Making Certain Methods available

from math import *
exec('print(dir())', {'sqrt': sqrt, 'pow': pow})

# object can have sqrt() module
exec('print(sqrt(9))', {'sqrt': sqrt, 'pow': pow})

Here, the code that is executed by exec() can also have sqrt() and pow() methods along with __builtins__.

It’s possible to change the name of the method according to your wish.

from math import *
exec('print(dir())', {'squareRoot': sqrt, 'pow': pow})

# object can have squareRoot() module
exec('print(squareRoot(9))', {'squareRoot': sqrt, 'pow': pow})

In the above program, squareRoot() calculates the square root (similar functionality like sqrt()). However, trying to use sqrt() will raise an exception.

Restricting the Use of built-ins

You can restrict the use of __builtins__ by giving value None to the '__builtins__' in the globals dictionary.

exec(object, {'__builtins__': None}) 

6.4. Passing both globals and locals dictionary

You can make needed functions and variables available for use by passing locals dictionary. For example:

from math import *

globalsParameter = {'__builtins__' : None}
localsParameter = {'print': print, 'dir': dir}
exec('print(dir())', globalsParameter, localsParameter)

Output

['dir', 'print']

Here, only two built-in methods print() and dir() can be executed by exec() method.

It’s important to note that, exec() executes the code and doesn’t return any value (returns None). Hence, you cannot use return and yield statements outside of the function definitions.