Using Python's exec() To Generate Code Dynamically
Sachin
Posted on April 25, 2023
The exec()
function in Python allows us to execute the block of Python code from a string. This built-in function in Python can come in handy when we need to run the dynamically generated Python code but it is recommended not to use it carelessly due to some security risks that come with it.
In this tutorial, we'll be going to learn
how to work with Python's exec() function
how to execute Python code using the exec() function with code examples
executing Python code from a string and python source file
using
globals
andlocals
parameters
Python's exec() function
Python's exec()
function allows us to execute any piece of Python code no matter how big or small that code is. This function helps us to execute the dynamically generated code.
Think of a Python interpreter that takes the piece of code, processes it internally, and executes it, the exec() function does the same. It is like an independent python interpreter.
The exec() is capable of executing simple Python programs as well as fully featured Python programs. It can execute function calls and definitions, class definitions and instantiations, imports, and much more.
Syntax
exec(object [ , globals [ , locals]])
object
- It must be a string or a code object. If it is a string, it is parsed as a suite of Python statements which is executed unless a syntax error occurs. If it is a code object then it will simply execute.globals
andlocals
- This allows us to provide dictionaries representing the global and local namespaces.
Return value
The return value of the exec() function is None. It may be because every piece of code doesn't have the final result.
Initial glance
Here's an initial look at the working of the exec() function.
obj = ["apple", "cherry", "melon", "strawberry"]
code = "print([sliced[:4] for sliced in obj if 'a' not in sliced])"
exec(code)
.........
['cher', 'melo']
Another example of using the exec() function
# The code will continuously run and we can run our code as we do in
# Python interpreter
while True:
exec(input(">>> "))
>>> print("Welcome to GeekPython")
Welcome to GeekPython
>>> import numpy as np
>>> print(np.random.randint(16, size=(2, 4)))
[[11 13 3 13]
[ 7 6 15 5]]
>>> x = ["apple", "banana", "cherry", "mango"]
>>> print([fruit for fruit in x if "a" not in fruit])
['cherry']
It behaves exactly like a Python interpreter, it takes our code, processes it internally, executes it, and returns the correct results.
We are running an infinite loop and inside it, we are taking the input from the user in the command line, and to execute the input we wrapped it with the exec() function.
Executing code from a string input
We can use the exec() function to execute the code which is in a string format. There are multiple ways that we can use to build the string-based input:
Using one-liners code
Using new line characters
Using triple-quoted strings with proper formatting
Using one-liner string-based input
Single-line code or one-liner in Python contains code written in a single line that is capable of doing multiple tasks at once.
If we write a single-line Python code it would be like
obj = ["apple", "cherry", "melon", "strawberry"]
print([sliced[:4] for sliced in obj if 'a' not in sliced])
Output
['cher', 'melo']
But if we run the above code using the exec(), the code would be
obj = ["apple", "cherry", "melon", "strawberry"]
exec("print([sliced[:4] for sliced in obj if 'a' not in sliced])")
#-----------------------------OR--------------------------------
exec("code = [sliced[:4] for sliced in obj if 'a' not in sliced]")
The other code we wrote above will return nothing if we execute it, instead, the output will be stored in the code
variable for later access.
Executing multiple lines of code separated by new line characters
We can combine multiple statements in a single-line string using the new line characters \n
.
exec("square = int(input('Enter the number: '))\nprint(f'The square of {square}:', square**2)")
Output
Enter the number: 30
The square of 30: 900
A new line character (\n
) is defined to make the exec() function understand our single-line string-based code as a multiline set of Python statements.
Using triple-quoted string
We often use triple quotes for commenting or documenting our code in Python. But here, we'll use it to generate string-based input that looks and works exactly like a normal Python code.
The code that we write within triple quotes must be properly indented and formatted like we do when we write normal Python code. Look at the example below to understand it more clearly.
sample_code = """
integers = [4, 7, 2, 9, 44]
def square(num):
return num ** 2
def odd_num(num):
return num % 2 == 1
square_if_even = [square(number) for number in integers if number % 2 == 0]
odd_number = [number for number in integers if odd_num(number)]
print("Original values:", integers)
print("Square of even number:", square_if_even)
print("Odd number:", odd_number)
"""
exec(sample_code)
Output
Original values: [4, 7, 2, 9, 44]
Square of even number: [16, 4, 1936]
Odd number: [7, 9]
The above code is like a normal Python code with proper indentation and formatting but the only difference is that it is wrapped within triple quotes making it string-based input stored in the sample_code
variable which was then executed using the exec() function.
Executing code from a Python file
We can use the exec() function to execute the code from Python(.py
) source file by reading the content of the file using the open() function.
If we look at the example, where we have a sample.py
file containing the following code:
# sample.py
def anime(name):
print(f"Favourite anime: {name}")
anime("One Piece")
list_of_anime = input("Enter your favourite anime: ").split(",")
print("Your Favourite anime:", list_of_anime)
Here the code simply takes the name of the anime and prints it, while the next block of code takes the input of your favourite anime separated by commas, and prints the desired output.
Executing the Python source file using the exec function
with open("sample.py", mode="r") as sample:
file = sample.read()
exec(file)
Output
Favourite anime: One Piece
Enter your favourite anime: One Piece, Naruto, Demon Slayer, Jujutsu kaisen
Your Favourite anime: ['One Piece', ' Naruto', ' Demon Slayer', ' Jujutsu kaisen']
We've used the open()
function using the with
statement to open the .py
file as a regular text file and then used the .read()
on the file object to read the content of the file as a string into the file
variable which is passed in the exec to execute the code.
Using globals
and locals
params
These parameters are optional. We can use globals
and locals
parameters to restrict the use of various functions, methods, and variables that might not be needed.
Since these parameters are optional and if we omit them, then the exec() function will execute the input code in the current scope. Look at the following example to understand it better.
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code)
Output
Hello
The above code ran successfully and printed the output combining both global variables. It happened so because the globals
and locals
parameters were not specified and hence the exec() function runs the code input in the current scope. In this case, the current scope is global.
Here's an example to show accessing the value of the variable of the code input in the current scope.
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
print(out)
Traceback (most recent call last):
.....
NameError: name 'out' is not defined. Did you mean: 'oct'?
In the above code, we are trying to access the value of the out
variable before calling the exec(), and we got an error.
But we can access the value of the out
variable after calling the exec() because after calling the exec(), the variable defined in the code input will be available in the current scope.
exec(code)
print(out)
Output
Hello
Hello
Using globals
and locals
parameters
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code, {"str1": str1})
Output
Traceback (most recent call last):
....
NameError: name 'str2' is not defined. Did you mean: 'str1'?
The code returns an error because we didn't define the key holding the str2
in the dictionary, so the exec() doesn't have access to it.
code = """
out = str1 + str2
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
exec(code, {"str1": str1, "str2": str2})
print(out)
Output
Hello
Traceback (most recent call last):
....
NameError: name 'out' is not defined. Did you mean: 'oct'?
Now the exec() has access to both of the global variables and the code returns the output without error but this time we didn't have access to the out
after the call to exec() because we're using the custom dictionary to provide an execution scope to the exec().
Here's an example of using locals
and globals
together
code = """
out = str1 + str2 + " " + x
print(out)
"""
# global variables
str1 = "Hel"
str2 = "lo"
def local():
# local variable
x = "there"
exec(code, {"str1": str1, "str2": str2}, {"x": x})
local()
Output
Hello there
We've called the exec() in the above code inside the local
function. We have global variables in the global scope and a local variable in the local scope(function level). The variables str1
and str2
are specified in the globals
parameter and the variable x
is specified in the locals
parameter.
Blocking unnecessary methods and variables
We can control whether to restrict or use any variable or methods in our code input using the globals
and locals
params. Here we'll restrict some of the functions in Python's datetime
module.
from datetime import *
code = """
curr_time = datetime.now()
print(curr_time)
"""
exec(code, {})
Output
Traceback (most recent call last):
....
NameError: name 'datetime' is not defined
We have restricted the use of the datetime
method from the datetime
module and therefore, we got an error.
Using necessary methods and variables
We can use only the necessary methods that we need to use with the exec().
from datetime import *
# Allowing only two methods
allowed_param = {'datetime': datetime, 'timedelta': timedelta}
exec("print(datetime.now())", allowed_param)
exec("print(datetime.now() + timedelta(days=2))", allowed_param)
# date() method is not allowed
exec("print(date(2022, 9, 4))", allowed_param)
Output
2022-10-15 18:40:44.290550
2022-10-17 18:40:44.290550
Traceback (most recent call last):
....
NameError: name 'date' is not defined
The error was raised because the date
method was not allowed. All the methods in the datetime
module were not allowed except for two methods datetime
and timedelta
.
Let's see what else we can do using globals
and locals
params.
from datetime import *
# Setting globals parameter to __builtins__
globals_param = {'__builtins__': __builtins__}
# Setting locals parameter to take only print(), slice() and dir()
locals_param = {'print': print, 'dir': dir, 'slice': slice}
exec('print(slice(2))', globals_param, locals_param)
exec('print(dir())', globals_param, locals_param)
Output
slice(None, 2, None)
['dir', 'print', 'slice']
Only the slice()
method along with all the built-in methods can be executed inside the exec() function. Here you can notice that the slice()
method works fine even though it isn't from the datetime
module.
We can also restrict the use of __builtins__
by making its value None
.
from datetime import *
# Setting globals parameter to none
globals_param = {'__builtins__': None}
# Setting locals parameter to take only print(), slice(), sum() and dir()
locals_param = {'print': print, 'dir': dir, 'slice': slice, 'sum': sum}
# Allowed methods directory
exec('print(dir())', globals_param, locals_param)
exec('print(f"Sum of numbers: {sum([4, 6, 7])}")', globals_param, locals_param)
Output
['dir', 'print', 'slice', 'sum']
Sum of numbers: 17
We restricted the use of __builtins__
, so we can't use the built-in methods and only print, sum, slice, and dir methods can be executed inside the exec().
Conclusion
We've learned the use of the built-in Python exec() function that allows us to execute the code from a string-based input. It provides functionality to run dynamically generated Python code.
The topics we've learned
what is exec() function and working with it
executing Python code from a string-based input and Python source files
understanding the use of the
globals
andlocals
parameters
Additionally, we've coded some of the examples which helped us understand the exec() function better.
🏆Other article you might like if you like this article
✅Upload and display static and dynamic images using Flask.
✅NumPy and TensorFlow have a common function.
✅Create a dynamic contact form that will save a message in database and will send you a mail.
✅Build a Covid-19 EDA and Visualization Streamlit app.
✅Use asynchronous programming in Python using asyncio module.
That's all for now
Keep Coding✌✌
Posted on April 25, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.